/*
* 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"
#ifndef WIN32
#include
#include
#include
#include
#include
#include
#include
#endif
initialiseSingleton(ScriptMgr);
initialiseSingleton(HookInterface);
void ScriptMgr::LoadScripts()
{
if(!HookInterface::getSingletonPtr())
new HookInterface;
sLog.outString("Loading External Script Libraries...");
sLog.outString("");
string start_path = Config.MainConfig.GetStringDefault("Script", "BinaryLocation", "script_bin") + "\\";
string search_path = start_path + "*.";
/* Loading system for win32 */
#ifdef WIN32
search_path += "dll";
WIN32_FIND_DATA data;
uint32 count = 0;
HANDLE find_handle = FindFirstFile(search_path.c_str(), &data);
if(find_handle == INVALID_HANDLE_VALUE)
sLog.outError(" No external scripts found! Server will continue to function with limited functionality.");
else
{
do
{
string full_path = start_path + data.cFileName;
HMODULE mod = LoadLibrary(full_path.c_str());
printf(" %s : 0x%.08X : ", data.cFileName, ((uint32)mod));
if(mod == 0)
printf("error!\n");
else
{
// find version import
exp_get_version vcall = (exp_get_version)GetProcAddress(mod, "_exp_get_version");
exp_script_register rcall = (exp_script_register)GetProcAddress(mod, "_exp_script_register");
if(vcall == 0 || rcall == 0)
{
printf("version functions not found!\n");
FreeLibrary(mod);
}
else
{
uint32 version = vcall();
if(UINT32_LOPART(version) == SCRIPTLIB_VERSION_MINOR && UINT32_HIPART(version) == SCRIPTLIB_VERSION_MAJOR)
{
_handles.push_back(((SCRIPT_MODULE)mod));
printf("v%u.%u : ", UINT32_HIPART(version), UINT32_LOPART(version));
rcall(this);
printf("loaded.\n");
++count;
}
else
{
FreeLibrary(mod);
printf("version mismatch!\n");
}
}
}
}
while(FindNextFile(find_handle, &data));
FindClose(find_handle);
sLog.outString("");
sLog.outString("Loaded %u external libraries.", count);
sLog.outString("");
}
#else
/* Loading system for *nix */
struct dirent ** list;
int filecount = scandir(PREFIX "/lib/", &list, 0, 0);
uint32 count = 0;
if(!filecount || !list || filecount < 0)
sLog.outError(" No external scripts found! Server will continue to function with limited functionality.");
else
{
char *ext;
while(filecount--)
{
ext = strrchr(list[filecount]->d_name, '.');
if (ext != NULL && !strcmp(ext, ".so")) {
string full_path = "../lib/" + string(list[filecount]->d_name);
SCRIPT_MODULE mod = dlopen(full_path.c_str(), RTLD_NOW);
printf(" %s : 0x%08X : ", list[filecount]->d_name, (unsigned int)mod);
if(mod == 0)
printf("error! [%s]\n", dlerror());
else
{
// find version import
exp_get_version vcall = (exp_get_version)dlsym(mod, "_exp_get_version");
exp_script_register rcall = (exp_script_register)dlsym(mod, "_exp_script_register");
if(vcall == 0 || rcall == 0)
{
printf("version functions not found!\n");
dlclose(mod);
}
else
{
uint32 version = vcall();
if(UINT32_LOPART(version) == SCRIPTLIB_VERSION_MINOR && UINT32_HIPART(version) == SCRIPTLIB_VERSION_MAJOR)
{
_handles.push_back(((SCRIPT_MODULE)mod));
printf("v%u.%u : ", UINT32_HIPART(version), UINT32_LOPART(version));
rcall(this);
printf("loaded.\n");
++count;
}
else
{
dlclose(mod);
printf("version mismatch!\n");
}
}
}
}
free(list[filecount]);
}
free(list);
sLog.outString("");
sLog.outString("Loaded %u external libraries.", count);
sLog.outString("");
}
#endif
}
void ScriptMgr::UnloadScripts()
{
if(HookInterface::getSingletonPtr())
delete HookInterface::getSingletonPtr();
LibraryHandleMap::iterator itr = _handles.begin();
for(; itr != _handles.end(); ++itr)
{
#ifdef WIN32
FreeLibrary(((HMODULE)*itr));
#else
dlclose(*itr);
#endif
}
_handles.clear();
}
void ScriptMgr::register_creature_script(uint32 entry, exp_create_creature_ai callback)
{
_creatures.insert( CreatureCreateMap::value_type( entry, callback ) );
}
void ScriptMgr::register_gameobject_script(uint32 entry, exp_create_gameobject_ai callback)
{
_gameobjects.insert( GameObjectCreateMap::value_type( entry, callback ) );
}
void ScriptMgr::register_dummy_aura(uint32 entry, exp_handle_dummy_aura callback)
{
_auras.insert( HandleDummyAuraMap::value_type( entry, callback ) );
}
void ScriptMgr::register_dummy_spell(uint32 entry, exp_handle_dummy_spell callback)
{
_spells.insert( HandleDummySpellMap::value_type( entry, callback ) );
}
void ScriptMgr::register_gossip_script(uint32 entry, exp_create_gossip_script callback)
{
_gossips.insert( GossipCreateMap::value_type( entry, callback ) );
}
CreatureAIScript* ScriptMgr::CreateAIScriptClassForEntry(Creature* pCreature)
{
CreatureCreateMap::iterator itr = _creatures.find(pCreature->GetEntry());
if(itr == _creatures.end())
return NULL;
exp_create_creature_ai function_ptr = itr->second;
return (function_ptr)(pCreature);
}
GameObjectAIScript * ScriptMgr::CreateAIScriptClassForGameObject(uint32 uEntryId, GameObject* pGameObject)
{
GameObjectCreateMap::iterator itr = _gameobjects.find(pGameObject->GetEntry());
if(itr == _gameobjects.end())
return NULL;
exp_create_gameobject_ai function_ptr = itr->second;
return (function_ptr)(pGameObject);
}
GossipScript * ScriptMgr::GetGossipScript(uint32 uEntryId)
{
GossipCreateMap::iterator itr = _gossips.find(uEntryId);
if(itr == _gossips.end())
{
return new GossipScript;
}
exp_create_gossip_script function_ptr = itr->second;
return (function_ptr)();
}
bool ScriptMgr::CallScriptedDummySpell(uint32 uSpellId, uint32 i, Spell* pSpell)
{
HandleDummySpellMap::iterator itr = _spells.find(uSpellId);
if(itr == _spells.end())
return false;
exp_handle_dummy_spell function_ptr = itr->second;
return (function_ptr)(i, pSpell);
}
bool ScriptMgr::CallScriptedDummyAura(uint32 uSpellId, uint32 i, Aura* pAura, bool apply)
{
HandleDummyAuraMap::iterator itr = _auras.find(uSpellId);
if(itr == _auras.end())
return false;
exp_handle_dummy_aura function_ptr = itr->second;
return (function_ptr)(i, pAura, apply);
}
/* CreatureAI Stuff */
CreatureAIScript::CreatureAIScript(Creature* creature) : _unit(creature)
{
}
void CreatureAIScript::RegisterAIUpdateEvent(uint32 frequency)
{
sEventMgr.AddEvent(_unit, &Creature::CallScriptUpdate, EVENT_SCRIPT_UPDATE_EVENT, frequency, 0,0);
}
void CreatureAIScript::RemoveAIUpdateEvent()
{
sEventMgr.RemoveEvents(_unit, EVENT_SCRIPT_UPDATE_EVENT);
}
/* GameObjectAI Stuff */
GameObjectAIScript::GameObjectAIScript(GameObject* goinstance) : _gameobject(goinstance)
{
}
void GameObjectAIScript::RegisterAIUpdateEvent(uint32 frequency)
{
sEventMgr.AddEvent(_gameobject, &GameObject::CallScriptUpdate, EVENT_SCRIPT_UPDATE_EVENT, frequency, 0,0);
}
/* InstanceAI Stuff */
InstanceScript::InstanceScript(MapMgr *instance) : _instance(instance)
{
}
/* QuestScript Stuff */
QuestScript::QuestScript(QuestLogEntry *qle) : _qLogEntry(qle)
{
}
void QuestScript::RegisterQuestEvent(uint32 frequency)
{
sEventMgr.AddEvent(_qLogEntry, &QuestLogEntry::CallScriptUpdate, EVENT_SCRIPT_UPDATE_EVENT, frequency, 0,0);
}
void QuestScript::RemoveQuestEvent()
{
sEventMgr.RemoveEvents(_qLogEntry, EVENT_SCRIPT_UPDATE_EVENT);
}
/* Gossip Stuff*/
GossipScript::GossipScript()
{
AutoCreated = false;
}
void GossipScript::GossipEnd(Creature* pCreature, Player* Plr)
{
Plr->CleanupGossipMenu();
}
void GossipScript::GossipHello(Creature* pCreature, Player* Plr, bool AutoSend)
{
GossipMenu *Menu;
uint32 TextID = 2;
Trainer *pTrainer = pCreature->GetTrainer();
uint32 Text = objmgr.GetGossipTextForNpc(pCreature->GetEntry());
if(Text != 0)
{
GossipText * text = NpcTextStorage.LookupEntry(Text);
if(text != 0)
TextID = Text;
}
objmgr.CreateGossipMenuForPlayer(&Menu, pCreature->GetGUID(), TextID, Plr);
uint32 flags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
if(flags & UNIT_NPC_FLAG_VENDOR)
Menu->AddItem(1, "I would like to browse your goods", 1);
if(flags & UNIT_NPC_FLAG_TRAINER && pTrainer != 0)
{
string name = pCreature->GetCreatureName()->Name;
string::size_type pos = name.find(" "); // only take first name
if(pos != string::npos)
name = name.substr(0, pos);
string msg = "I seek ";
if(pTrainer->RequiredClass && pTrainer->RequiredClass != Plr->getClass())
{
// WARRIOR PALADIN HUNTER ROGUE PRIEST - SHAMAN MAGE WARLOCK - DRUID
uint32 notclass[13] = {0 ,5721 ,3976 ,5839 ,4797 ,4435 ,0 ,5006 ,539 ,5836 ,0,4774};
Menu->SetTextID(notclass[pTrainer->RequiredClass]);
} else if(pTrainer->RequiredClass) {
// WARRIOR PALADIN HUNTER ROGUE PRIEST - SHAMAN MAGE WARLOCK - DRUID
uint32 isclass[13] = {0 ,1040 ,3974 ,4864 ,4835 ,4436 ,0 ,5005 ,538 ,5835 ,0,4775};
Menu->SetTextID(isclass[pTrainer->RequiredClass]);
switch(Plr->getClass())
{
case MAGE:
msg += "mage";
break;
case SHAMAN:
msg += "shaman";
break;
case WARRIOR:
msg += "warrior";
break;
case PALADIN:
msg += "paladin";
break;
case WARLOCK:
msg += "warlock";
break;
case HUNTER:
msg += "hunter";
break;
case ROGUE:
msg += "rogue";
break;
case DRUID:
msg += "druid";
break;
case PRIEST:
msg += "priest";
break;
}
msg += " training, ";
msg += name;
msg += ".";
Menu->AddItem(3, msg.c_str(), 2);
}
else
{
msg += "training, ";
msg += name;
msg += ".";
Menu->AddItem(3, msg.c_str(), 2);
}
}
if(flags & UNIT_NPC_FLAG_TAXIVENDOR)
Menu->AddItem(2, "Give me a ride.", 3);
if(flags & UNIT_NPC_FLAG_AUCTIONEER)
Menu->AddItem(0, "I would like to make a bid.", 4);
if(flags & UNIT_NPC_FLAG_INNKEEPER)
Menu->AddItem(5, "Make this inn your home.", 5);
if(flags & UNIT_NPC_FLAG_BANKER)
Menu->AddItem(0, "I would like to check my deposit box.", 6);
if(flags & UNIT_NPC_FLAG_SPIRITHEALER)
Menu->AddItem(0, "Bring me back to life.", 7);
if(flags & UNIT_NPC_FLAG_PETITIONER)
Menu->AddItem(0, "How do I create a guild?", 8);
if(flags & UNIT_NPC_FLAG_TABARDCHANGER)
Menu->AddItem(0, "I want to create a guild crest.", 9);
if(flags & UNIT_NPC_FLAG_BATTLEFIELDPERSON)
Menu->AddItem(0, "I would like to go to the battleground.", 10);
if( pTrainer &&
pTrainer->RequiredClass && // class trainer
pTrainer->RequiredClass == Plr->getClass() && // correct class
pCreature->getLevel() > 10 && // creature level
Plr->getLevel() > 10 ) // player level
{
Menu->AddItem(0, "I would like to reset my talents.", 11);
}
if(AutoSend)
Menu->SendTo(Plr);
}
void GossipScript::GossipSelectOption(Creature* pCreature, Player* Plr, uint32 Id, uint32 IntId)
{
switch(IntId)
{
case 1:
// vendor
Plr->GetSession()->SendInventoryList(pCreature);
break;
case 2:
// trainer
Plr->GetSession()->SendTrainerList(pCreature);
break;
case 3:
// taxi
Plr->GetSession()->SendTaxiList(pCreature);
break;
case 4:
// auction
Plr->GetSession()->SendAuctionList(pCreature);
break;
case 5:
// innkeeper
Plr->GetSession()->SendInnkeeperBind(pCreature);
break;
case 6:
// banker
Plr->GetSession()->SendBankerList(pCreature);
break;
case 7:
// spirit
Plr->GetSession()->SendSpiritHealerRequest(pCreature);
break;
case 8:
// petition
Plr->GetSession()->SendCharterRequest(pCreature);
break;
case 9:
// guild crest
Plr->GetSession()->SendTabardHelp(pCreature);
break;
case 10:
// battlefield
Plr->GetSession()->SendBattlegroundList(pCreature, 2);
break;
case 11:
// switch to talent reset message
{
GossipMenu *Menu;
objmgr.CreateGossipMenuForPlayer(&Menu, pCreature->GetGUID(), 5674, Plr);
Menu->AddItem(0, "I understand, continue.", 12);
Menu->SendTo(Plr);
}break;
case 12:
// talent reset
{
Plr->Gossip_Complete();
Plr->SendTalentResetConfirm();
}break;
default:
sLog.outError("Unknown IntId %u on entry %u", IntId, pCreature->GetEntry());
break;
}
}
void GossipScript::Destroy()
{
delete this;
}
void ScriptMgr::register_hook(ServerHookEvents event, void * function_pointer)
{
ASSERT(event < NUM_SERVER_HOOKS);
_hooks[event].push_back(function_pointer);
}
/* Hook Implementations */
#define OUTER_LOOP_BEGIN(type, fptr_type) if(!sScriptMgr._hooks[type].size()) { \
return; } \
fptr_type call; \
for(ServerHookList::iterator itr = sScriptMgr._hooks[type].begin(); itr != sScriptMgr._hooks[type].end(); ++itr) { \
call = ((fptr_type)*itr);
#define OUTER_LOOP_END }
#define OUTER_LOOP_BEGIN_COND(type, fptr_type) if(!sScriptMgr._hooks[type].size()) { \
return true; } \
fptr_type call; \
bool ret_val = true; \
for(ServerHookList::iterator itr = sScriptMgr._hooks[type].begin(); itr != sScriptMgr._hooks[type].end(); ++itr) { \
call = ((fptr_type)*itr);
#define OUTER_LOOP_END_COND } return ret_val;
bool HookInterface::OnNewCharacter(uint32 Race, uint32 Class, WorldSession * Session, const char * Name)
{
OUTER_LOOP_BEGIN_COND(SERVER_HOOK_EVENT_ON_NEW_CHARACTER, tOnNewCharacter)
ret_val = (call)(Race, Class, Session, Name);
OUTER_LOOP_END_COND
}
void HookInterface::OnKillPlayer(Player * pPlayer, Player * pVictim)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_KILL_PLAYER, tOnKillPlayer)
(call)(pPlayer, pVictim);
OUTER_LOOP_END
}
void HookInterface::OnFirstEnterWorld(Player * pPlayer)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_FIRST_ENTER_WORLD, tOnFirstEnterWorld)
(call)(pPlayer);
OUTER_LOOP_END
}
void HookInterface::OnEnterWorld(Player * pPlayer)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_ENTER_WORLD, tOnEnterWorld)
(call)(pPlayer);
OUTER_LOOP_END
}
void HookInterface::OnGuildCreate(Player * pLeader, Guild * pGuild)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_GUILD_CREATE, tOnGuildCreate)
(call)(pLeader, pGuild);
OUTER_LOOP_END
}
void HookInterface::OnGuildJoin(Player * pPlayer, Guild * pGuild)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_GUILD_JOIN, tOnGuildJoin)
(call)(pPlayer, pGuild);
OUTER_LOOP_END
}
void HookInterface::OnDeath(Player * pPlayer)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_DEATH, tOnDeath)
(call)(pPlayer);
OUTER_LOOP_END
}
bool HookInterface::OnRepop(Player * pPlayer)
{
OUTER_LOOP_BEGIN_COND(SERVER_HOOK_EVENT_ON_REPOP, tOnRepop)
ret_val = (call)(pPlayer);
OUTER_LOOP_END_COND
}
void HookInterface::OnEmote(Player * pPlayer, uint32 Emote)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_EMOTE, tOnEmote)
(call)(pPlayer, Emote);
OUTER_LOOP_END
}
void HookInterface::OnEnterCombat(Player * pPlayer, Unit * pTarget)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_ENTER_COMBAT, tOnEnterCombat)
(call)(pPlayer, pTarget);
OUTER_LOOP_END
}
bool HookInterface::OnCastSpell(Player * pPlayer, SpellEntry* pSpell)
{
OUTER_LOOP_BEGIN_COND(SERVER_HOOK_EVENT_ON_CAST_SPELL, tOnCastSpell)
ret_val = (call)(pPlayer, pSpell);
OUTER_LOOP_END_COND
}
bool HookInterface::OnLogoutRequest(Player * pPlayer)
{
OUTER_LOOP_BEGIN_COND(SERVER_HOOK_EVENT_ON_LOGOUT, tOnLogoutRequest)
ret_val = (call)(pPlayer);
OUTER_LOOP_END_COND
}
void HookInterface::OnQuestAccept(Player * pPlayer, Quest * pQuest)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_QUEST_ACCEPT, tOnQuestAccept)
(call)(pPlayer, pQuest);
OUTER_LOOP_END
}
void HookInterface::OnZone(Player * pPlayer, uint32 Zone)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_ZONE, tOnZone)
(call)(pPlayer, Zone);
OUTER_LOOP_END
}
void HookInterface::OnChat(Player * pPlayer, uint32 Type, uint32 Lang, const char * Message, const char * Misc)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_CHAT, tOnChat)
(call)(pPlayer, Type, Lang, Message, Misc);
OUTER_LOOP_END
}
void HookInterface::OnLoot(Player * pPlayer, Unit * pTarget, uint32 Money, uint32 ItemId)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_LOOT, tOnLoot)
(call)(pPlayer, pTarget, Money, ItemId);
OUTER_LOOP_END
}
void HookInterface::OnEnterWorld2(Player * pPlayer)
{
OUTER_LOOP_BEGIN(SERVER_HOOK_EVENT_ON_ENTER_WORLD_2, tOnEnterWorld)
(call)(pPlayer);
OUTER_LOOP_END
}