/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "CharmInfo.h"
#include "Creature.h"
#include "Map.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "StringConvert.h"
#include "Unit.h"
CharmInfo::CharmInfo(Unit* unit)
: _unit(unit), _CommandState(COMMAND_FOLLOW), _petnumber(0), _oldReactState(REACT_PASSIVE),
_isCommandAttack(false), _isCommandFollow(false), _isAtStay(false), _isFollowing(false), _isReturning(false),
_stayX(0.0f), _stayY(0.0f), _stayZ(0.0f)
{
for (uint8 i = 0; i < MAX_SPELL_CHARM; ++i)
_charmspells[i].SetActionAndType(0, ACT_DISABLED);
if (Creature* creature = _unit->ToCreature())
{
_oldReactState = creature->GetReactState();
creature->SetReactState(REACT_PASSIVE);
}
}
CharmInfo::~CharmInfo() { }
void CharmInfo::RestoreState()
{
if (Creature* creature = _unit->ToCreature())
creature->SetReactState(_oldReactState);
}
void CharmInfo::InitPetActionBar()
{
// the first 3 SpellOrActions are attack, follow and stay
for (uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_START - ACTION_BAR_INDEX_START; ++i)
SetActionBar(ACTION_BAR_INDEX_START + i, COMMAND_ATTACK - i, ACT_COMMAND);
// middle 4 SpellOrActions are spells/special attacks/abilities
for (uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_END-ACTION_BAR_INDEX_PET_SPELL_START; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_START + i, 0, ACT_PASSIVE);
// last 3 SpellOrActions are reactions
for (uint32 i = 0; i < ACTION_BAR_INDEX_END - ACTION_BAR_INDEX_PET_SPELL_END; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_END + i, COMMAND_ATTACK - i, ACT_REACTION);
}
void CharmInfo::InitEmptyActionBar(bool withAttack)
{
if (withAttack)
SetActionBar(ACTION_BAR_INDEX_START, COMMAND_ATTACK, ACT_COMMAND);
else
SetActionBar(ACTION_BAR_INDEX_START, 0, ACT_PASSIVE);
for (uint32 x = ACTION_BAR_INDEX_START+1; x < ACTION_BAR_INDEX_END; ++x)
SetActionBar(x, 0, ACT_PASSIVE);
}
void CharmInfo::InitPossessCreateSpells()
{
if (_unit->GetTypeId() == TYPEID_UNIT)
{
// Adding switch until better way is found. Malcrom
// Adding entrys to this switch will prevent COMMAND_ATTACK being added to pet bar.
switch (_unit->GetEntry())
{
case 23575: // Mindless Abomination
case 24783: // Trained Rock Falcon
case 27664: // Crashin' Thrashin' Racer
case 40281: // Crashin' Thrashin' Racer
case 28511: // Eye of Acherus
break;
default:
InitEmptyActionBar();
break;
}
for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
{
uint32 spellId = _unit->ToCreature()->m_spells[i];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, _unit->GetMap()->GetDifficultyID());
if (spellInfo)
{
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_AVAILABLE_WHILE_CHARMED))
continue;
if (spellInfo->IsPassive())
_unit->CastSpell(_unit, spellInfo->Id, true);
else
AddSpellToActionBar(spellInfo, ACT_PASSIVE, i % MAX_UNIT_ACTION_BAR_INDEX);
}
}
}
else
InitEmptyActionBar();
}
void CharmInfo::InitCharmCreateSpells()
{
if (_unit->GetTypeId() == TYPEID_PLAYER) // charmed players don't have spells
{
InitEmptyActionBar();
return;
}
InitPetActionBar();
for (uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
{
uint32 spellId = _unit->ToCreature()->m_spells[x];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, _unit->GetMap()->GetDifficultyID());
if (!spellInfo)
{
_charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
continue;
}
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_AVAILABLE_WHILE_CHARMED))
continue;
if (spellInfo->IsPassive())
{
_unit->CastSpell(_unit, spellInfo->Id, true);
_charmspells[x].SetActionAndType(spellId, ACT_PASSIVE);
}
else
{
_charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
ActiveStates newstate = ACT_PASSIVE;
if (!spellInfo->IsAutocastable())
newstate = ACT_PASSIVE;
else
{
if (spellInfo->IsAutocastEnabledByDefault() && spellInfo->NeedsExplicitUnitTarget())
{
newstate = ACT_ENABLED;
ToggleCreatureAutocast(spellInfo, true);
}
else
newstate = ACT_DISABLED;
}
AddSpellToActionBar(spellInfo, newstate);
}
}
}
bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates newstate, uint8 preferredSlot)
{
uint32 spell_id = spellInfo->Id;
uint32 first_id = spellInfo->GetFirstRankSpell()->Id;
ASSERT(preferredSlot < MAX_UNIT_ACTION_BAR_INDEX);
// new spell rank can be already listed
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (uint32 action = PetActionBar[i].GetAction())
{
if (PetActionBar[i].IsActionBarForSpell() && sSpellMgr->GetFirstSpellInChain(action) == first_id)
{
PetActionBar[i].SetAction(spell_id);
return true;
}
}
}
// or use empty slot in other case
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
uint8 j = (preferredSlot + i) % MAX_UNIT_ACTION_BAR_INDEX;
if (!PetActionBar[j].GetAction() && PetActionBar[j].IsActionBarForSpell())
{
newstate = [&]
{
if (newstate != ACT_DECIDE)
return newstate;
if (!spellInfo->IsAutocastable())
return ACT_PASSIVE;
if (spellInfo->IsAutocastEnabledByDefault())
return ACT_ENABLED;
return ACT_DISABLED;
}();
SetActionBar(j, spell_id, newstate);
return true;
}
}
return false;
}
bool CharmInfo::RemoveSpellFromActionBar(uint32 spell_id)
{
uint32 first_id = sSpellMgr->GetFirstSpellInChain(spell_id);
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (uint32 action = PetActionBar[i].GetAction())
{
if (PetActionBar[i].IsActionBarForSpell() && sSpellMgr->GetFirstSpellInChain(action) == first_id)
{
SetActionBar(i, 0, ACT_PASSIVE);
return true;
}
}
}
return false;
}
void CharmInfo::ToggleCreatureAutocast(SpellInfo const* spellInfo, bool apply)
{
if (spellInfo->IsPassive())
return;
for (uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
if (spellInfo->Id == _charmspells[x].GetAction())
_charmspells[x].SetType(apply ? ACT_ENABLED : ACT_DISABLED);
}
void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
{
_petnumber = petnumber;
if (statwindow)
_unit->SetPetNumberForClient(_petnumber);
else
_unit->SetPetNumberForClient(0);
}
void CharmInfo::LoadPetActionBar(const std::string& data)
{
InitPetActionBar();
std::vector tokens = Trinity::Tokenize(data, ' ', false);
if (tokens.size() != (ACTION_BAR_INDEX_END-ACTION_BAR_INDEX_START) * 2)
return; // non critical, will reset to default
auto iter = tokens.begin();
for (uint8 index = ACTION_BAR_INDEX_START; index < ACTION_BAR_INDEX_END; ++index)
{
Optional type = Trinity::StringTo(*(iter++));
Optional action = Trinity::StringTo(*(iter++));
if (!type || !action)
continue;
PetActionBar[index].SetActionAndType(*action, static_cast(*type));
// check correctness
if (PetActionBar[index].IsActionBarForSpell())
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(PetActionBar[index].GetAction(), _unit->GetMap()->GetDifficultyID());
if (!spellInfo)
SetActionBar(index, 0, ACT_PASSIVE);
else if (!spellInfo->IsAutocastable())
SetActionBar(index, PetActionBar[index].GetAction(), ACT_PASSIVE);
}
}
}
void CharmInfo::BuildActionBar(WorldPacket* data)
{
for (uint32 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
*data << uint32(PetActionBar[i].packedData);
}
void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state)
{
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (spellInfo->Id == PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell())
{
PetActionBar[i].SetType(state ? ACT_ENABLED : ACT_DISABLED);
break;
}
}
}