Albireo Update #2 (persisting spells cross zone between group members, self pet, and more)

- /spawn details now includes the stat / base stats (STR/AGI/STA/INT/WIS) of an entity (NPC/PET/Player)
- SetInfoStructUInt and GetInfoStructUInt now support interaction_flag, when not set we will default to 12 which is looking at other players nearby.
- Fixed NPCs / Pets having spell bonuses, base stats updated (sta/str/agi/wis/int)
- Deprecated GiveImmediateQuestReward
- With spell persistence (groups, direct target and self pet) added new rule options for cross zone buffs:
	RULE_INIT(R_Spells, EnableCrossZoneGroupBuffs, "0"); // enables/disables allowing cross zone group buffs
	RULE_INIT(R_Spells, EnableCrossZoneTargetBuffs, "0"); // enables/disables allowing cross zone target buffs
This commit is contained in:
Image 2021-03-10 08:36:46 -05:00
parent 92bd67d314
commit 3d3ea08dde
18 changed files with 516 additions and 203 deletions

View file

@ -4098,6 +4098,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
details3 += "Speed: " + to_string(spawn->GetSpeed()) + "\n";
details3 += "BaseSpeed: " + to_string(spawn->GetBaseSpeed()) + "\n";
if(spawn->IsEntity())
{
Entity* ent = (Entity*)spawn;
details3 += "STR / STRBase: " + to_string(ent->GetInfoStruct()->get_str()) + " / " + to_string(ent->GetInfoStruct()->get_str_base()) + "\n";
details3 += "AGI / AGIBase: " + to_string(ent->GetInfoStruct()->get_agi()) + " / " + to_string(ent->GetInfoStruct()->get_agi_base()) + "\n";
details3 += "STA / STABase: " + to_string(ent->GetInfoStruct()->get_sta()) + " / " + to_string(ent->GetInfoStruct()->get_sta_base()) + "\n";
details3 += "INT / INTBase: " + to_string(ent->GetInfoStruct()->get_intel()) + " / " + to_string(ent->GetInfoStruct()->get_intel_base()) + "\n";
details3 += "WIS / WISBase: " + to_string(ent->GetInfoStruct()->get_wis()) + " / " + to_string(ent->GetInfoStruct()->get_wis_base()) + "\n";
}
string details4;
if (spawn->IsEntity()) {
details4 += "Facial Hair Type: " + to_string(((Entity*)spawn)->GetFacialHairType()) + "\n";

View file

@ -109,7 +109,7 @@ Entity::~Entity(){
DeleteSpellEffects();
}
void Entity::DeleteSpellEffects()
void Entity::DeleteSpellEffects(bool removeClient)
{
map<LuaSpell*,bool> deletedPtrs;
@ -119,7 +119,7 @@ void Entity::DeleteSpellEffects()
{
if(deletedPtrs.find(GetInfoStruct()->spell_effects[i].spell) == deletedPtrs.end())
{
lua_interface->RemoveSpell(GetInfoStruct()->maintained_effects[i].spell, IsPlayer() ? false: true);
lua_interface->RemoveSpell(GetInfoStruct()->maintained_effects[i].spell, false, removeClient, "", removeClient);
if (IsPlayer())
GetInfoStruct()->maintained_effects[i].icon = 0xFFFF;
@ -132,11 +132,6 @@ void Entity::DeleteSpellEffects()
}
if(GetInfoStruct()->spell_effects[i].spell_id != 0xFFFFFFFF)
{
if(deletedPtrs.find(GetInfoStruct()->spell_effects[i].spell) == deletedPtrs.end())
{
lua_interface->RemoveSpell(GetInfoStruct()->spell_effects[i].spell, IsPlayer() ? false: true);
deletedPtrs[GetInfoStruct()->spell_effects[i].spell] = true;
}
GetInfoStruct()->spell_effects[i].spell_id = 0xFFFFFFFF;
GetInfoStruct()->spell_effects[i].spell = nullptr;
}
@ -289,6 +284,8 @@ void Entity::MapInfoStruct()
get_int8_funcs["no_interrupt"] = l::bind(&InfoStruct::get_no_interrupt, &info_struct);
get_int8_funcs["interaction_flag"] = l::bind(&InfoStruct::get_interaction_flag, &info_struct);
/** SETS **/
set_string_funcs["name"] = l::bind(&InfoStruct::set_name, &info_struct, l::_1);
@ -434,6 +431,8 @@ void Entity::MapInfoStruct()
set_int8_funcs["no_interrupt"] = l::bind(&InfoStruct::set_no_interrupt, &info_struct, l::_1);
set_int8_funcs["interaction_flag"] = l::bind(&InfoStruct::set_interaction_flag, &info_struct, l::_1);
}
bool Entity::HasMoved(bool include_heading){
@ -816,7 +815,7 @@ void Entity::AddMaintainedSpell(LuaSpell* luaspell){
}
}
void Entity::AddSpellEffect(LuaSpell* luaspell){
void Entity::AddSpellEffect(LuaSpell* luaspell, int32 override_expire_time){
if (!luaspell || !luaspell->caster)
return;
@ -827,7 +826,11 @@ void Entity::AddSpellEffect(LuaSpell* luaspell){
GetZone()->RemoveTargetFromSpell(old_effect->spell, this);
RemoveSpellEffect(old_effect->spell);
}
effect = GetFreeSpellEffectSlot();
LogWrite(SPELL__DEBUG, 0, "Spell", "%s AddSpellEffect %s (%u).", spell->GetName(), GetName(), GetID());
if(!effect)
effect = GetFreeSpellEffectSlot();
if(effect){
MSpellEffects.writelock(__FUNCTION__, __LINE__);
@ -837,6 +840,8 @@ void Entity::AddSpellEffect(LuaSpell* luaspell){
effect->total_time = spell->GetSpellDuration()/10;
if (spell->GetSpellData()->duration_until_cancel)
effect->expire_timestamp = 0xFFFFFFFF;
else if(override_expire_time)
effect->expire_timestamp = Timer::GetCurrentTime2() + override_expire_time;
else
effect->expire_timestamp = Timer::GetCurrentTime2() + (spell->GetSpellDuration()*100);
effect->icon = spell->GetSpellData()->icon;
@ -889,6 +894,8 @@ void Entity::RemoveSpellEffect(LuaSpell* spell) {
found = true;
}
if (found) {
LogWrite(SPELL__DEBUG, 0, "Spell", "%s RemoveSpellEffect %s (%u).", spell->spell->GetName(), GetName(), GetID());
GetZone()->GetSpellProcess()->RemoveTargetFromSpell(spell, this);
memset(&GetInfoStruct()->spell_effects[44], 0, sizeof(SpellEffects));
GetInfoStruct()->spell_effects[44].spell_id = 0xFFFFFFFF;
changed = true;
@ -1147,11 +1154,11 @@ void Entity::CalculateBonuses(){
stats.clear();
ItemStatsValues* values = equipment_list.CalculateEquipmentBonuses(this);
CalculateSpellBonuses(values);
info->add_sta(values->sta);
info->add_str(values->str);
info->add_agi(values->agi);
info->add_wis(values->wis);
info->add_intel(values->int_);
info->add_sta((float)values->sta);
info->add_str((float)values->str);
info->add_agi((float)values->agi);
info->add_wis((float)values->wis);
info->add_intel((float)values->int_);
info->add_disease(values->vs_disease);
info->add_divine(values->vs_divine);
@ -1433,6 +1440,8 @@ void Entity::AddSpellBonus(LuaSpell* spell, int16 type, float value, int64 class
bonus->tier = spell ? spell->spell->GetSpellTier() : 0;
bonus_list.Add(bonus);
if(IsNPC())
CalculateBonuses();
}
BonusValues* Entity::GetSpellBonus(int32 spell_id) {
@ -1464,6 +1473,9 @@ void Entity::RemoveSpellBonus(const LuaSpell* spell){
if(itr.value->luaspell == spell)
bonus_list.Remove(itr.value, true);
}
if(IsNPC())
CalculateBonuses();
}
void Entity::CalculateSpellBonuses(ItemStatsValues* stats){
@ -1715,7 +1727,8 @@ void Entity::DismissPet(Entity* pet, bool from_death, bool spawnListLocked) {
}
if (pet->GetPetType() == PET_TYPE_CHARMED) {
PetOwner->SetCharmedPet(0);
if(PetOwner)
PetOwner->SetCharmedPet(0);
if (!from_death) {
// set the pet flag to false, owner to 0, and give the mob its old brain back
@ -1727,15 +1740,15 @@ void Entity::DismissPet(Entity* pet, bool from_death, bool spawnListLocked) {
pet->SetDismissing(false);
}
}
else if (pet->GetPetType() == PET_TYPE_COMBAT)
else if (PetOwner && pet->GetPetType() == PET_TYPE_COMBAT)
PetOwner->SetCombatPet(0);
else if (pet->GetPetType() == PET_TYPE_DEITY)
else if (PetOwner && pet->GetPetType() == PET_TYPE_DEITY)
PetOwner->SetDeityPet(0);
else if (pet->GetPetType() == PET_TYPE_COSMETIC)
else if (PetOwner && pet->GetPetType() == PET_TYPE_COSMETIC)
PetOwner->SetCosmeticPet(0);
// if owner is player and no combat pets left reset the pet info
if (PetOwner->IsPlayer()) {
if (PetOwner && PetOwner->IsPlayer()) {
if (!PetOwner->GetPet() && !PetOwner->GetCharmedPet())
((Player*)PetOwner)->ResetPetInfo();
}

View file

@ -256,6 +256,8 @@ struct InfoStruct{
flying_type_ = 0;
no_interrupt_ = 0;
interaction_flag_ = 0;
}
@ -407,6 +409,8 @@ struct InfoStruct{
flying_type_ = oldStruct->get_flying_type();
no_interrupt_ = oldStruct->get_no_interrupt();
interaction_flag_ = oldStruct->get_interaction_flag();
}
//mutable std::shared_mutex mutex_;
@ -569,6 +573,8 @@ struct InfoStruct{
int8 get_no_interrupt() { std::lock_guard<std::mutex> lk(classMutex); return no_interrupt_; }
int8 get_interaction_flag() { std::lock_guard<std::mutex> lk(classMutex); return interaction_flag_; }
void set_name(std::string value) { std::lock_guard<std::mutex> lk(classMutex); name_ = value; }
void set_deity(std::string value) { std::lock_guard<std::mutex> lk(classMutex); deity_ = value; }
@ -814,6 +820,8 @@ struct InfoStruct{
void set_no_interrupt(int8 value) { std::lock_guard<std::mutex> lk(classMutex); no_interrupt_ = value; }
void set_interaction_flag(int8 value) { std::lock_guard<std::mutex> lk(classMutex); interaction_flag_ = value; }
void ResetEffects(Spawn* spawn)
{
for(int i=0;i<45;i++){
@ -981,6 +989,8 @@ private:
int8 flying_type_;
int8 no_interrupt_;
int8 interaction_flag_;
// when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock
std::mutex classMutex;
};
@ -1086,11 +1096,11 @@ public:
Entity();
virtual ~Entity();
void DeleteSpellEffects();
void DeleteSpellEffects(bool removeClient = false);
void MapInfoStruct();
virtual float GetDodgeChance();
virtual void AddMaintainedSpell(LuaSpell* spell);
virtual void AddSpellEffect(LuaSpell* spell);
virtual void AddSpellEffect(LuaSpell* spell, int32 override_expire_time = 0);
virtual void RemoveMaintainedSpell(LuaSpell* spell);
virtual void RemoveSpellEffect(LuaSpell* spell);
virtual bool HasActiveMaintainedSpell(Spell* spell, Spawn* target);

View file

@ -349,6 +349,7 @@ int EQ2Emu_lua_SetVisualFlag(lua_State* state) {
Spawn* spawn = lua_interface->GetSpawn(state);
if (spawn) {
spawn->vis_changed = true;
spawn->GetZone()->AddChangedSpawn(spawn);
}
return 0;
}
@ -361,6 +362,7 @@ int EQ2Emu_lua_SetInfoFlag(lua_State* state) {
Spawn* spawn = lua_interface->GetSpawn(state);
if (spawn) {
spawn->info_changed = true;
spawn->GetZone()->AddChangedSpawn(spawn);
}
return 0;
}
@ -3719,6 +3721,7 @@ int EQ2Emu_lua_AddQuestStepKill(lua_State* state) {
Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -3753,6 +3756,7 @@ int EQ2Emu_lua_AddQuestStepChat(lua_State* state) {
if(client)
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -3787,6 +3791,7 @@ int EQ2Emu_lua_AddQuestStepObtainItem(lua_State* state) {
Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -3893,6 +3898,7 @@ int EQ2Emu_lua_AddQuestStepSpell(lua_State* state) {
Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -3927,6 +3933,7 @@ int EQ2Emu_lua_AddQuestStepCraft(lua_State* state) {
Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -3961,6 +3968,7 @@ int EQ2Emu_lua_AddQuestStepHarvest(lua_State* state) {
Client* client = quest->GetPlayer()->GetZone()->GetClientBySpawn(quest->GetPlayer());
quest->GetPlayer()->GetZone()->SendQuestUpdates(client);
}
safe_delete(ids);
}
return 0;
}
@ -4066,61 +4074,6 @@ int EQ2Emu_lua_UpdateQuestZone(lua_State* state) {
return 0;
}
int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state) {
if (!lua_interface)
return 0;
Quest* quest = lua_interface->GetQuest(state);
Spawn* playerSpawn = lua_interface->GetSpawn(state, 2);
int32 coin = lua_interface->GetInt32Value(state, 3);
int32 status_points = lua_interface->GetInt32Value(state, 4);
string rewards_str = lua_interface->GetStringValue(state, 5);
string select_rewards_str = lua_interface->GetStringValue(state, 6);
string factions_map_str = lua_interface->GetStringValue(state, 7);
string text = lua_interface->GetStringValue(state, 8);
if (playerSpawn && playerSpawn->IsPlayer()) {
Player* player = (Player*)playerSpawn;
Client* client = player->GetZone()->GetClientBySpawn(player);
if (client) {
vector<Item*> reward_items;
vector<Item*> selectable_reward_items;
if (rewards_str.length() > 0) {
map<unsigned int, unsigned short> rewards = ParseIntMap(rewards_str);
map<unsigned int, unsigned short>::iterator itr;
for (itr = rewards.begin(); itr != rewards.end(); itr++) {
if (itr->first > 0) {
Item* item = new Item(master_item_list.GetItem(itr->first));
if (item) {
if (itr->second > 0)
item->details.count = itr->second;
reward_items.push_back(item);
}
}
}
}
if (select_rewards_str.length() > 0) {
map<unsigned int, unsigned short> rewards = ParseIntMap(select_rewards_str);
map<unsigned int, unsigned short>::iterator itr;
for (itr = rewards.begin(); itr != rewards.end(); itr++) {
if (itr->first > 0) {
Item* item = new Item(master_item_list.GetItem(itr->first));
if (item) {
if (itr->second > 0)
item->stack_count = itr->second;
selectable_reward_items.push_back(item);
}
}
}
}
map<unsigned int, signed int> faction_rewards = ParseSInt32Map(factions_map_str);
const char* reward_type = "Quest Reward!";
if (!quest)
reward_type = "Reward!";
client->DisplayQuestRewards(quest, coin, &reward_items, &selectable_reward_items, &faction_rewards, reward_type, status_points, text.c_str());
}
}
return 0;
}
int EQ2Emu_lua_GiveQuestReward(lua_State* state) {
if (!lua_interface)
return 0;
@ -5878,7 +5831,7 @@ int EQ2Emu_lua_AddToWard(lua_State* state) {
if (zone->GetSpawnByID(spell->targets.at(0))->IsEntity()) {
Entity* target = (Entity*)zone->GetSpawnByID(spell->targets.at(0));
ward = target->GetWard(spell->spell->GetSpellID());
if (ward) {
if (target && ward) {
ward->DamageLeft += amount;
if (ward->DamageLeft > ward->BaseDamage)
ward->DamageLeft = ward->BaseDamage;
@ -8264,7 +8217,7 @@ int EQ2Emu_lua_SetVision(lua_State* state) {
ZoneServer* zone = spell->caster->GetZone();
for (int8 i = 0; i < spell->targets.size(); i++) {
Spawn* target = zone->GetSpawnByID(spell->targets.at(i));
if (target->IsEntity()) {
if (target && target->IsEntity()) {
((Entity*)target)->GetInfoStruct()->set_vision(vision);
if (target->IsPlayer())
((Player*)target)->SetCharSheetChanged(true);
@ -8338,7 +8291,7 @@ int EQ2Emu_lua_BreatheUnderwater(lua_State* state) {
ZoneServer* zone = spell->caster->GetZone();
for (int8 i = 0; i < spell->targets.size(); i++) {
Spawn* target = zone->GetSpawnByID(spell->targets.at(i));
if (target->IsEntity()) {
if (target && target->IsEntity()) {
((Entity*)target)->GetInfoStruct()->set_breathe_underwater(breatheUnderwater);
if (target->IsPlayer())
((Player*)target)->SetCharSheetChanged(true);
@ -10959,7 +10912,7 @@ int EQ2Emu_lua_SetAlignment(lua_State* state) {
ZoneServer* zone = spell->caster->GetZone();
for (int8 i = 0; i < spell->targets.size(); i++) {
Spawn* target = zone->GetSpawnByID(spell->targets.at(i));
if (target->IsEntity()) {
if (target && target->IsEntity()) {
((Entity*)target)->GetInfoStruct()->set_alignment((sint8)alignment);
if (target->IsPlayer())
((Player*)target)->SetCharSheetChanged(true);

View file

@ -273,7 +273,6 @@ int EQ2Emu_lua_AddQuestStepCompleteAction(lua_State* state);
int EQ2Emu_lua_AddQuestStepProgressAction(lua_State* state);
int EQ2Emu_lua_SetQuestCompleteAction(lua_State* state);
int EQ2Emu_lua_GiveQuestReward(lua_State* state);
int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state);
int EQ2Emu_lua_UpdateQuestTaskGroupDescription(lua_State* state);
int EQ2Emu_lua_UpdateQuestStepDescription(lua_State* state);
int EQ2Emu_lua_UpdateQuestDescription(lua_State* state);

View file

@ -22,6 +22,7 @@
#include "WorldDatabase.h"
#include "SpellProcess.h"
#include "../common/Log.h"
#include "World.h"
#ifndef WIN32
#include <stdio.h>
@ -34,6 +35,7 @@
#endif
extern WorldDatabase database;
extern ZoneList zone_list;
LuaInterface::LuaInterface() {
@ -277,6 +279,8 @@ bool LuaInterface::LoadLuaSpell(const char* name) {
spell->damage_remaining = 0;
spell->effect_bitmask = 0;
spell->restored = false;
spell->initial_caster_char_id = 0;
spell->initial_target_char_id = 0;
MSpells.lock();
if (spells.count(lua_script) > 0) {
@ -751,7 +755,22 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
continue;
((Entity*)target)->RemoveProc(0, spell);
((Entity*)target)->RemoveSpellEffect(spell);
((Entity*)target)->RemoveSpellBonus(spell);
}
multimap<int32,int8>::iterator entries;
for(entries = spell->char_id_targets.begin(); entries != spell->char_id_targets.end(); entries++)
{
Client* tmpClient = zone_list.GetClientByCharID(entries->first);
if(tmpClient && tmpClient->GetPlayer())
{
tmpClient->GetPlayer()->RemoveProc(0, spell);
tmpClient->GetPlayer()->RemoveSpellEffect(spell);
tmpClient->GetPlayer()->RemoveSpellBonus(spell);
}
}
spell->char_id_targets.clear(); // TODO: reach out to those clients in different
spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
// we need to make sure all memory is purged for a copied spell, its only used once
@ -1037,7 +1056,6 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "AddQuestStepProgressAction", EQ2Emu_lua_AddQuestStepProgressAction);
lua_register(state, "SetQuestCompleteAction", EQ2Emu_lua_SetQuestCompleteAction);
lua_register(state, "GiveQuestReward", EQ2Emu_lua_GiveQuestReward);
lua_register(state, "GiveImmediateQuestReward", EQ2Emu_lua_GiveImmediateQuestReward);
lua_register(state, "UpdateQuestStepDescription", EQ2Emu_lua_UpdateQuestStepDescription);
lua_register(state, "UpdateQuestDescription", EQ2Emu_lua_UpdateQuestDescription);
lua_register(state, "UpdateQuestZone", EQ2Emu_lua_UpdateQuestZone);
@ -1783,6 +1801,8 @@ LuaSpell* LuaInterface::GetSpell(const char* name) {
new_spell->initial_target = 0;
new_spell->spell = 0;
new_spell->restored = false;
new_spell->initial_caster_char_id = 0;
new_spell->initial_target_char_id = 0;
return new_spell;
}
else{

View file

@ -72,8 +72,11 @@ struct OptionWindowOption {
struct LuaSpell{
Entity* caster;
int32 initial_caster_char_id;
int32 initial_target;
int32 initial_target_char_id;
vector<int32> targets;
multimap<int32, int8> char_id_targets;
Spell* spell;
lua_State* state;
string file_name;

View file

@ -1065,8 +1065,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
packet->setDataByName("mitigation_max2", info_struct->get_max_mitigation());
packet->setDataByName("mitigation_base2", info_struct->get_mitigation_base());
if (version < 1096)
packet->setDataByName("weight", info_struct->get_weight());
packet->setDataByName("weight", info_struct->get_weight());
packet->setDataByName("max_weight", info_struct->get_max_weight());
packet->setDataByName("unknownint32a", 777777);
packet->setDataByName("unknownint32b", 666666);
@ -3126,7 +3125,8 @@ void Player::AddMaintainedSpell(LuaSpell* luaspell){
effect->target_type = target_type;
effect->spell = luaspell;
luaspell->slot_pos = effect->slot_pos;
if(!luaspell->slot_pos)
luaspell->slot_pos = effect->slot_pos;
effect->spell_id = spell->GetSpellData()->id;
LogWrite(PLAYER__DEBUG, 5, "Player", "AddMaintainedSpell Spell ID: %u, req concentration: %u", spell->GetSpellData()->id, spell->GetSpellData()->req_concentration);
effect->icon = spell->GetSpellData()->icon;
@ -3142,7 +3142,7 @@ void Player::AddMaintainedSpell(LuaSpell* luaspell){
charsheet_changed = true;
}
}
void Player::AddSpellEffect(LuaSpell* luaspell){
void Player::AddSpellEffect(LuaSpell* luaspell, int32 override_expire_time){
if(!luaspell || !luaspell->caster)
return;
@ -3163,6 +3163,8 @@ void Player::AddSpellEffect(LuaSpell* luaspell){
effect->total_time = spell->GetSpellDuration()/10;
if (spell->GetSpellData()->duration_until_cancel)
effect->expire_timestamp = 0xFFFFFFFF;
else if(override_expire_time)
effect->expire_timestamp = Timer::GetCurrentTime2() + override_expire_time;
else
effect->expire_timestamp = Timer::GetCurrentTime2() + (spell->GetSpellDuration()*100);
effect->icon = spell->GetSpellData()->icon;
@ -6374,17 +6376,18 @@ void Player::SaveSpellEffects()
for(int i = 0; i < 45; i++) {
if(info->spell_effects[i].spell_id != 0xFFFFFFFF)
{
Spawn* spawn = GetZone()->GetSpawnByID(info->spell_effects[i].spell->initial_target);
Spawn* spawn = nullptr;
int32 target_char_id = 0;
if(spawn && spawn->IsPlayer())
if(info->spell_effects[i].spell->initial_target_char_id != 0)
target_char_id = info->spell_effects[i].spell->initial_target_char_id;
else if((spawn = GetZone()->GetSpawnByID(info->spell_effects[i].spell->initial_target)) != nullptr && spawn->IsPlayer())
target_char_id = ((Player*)spawn)->GetCharacterID();
int32 timestamp = 0xFFFFFFFF;
if(!info->spell_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
timestamp = info->spell_effects[i].expire_timestamp - Timer::GetCurrentTime2();
int32 caster_char_id = (info->spell_effects[i].caster && info->spell_effects[i].caster->IsPlayer()) ? ((Player*)info->spell_effects[i].caster)->GetCharacterID() : 0;
int32 caster_char_id = info->spell_effects[i].spell->initial_caster_char_id;
if(caster_char_id == 0)
continue;
@ -6398,34 +6401,20 @@ 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->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).c_str());
/* info->spell_effects[i].spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
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;
for (int8 t = 0; t < info->spell_effects[i].spell->targets.size(); t++) {
Spawn* spawn = GetZone()->GetSpawnByID(info->spell_effects[i].spell->targets.at(t));
if(spawn && spawn->IsPlayer())
{
if(!firstTarget)
insertTargets.append(", ");
insertTargets.append("(" + caster_char_id + ", " + target_char_id + ", " + "0" + ", " std::to_string(DB_TYPE_SPELLEFFECTS) + ", " + info->spell_effects[i].spell_id + ", " + i + ", " + info->spell_effects[i].spell->slot_pos + ")");
firstTarget = false;
}
}
info->spell_effects[i].spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
if(!firstTarget)
{
Query targetSave;
targetSave.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, insertTarget.c_str());
}*/
}
if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell_id != 0xFFFFFFFF){
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->initial_target);
int32 target_char_id = 0;
if(spawn && spawn->IsPlayer())
if(info->maintained_effects[i].spell->initial_target_char_id != 0)
target_char_id = info->maintained_effects[i].spell->initial_target_char_id;
else if(!info->maintained_effects[i].spell->initial_target)
target_char_id = GetCharacterID();
else if(spawn && spawn->IsPlayer())
target_char_id = ((Player*)spawn)->GetCharacterID();
else if (spawn && spawn->IsPet() && ((Entity*)spawn)->GetOwner() == (Entity*)this)
target_char_id = 0xFFFFFFFF;
int32 caster_char_id = (info->maintained_effects[i].spell->caster && info->maintained_effects[i].spell->caster->IsPlayer()) ? ((Player*)info->maintained_effects[i].spell->caster)->GetCharacterID() : 0;
@ -6446,11 +6435,19 @@ void Player::SaveSpellEffects()
bool firstTarget = true;
map<int32, bool> targetsInserted;
for (int8 t = 0; t < info->maintained_effects[i].spell->targets.size(); t++) {
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->targets.at(t));
if(spawn && spawn->IsPlayer())
int32 spawn_id = info->maintained_effects[i].spell->targets.at(t);
Spawn* spawn = GetZone()->GetSpawnByID(spawn_id);
LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target %u to identify for spell %s", GetName(), spawn_id, info->maintained_effects[i].spell->spell->GetName());
if(spawn && (spawn->IsPlayer() || spawn->IsPet()))
{
int32 tmpCharID = ((Player*)spawn)->GetCharacterID();
int32 tmpCharID = 0;
if(spawn->IsPlayer())
tmpCharID = ((Player*)spawn)->GetCharacterID();
else if (spawn->IsPet() && ((Entity*)spawn)->GetOwner() == (Entity*)this)
{
tmpCharID = 0xFFFFFFFF;
}
if(targetsInserted.find(tmpCharID) != targetsInserted.end())
continue;
@ -6459,12 +6456,27 @@ void Player::SaveSpellEffects()
targetsInserted.insert(make_pair(tmpCharID, true));
LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target %s (%u) added to spell %s", GetName(), spawn ? spawn->GetName() : "NA", tmpCharID, info->maintained_effects[i].spell->spell->GetName());
insertTargets.append("(" + std::to_string(caster_char_id) + ", " + std::to_string(tmpCharID) + ", " + "0" + ", " +
std::to_string(DB_TYPE_MAINTAINEDEFFECTS) + ", " + std::to_string(info->maintained_effects[i].spell_id) + ", " + std::to_string(i) +
", " + std::to_string(info->maintained_effects[i].spell->slot_pos) + ")");
", " + std::to_string(info->maintained_effects[i].slot_pos) + ")");
firstTarget = false;
}
}
multimap<int32,int8>::iterator entries;
for(entries = info->maintained_effects[i].spell->char_id_targets.begin(); entries != info->maintained_effects[i].spell->char_id_targets.end(); entries++)
{
if(!firstTarget)
insertTargets.append(", ");
LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target %s (%u) added to spell %s", GetName(), spawn ? spawn->GetName() : "NA", entries->first, info->maintained_effects[i].spell->spell->GetName());
insertTargets.append("(" + std::to_string(caster_char_id) + ", " + std::to_string(entries->first) + ", " + "0" + ", " +
std::to_string(DB_TYPE_MAINTAINEDEFFECTS) + ", " + std::to_string(info->maintained_effects[i].spell_id) + ", " + std::to_string(i) +
", " + std::to_string(info->maintained_effects[i].slot_pos) + ")");
firstTarget = false;
}
info->maintained_effects[i].spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
if(!firstTarget)
{

View file

@ -431,7 +431,7 @@ public:
tutorial_step = val;
}
void AddMaintainedSpell(LuaSpell* spell);
void AddSpellEffect(LuaSpell* spell);
void AddSpellEffect(LuaSpell* spell, int32 override_expire_time = 0);
void RemoveMaintainedSpell(LuaSpell* spell);
void RemoveSpellEffect(LuaSpell* spell);
bool HasActiveMaintainedSpell(Spell* spell, Spawn* target);

View file

@ -24,8 +24,11 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
#include "Spells.h"
#include "LuaInterface.h"
#include "Bots/Bot.h"
#include "SpellProcess.h"
#include "Rules/Rules.h"
extern ZoneList zone_list;
extern RuleManager rule_manager;
/******************************************************** PlayerGroup ********************************************************/
@ -564,6 +567,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
PlayerGroup* group = nullptr;
Player* caster = nullptr;
vector<int32> new_target_list;
vector<int32> char_list;
Client* client = nullptr;
bool has_effect = false;
vector<BonusValues*>* sb_list = nullptr;
@ -614,6 +618,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
(spell->GetSpellData()->target_type == SPELL_TARGET_GROUP_AE || spell->GetSpellData()->target_type == SPELL_TARGET_RAID_AE)) {
luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
luaspell->char_id_targets.clear();
for (target_itr = group->GetMembers()->begin(); target_itr != group->GetMembers()->end(); target_itr++) {
group_member = (*target_itr)->member;
@ -632,7 +637,8 @@ void PlayerGroupManager::UpdateGroupBuffs() {
has_effect = true;
// Check if player is within range of the caster
if (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius) {
if (!rule_manager.GetGlobalRule(R_Spells, EnableCrossZoneGroupBuffs)->GetInt8() &&
(group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius)) {
if (has_effect) {
group_member->RemoveSpellEffect(luaspell);
group_member->RemoveSpellBonus(luaspell);
@ -659,13 +665,20 @@ void PlayerGroupManager::UpdateGroupBuffs() {
continue;
}
//this group member is a target of the spell
new_target_list.push_back(group_member->GetID());
if(group_member->GetZone() != caster->GetZone())
{
if(group_member->IsPlayer())
luaspell->char_id_targets.insert(make_pair(((Player*)group_member)->GetCharacterID(), 0));
}
else
{
//this group member is a target of the spell
new_target_list.push_back(group_member->GetID());
}
if (has_effect)
continue;
pet = 0;
charmed_pet = 0;
@ -674,11 +687,11 @@ void PlayerGroupManager::UpdateGroupBuffs() {
charmed_pet = group_member->GetCharmedPet();
}
group_member->AddSpellEffect(luaspell);
group_member->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0);
if (pet)
pet->AddSpellEffect(luaspell);
pet->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0);
if (charmed_pet)
charmed_pet->AddSpellEffect(luaspell);
charmed_pet->AddSpellEffect(luaspell, luaspell->timer.GetRemainingTime() != 0 ? luaspell->timer.GetRemainingTime() : 0);
if (pet)
new_target_list.push_back(pet->GetID());
@ -714,8 +727,8 @@ void PlayerGroupManager::UpdateGroupBuffs() {
}
}
new_target_list.push_back(caster->GetID());
luaspell->targets.swap(new_target_list);
SpellProcess::AddSelfAndPet(luaspell, caster);
luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
new_target_list.clear();
}

View file

@ -328,6 +328,8 @@ void RuleManager::Init()
RULE_INIT(R_Spells, DefaultFizzleChance, "10.0"); // default percentage x / 100, eg 10% is 10.0
RULE_INIT(R_Spells, FizzleMaxSkill, "1.2"); // 1.0 is 100%, 1.2 is 120%, so you get 120% your max skill against a spell, no fizzle
RULE_INIT(R_Spells, FizzleDefaultSkill, ".2"); // offset against MaxSkill to average out to 100%, default of .2f so we don't go over the threshold if no skill
RULE_INIT(R_Spells, EnableCrossZoneGroupBuffs, "0"); // enables/disables allowing cross zone group buffs
RULE_INIT(R_Spells, EnableCrossZoneTargetBuffs, "0"); // enables/disables allowing cross zone target buffs
RULE_INIT(R_Expansion, GlobalExpansionFlag, "0");
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");

View file

@ -177,6 +177,8 @@ enum RuleType {
DefaultFizzleChance,
FizzleMaxSkill,
FizzleDefaultSkill,
EnableCrossZoneGroupBuffs,
EnableCrossZoneTargetBuffs,
/* ZONE TIMERS */
RegenTimer,

View file

@ -2291,8 +2291,13 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
packet->setDataByName("difficulty", appearance.encounter_level); //6);
packet->setDataByName("unknown6", 1);
packet->setDataByName("heroic_flag", appearance.heroic_flag);
if (!IsObject() && !IsGroundSpawn() && !IsWidget() && !IsSign())
packet->setDataByName("interaction_flag", 12); //this makes NPCs head turn to look at you
if (IsNPC() && !IsPet())
{
if(((Entity*)this)->GetInfoStruct()->get_interaction_flag())
packet->setDataByName("interaction_flag", ((Entity*)this)->GetInfoStruct()->get_interaction_flag()); //this makes NPCs head turn to look at you (12)
else
packet->setDataByName("interaction_flag", 12); //turn head since no other value is set
}
packet->setDataByName("class", appearance.adventure_class);

View file

@ -392,7 +392,7 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi
spell->caster->RemoveProc(0, spell);
spell->caster->RemoveMaintainedSpell(spell);
CheckRemoveTargetFromSpell(spell, false);
CheckRemoveTargetFromSpell(spell, removing_all_spells, removing_all_spells);
ZoneServer* zone = spell->caster->GetZone();
spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
for (int32 i = 0; i < spell->targets.size(); i++) {
@ -514,8 +514,10 @@ bool SpellProcess::CastInstant(Spell* spell, Entity* caster, Entity* target, boo
}
lua_spell->caster = caster;
lua_spell->initial_caster_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
lua_spell->spell = spell;
lua_spell->initial_target = target->GetID();
lua_spell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0;
GetSpellTargets(lua_spell);
if (!lua_spell->spell->IsCopiedSpell())
@ -903,10 +905,12 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
int8 target_type = spell->GetSpellData()->target_type;
lua_spell->caster = caster;
lua_spell->initial_caster_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
lua_spell->spell = spell;
int32 target_id = target->GetID();
lua_spell->initial_target = target_id;
lua_spell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0;
if (!harvest_spell)
GetSpellTargets(lua_spell);
@ -1036,17 +1040,20 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
if(client)
UnlockSpell(client, conflictSpell->spell);
}
DeleteSpell(lua_spell);
return;
}
else if(lua_spell->spell->GetSpellData()->spell_type == SPELL_TYPE_DEBUFF)
{
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
DeleteSpell(lua_spell);
return;
}
}
else
{
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
DeleteSpell(lua_spell);
return;
}
}
@ -1943,6 +1950,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
secondary_target = target;
implied = true;
luaspell->initial_target = target->GetID();
luaspell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0;
luaspell->targets.push_back(target->GetID());
GetPlayerGroupTargets((Player*)target, caster, luaspell);
@ -1951,6 +1959,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
implied = true;
luaspell->initial_target = secondary_target->GetID();
luaspell->initial_target_char_id = (secondary_target && secondary_target->IsPlayer()) ? ((Player*)secondary_target)->GetCharacterID() : 0;
luaspell->targets.push_back(secondary_target->GetID());
GetPlayerGroupTargets((Player*)secondary_target, caster, luaspell);
}
@ -1964,6 +1973,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
if (!target->IsPet() || (target->IsPet() && ((NPC*)target)->GetOwner()->IsNPC())) {
target = caster;
luaspell->initial_target = caster->GetID();
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
}
}
}
@ -1971,6 +1981,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
if (target->IsPlayer() && ((Entity*)caster)->AttackAllowed((Entity*)target)) {
luaspell->initial_target = target->GetID();
luaspell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0;
luaspell->targets.push_back(target->GetID());
}
}
@ -1999,14 +2010,17 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
// add self
target = NULL;
luaspell->targets.push_back(caster->GetID());
luaspell->initial_target = 0;
AddSelfAndPet(luaspell, caster);
luaspell->initial_target = caster->GetID();
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
}
}
else // default self cast for group/raid AE
{
target = caster;
luaspell->initial_target = caster->GetID();
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
}
// spell target versus self cast
}
@ -2014,11 +2028,13 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
target = caster;
luaspell->initial_target = caster->GetID();
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
}
}
else if (target_type == SPELL_TARGET_SELF){
target = caster;
luaspell->initial_target = caster->GetID();
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
}
//if using implied target, target = the implied target
@ -2026,6 +2042,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
target = secondary_target;
luaspell->initial_target = secondary_target->GetID();
luaspell->initial_target_char_id = (secondary_target && secondary_target->IsPlayer()) ? ((Player*)secondary_target)->GetCharacterID() : 0;
}
luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
@ -2062,15 +2079,27 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
// get group member player info
Entity* group_member = (*itr)->member;
LogWrite(SPELL__DEBUG, 0, "Player", "%s is group member for spell %s", group_member->GetName(), luaspell->spell->GetName());
// if the group member is in the casters zone, and is alive
if (group_member->GetZone() == luaspell->caster->GetZone() && group_member->Alive()) {
luaspell->targets.push_back(group_member->GetID());
if (group_member->HasPet()) {
Entity* pet = group_member->GetPet();
if (!pet)
pet = group_member->GetCharmedPet();
if (pet)
luaspell->targets.push_back(pet->GetID());
if( group_member->Alive())
{
if(group_member->GetZone() != caster->GetZone())
{
if(group_member->IsPlayer())
luaspell->char_id_targets.insert(make_pair(((Player*)group_member)->GetCharacterID(), 0));
}
else if (group_member->GetZone() == luaspell->caster->GetZone()) {
luaspell->targets.push_back(group_member->GetID());
if (group_member->HasPet()) {
Entity* pet = group_member->GetPet();
if (!pet)
pet = group_member->GetCharmedPet();
if (pet)
luaspell->targets.push_back(pet->GetID());
LogWrite(SPELL__DEBUG, 0, "Player", "%s added a pet %s (%u) for spell %s", group_member->GetName(), pet ? pet->GetName() : "", pet ? pet->GetID() : 0, luaspell->spell->GetName());
}
}
}
}
@ -2080,7 +2109,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
else
luaspell->targets.push_back(caster->GetID()); // else caster is not in a group, thus alone
AddSelfAndPet(luaspell, caster); // else caster is not in a group, thus alone
}
else if (caster->IsNPC()) // caster is NOT a player
{
@ -2108,7 +2137,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
}
}
else
luaspell->targets.push_back(caster->GetID());
AddSelfAndPet(luaspell, caster);
safe_delete(group);
} // end is player
@ -2119,10 +2148,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
luaspell->targets.push_back(caster->GetID()); // if spell is SELF, return caster
else if (target_type == SPELL_TARGET_CASTER_PET && caster && caster->IsEntity() && ((Entity*)caster)->HasPet()) {
if (((Entity*)caster)->GetPet())
luaspell->targets.push_back(((Entity*)caster)->GetPet()->GetID());
if (((Entity*)caster)->GetCharmedPet())
luaspell->targets.push_back(((Entity*)caster)->GetCharmedPet()->GetID());
AddSelfAndPet(luaspell, caster, true);
}
else if (target_type == SPELL_TARGET_ENEMY && target && target->Alive()) // if target is enemy, and is alive
@ -2158,13 +2184,13 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
luaspell->targets.push_back(target->GetID()); // return the target
}
else
luaspell->targets.push_back(caster->GetID()); // else return the caster
AddSelfAndPet(luaspell, caster);
}
// if NPC caster is in a group, and target is a player and targeted player is a group member
else if (((NPC*)caster)->HasSpawnGroup() && target->IsNPC() && ((NPC*)caster)->IsInSpawnGroup((NPC*)target))
luaspell->targets.push_back(target->GetID()); // return the target
else
luaspell->targets.push_back(caster->GetID()); // else return the caster
AddSelfAndPet(luaspell, caster);
}
else if (target->IsNPC())
luaspell->targets.push_back(target->GetID()); // return target for single spell
@ -2193,8 +2219,9 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
{
Spawn* group_member = *itr;
// if NPC group member is (still) an NPC (wtf?) and is alive, send the NPC group member back as a successful target of non-friendly spell
if (group_member->IsNPC() && group_member->Alive() && !((Entity*)group_member)->IsAOEImmune() && (!((Entity*)group_member)->IsMezzed() || group_member == target))
// if NPC group member is (still) an NPC (wtf?) and is alive, send the NPC group member back as a successful target of non-friendly spell group_member->Alive()
if (group_member->GetZone() == caster->GetZone() &&
group_member->IsNPC() && group_member->Alive() && !((Entity*)group_member)->IsAOEImmune() && (!((Entity*)group_member)->IsMezzed() || group_member == target))
luaspell->targets.push_back(group_member->GetID());
// note: this should generate some hate towards the caster
@ -2314,7 +2341,7 @@ void SpellProcess::GetPlayerGroupTargets(Player* target, Spawn* caster, LuaSpell
info->client->GetPlayer()->GetZone() == ((Player*)target)->GetZone() && info->client->GetPlayer()->Alive()
&& (bypassRangeChecks || caster->GetDistance((Entity*)info->client->GetPlayer()) <= luaspell->spell->GetSpellData()->range))
{
luaspell->targets.push_back(info->client->GetPlayer()->GetID());
AddSelfAndPet(luaspell, info->client->GetPlayer());
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
@ -2328,7 +2355,10 @@ void SpellProcess::GetSpellTargetsTrueAOE(LuaSpell* luaspell) {
if (luaspell->caster->HasTarget() && luaspell->caster->GetTarget() != luaspell->caster){
//Check if the caster has an implied target
if (luaspell->caster->GetDistance(luaspell->caster->GetTarget()) <= luaspell->spell->GetSpellData()->radius)
{
luaspell->initial_target = luaspell->caster->GetTarget()->GetID();
luaspell->initial_target_char_id = (luaspell->caster->GetTarget() && luaspell->caster->GetTarget()->IsPlayer()) ? ((Player*)luaspell->caster->GetTarget())->GetCharacterID() : 0;
}
}
int32 ignore_target = 0;
vector<Spawn*> spawns = luaspell->caster->GetZone()->GetAttackableSpawnsByDistance(luaspell->caster, luaspell->spell->GetSpellData()->radius);
@ -2497,6 +2527,7 @@ void SpellProcess::RemoveTargetFromSpell(LuaSpell* spell, Spawn* target){
if (!spell || !target)
return;
LogWrite(SPELL__DEBUG, 0, "Spell", "%s RemoveTargetFromSpell %s (%u).", spell->spell->GetName(), target->GetName(), target->GetID());
MRemoveTargetList.writelock(__FUNCTION__, __LINE__);
if (!remove_target_list[spell])
@ -2506,7 +2537,7 @@ void SpellProcess::RemoveTargetFromSpell(LuaSpell* spell, Spawn* target){
MRemoveTargetList.releasewritelock(__FUNCTION__, __LINE__);
}
void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete){
void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete, bool removing_all_spells){
if (!spell)
return;
@ -2529,19 +2560,34 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete
remove_spawn = spell->caster->GetZone()->GetSpawnByID((*remove_target_itr));
if (remove_spawn) {
spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
for (target_itr = targets->begin(); target_itr != targets->end(); target_itr++) {
((Entity*)remove_spawn)->RemoveProc(0, spell);
((Entity*)remove_spawn)->RemoveMaintainedSpell(spell);
if(remove_spawn && remove_spawn->IsPlayer())
{
multimap<int32,int8>::iterator entries;
while((entries = spell->char_id_targets.find(((Player*)remove_spawn)->GetCharacterID())) != spell->char_id_targets.end())
{
spell->char_id_targets.erase(entries);
}
}
for (target_itr = targets->begin(); target_itr != targets->end(); target_itr++) {
if (remove_spawn->GetID() == (*target_itr)) {
((Entity*)remove_spawn)->RemoveProc(0, spell);
((Entity*)remove_spawn)->RemoveMaintainedSpell(spell);
LogWrite(SPELL__DEBUG, 0, "Spell", "%s CheckRemoveTargetFromSpell %s (%u).", spell->spell->GetName(), remove_spawn->GetName(), remove_spawn->GetID());
targets->erase(target_itr);
if (remove_spawn->IsEntity())
{
if(!removing_all_spells && remove_spawn->IsPlayer())
{
spell->char_id_targets.insert(make_pair(((Player*)remove_spawn)->GetCharacterID(),0));
}
((Entity*)remove_spawn)->RemoveEffectsFromLuaSpell(spell);
}
break;
}
}
spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
if (targets->size() == 0 && allow_delete) {
if (targets->size() == 0 && spell->char_id_targets.size() == 0 && allow_delete) {
should_delete = true;
break;
}
@ -2688,4 +2734,17 @@ void SpellProcess::AddActiveSpell(LuaSpell* spell)
{
if(!active_spells.count(spell))
active_spells.Add(spell);
}
void SpellProcess::AddSelfAndPet(LuaSpell* spell, Spawn* caster, bool onlyPet)
{
if(!onlyPet)
spell->targets.push_back(caster->GetID());
if(caster->IsEntity() && ((Entity*)caster)->HasPet() && ((Entity*)caster)->GetPet())
spell->targets.push_back(((Entity*)caster)->GetPet()->GetID());
if(caster->IsEntity() && ((Entity*)caster)->HasPet() && ((Entity*)caster)->GetCharmedPet())
spell->targets.push_back(((Entity*)caster)->GetCharmedPet()->GetID());
if(!onlyPet && caster->IsEntity() && ((Entity*)caster)->IsPet() && ((Entity*)caster)->GetOwner())
spell->targets.push_back(((Entity*)caster)->GetOwner()->GetID());
}

View file

@ -363,7 +363,7 @@ public:
MutexList<LuaSpell*>* GetActiveSpells() { return &active_spells; }
void RemoveTargetFromSpell(LuaSpell* spell, Spawn* target);
void CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete = true);
void CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete = true, bool removing_all_spells = false);
/// <summary>Adds a solo HO to the SpellProcess</summary>
/// <param name='client'>The client who is starting the HO</param>
@ -388,6 +388,7 @@ public:
bool ProcessSpell(LuaSpell* spell, bool first_cast = true, const char* function = 0, SpellScriptTimer* timer = 0);
void AddActiveSpell(LuaSpell* spell);
static void AddSelfAndPet(LuaSpell* spell, Spawn* caster, bool onlyPet=false);
private:
Mutex MSpellProcess;
MutexMap<Entity*,Spell*> spell_que;

View file

@ -2478,4 +2478,4 @@ Map* World::GetMap(std::string zoneFile, int32 client_version)
MWorldMaps.releasereadlock();
return nullptr;
}
}

View file

@ -3598,10 +3598,10 @@ void WorldDatabase::UpdateStartingZone(int32 char_id, int8 class_id, int8 race_i
int8 deity = create->getType_int8_ByName("deity"); // aka 'alignment' for early DOF, 0 = evil, 1 = good
int32 startingZoneRuleFlag = rule_manager.GetGlobalRule(R_World, StartingZoneRuleFlag)->GetInt32();
if((startingZoneRuleFlag == 1 || startingZoneRuleFlag == 2) && choice > 1)
if((startingZoneRuleFlag == 1 || startingZoneRuleFlag == 2) && packetVersion > 546)
{
LogWrite(PLAYER__INFO, 0, "Player", "Starting zone rule flag %u override choice %u to deity value of %u", startingZoneRuleFlag, choice, deity);
choice = deity; // inherit deity to know starting choice is 'good' or evil
LogWrite(PLAYER__INFO, 0, "Player", "Starting zone rule flag %u override choice %u to deity value of 0", startingZoneRuleFlag, choice);
choice = 0;
}
LogWrite(PLAYER__INFO, 0, "Player", "Adding default zone for race: %i, class: %i for char_id: %u (choice: %i), deity(alignment): %u, version: %u.", race_id, class_id, char_id, choice, deity, packetVersion);
@ -7354,13 +7354,14 @@ int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race,
void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type)
{
SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess();
Player* player = client->GetPlayer();
if(!spellProcess)
return;
DatabaseResult result;
Player* player = client->GetPlayer();
multimap<LuaSpell*, Entity*> restoreSpells;
// Use -1 on type and subtype to turn the enum into an int and make it a 0 index
if (!database_new.Select(&result, "SELECT name, caster_char_id, target_char_id, target_type, spell_id, effect_slot, slot_pos, icon, icon_backdrop, conc_used, tier, total_time, expire_timestamp, lua_file, custom_spell, damage_remaining, effect_bitmask, num_triggers, had_triggers, cancel_after_triggers, crit, last_spellattack_hit, interrupted, resisted, custom_function FROM character_spell_effects WHERE charid = %u and db_effect_type = %u", char_id, db_spell_type)) {
LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", database_new.GetError(), database_new.GetErrorMsg());
@ -7413,9 +7414,9 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
bool isMaintained = false;
bool isExistingLuaSpell = false;
MaintainedEffects* effect;
MaintainedEffects* effect = nullptr;
Client* tmpCaster = nullptr;
if(caster_char_id == player->GetCharacterID() && 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)) != nullptr)
{
safe_delete(lua_spell);
lua_spell = effect->spell;
@ -7427,25 +7428,22 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
&& tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr)
{
if(tmpCaster->GetCurrentZone() != client->GetCurrentZone())
{
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), characters in different zones, cannot assign maintained spell.", spell_id, tier, lua_file.c_str());
safe_delete(lua_spell);
continue;
}
else if(effect->spell)
if(effect->spell && effect->spell_id == spell_id)
{
safe_delete(lua_spell);
effect->spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
effect->spell->targets.push_back(client->GetPlayer()->GetID());
if(tmpCaster->GetCurrentZone() == player->GetZone())
effect->spell->targets.push_back(client->GetPlayer()->GetID());
effect->spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
lua_spell = effect->spell;
spell = effect->spell->spell;
isExistingLuaSpell = true;
isMaintained = true;
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), effect spell id %u maintained spell recovered from %s", spell_id, tier, spell_name, effect ? effect->spell_id : 0, (tmpCaster && tmpCaster->GetPlayer()) ? tmpCaster->GetPlayer()->GetName() : "?");
}
else
{
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), something went wrong loading another characters maintained spell.", spell_id, tier, lua_file.c_str());
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: GetSpell(%u, %u, '%s'), something went wrong loading another characters maintained spell. Effect has spell id %u", spell_id, tier, lua_file.c_str(), effect ? effect->spell_id : 0);
safe_delete(lua_spell);
continue;
}
@ -7456,7 +7454,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
lua_interface->AddCustomSpell(lua_spell);
}
else
else if(db_spell_type == DB_TYPE_MAINTAINEDEFFECTS)
{
safe_delete(lua_spell);
lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
@ -7483,26 +7481,95 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
timer->customFunction = string(custom_function); // TODO
timer->spell = lua_spell;
timer->caster = (caster_char_id == player->GetCharacterID()) ? player->GetID() : 0;
timer->target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
if(target_char_id == 0xFFFFFFFF && player->HasPet())
timer->target = player->GetPet()->GetID();
else
timer->target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
if(!timer->target && target_char_id)
{
Client* tmpClient = zone_list.GetClientByCharID(target_char_id);
if(tmpClient && tmpClient->GetPlayer() && tmpClient->GetPlayer()->GetZone() == player->GetZone())
timer->target = tmpClient->GetPlayer()->GetID();
}
}
lua_spell->crit = crit;
lua_spell->damage_remaining = damage_remaining;
lua_spell->effect_bitmask = effect_bitmask;
lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
lua_spell->had_triggers = had_triggers;
lua_spell->initial_target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
lua_spell->interrupted = interrupted;
lua_spell->last_spellattack_hit = last_spellattack_hit;
lua_spell->num_triggers = num_triggers;
if(!isExistingLuaSpell)
{
lua_spell->crit = crit;
lua_spell->damage_remaining = damage_remaining;
lua_spell->effect_bitmask = effect_bitmask;
lua_spell->had_dmg_remaining = (damage_remaining>0) ? true : false;
lua_spell->had_triggers = had_triggers;
lua_spell->initial_caster_char_id = caster_char_id;
lua_spell->initial_target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
lua_spell->initial_target_char_id = target_char_id;
lua_spell->interrupted = interrupted;
lua_spell->last_spellattack_hit = last_spellattack_hit;
lua_spell->num_triggers = num_triggers;
}
if(lua_spell->initial_target == 0 && target_char_id == 0xFFFFFFFF && player->HasPet())
{
lua_spell->initial_target = player->GetPet()->GetID();
lua_spell->initial_target_char_id = target_char_id;
}
//lua_spell->num_calls ??
//if(target_char_id == player->GetCharacterID())
// lua_spell->targets.push_back(player->GetID());
if(db_spell_type == DB_TYPE_SPELLEFFECTS)
{
if (caster_char_id != player->GetCharacterID() && lua_spell->spell->GetSpellData()->group_spell && lua_spell->spell->GetSpellData()->friendly_spell)
{
if(!isExistingLuaSpell)
safe_delete(lua_spell);
continue;
}
player->MSpellEffects.writelock();
info->spell_effects[effect_slot].caster = (caster_char_id == player->GetCharacterID()) ? player : 0;
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id);
if(lua_spell->caster && (rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
info->spell_effects[effect_slot].caster = lua_spell->caster;
else if(caster_char_id != player->GetCharacterID())
{
Client* tmpCaster = zone_list.GetClientByCharID(caster_char_id);
if(tmpCaster)
{
if((rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() || (!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && lua_spell->caster->GetZone() == player->GetZone())))
{
info->spell_effects[effect_slot].caster = tmpCaster->GetPlayer();
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, found player %s.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, tmpCaster->GetPlayer()->GetName());
}
else
{
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, found player %s, SKIPPED due to R_Spells, EnableCrossZoneTargetBuffs.", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, tmpCaster->GetPlayer()->GetName());
if(!isExistingLuaSpell)
{
safe_delete(lua_spell);
}
else
{
lua_spell->char_id_targets.insert(make_pair(player->GetCharacterID(),0));
}
player->MSpellEffects.releasewritelock();
continue;
}
}
}
else if(caster_char_id == player->GetCharacterID())
info->spell_effects[effect_slot].caster = player;
else
{
LogWrite(LUA__ERROR, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s lua_spell caster %s (%u), caster char id: %u, failed to find caster will delete: %u", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, isExistingLuaSpell);
if(!isExistingLuaSpell)
safe_delete(lua_spell);
continue;
}
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;
@ -7510,11 +7577,24 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
info->spell_effects[effect_slot].tier = tier;
info->spell_effects[effect_slot].total_time = total_time;
info->spell_effects[effect_slot].spell = lua_spell;
lua_spell->caster = player; // TODO: get actual player
printf("SlotPos: %u %s\n",slot_pos,spell->GetName());
multimap<int32,int8>::iterator entries;
while((entries = lua_spell->char_id_targets.find(player->GetCharacterID())) != lua_spell->char_id_targets.end())
{
lua_spell->char_id_targets.erase(entries);
}
lua_spell->slot_pos = slot_pos;
if(!isExistingLuaSpell)
lua_spell->caster = player; // TODO: get actual player
player->MSpellEffects.releasewritelock();
if(!isMaintained)
spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
else
{
// track target id when caster isnt in zone somehow
}
}
else if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
{
@ -7530,18 +7610,59 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
if(spell_id != in_spell_id)
continue;
Client* client2 = zone_list.GetClientByCharID(target_char);
int32 idToAdd = 0;
if(target_char == 0xFFFFFFFF)
{
if( player->HasPet() )
{
idToAdd = player->GetPet()->GetID();
restoreSpells.insert(make_pair(lua_spell, player->GetPet()));
}
}
else
{
Client* client2 = zone_list.GetClientByCharID(target_char);
if(client2 && client2->GetPlayer() && client2->GetCurrentZone() == client->GetCurrentZone())
{
idToAdd = client2->GetPlayer()->GetID();
if(client != client2)
restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()));
multimap<int32,int8>::iterator entries;
while((entries = lua_spell->char_id_targets.find(target_char)) != lua_spell->char_id_targets.end())
{
lua_spell->char_id_targets.erase(entries);
}
}
else
{
lua_spell->char_id_targets.insert(make_pair(target_char,0));
}
}
if(!idToAdd)
continue;
lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__);
if(client2 && client2->GetPlayer() && client2->GetCurrentZone() == client->GetCurrentZone())
lua_spell->targets.push_back(client2->GetPlayer()->GetID());
lua_spell->targets.push_back(idToAdd);
lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
}
}
Client* tmpClient = 0;
int32 targetID = 0;
if(target_char_id == 0xFFFFFFFF && player->HasPet())
targetID = player->GetPet()->GetID();
else if(target_char_id == player->GetCharacterID())
targetID = player->GetID();
else if((tmpClient = zone_list.GetClientByCharID(target_char_id)) != nullptr && tmpClient->GetPlayer())
targetID = tmpClient->GetPlayer()->GetID();
info->maintained_effects[effect_slot].conc_used = conc_used;
strncpy(info->maintained_effects[effect_slot].name, spell_name, 60);
info->maintained_effects[effect_slot].slot_pos = slot_pos;
info->maintained_effects[effect_slot].target = (target_char_id == player->GetCharacterID()) ? player->GetID() : 0;
info->maintained_effects[effect_slot].target = targetID;
info->maintained_effects[effect_slot].target_type = target_type;
info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp;
info->maintained_effects[effect_slot].icon = icon;
@ -7550,9 +7671,18 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
info->maintained_effects[effect_slot].tier = tier;
info->maintained_effects[effect_slot].total_time = total_time;
info->maintained_effects[effect_slot].spell = lua_spell;
lua_spell->caster = player;
if(!isExistingLuaSpell)
lua_spell->caster = player;
player->MMaintainedSpells.releasewritelock();
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s process spell caster %s (%u), caster char id: %u, target id %u (%s).", lua_spell->spell->GetName(), lua_spell->caster ? lua_spell->caster->GetName() : "",
lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, targetID, tmpClient ? tmpClient->GetPlayer()->GetName() : "");
if(tmpClient && lua_spell->initial_target_char_id == tmpClient->GetCharacterID())
{
lua_spell->initial_target = tmpClient->GetPlayer()->GetID();
lua_spell->targets.push_back(lua_spell->initial_target);
}
spellProcess->ProcessSpell(lua_spell, true, "cast", timer);
}
if(!isExistingLuaSpell && expire_timestamp != 0xFFFFFFFF && !isMaintained)
@ -7562,20 +7692,99 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int
lua_spell->timer.Start();
}
if(lua_spell->spell->GetSpellData()->det_type)
if(target_char_id == player->GetCharacterID() && lua_spell->spell->GetSpellData()->det_type)
player->AddDetrimentalSpell(lua_spell, expire_timestamp);
if(timer)
spellProcess->AddSpellScriptTimer(timer);
lua_spell->num_calls = 1;
lua_spell->restored = true;
if(!isExistingLuaSpell)
{
if(timer)
spellProcess->AddSpellScriptTimer(timer);
lua_spell->num_calls = 1;
lua_spell->restored = true;
}
if(!lua_spell->resisted && (lua_spell->spell->GetSpellDuration() > 0 || lua_spell->spell->GetSpellData()->duration_until_cancel))
spellProcess->AddActiveSpell(lua_spell);
if (num_triggers > 0)
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, num_triggers, 0);
if (damage_remaining > 0)
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, damage_remaining, 1);
if ( db_spell_type == DB_TYPE_MAINTAINEDEFFECTS )
{
if (num_triggers > 0)
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, num_triggers, 0);
if (damage_remaining > 0)
ClientPacketFunctions::SendMaintainedExamineUpdate(client, slot_pos, damage_remaining, 1);
}
}
if(!rule_manager.GetGlobalRule(R_Spells,EnableCrossZoneTargetBuffs)->GetInt8() && db_spell_type == DB_TYPE_SPELLEFFECTS)
{
DatabaseResult targets;
if (database_new.Select(&targets, "SELECT caster_char_id, target_type, spell_id from character_spell_effect_targets where target_char_id = %u", player->GetCharacterID())) {
while (targets.Next()) {
int32 caster_char_id = targets.GetInt32Str("caster_char_id");
int16 target_type = targets.GetInt32Str("target_type");
int32 in_spell_id = targets.GetInt32Str("spell_id");
Client* tmpCaster = nullptr;
MaintainedEffects* effect = nullptr;
if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr
&& tmpCaster->GetCurrentZone() == player->GetZone() && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id)) != nullptr)
{
if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer()))
{
if(effect->spell->initial_target_char_id == player->GetCharacterID())
effect->spell->initial_target = player->GetID();
restoreSpells.insert(make_pair(effect->spell, player));
}
}
}
}
}
multimap<LuaSpell*, Entity*>::const_iterator itr;
for (itr = restoreSpells.begin(); itr != restoreSpells.end(); itr++)
{
LuaSpell* tmpSpell = itr->first;
Entity* target = itr->second;
if(!target)
{
target = client->GetPlayer()->GetPet();
if(!target)
continue;
}
Entity* caster = tmpSpell->caster;
if(!caster)
caster = client->GetPlayer();
if(caster != target && caster->GetPet() != target &&
tmpSpell->spell->GetSpellData()->group_spell && tmpSpell->spell->GetSpellData()->friendly_spell && (caster->group_id == 0 || target->group_id == 0 || caster->group_id != target->group_id))
{
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s player no longer grouped with %s to reload bonuses for spell %s.", target->GetName(), caster ? caster->GetName() : "?", tmpSpell->spell->GetName());
continue;
}
LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCharacterSpellEffects: %s using caster %s to reload bonuses for spell %s.", player->GetName(), caster ? caster->GetName() : "?", tmpSpell->spell->GetName());
target->AddSpellEffect(tmpSpell, tmpSpell->timer.GetRemainingTime() != 0 ? tmpSpell->timer.GetRemainingTime() : 0);
vector<BonusValues*>* sb_list = caster->GetAllSpellBonuses(tmpSpell);
for (int32 x = 0; x < sb_list->size(); x++) {
BonusValues* bv = sb_list->at(x);
target->AddSpellBonus(tmpSpell, bv->type, bv->value, bv->class_req, bv->race_req, bv->faction_req);
}
sb_list->clear();
safe_delete(sb_list);
// look for a skill bonus on the caster's spell
if(caster->IsPlayer())
{
SkillBonus* sb = ((Player*)caster)->GetSkillBonus(tmpSpell->spell->GetSpellID());
if (sb) {
map<int32, SkillBonusValue*>::iterator itr_skills;
for (itr_skills = sb->skills.begin(); itr_skills != sb->skills.end(); itr_skills++)
target->AddSkillBonus(sb->spell_id, (*itr_skills).second->skill_id, (*itr_skills).second->value);
}
}
}
}

View file

@ -3018,6 +3018,8 @@ void ZoneServer::RemoveClient(Client* client)
LogWrite(MISC__TODO, 1, "TODO", "Put Player Online Status updates in a timer eventually\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
database.ToggleCharacterOnline(client, 0);
client->GetPlayer()->DeleteSpellEffects(true);
RemoveSpawn(client->GetPlayer(), false);
connected_clients.Remove(client, true, DisconnectClientTimer); // changed from a hardcoded 30000 (30 sec) to the DisconnectClientTimer rule
}