/* * 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; } } }