/* * Ascent MMORPG Server * Copyright (C) 2005-2007 Ascent Team * * 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 3 of the License, or * 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 "StdAfx.h" trainertype trainer_types[TRAINER_TYPE_MAX] = { { "Warrior", 0 }, { "Paladin", 0 }, { "Rogue" , 0 }, { "Warlock", 0 }, { "Mage", 0 }, { "Shaman", 0 }, { "Priest", 0 }, { "Hunter", 0 }, { "Druid", 0 }, { "Leatherwork", 2 }, { "Skinning", 2 }, { "Fishing", 2 }, { "First Aid", 2 }, { "Physician", 2 }, { "Engineer", 2 }, { "Weapon Master", 0 }, }; ////////////////////////////////////////////////////////////// /// This function handles MSG_TABARDVENDOR_ACTIVATE: ////////////////////////////////////////////////////////////// void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; recv_data >> guid; Creature *pCreature = _player->GetMapMgr()->GetCreature(guid); if(!pCreature) return; SendTabardHelp(pCreature); } void WorldSession::SendTabardHelp(Creature* pCreature) { if(!_player->IsInWorld()) return; WorldPacket data(8); data.Initialize( MSG_TABARDVENDOR_ACTIVATE ); data << pCreature->GetGUID(); SendPacket( &data ); } ////////////////////////////////////////////////////////////// /// This function handles CMSG_BANKER_ACTIVATE: ////////////////////////////////////////////////////////////// void WorldSession::HandleBankerActivateOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; recv_data >> guid; Creature *pCreature = _player->GetMapMgr()->GetCreature(guid); if(!pCreature) return; SendBankerList(pCreature); } void WorldSession::SendBankerList(Creature* pCreature) { if(!_player->IsInWorld()) return; WorldPacket data(8); data.Initialize( SMSG_SHOW_BANK ); data << pCreature->GetGUID(); SendPacket( &data ); } ////////////////////////////////////////////////////////////// /// This function handles CMSG_TRAINER_LIST ////////////////////////////////////////////////////////////// //NOTE: we select prerequirements for spell that TEACHES you //not by spell that you learn! void WorldSession::HandleTrainerListOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; // Inits, grab creature, check. uint64 guid; recv_data >> guid; Creature *train = GetPlayer()->GetMapMgr()->GetCreature(guid); if(!train) return; SendTrainerList(train); } void WorldSession::SendTrainerList(Creature* pCreature) { Trainer * pTrainer = objmgr.GetTrainer(pCreature->GetEntry()); if(pTrainer == 0) return; WorldPacket data(SMSG_TRAINER_LIST, 5000); TrainerSpell * pSpell; uint32 Spacer = 0; uint8 Status; string Text; data << pCreature->GetGUID(); data << pTrainer->TrainerType; if(pTrainer->IsSimpleTrainer) { if( (pTrainer->Req_lvl && _player->getLevel()Req_lvl) || (pTrainer->Req_rep && _player->GetStanding(pTrainer->Req_rep) < pTrainer->Req_rep_value) || (pTrainer->RequiredClass && _player->getClass() != pTrainer->RequiredClass)) { Text = pTrainer->NoTrainMsg; data << 0; //no spells for you data << Text; } else { data << pTrainer->SpellCount; for(uint32 i = 0; i < pTrainer->SpellCount; ++i) { pSpell = pTrainer->SpellList[i]; Status = TrainerGetSpellStatus(pSpell,false); data << pSpell->TeachingSpellID; data << Status; data << pSpell->Cost; data << Spacer; data << pSpell->IsProfession; data << (uint8)pSpell->RequiredLevel; data << pSpell->RequiredSkillLine; data << pSpell->RequiredSkillLineValue; data << pSpell->RequiredSpell; data << Spacer;//this is like a spell override or something, ex : (id=34568 or id=34547) or (id=36270 or id=34546) or (id=36271 or id=34548) data << Spacer; } Text = pTrainer->TrainMsg; data << Text; } } else { uint32 RequiredLevel; data << pTrainer->SpellCount; uint32 * SpellCount = (uint32*)&data.contents()[12]; for(uint32 i = 0; i < pTrainer->SpellCount; ++i) { // We need the info for the teaching spell ;) pSpell = pTrainer->SpellList[i]; RequiredLevel = pSpell->RequiredLevel; if(pSpell->RequiredClass != -1) { // Check class if(!(_player->getClassMask() & pSpell->RequiredClass)) { // Hide this spell. *SpellCount--; continue; } } //Dual Wield if(pSpell->SpellID == 674 || pSpell->SpellID == 29651) { switch(_player->getClass()) { case WARRIOR: case HUNTER: RequiredLevel = 20; break; case ROGUE: RequiredLevel = 10; break; default: RequiredLevel = uint32(-1); } } Status = TrainerGetSpellStatus(pSpell); // HACKFIX: dont show already known spells if((Status == TRAINER_STATUS_ALREADY_HAVE && _player->getClass() == PALADIN) || RequiredLevel < 0) { *SpellCount--; continue; } data << pSpell->TeachingSpellID; data << Status; data << pSpell->Cost; data << Spacer; data << pSpell->IsProfession; data << (uint8)pSpell->RequiredLevel; data << pSpell->RequiredSkillLine; data << pSpell->RequiredSkillLineValue; data << pSpell->RequiredSpell; data << Spacer;//this is like a spell owerride or something, ex : (id=34568 or id=34547) or (id=36270 or id=34546) or (id=36271 or id=34548) data << Spacer; } Text = pTrainer->TalkMessage; data << Text; } SendPacket(&data); } void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvPacket) { if(!_player->IsInWorld()) return; uint64 Guid; uint32 TeachingSpellID; recvPacket >> Guid >> TeachingSpellID; Creature *pCreature = _player->GetMapMgr()->GetCreature(Guid); if(pCreature == 0) return; Trainer *pTrainer = objmgr.GetTrainer(pCreature->GetEntry()); if(pTrainer == 0) return; // Find teaching spell index. uint32 i; for(i = 0; i < pTrainer->SpellCount; ++i) { if(pTrainer->SpellList[i]->TeachingSpellID == TeachingSpellID) break; } if(i == pTrainer->SpellCount) return; TrainerSpell *pSpell = pTrainer->SpellList[i]; if(TrainerGetSpellStatus(pSpell) > 0) return; _player->ModUInt32Value(PLAYER_FIELD_COINAGE, -(int32)pSpell->Cost); if(pSpell->DeleteSpell) { // Remove old spell. _player->removeSpell(pSpell->DeleteSpell, true, true, pSpell->DeleteSpell); } // Cast teaching spell on player pCreature->CastSpell(_player, pSpell->TeachingSpellID, true); } uint8 WorldSession::TrainerGetSpellStatus(TrainerSpell* pSpell,bool oldtrainer) { if(oldtrainer==false) { if( (pSpell->RequiredLevel && _player->getLevel()RequiredLevel) || (pSpell->RequiredSpell && !_player->HasSpell(pSpell->RequiredSpell)) || (pSpell->Cost && _player->GetUInt32Value(PLAYER_FIELD_COINAGE) < pSpell->Cost) || (pSpell->RequiredSkillLine && _player->_GetSkillLineCurrent(pSpell->RequiredSkillLine,false) < pSpell->RequiredSkillLineValue) || (pSpell->IsProfession && !_player->_HasSkillLine(pSpell->RequiredSkillLine) && _player->GetUInt32Value(PLAYER_CHARACTER_POINTS2) == 0) ) return TRAINER_STATUS_NOT_LEARNABLE; if( _player->GetMaxLearnedSpellLevel(pSpell->TeachSpellID)>= pSpell->SpellRank) return TRAINER_STATUS_ALREADY_HAVE; return TRAINER_STATUS_LEARNABLE; } else { // check player's lavel if(pSpell->RequiredLevel && _player->getLevel() < pSpell->RequiredLevel) return TRAINER_STATUS_NOT_LEARNABLE; // check if we need a skill line if(pSpell->RequiredSkillLine && !_player->_HasSkillLine( pSpell->RequiredSkillLine ) ) return TRAINER_STATUS_NOT_LEARNABLE; // check if we have the required value if(pSpell->RequiredSkillLineValue && pSpell->RequiredSkillLine && _player->_GetSkillLineCurrent( pSpell->RequiredSkillLine,false ) < pSpell->RequiredSkillLineValue ) return TRAINER_STATUS_NOT_LEARNABLE; // check if we already have this spell if(_player->HasSpell( pSpell->SpellID ) || _player->HasDeletedSpell(pSpell->SpellID) ) // Check deleted here too.) return TRAINER_STATUS_ALREADY_HAVE; // check if we have a required spell if(pSpell->RequiredSpell && !_player->HasSpell( pSpell->RequiredSpell)) return TRAINER_STATUS_NOT_LEARNABLE; // check if we have the $$$ if(pSpell->Cost && _player->GetUInt32Value(PLAYER_FIELD_COINAGE) < pSpell->Cost) return TRAINER_STATUS_NOT_LEARNABLE; // see if we passed the profession limit if(pSpell->IsProfession && !_player->_HasSkillLine(pSpell->RequiredSkillLine) && pSpell->TeachingLine && _player->GetUInt32Value(PLAYER_CHARACTER_POINTS2) == 0) return TRAINER_STATUS_NOT_LEARNABLE; return TRAINER_STATUS_LEARNABLE; } } ////////////////////////////////////////////////////////////// /// This function handles CMSG_PETITION_SHOWLIST: ////////////////////////////////////////////////////////////// void WorldSession::HandleCharterShowListOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; recv_data >> guid; Creature *pCreature = _player->GetMapMgr()->GetCreature(guid); if(!pCreature) return; SendCharterRequest(pCreature); } void WorldSession::SendCharterRequest(Creature* pCreature) { if(!_player->IsInWorld()) return; WorldPacket data(29); data.Initialize( SMSG_PETITION_SHOWLIST ); data << pCreature->GetGUID(); data << uint8(1); // BOOL SHOW_COST = 1 data << uint32(1); // unknown data << uint16(0x16E7); // ItemId of the guild charter data << float(0.62890625); // strange floating point data << uint16(0); // unknown // data << uint32(0x3F21); // unknown data << uint32(1000); // charter prise data << uint32(0); // unknown, maybe charter type data << uint32(9); // amount of unique players needed to sign the charter SendPacket( &data ); } ////////////////////////////////////////////////////////////// /// This function handles MSG_AUCTION_HELLO: ////////////////////////////////////////////////////////////// void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; recv_data >> guid; Creature* auctioneer = _player->GetMapMgr()->GetCreature(guid); if(!auctioneer) return; SendAuctionList(auctioneer); } void WorldSession::SendAuctionList(Creature* auctioneer) { AuctionHouse* AH = sAuctionMgr.GetAuctionHouse(auctioneer->GetEntry()); if(!AH) { sChatHandler.BlueSystemMessage(this, "Report to devs: Unbound auction house npc %u.", auctioneer->GetEntry()); return; } WorldPacket data(MSG_AUCTION_HELLO, 12); data << auctioneer->GetGUID(); data << uint32(AH->GetID()); SendPacket( &data ); } ////////////////////////////////////////////////////////////// /// This function handles CMSG_GOSSIP_HELLO: ////////////////////////////////////////////////////////////// void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; list::iterator it; std::set ql; recv_data >> guid; Creature *qst_giver = _player->GetMapMgr()->GetCreature(guid); if(!qst_giver) return; //stop when talked to for 3 min if(qst_giver->GetAIInterface()) qst_giver->GetAIInterface()->StopMovement(180000); // reputation _player->Reputation_OnTalk(qst_giver->m_factionDBC); sLog.outDebug( "WORLD: Received CMSG_GOSSIP_HELLO from %u",GUID_LOPART(guid) ); /* script */ ScriptSystem->OnCreatureEvent(qst_giver, _player, CREATURE_EVENT_ON_GOSSIP_TALK); GossipScript * Script = qst_giver->GetGossipScript(); if(!Script) return; if (qst_giver->isQuestGiver() && qst_giver->HasQuests()) { WorldPacket data; data.SetOpcode(SMSG_GOSSIP_MESSAGE); Script->GossipHello(qst_giver, _player, false); if(!_player->CurrentGossipMenu) return; _player->CurrentGossipMenu->BuildPacket(data); uint32 count=0;//sQuestMgr.ActiveQuestsCount(qst_giver, GetPlayer()); uint32 pos=data.wpos(); data << uint32(count); for (it = qst_giver->QuestsBegin(); it != qst_giver->QuestsEnd(); ++it) { uint32 status = sQuestMgr.CalcQuestStatus(qst_giver, GetPlayer(), *it); if (status >= QMGR_QUEST_NOT_FINISHED) { if (!ql.count((*it)->qst->id) ) { ql.insert((*it)->qst->id); count++; data << (*it)->qst->id; data << status;//sQuestMgr.CalcQuestStatus(qst_giver, GetPlayer(), *it); data << uint32(0); data << (*it)->qst->title; } } } data.wpos(pos); data << count; SendPacket(&data); sLog.outDebug( "WORLD: Sent SMSG_GOSSIP_MESSAGE" ); } else { Script->GossipHello(qst_giver, _player, true); } } ////////////////////////////////////////////////////////////// /// This function handles CMSG_GOSSIP_SELECT_OPTION: ////////////////////////////////////////////////////////////// void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; //WorldPacket data; uint32 option; uint64 guid; recv_data >> guid >> option; sLog.outDetail("WORLD: CMSG_GOSSIP_SELECT_OPTION Option %i Guid %.8X", option, guid ); Creature *qst_giver = _player->GetMapMgr()->GetCreature(guid); if(!qst_giver) return; GossipScript * Script = qst_giver->GetGossipScript(); if(!Script) return; uint32 IntId = 1; if(_player->CurrentGossipMenu) { GossipMenuItem item = _player->CurrentGossipMenu->GetItem(option); IntId = item.IntId; } Script->GossipSelectOption(qst_giver, _player, option, IntId); } ////////////////////////////////////////////////////////////// /// This function handles CMSG_SPIRIT_HEALER_ACTIVATE: ////////////////////////////////////////////////////////////// void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld() ||!_player->isDead()) return; GetPlayer( )->DeathDurabilityLoss(0.25); GetPlayer( )->ResurrectPlayer(); Aura *aur = GetPlayer()->FindAura(15007); if(aur) // If the player already have the aura, just extend it. { GetPlayer()->SetAurDuration(15007,aur->GetDuration()); } else // else add him one, that fucker, he think he will get away!? { SpellEntry *spellInfo = sSpellStore.LookupEntry( 15007 );//resurrection sickness SpellCastTargets targets; targets.m_unitTarget = GetPlayer()->GetGUID(); Spell*sp=new Spell(_player,spellInfo,true,NULL); sp->prepare(&targets); } GetPlayer( )->SetUInt32Value(UNIT_FIELD_HEALTH, GetPlayer()->GetUInt32Value(UNIT_FIELD_MAXHEALTH)/2); } ////////////////////////////////////////////////////////////// /// This function handles CMSG_NPC_TEXT_QUERY: ////////////////////////////////////////////////////////////// void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) { WorldPacket data; uint32 textID; uint64 targetGuid; GossipText *pGossip; recv_data >> textID; sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID ); recv_data >> targetGuid; GetPlayer()->SetUInt64Value(UNIT_FIELD_TARGET, targetGuid); pGossip = NpcTextStorage.LookupEntry(textID); data.Initialize( SMSG_NPC_TEXT_UPDATE ); data << textID; if(pGossip) { data << float(1.0f); // Unknown for(uint32 i=0;i<8;i++) { if(pGossip->Texts[i].Text[0][0]) data << pGossip->Texts[i].Text[1]; else data << pGossip->Texts[i].Text[0]; if(pGossip->Texts[i].Text[1][0]) data << pGossip->Texts[i].Text[0]; else data << pGossip->Texts[i].Text[1]; data << pGossip->Texts[i].Lang; data << uint32(0x00); // Was prob.. but if you set it to 0 emotes work ;) for(uint32 e=0;e<6;e++) data << uint32(pGossip->Texts[i].Emote[e]); if(i!=7) data << uint32(0x00); // don't append to last } } else { data << float(1.0f); // unknown data << "Hey there, $N. How can I help you?"; data << "Hey there, $N. How can I help you?"; data << uint32(0x00); // ? data << uint32(0x00); // ? for(uint32 e=0;e<6;e++) data << uint32(0x00); for(int i=0;i<7;i++) { data << uint32(0x00); data << uint8(0x00) << uint8(0x00); data << uint32(0x00); // ? data << uint32(0x00); // ? for(uint32 e=0;e<6;e++) data << uint32(0x00); // emote 1 } } SendPacket(&data); return; } void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data ) { if(!_player->IsInWorld()) return; uint64 guid; recv_data >> guid; Creature *pC = _player->GetMapMgr()->GetCreature(guid); if(!pC) return; SendInnkeeperBind(pC); } #define ITEM_ID_HEARTH_STONE 6948 #define BIND_SPELL_ID 3286 void WorldSession::SendInnkeeperBind(Creature* pCreature) { if(!_player->IsInWorld()) return; WorldPacket data(45); if(!_player->bHasBindDialogOpen) { OutPacket(SMSG_GOSSIP_COMPLETE, 0, NULL); data.Initialize(SMSG_BINDER_CONFIRM); data << pCreature->GetGUID() << _player->GetZoneId(); SendPacket(&data); _player->bHasBindDialogOpen = true; return; } // Add a hearthstone if they don't have one if(!_player->GetItemInterface()->GetItemCount(ITEM_ID_HEARTH_STONE, true)) { // We don't have a hearthstone. Add one. if(_player->GetItemInterface()->CalculateFreeSlots(NULL) > 0) { BuildItemPushResult(&data, _player->GetGUID(), ITEM_PUSH_TYPE_RECEIVE, 1, ITEM_ID_HEARTH_STONE, 0); SendPacket(&data); Item *item = objmgr.CreateItem( ITEM_ID_HEARTH_STONE, _player); _player->GetItemInterface()->AddItemToFreeSlot(item); } } _player->bHasBindDialogOpen = false; _player->SetBindPoint(_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetMapId(),_player->GetZoneId()); data.Initialize(SMSG_BINDPOINTUPDATE); data << _player->GetBindPositionX() << _player->GetBindPositionY() << _player->GetBindPositionZ() << _player->GetBindMapId() << _player->GetBindZoneId(); SendPacket( &data ); data.Initialize(SMSG_PLAYERBOUND); data << pCreature->GetGUID() << _player->GetBindZoneId(); SendPacket(&data); OutPacket(SMSG_GOSSIP_COMPLETE, 0, NULL); data.Initialize( SMSG_SPELL_START ); data << pCreature->GetNewGUID(); data << pCreature->GetNewGUID(); data << uint32(BIND_SPELL_ID); data << uint16(0); data << uint32(0); data << uint16(2); data << _player->GetGUID(); _player->SendMessageToSet(&data, true); data.Initialize( SMSG_SPELL_GO ); data << pCreature->GetNewGUID(); data << pCreature->GetNewGUID(); data << uint32(BIND_SPELL_ID); // spellID data << uint8(0) << uint8(1); // flags data << uint8(1); // amount of targets data << _player->GetGUID(); data << uint8(0); data << uint16(2); data << _player->GetGUID(); _player->SendMessageToSet( &data, true ); } #undef ITEM_ID_HEARTH_STONE #undef BIND_SPELL_ID void WorldSession::SendSpiritHealerRequest(Creature* pCreature) { WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8); data << pCreature->GetGUID(); SendPacket(&data); }