Spell stacking restrictions integrated using linked_timer_id and a new field, type_group_spell_id
Fix #267 - spell stacking restrictions in place min_class_skill_req is now pulled from the database sint32 type_group_spell_id added to spells, can use this to distinguish spells of different classes not competing (eg. wards of templar/inquisitor dont stack) GetSpellData and SetSpellData support min_class_skill_req and type_group_spell_id
This commit is contained in:
parent
ba0fbaee7e
commit
4663fa36c9
12 changed files with 204 additions and 6 deletions
1
DB/updates/spells_update_feb24_2021.sql
Normal file
1
DB/updates/spells_update_feb24_2021.sql
Normal file
|
@ -0,0 +1 @@
|
|||
alter table spells add column type_group_spell_id int(10) signed not null default '0';
|
|
@ -931,6 +931,61 @@ SpellEffects* Entity::GetSpellEffect(int32 id, Entity* caster) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer, sint32 type_group_spell_id, Entity* caster) {
|
||||
SpellEffects* ret = 0;
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
for(int i = 0; i < 45; i++) {
|
||||
if(info->spell_effects[i].spell_id != 0xFFFFFFFF)
|
||||
{
|
||||
if( (info->spell_effects[i].spell_id == id && linked_timer == 0 && type_group_spell_id == 0) ||
|
||||
(linked_timer > 0 && info->spell_effects[i].spell->spell->GetSpellData()->linked_timer == linked_timer) ||
|
||||
(type_group_spell_id > 0 && info->spell_effects[i].spell->spell->GetSpellData()->type_group_spell_id == type_group_spell_id))
|
||||
{
|
||||
if (type_group_spell_id >= -1 && (!caster || info->spell_effects[i].caster == caster)){
|
||||
ret = &info->spell_effects[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MSpellEffects.releasereadlock(__FUNCTION__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers) {
|
||||
if(!spell->spell->GetSpellData()->linked_timer)
|
||||
return nullptr;
|
||||
LuaSpell* ret = nullptr;
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
//this for loop primarily handles self checks and 'friendly' checks
|
||||
for(int i = 0; i < NUM_MAINTAINED_EFFECTS; i++) {
|
||||
if(info->maintained_effects[i].spell_id != 0xFFFFFFFF)
|
||||
{
|
||||
if( ((info->maintained_effects[i].spell_id == spell->spell->GetSpellID() && spell->spell->GetSpellData()->type_group_spell_id >= 0) ||
|
||||
(info->maintained_effects[i].spell->spell->GetSpellData()->linked_timer > 0 && info->maintained_effects[i].spell->spell->GetSpellData()->linked_timer == spell->spell->GetSpellData()->linked_timer) ||
|
||||
(spell->spell->GetSpellData()->type_group_spell_id > 0 && spell->spell->GetSpellData()->type_group_spell_id == info->maintained_effects[i].spell->spell->GetSpellData()->type_group_spell_id)) &&
|
||||
((spell->spell->GetSpellData()->friendly_spell) ||
|
||||
(!spell->spell->GetSpellData()->friendly_spell && spell->spell->GetSpellData()->type_group_spell_id >= -1 && spell->caster == info->maintained_effects[i].spell->caster) ) &&
|
||||
(target == nullptr || info->maintained_effects[i].spell->initial_target == target->GetID())) {
|
||||
ret = info->maintained_effects[i].spell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MSpellEffects.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(!ret && !stackWithOtherPlayers && target && target->IsEntity())
|
||||
{
|
||||
SpellEffects* effect = ((Entity*)target)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, nullptr);
|
||||
if(effect)
|
||||
ret = effect->spell;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
InfoStruct* Entity::GetInfoStruct(){
|
||||
return &info_struct;
|
||||
}
|
||||
|
|
|
@ -1157,6 +1157,8 @@ public:
|
|||
MaintainedEffects* GetFreeMaintainedSpellSlot();
|
||||
SpellEffects* GetFreeSpellEffectSlot();
|
||||
SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0);
|
||||
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);
|
||||
|
||||
//flags
|
||||
int32 GetFlags() { return info_struct.get_flags(); }
|
||||
|
|
|
@ -2282,6 +2282,7 @@ void Player::AddSpellBookEntry(int32 spell_id, int8 tier, sint32 slot, int32 typ
|
|||
spell->recast_available = 0;
|
||||
spell->player = this;
|
||||
spell->visible = true;
|
||||
spell->in_use = false;
|
||||
MSpellsBook.lock();
|
||||
spells.push_back(spell);
|
||||
MSpellsBook.unlock();
|
||||
|
@ -2600,7 +2601,10 @@ void Player::UnlockAllSpells(bool modify_recast, Spell* exception) {
|
|||
exception_spell_id = exception->GetSpellID();
|
||||
MSpellsBook.writelock(__FUNCTION__, __LINE__);
|
||||
for (itr = spells.begin(); itr != spells.end(); itr++) {
|
||||
if ((*itr)->spell_id != exception_spell_id && (*itr)->type != SPELL_BOOK_TYPE_TRADESKILL)
|
||||
if ((*itr)->in_use == false &&
|
||||
(((*itr)->spell_id != exception_spell_id ||
|
||||
(*itr)->timer > 0 && (*itr)->timer != exception->GetSpellData()->linked_timer)
|
||||
&& (*itr)->type != SPELL_BOOK_TYPE_TRADESKILL))
|
||||
AddSpellStatus((*itr), SPELL_STATUS_LOCK, modify_recast);
|
||||
}
|
||||
|
||||
|
@ -2613,8 +2617,13 @@ void Player::LockSpell(Spell* spell, int16 recast) {
|
|||
MSpellsBook.writelock(__FUNCTION__, __LINE__);
|
||||
for (itr = spells.begin(); itr != spells.end(); itr++) {
|
||||
spell2 = *itr;
|
||||
if (spell2->spell_id == spell->GetSpellID() /*|| (spell->GetSpellData()->linked_timer > 0 && spell->GetSpellData()->linked_timer == spell2->timer)*/)
|
||||
if (spell2->spell_id == spell->GetSpellID() || (spell->GetSpellData()->linked_timer > 0 && spell->GetSpellData()->linked_timer == spell2->timer))
|
||||
{
|
||||
spell2->in_use = true;
|
||||
RemoveSpellStatus(spell2, SPELL_STATUS_LOCK, true, recast);
|
||||
}
|
||||
else if(spell2->in_use)
|
||||
RemoveSpellStatus(spell2, SPELL_STATUS_LOCK, false, 0);
|
||||
}
|
||||
MSpellsBook.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
@ -2627,8 +2636,11 @@ void Player::UnlockSpell(Spell* spell) {
|
|||
MSpellsBook.writelock(__FUNCTION__, __LINE__);
|
||||
for (itr = spells.begin(); itr != spells.end(); itr++) {
|
||||
spell2 = *itr;
|
||||
if (spell2->spell_id == spell->GetSpellID() /*|| (spell->GetSpellData()->linked_timer > 0 && spell->GetSpellData()->linked_timer == spell2->timer)*/)
|
||||
if (spell2->spell_id == spell->GetSpellID() || (spell->GetSpellData()->linked_timer > 0 && spell->GetSpellData()->linked_timer == spell2->timer))
|
||||
{
|
||||
spell2->in_use = false;
|
||||
AddSpellStatus(spell2, SPELL_STATUS_LOCK);
|
||||
}
|
||||
}
|
||||
MSpellsBook.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
|
|
@ -161,6 +161,7 @@ struct SpellBookEntry{
|
|||
int16 recast;
|
||||
int32 timer;
|
||||
bool save_needed;
|
||||
bool in_use;
|
||||
Player* player;
|
||||
bool visible;
|
||||
};
|
||||
|
|
|
@ -336,6 +336,11 @@ void SpellProcess::CheckInterrupt(InterruptStruct* interrupt){
|
|||
entity->GetZone()->SendInterruptPacket(entity, interrupt->spell);
|
||||
if(interrupt->error_code > 0)
|
||||
entity->GetZone()->SendSpellFailedPacket(client, interrupt->error_code);
|
||||
if(entity->IsPlayer())
|
||||
{
|
||||
((Player*)entity)->UnlockSpell(interrupt->spell->spell);
|
||||
SendSpellBookUpdate(((Player*)entity)->GetClient());
|
||||
}
|
||||
}
|
||||
|
||||
bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell, string reason){
|
||||
|
@ -377,11 +382,13 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){
|
|||
spell->caster->GetZone()->TriggerCharSheetTimer();
|
||||
}
|
||||
}
|
||||
|
||||
CheckRecast(spell->spell, spell->caster);
|
||||
if (spell->caster && spell->caster->IsPlayer())
|
||||
SendSpellBookUpdate(spell->caster->GetZone()->GetClientBySpawn(spell->caster));
|
||||
}
|
||||
if(spell->caster->IsPlayer())
|
||||
((Player*)spell->caster)->UnlockSpell(spell->spell);
|
||||
|
||||
spell->caster->RemoveProc(0, spell);
|
||||
spell->caster->RemoveMaintainedSpell(spell);
|
||||
CheckRemoveTargetFromSpell(spell, false);
|
||||
|
@ -561,10 +568,15 @@ void SpellProcess::SendFinishedCast(LuaSpell* spell, Client* client){
|
|||
UnlockAllSpells(client, spell->spell);
|
||||
else
|
||||
UnlockAllSpells(client);
|
||||
|
||||
if(spell->resisted && spell->spell->GetSpellData()->recast > 0)
|
||||
CheckRecast(spell->spell, client->GetPlayer(), 0.5); // half sec recast on resisted spells
|
||||
else if (!spell->interrupted && spell->spell->GetSpellData()->cast_type != SPELL_CAST_TYPE_TOGGLE)
|
||||
CheckRecast(spell->spell, client->GetPlayer());
|
||||
else if(spell->caster && spell->caster->IsPlayer())
|
||||
{
|
||||
((Player*)spell->caster)->LockSpell(spell->spell, (int16)(spell->spell->GetSpellData()->recast * 10));
|
||||
}
|
||||
PacketStruct* packet = configReader.getStruct("WS_FinishCastSpell", client->GetVersion());
|
||||
if(packet){
|
||||
packet->setMediumStringByName("spell_name", spell->spell->GetSpellData()->name.data.c_str());
|
||||
|
@ -994,6 +1006,41 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
|||
DeleteSpell(lua_spell);
|
||||
return;
|
||||
}
|
||||
int8 spell_type = lua_spell->spell->GetSpellData()->spell_type;
|
||||
|
||||
LuaSpell* conflictSpell = caster->HasLinkedTimerID(lua_spell, target, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT));
|
||||
if(conflictSpell)
|
||||
{
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req > 0 && lua_spell->spell->GetSpellData()->min_class_skill_req > 0)
|
||||
{
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req <= lua_spell->spell->GetSpellData()->min_class_skill_req)
|
||||
{
|
||||
if(spell->GetSpellData()->friendly_spell)
|
||||
{
|
||||
ZoneServer* zone = caster->GetZone();
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(conflictSpell->initial_target);
|
||||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget);
|
||||
((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell);
|
||||
if(client)
|
||||
UnlockSpell(client, conflictSpell->spell);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(lua_spell->spell->GetSpellData()->spell_type == SPELL_TYPE_DEBUFF)
|
||||
{
|
||||
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((caster->IsMezzed() && !spell->CastWhileMezzed()) || (caster->IsStunned() && !spell->CastWhileStunned()))
|
||||
{
|
||||
|
@ -1407,10 +1454,29 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
|
|||
if (spell->spell->GetSpellData()->max_aoe_targets > 0 && spell->targets.size() == 0) {
|
||||
GetSpellTargetsTrueAOE(spell);
|
||||
if (spell->targets.size() == 0) {
|
||||
if(client)
|
||||
{
|
||||
client->GetPlayer()->UnlockAllSpells(true);
|
||||
SendSpellBookUpdate(client);
|
||||
}
|
||||
spell->caster->GetZone()->SendSpellFailedPacket(client, SPELL_ERROR_NO_TARGETS_IN_RANGE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!spell->spell->GetSpellData()->friendly_spell)
|
||||
{
|
||||
ZoneServer* zone = client->GetCurrentZone();
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(spell->initial_target);
|
||||
int8 spell_type = spell->spell->GetSpellData()->spell_type;
|
||||
LuaSpell* conflictSpell = spell->caster->HasLinkedTimerID(spell, tmpTarget, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT));
|
||||
if(conflictSpell && tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell);
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget);
|
||||
}
|
||||
}
|
||||
|
||||
MutexList<LuaSpell*>::iterator itr = active_spells.begin();
|
||||
bool processedSpell = false;
|
||||
LuaSpell* replace_spell = 0;
|
||||
|
@ -2552,3 +2618,11 @@ void SpellProcess::DeleteSpell(LuaSpell* spell)
|
|||
|
||||
safe_delete(spell);
|
||||
}
|
||||
|
||||
void SpellProcess::SpellCannotStack(ZoneServer* zone, Client* client, Entity* caster, LuaSpell* lua_spell, LuaSpell* conflictSpell)
|
||||
{
|
||||
LogWrite(SPELL__DEBUG, 1, "Spell", "%s cannot stack spell %s, conflicts with %s.", caster->GetName(), lua_spell->spell->GetName(), conflictSpell->spell->GetName());
|
||||
zone->SendSpellFailedPacket(client, SPELL_ERROR_TAKE_EFFECT_MOREPOWERFUL);
|
||||
lua_spell->caster->GetZone()->GetSpellProcess()->RemoveSpellScriptTimerBySpell(lua_spell);
|
||||
DeleteSpell(lua_spell);
|
||||
}
|
|
@ -381,6 +381,8 @@ public:
|
|||
void AddSpellCancel(LuaSpell* spell);
|
||||
|
||||
void DeleteSpell(LuaSpell* spell);
|
||||
|
||||
void SpellCannotStack(ZoneServer* zone, Client* client, Entity* caster, LuaSpell* lua_spell, LuaSpell* conflictSpell);
|
||||
private:
|
||||
/// <summary>Sends the spell data to the lua script</summary>
|
||||
/// <param name='spell'>LuaSpell to call the lua script for</param>
|
||||
|
|
|
@ -69,6 +69,7 @@ Spell::Spell(Spell* host_spell)
|
|||
spell->cast_type = host_spell->GetSpellData()->cast_type;
|
||||
spell->cast_while_moving = host_spell->GetSpellData()->cast_while_moving;
|
||||
spell->class_skill = host_spell->GetSpellData()->class_skill;
|
||||
spell->min_class_skill_req = host_spell->GetSpellData()->min_class_skill_req;
|
||||
spell->control_effect_type = host_spell->GetSpellData()->control_effect_type;
|
||||
spell->description = EQ2_16BitString(host_spell->GetSpellData()->description);
|
||||
spell->det_type = host_spell->GetSpellData()->det_type;
|
||||
|
@ -139,6 +140,7 @@ Spell::Spell(Spell* host_spell)
|
|||
spell->tier = host_spell->GetSpellData()->tier;
|
||||
spell->ts_loc_index = host_spell->GetSpellData()->ts_loc_index;
|
||||
spell->type = host_spell->GetSpellData()->type;
|
||||
spell->type_group_spell_id = host_spell->GetSpellData()->type_group_spell_id;
|
||||
}
|
||||
|
||||
heal_spell = host_spell->IsHealSpell();
|
||||
|
@ -726,6 +728,7 @@ void Spell::SetPacketInformation(PacketStruct* packet, Client* client, bool disp
|
|||
packet->setSubstructDataByName("spell_info", "type", spell->type);
|
||||
packet->setSubstructDataByName("spell_info", "unknown_MJ1d", 1); //63119 test
|
||||
packet->setSubstructDataByName("spell_info", "class_skill", spell->class_skill);
|
||||
packet->setSubstructDataByName("spell_info", "min_class_skill_req", spell->min_class_skill_req);
|
||||
packet->setSubstructDataByName("spell_info", "mastery_skill", spell->mastery_skill);
|
||||
packet->setSubstructDataByName("spell_info", "duration_flag", spell->duration_until_cancel);
|
||||
if (client && spell->type != 2) {
|
||||
|
@ -1331,6 +1334,11 @@ bool Spell::GetSpellData(lua_State* state, std::string field)
|
|||
lua_interface->SetInt32Value(state, GetSpellData()->class_skill);
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "min_class_skill_req")
|
||||
{
|
||||
lua_interface->SetInt32Value(state, GetSpellData()->min_class_skill_req);
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "mastery_skill")
|
||||
{
|
||||
lua_interface->SetInt32Value(state, GetSpellData()->mastery_skill);
|
||||
|
@ -1636,6 +1644,11 @@ bool Spell::GetSpellData(lua_State* state, std::string field)
|
|||
lua_interface->SetSInt32Value(state, GetSpellData()->spell_name_crc);
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "type_group_spell_id")
|
||||
{
|
||||
lua_interface->SetSInt32Value(state, GetSpellData()->type_group_spell_id);
|
||||
valSet = true;
|
||||
}
|
||||
|
||||
return valSet;
|
||||
}
|
||||
|
@ -1683,6 +1696,12 @@ bool Spell::SetSpellData(lua_State* state, std::string field, int8 fieldArg)
|
|||
GetSpellData()->class_skill = class_skill;
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "min_class_skill_req")
|
||||
{
|
||||
int16 min_class_skill_req = lua_interface->GetInt16Value(state, fieldArg);
|
||||
GetSpellData()->min_class_skill_req = min_class_skill_req;
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "mastery_skill")
|
||||
{
|
||||
int32 mastery_skill = lua_interface->GetInt32Value(state, fieldArg);
|
||||
|
@ -2031,6 +2050,12 @@ bool Spell::SetSpellData(lua_State* state, std::string field, int8 fieldArg)
|
|||
GetSpellData()->spell_type = spell_type;
|
||||
valSet = true;
|
||||
}
|
||||
else if (field == "type_group_spell_id")
|
||||
{
|
||||
sint32 type_group_spell_id = lua_interface->GetSInt32Value(state, fieldArg);
|
||||
GetSpellData()->type_group_spell_id = type_group_spell_id;
|
||||
valSet = true;
|
||||
}
|
||||
|
||||
return valSet;
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ struct SpellData{
|
|||
int16 icon_backdrop;
|
||||
int16 type;
|
||||
int32 class_skill;
|
||||
int16 min_class_skill_req;
|
||||
int32 mastery_skill;
|
||||
int8 ts_loc_index;
|
||||
int8 num_levels;
|
||||
|
@ -286,6 +287,7 @@ struct SpellData{
|
|||
int32 soe_spell_crc;
|
||||
int8 spell_type;
|
||||
int32 spell_name_crc;
|
||||
sint32 type_group_spell_id;
|
||||
};
|
||||
class Spell{
|
||||
public:
|
||||
|
|
|
@ -4555,7 +4555,7 @@ void WorldDatabase::LoadSpells()
|
|||
int32 total = 0;
|
||||
map<int32, vector<LevelArray*> >* level_data = LoadSpellClasses();
|
||||
|
||||
if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc' "
|
||||
if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `min_class_skill_req`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc', type_group_spell_id "
|
||||
"FROM (spells s, spell_tiers st) "
|
||||
"LEFT JOIN spell_ts_ability_index ts "
|
||||
"ON s.`id` = ts.spell_id "
|
||||
|
@ -4614,8 +4614,8 @@ void WorldDatabase::LoadSpells()
|
|||
|
||||
/* Skill Requirements */
|
||||
data->class_skill = result.GetInt32Str("class_skill");
|
||||
data->min_class_skill_req = result.GetInt16Str("min_class_skill_req");
|
||||
data->mastery_skill = result.GetInt32Str("mastery_skill");
|
||||
// no min_class_skill_req?
|
||||
|
||||
/* Cost */
|
||||
data->req_concentration = result.GetInt16Str("req_concentration");
|
||||
|
@ -4653,6 +4653,7 @@ void WorldDatabase::LoadSpells()
|
|||
data->resistibility = result.GetFloatStr("resistibility");
|
||||
data->linked_timer = result.GetInt32Str("linked_timer_id");
|
||||
data->spell_name_crc = result.GetInt32Str("spell_name_crc");
|
||||
data->type_group_spell_id = result.GetSInt32Str("type_group_spell_id");
|
||||
|
||||
/* Cast Messaging */
|
||||
string message = result.GetStringStr("success_message");
|
||||
|
|
|
@ -7814,4 +7814,25 @@ void ZoneServer::ProcessSpawnRemovals()
|
|||
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void ZoneServer::AddSpawnToGroup(Spawn* spawn, int32 group_id)
|
||||
{
|
||||
if( spawn->GetSpawnGroupID() > 0 )
|
||||
spawn->RemoveSpawnFromGroup();
|
||||
MutexList<int32>* groupList = &spawn_group_map.Get(group_id);
|
||||
MutexList<int32>::iterator itr2 = groupList->begin();
|
||||
|
||||
while(itr2.Next())
|
||||
{
|
||||
Spawn* groupSpawn = GetSpawnByID(itr2.value);
|
||||
if(groupSpawn)
|
||||
{
|
||||
// found existing group member to add it in
|
||||
spawn->AddSpawnToGroup(groupSpawn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
groupList->Add(spawn->GetID());
|
||||
spawn->SetSpawnGroupID(group_id);
|
||||
}
|
|
@ -666,6 +666,8 @@ public:
|
|||
void ProcessSpawnRemovals();
|
||||
|
||||
bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false);
|
||||
|
||||
void AddSpawnToGroup(Spawn* spawn, int32 group_id);
|
||||
private:
|
||||
#ifndef WIN32
|
||||
pthread_t ZoneThread;
|
||||
|
|
Loading…
Reference in a new issue