First Commit

For Azeroth!
This commit is contained in:
Yehonal
2016-06-26 10:39:44 +02:00
commit e8e94a0a66
3777 changed files with 1419268 additions and 0 deletions

35
src/server/CMakeLists.txt Normal file
View File

@@ -0,0 +1,35 @@
# Copyright (C)
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# Enforce compileparameters for corebuilds under GCC
# This to stop a few silly crashes that could have been avoided IF people
# weren't doing some -O3 psychooptimizations etc.
if(CMAKE_COMPILER_IS_GNUCXX AND NOT MINGW)
add_definitions(-fno-delete-null-pointer-checks)
endif()
if( SERVERS )
set(sources_windows_Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging/WheatyExceptionReport.cpp
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging/WheatyExceptionReport.h
)
add_subdirectory(shared)
add_subdirectory(game)
add_subdirectory(collision)
add_subdirectory(authserver)
add_subdirectory(scripts)
add_subdirectory(worldserver)
else()
if( TOOLS )
add_subdirectory(collision)
add_subdirectory(shared)
endif()
endif()

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "AuthCodes.h"
#include <cstddef>
namespace AuthHelper
{
static RealmBuildInfo const PostBcAcceptedClientBuilds[] =
{
{15595, 4, 3, 4, ' '},
{14545, 4, 2, 2, ' '},
{13623, 4, 0, 6, 'a'},
{12340, 3, 3, 5, 'a'},
{11723, 3, 3, 3, 'a'},
{11403, 3, 3, 2, ' '},
{11159, 3, 3, 0, 'a'},
{10505, 3, 2, 2, 'a'},
{9947, 3, 1, 3, ' '},
{8606, 2, 4, 3, ' '},
{0, 0, 0, 0, ' '} // terminator
};
static RealmBuildInfo const PreBcAcceptedClientBuilds[] =
{
{6141, 1, 12, 3, ' '},
{6005, 1, 12, 2, ' '},
{5875, 1, 12, 1, ' '},
{0, 0, 0, 0, ' '} // terminator
};
bool IsPreBCAcceptedClientBuild(int build)
{
for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i)
if (PreBcAcceptedClientBuilds[i].Build == build)
return true;
return false;
}
bool IsPostBCAcceptedClientBuild(int build)
{
for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i)
if (PostBcAcceptedClientBuilds[i].Build == build)
return true;
return false;
}
bool IsAcceptedClientBuild(int build)
{
return (IsPostBCAcceptedClientBuild(build) || IsPreBCAcceptedClientBuild(build));
}
RealmBuildInfo const* GetBuildInfo(int build)
{
for (int i = 0; PostBcAcceptedClientBuilds[i].Build; ++i)
if (PostBcAcceptedClientBuilds[i].Build == build)
return &PostBcAcceptedClientBuilds[i];
for (int i = 0; PreBcAcceptedClientBuilds[i].Build; ++i)
if (PreBcAcceptedClientBuilds[i].Build == build)
return &PreBcAcceptedClientBuilds[i];
return NULL;
}
};

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _AUTHCODES_H
#define _AUTHCODES_H
enum AuthResult
{
WOW_SUCCESS = 0x00,
WOW_FAIL_BANNED = 0x03,
WOW_FAIL_UNKNOWN_ACCOUNT = 0x04,
WOW_FAIL_INCORRECT_PASSWORD = 0x05,
WOW_FAIL_ALREADY_ONLINE = 0x06,
WOW_FAIL_NO_TIME = 0x07,
WOW_FAIL_DB_BUSY = 0x08,
WOW_FAIL_VERSION_INVALID = 0x09,
WOW_FAIL_VERSION_UPDATE = 0x0A,
WOW_FAIL_INVALID_SERVER = 0x0B,
WOW_FAIL_SUSPENDED = 0x0C,
WOW_FAIL_FAIL_NOACCESS = 0x0D,
WOW_SUCCESS_SURVEY = 0x0E,
WOW_FAIL_PARENTCONTROL = 0x0F,
WOW_FAIL_LOCKED_ENFORCED = 0x10,
WOW_FAIL_TRIAL_ENDED = 0x11,
WOW_FAIL_USE_BATTLENET = 0x12,
WOW_FAIL_ANTI_INDULGENCE = 0x13,
WOW_FAIL_EXPIRED = 0x14,
WOW_FAIL_NO_GAME_ACCOUNT = 0x15,
WOW_FAIL_CHARGEBACK = 0x16,
WOW_FAIL_INTERNET_GAME_ROOM_WITHOUT_BNET = 0x17,
WOW_FAIL_GAME_ACCOUNT_LOCKED = 0x18,
WOW_FAIL_UNLOCKABLE_LOCK = 0x19,
WOW_FAIL_CONVERSION_REQUIRED = 0x20,
WOW_FAIL_DISCONNECTED = 0xFF
};
enum LoginResult
{
LOGIN_OK = 0x00,
LOGIN_FAILED = 0x01,
LOGIN_FAILED2 = 0x02,
LOGIN_BANNED = 0x03,
LOGIN_UNKNOWN_ACCOUNT = 0x04,
LOGIN_UNKNOWN_ACCOUNT3 = 0x05,
LOGIN_ALREADYONLINE = 0x06,
LOGIN_NOTIME = 0x07,
LOGIN_DBBUSY = 0x08,
LOGIN_BADVERSION = 0x09,
LOGIN_DOWNLOAD_FILE = 0x0A,
LOGIN_FAILED3 = 0x0B,
LOGIN_SUSPENDED = 0x0C,
LOGIN_FAILED4 = 0x0D,
LOGIN_CONNECTED = 0x0E,
LOGIN_PARENTALCONTROL = 0x0F,
LOGIN_LOCKED_ENFORCED = 0x10
};
enum ExpansionFlags
{
POST_BC_EXP_FLAG = 0x2,
PRE_BC_EXP_FLAG = 0x1,
NO_VALID_EXP_FLAG = 0x0
};
struct RealmBuildInfo
{
int Build;
int MajorVersion;
int MinorVersion;
int BugfixVersion;
int HotfixVersion;
};
namespace AuthHelper
{
RealmBuildInfo const* GetBuildInfo(int build);
bool IsAcceptedClientBuild(int build);
bool IsPostBCAcceptedClientBuild(int build);
bool IsPreBCAcceptedClientBuild(int build);
};
#endif

View File

@@ -0,0 +1,110 @@
# Copyright (C)
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
########### authserver ###############
file(GLOB_RECURSE sources_authentication Authentication/*.cpp Authentication/*.h)
file(GLOB_RECURSE sources_realms Realms/*.cpp Realms/*.h)
file(GLOB_RECURSE sources_server Server/*.cpp Server/*.h)
file(GLOB sources_localdir *.cpp *.h)
if (USE_COREPCH)
set(authserver_PCH_HDR PrecompiledHeaders/authPCH.h)
set(authserver_PCH_SRC PrecompiledHeaders/authPCH.cpp)
endif()
set(authserver_SRCS
${authserver_SRCS}
${sources_authentication}
${sources_realms}
${sources_server}
${sources_localdir}
)
if( WIN32 )
set(authserver_SRCS
${authserver_SRCS}
${sources_windows_Debugging}
)
if ( MSVC )
set(authserver_SRCS
${authserver_SRCS}
authserver.rc
)
endif ()
endif()
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
${CMAKE_SOURCE_DIR}/src/server/shared/Cryptography
${CMAKE_SOURCE_DIR}/src/server/shared/Cryptography/Authentication
${CMAKE_SOURCE_DIR}/src/server/shared/Logging
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/Authentication
${CMAKE_CURRENT_SOURCE_DIR}/Realms
${CMAKE_CURRENT_SOURCE_DIR}/Server
${ACE_INCLUDE_DIR}
${MYSQL_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR}
)
add_executable(authserver
${authserver_SRCS}
${authserver_PCH_SRC}
)
add_dependencies(authserver revision.h)
if( NOT WIN32 )
set_target_properties(authserver PROPERTIES
COMPILE_DEFINITIONS _TRINITY_REALM_CONFIG="${CONF_DIR}/authserver.conf"
)
endif()
target_link_libraries(authserver
shared
${MYSQL_LIBRARY}
${OPENSSL_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
${ACE_LIBRARY}
)
if( WIN32 )
if ( MSVC )
add_custom_command(TARGET authserver
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/authserver.conf.dist ${CMAKE_BINARY_DIR}/bin/$(ConfigurationName)/
)
elseif ( MINGW )
add_custom_command(TARGET authserver
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/authserver.conf.dist ${CMAKE_BINARY_DIR}/bin/
)
endif()
endif()
if( UNIX )
install(TARGETS authserver DESTINATION bin)
install(FILES authserver.conf.dist DESTINATION ${CONF_DIR})
elseif( WIN32 )
install(TARGETS authserver DESTINATION "${CMAKE_INSTALL_PREFIX}")
install(FILES authserver.conf.dist DESTINATION "${CMAKE_INSTALL_PREFIX}")
endif()
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(authserver ${authserver_PCH_HDR} ${authserver_PCH_SRC})
endif()

View File

@@ -0,0 +1,330 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
/**
* @file main.cpp
* @brief Authentication Server main program
*
* This file contains the main program for the
* authentication server
*/
#include <ace/Dev_Poll_Reactor.h>
#include <ace/TP_Reactor.h>
#include <ace/ACE.h>
#include <ace/Sig_Handler.h>
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Configuration/Config.h"
#include "Log.h"
#include "SystemConfig.h"
#include "Util.h"
#include "SignalHandler.h"
#include "RealmList.h"
#include "RealmAcceptor.h"
#ifdef __linux__
#include <sched.h>
#include <sys/resource.h>
#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0
#endif
#ifndef _TRINITY_REALM_CONFIG
# define _TRINITY_REALM_CONFIG "authserver.conf"
#endif
bool StartDB();
void StopDB();
bool stopEvent = false; // Setting it to true stops the server
LoginDatabaseWorkerPool LoginDatabase; // Accessor to the authserver database
/// Handle authserver's termination signals
class AuthServerSignalHandler : public Trinity::SignalHandler
{
public:
virtual void HandleSignal(int sigNum)
{
switch (sigNum)
{
case SIGINT:
case SIGTERM:
stopEvent = true;
break;
}
}
};
/// Print out the usage string for this program on the console.
void usage(const char* prog)
{
sLog->outString("Usage: \n %s [<options>]\n"
" -c config_file use config_file as configuration file\n\r",
prog);
}
/// Launch the auth server
extern int main(int argc, char** argv)
{
// Command line parsing to get the configuration file name
char const* configFile = _TRINITY_REALM_CONFIG;
int count = 1;
while (count < argc)
{
if (strcmp(argv[count], "-c") == 0)
{
if (++count >= argc)
{
printf("Runtime-Error: -c option requires an input argument\n");
usage(argv[0]);
return 1;
}
else
configFile = argv[count];
}
++count;
}
if (!sConfigMgr->LoadInitial(configFile))
{
printf("Invalid or missing configuration file : %s\n", configFile);
printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n");
return 1;
}
sLog->outString("%s (authserver)", _FULLVERSION);
sLog->outString("<Ctrl-C> to stop.\n");
sLog->outString("Using configuration file %s.", configFile);
sLog->outDetail("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
ACE_Reactor::instance(new ACE_Reactor(new ACE_Dev_Poll_Reactor(ACE::max_handles(), 1), 1), true);
#else
ACE_Reactor::instance(new ACE_Reactor(new ACE_TP_Reactor(), true), true);
#endif
sLog->outBasic("Max allowed open files is %d", ACE::max_handles());
// authserver PID file creation
std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
if (!pidFile.empty())
{
if (uint32 pid = CreatePIDFile(pidFile))
sLog->outError("Daemon PID: %u\n", pid);
else
{
sLog->outError("Cannot create PID file %s.\n", pidFile.c_str());
return 1;
}
}
// Initialize the database connection
if (!StartDB())
return 1;
// Initialize the log database
sLog->SetLogDB(false);
sLog->SetRealmID(0); // ensure we've set realm to 0 (authserver realmid)
// Get the list of realms for the server
sRealmList->Initialize(sConfigMgr->GetIntDefault("RealmsStateUpdateDelay", 20));
if (sRealmList->size() == 0)
{
sLog->outError("No valid realms specified.");
return 1;
}
// Launch the listening network socket
RealmAcceptor acceptor;
int32 rmport = sConfigMgr->GetIntDefault("RealmServerPort", 3724);
if (rmport < 0 || rmport > 0xFFFF)
{
sLog->outError("Specified port out of allowed range (1-65535)");
return 1;
}
std::string bind_ip = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
ACE_INET_Addr bind_addr(uint16(rmport), bind_ip.c_str());
if (acceptor.open(bind_addr, ACE_Reactor::instance(), ACE_NONBLOCK) == -1)
{
sLog->outError("Auth server can not bind to %s:%d", bind_ip.c_str(), rmport);
return 1;
}
// Initialize the signal handlers
AuthServerSignalHandler SignalINT, SignalTERM;
// Register authservers's signal handlers
ACE_Sig_Handler Handler;
Handler.register_handler(SIGINT, &SignalINT);
Handler.register_handler(SIGTERM, &SignalTERM);
#if defined(_WIN32) || defined(__linux__)
///- Handle affinity for multiple processors and process priority
uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0);
bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false);
#ifdef _WIN32 // Windows
HANDLE hProcess = GetCurrentProcess();
if (affinity > 0)
{
ULONG_PTR appAff;
ULONG_PTR sysAff;
if (GetProcessAffinityMask(hProcess, &appAff, &sysAff))
{
// remove non accessible processors
ULONG_PTR currentAffinity = affinity & appAff;
if (!currentAffinity)
sLog->outError("server.authserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. Accessible processors bitmask (hex): %x", affinity, appAff);
else if (SetProcessAffinityMask(hProcess, currentAffinity))
sLog->outString("server.authserver", "Using processors (bitmask, hex): %x", currentAffinity);
else
sLog->outError("server.authserver", "Can't set used processors (hex): %x", currentAffinity);
}
}
if (highPriority)
{
if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS))
sLog->outString("server.authserver", "authserver process priority class set to HIGH");
else
sLog->outError("server.authserver", "Can't set authserver process priority class.");
}
#else // Linux
if (affinity > 0)
{
cpu_set_t mask;
CPU_ZERO(&mask);
for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i)
if (affinity & (1 << i))
CPU_SET(i, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask))
sLog->outError("server.authserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno));
else
{
CPU_ZERO(&mask);
sched_getaffinity(0, sizeof(mask), &mask);
sLog->outString("server.authserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask));
}
}
if (highPriority)
{
if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY))
sLog->outError("server.authserver", "Can't set authserver process priority class, error: %s", strerror(errno));
else
sLog->outString("server.authserver", "authserver process priority class set to %i", getpriority(PRIO_PROCESS, 0));
}
#endif
#endif
// maximum counter for next ping
uint32 numLoops = (sConfigMgr->GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
uint32 loopCounter = 0;
// possibly enable db logging; avoid massive startup spam by doing it here.
if (sConfigMgr->GetBoolDefault("EnableLogDB", false))
{
sLog->outString("Enabling database logging...");
sLog->SetLogDB(true);
}
// Wait for termination signal
while (!stopEvent)
{
// dont move this outside the loop, the reactor will modify it
ACE_Time_Value interval(0, 100000);
if (ACE_Reactor::instance()->run_reactor_event_loop(interval) == -1)
break;
if ((++loopCounter) == numLoops)
{
loopCounter = 0;
sLog->outDetail("Ping MySQL to keep connection alive");
LoginDatabase.KeepAlive();
}
}
// Close the Database Pool and library
StopDB();
sLog->outString("Halting process...");
return 0;
}
/// Initialize connection to the database
bool StartDB()
{
MySQL::Library_Init();
std::string dbstring = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
if (dbstring.empty())
{
sLog->outError("Database not specified");
return false;
}
int32 worker_threads = sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1);
if (worker_threads < 1 || worker_threads > 32)
{
sLog->outError("Improper value specified for LoginDatabase.WorkerThreads, defaulting to 1.");
worker_threads = 1;
}
int32 synch_threads = sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1);
if (synch_threads < 1 || synch_threads > 32)
{
sLog->outError("Improper value specified for LoginDatabase.SynchThreads, defaulting to 1.");
synch_threads = 1;
}
// NOTE: While authserver is singlethreaded you should keep synch_threads == 1. Increasing it is just silly since only 1 will be used ever.
if (!LoginDatabase.Open(dbstring.c_str(), uint8(worker_threads), uint8(synch_threads)))
{
sLog->outError("Cannot connect to database");
return false;
}
sLog->outString("Started auth database connection pool.");
return true;
}
/// Close the connection to the database
void StopDB()
{
LoginDatabase.Close();
MySQL::Library_End();
}

View File

@@ -0,0 +1 @@
#include "authPCH.h"

View File

@@ -0,0 +1,6 @@
#include "Configuration/Config.h"
#include "Database/DatabaseEnv.h"
#include "Log.h"
#include "RealmList.h"
#include "RealmSocket.h"
#include "Common.h"

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "RealmList.h"
#include "Database/DatabaseEnv.h"
RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL)) { }
// Load the realm list from the database
void RealmList::Initialize(uint32 updateInterval)
{
m_UpdateInterval = updateInterval;
// Get the content of the realmlist table in the database
UpdateRealms(true);
}
void RealmList::UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build)
{
// Create new if not exist or update existed
Realm& realm = m_realms[name];
realm.m_ID = id;
realm.name = name;
realm.icon = icon;
realm.flag = flag;
realm.timezone = timezone;
realm.allowedSecurityLevel = allowedSecurityLevel;
realm.populationLevel = popu;
// Append port to IP address.
realm.ExternalAddress = address;
realm.LocalAddress = localAddr;
realm.LocalSubnetMask = localSubmask;
realm.gamebuild = build;
}
void RealmList::UpdateIfNeed()
{
// maybe disabled or updated recently
if (!m_UpdateInterval || m_NextUpdateTime > time(NULL))
return;
m_NextUpdateTime = time(NULL) + m_UpdateInterval;
// Clears Realm list
m_realms.clear();
// Get the content of the realmlist table in the database
UpdateRealms();
}
void RealmList::UpdateRealms(bool init)
{
sLog->outString("Updating Realm List...");
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST);
PreparedQueryResult result = LoginDatabase.Query(stmt);
// Circle through results and add them to the realm map
if (result)
{
do
{
Field* fields = result->Fetch();
uint32 realmId = fields[0].GetUInt32();
std::string name = fields[1].GetString();
std::string externalAddress = fields[2].GetString();
std::string localAddress = fields[3].GetString();
std::string localSubmask = fields[4].GetString();
uint16 port = fields[5].GetUInt16();
uint8 icon = fields[6].GetUInt8();
RealmFlags flag = RealmFlags(fields[7].GetUInt8());
uint8 timezone = fields[8].GetUInt8();
uint8 allowedSecurityLevel = fields[9].GetUInt8();
float pop = fields[10].GetFloat();
uint32 build = fields[11].GetUInt32();
ACE_INET_Addr externalAddr(port, externalAddress.c_str(), AF_INET);
ACE_INET_Addr localAddr(port, localAddress.c_str(), AF_INET);
ACE_INET_Addr submask(0, localSubmask.c_str(), AF_INET);
UpdateRealm(realmId, name, externalAddr, localAddr, submask, icon, flag, timezone, (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), pop, build);
if (init)
sLog->outString("Added realm \"%s\" at %s:%u.", name.c_str(), m_realms[name].ExternalAddress.get_host_addr(), port);
}
while (result->NextRow());
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _REALMLIST_H
#define _REALMLIST_H
#include <ace/Singleton.h>
#include <ace/Null_Mutex.h>
#include <ace/INET_Addr.h>
#include "Common.h"
enum RealmFlags
{
REALM_FLAG_NONE = 0x00,
REALM_FLAG_INVALID = 0x01,
REALM_FLAG_OFFLINE = 0x02,
REALM_FLAG_SPECIFYBUILD = 0x04,
REALM_FLAG_UNK1 = 0x08,
REALM_FLAG_UNK2 = 0x10,
REALM_FLAG_RECOMMENDED = 0x20,
REALM_FLAG_NEW = 0x40,
REALM_FLAG_FULL = 0x80
};
// Storage object for a realm
struct Realm
{
ACE_INET_Addr ExternalAddress;
ACE_INET_Addr LocalAddress;
ACE_INET_Addr LocalSubnetMask;
std::string name;
uint8 icon;
RealmFlags flag;
uint8 timezone;
uint32 m_ID;
AccountTypes allowedSecurityLevel;
float populationLevel;
uint32 gamebuild;
};
/// Storage object for the list of realms on the server
class RealmList
{
public:
typedef std::map<std::string, Realm> RealmMap;
RealmList();
~RealmList() { }
void Initialize(uint32 updateInterval);
void UpdateIfNeed();
void AddRealm(const Realm& NewRealm) { m_realms[NewRealm.name] = NewRealm; }
RealmMap::const_iterator begin() const { return m_realms.begin(); }
RealmMap::const_iterator end() const { return m_realms.end(); }
uint32 size() const { return m_realms.size(); }
private:
void UpdateRealms(bool init=false);
void UpdateRealm(uint32 id, const std::string& name, ACE_INET_Addr const& address, ACE_INET_Addr const& localAddr, ACE_INET_Addr const& localSubmask, uint8 icon, RealmFlags flag, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build);
RealmMap m_realms;
uint32 m_UpdateInterval;
time_t m_NextUpdateTime;
};
#define sRealmList ACE_Singleton<RealmList, ACE_Null_Mutex>::instance()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _AUTHSOCKET_H
#define _AUTHSOCKET_H
#include "Common.h"
#include "BigNumber.h"
#include "RealmSocket.h"
class ACE_INET_Addr;
struct Realm;
// Handle login commands
class AuthSocket: public RealmSocket::Session
{
public:
const static int s_BYTE_SIZE = 32;
AuthSocket(RealmSocket& socket);
virtual ~AuthSocket(void);
virtual void OnRead(void);
virtual void OnAccept(void);
virtual void OnClose(void);
static ACE_INET_Addr const& GetAddressForClient(Realm const& realm, ACE_INET_Addr const& clientAddr);
bool _HandleLogonChallenge();
bool _HandleLogonProof();
bool _HandleReconnectChallenge();
bool _HandleReconnectProof();
bool _HandleRealmList();
//data transfer handle for patch
bool _HandleXferResume();
bool _HandleXferCancel();
bool _HandleXferAccept();
void _SetVSFields(const std::string& rI);
FILE* pPatch;
ACE_Thread_Mutex patcherLock;
private:
RealmSocket& socket_;
RealmSocket& socket(void) { return socket_; }
BigNumber N, s, g, v;
BigNumber b, B;
BigNumber K;
BigNumber _reconnectProof;
bool _authed;
std::string _login;
// Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ
// between enUS and enGB, which is important for the patch system
std::string _localizationName;
std::string _os;
uint16 _build;
uint8 _expversion;
AccountTypes _accountSecurityLevel;
};
#endif

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __REALMACCEPTOR_H__
#define __REALMACCEPTOR_H__
#include <ace/Acceptor.h>
#include <ace/SOCK_Acceptor.h>
#include "RealmSocket.h"
#include "AuthSocket.h"
class RealmAcceptor : public ACE_Acceptor<RealmSocket, ACE_SOCK_Acceptor>
{
public:
RealmAcceptor(void) { }
virtual ~RealmAcceptor(void)
{
if (reactor())
reactor()->cancel_timer(this, 1);
}
protected:
virtual int make_svc_handler(RealmSocket* &sh)
{
if (sh == 0)
ACE_NEW_RETURN(sh, RealmSocket, -1);
sh->reactor(reactor());
sh->set_session(new AuthSocket(*sh));
return 0;
}
virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/)
{
sLog->outBasic("Resuming acceptor");
reactor()->cancel_timer(this, 1);
return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
}
virtual int handle_accept_error(void)
{
#if defined(ENFILE) && defined(EMFILE)
if (errno == ENFILE || errno == EMFILE)
{
sLog->outError("Out of file descriptors, suspending incoming connections for 10 seconds");
reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL);
reactor()->schedule_timer(this, NULL, ACE_Time_Value(10));
}
#endif
return 0;
}
};
#endif

View File

@@ -0,0 +1,291 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <ace/OS_NS_string.h>
#include <ace/INET_Addr.h>
#include <ace/SString.h>
#include "RealmSocket.h"
#include "Log.h"
RealmSocket::Session::Session(void) { }
RealmSocket::Session::~Session(void) { }
RealmSocket::RealmSocket(void) :
input_buffer_(4096), session_(NULL),
_remoteAddress(), _remotePort(0)
{
reference_counting_policy().value(ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
msg_queue()->high_water_mark(8 * 1024 * 1024);
msg_queue()->low_water_mark(8 * 1024 * 1024);
}
RealmSocket::~RealmSocket(void)
{
if (msg_queue())
msg_queue()->close();
// delete RealmSocketObject must never be called from our code.
closing_ = true;
delete session_;
peer().close();
}
int RealmSocket::open(void * arg)
{
ACE_INET_Addr addr;
if (peer().get_remote_addr(addr) == -1)
{
sLog->outError("Error %s while opening realm socket!", ACE_OS::strerror(errno));
return -1;
}
_remoteAddress = addr.get_host_addr();
_remotePort = addr.get_port_number();
// Register with ACE Reactor
if (Base::open(arg) == -1)
return -1;
if (session_)
session_->OnAccept();
// reactor takes care of the socket from now on
remove_reference();
return 0;
}
int RealmSocket::close(u_long)
{
shutdown();
closing_ = true;
remove_reference();
return 0;
}
const std::string& RealmSocket::getRemoteAddress(void) const
{
return _remoteAddress;
}
uint16 RealmSocket::getRemotePort(void) const
{
return _remotePort;
}
size_t RealmSocket::recv_len(void) const
{
return input_buffer_.length();
}
bool RealmSocket::recv_soft(char *buf, size_t len)
{
if (input_buffer_.length() < len)
return false;
ACE_OS::memcpy(buf, input_buffer_.rd_ptr(), len);
return true;
}
bool RealmSocket::recv(char *buf, size_t len)
{
bool ret = recv_soft(buf, len);
if (ret)
recv_skip(len);
return ret;
}
void RealmSocket::recv_skip(size_t len)
{
input_buffer_.rd_ptr(len);
}
ssize_t RealmSocket::noblk_send(ACE_Message_Block &message_block)
{
const size_t len = message_block.length();
if (len == 0)
return -1;
// Try to send the message directly.
#ifdef MSG_NOSIGNAL
ssize_t n = peer().send(message_block.rd_ptr(), len, MSG_NOSIGNAL);
#else
ssize_t n = peer().send(message_block.rd_ptr(), len);
#endif // MSG_NOSIGNAL
if (n < 0)
{
if (errno == EWOULDBLOCK) // Blocking signal
return 0;
else // Error happened
return -1;
}
else if (n == 0)
{
// Can this happen ?
return -1;
}
// return bytes transmitted
return n;
}
bool RealmSocket::send(const char *buf, size_t len)
{
if (buf == NULL || len == 0)
return true;
ACE_Data_Block db(len, ACE_Message_Block::MB_DATA, (const char*)buf, 0, 0, ACE_Message_Block::DONT_DELETE, 0);
ACE_Message_Block message_block(&db, ACE_Message_Block::DONT_DELETE, 0);
message_block.wr_ptr(len);
if (msg_queue()->is_empty())
{
// Try to send it directly.
ssize_t n = noblk_send(message_block);
if (n < 0)
return false;
size_t un = size_t(n);
if (un == len)
return true;
// fall down
message_block.rd_ptr(un);
}
ACE_Message_Block* mb = message_block.clone();
if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value *)(&ACE_Time_Value::zero)) == -1)
{
mb->release();
return false;
}
if (reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK) == -1)
return false;
return true;
}
int RealmSocket::handle_output(ACE_HANDLE)
{
if (closing_)
return -1;
ACE_Message_Block* mb = 0;
if (msg_queue()->is_empty())
{
reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK);
return 0;
}
if (msg_queue()->dequeue_head(mb, (ACE_Time_Value *)(&ACE_Time_Value::zero)) == -1)
return -1;
ssize_t n = noblk_send(*mb);
if (n < 0)
{
mb->release();
return -1;
}
else if (size_t(n) == mb->length())
{
mb->release();
return 1;
}
else
{
mb->rd_ptr(n);
if (msg_queue()->enqueue_head(mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1)
{
mb->release();
return -1;
}
return 0;
}
ACE_NOTREACHED(return -1);
}
int RealmSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask)
{
// As opposed to WorldSocket::handle_close, we don't need locks here.
closing_ = true;
if (h == ACE_INVALID_HANDLE)
peer().close_writer();
if (session_)
session_->OnClose();
reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK);
return 0;
}
int RealmSocket::handle_input(ACE_HANDLE)
{
if (closing_)
return -1;
const ssize_t space = input_buffer_.space();
ssize_t n = peer().recv(input_buffer_.wr_ptr(), space);
if (n < 0)
return errno == EWOULDBLOCK ? 0 : -1;
else if (n == 0) // EOF
return -1;
input_buffer_.wr_ptr((size_t)n);
if (session_ != NULL)
{
session_->OnRead();
input_buffer_.crunch();
}
// return 1 in case there is more data to read from OS
return n == space ? 1 : 0;
}
void RealmSocket::set_session(Session* session)
{
delete session_;
session_ = session;
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __REALMSOCKET_H__
#define __REALMSOCKET_H__
#include <ace/Synch_Traits.h>
#include <ace/Svc_Handler.h>
#include <ace/SOCK_Stream.h>
#include <ace/Message_Block.h>
#include <ace/Basic_Types.h>
#include "Common.h"
class RealmSocket : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH>
{
private:
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> Base;
public:
class Session
{
public:
Session(void);
virtual ~Session(void);
virtual void OnRead(void) = 0;
virtual void OnAccept(void) = 0;
virtual void OnClose(void) = 0;
};
RealmSocket(void);
virtual ~RealmSocket(void);
size_t recv_len(void) const;
bool recv_soft(char *buf, size_t len);
bool recv(char *buf, size_t len);
void recv_skip(size_t len);
bool send(const char *buf, size_t len);
const std::string& getRemoteAddress(void) const;
uint16 getRemotePort(void) const;
virtual int open(void *);
virtual int close(u_long);
virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE);
virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE);
virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
void set_session(Session* session);
private:
ssize_t noblk_send(ACE_Message_Block &message_block);
ACE_Message_Block input_buffer_;
Session* session_;
std::string _remoteAddress;
uint16 _remotePort;
};
#endif /* __REALMSOCKET_H__ */

Binary file not shown.

View File

@@ -0,0 +1,260 @@
###############################################
# Trinity Core Auth Server configuration file #
###############################################
[authserver]
###################################################################################################
# SECTION INDEX
#
# EXAMPLE CONFIG
# AUTH SERVER SETTINGS
# MYSQL SETTINGS
#
###################################################################################################
###################################################################################################
# EXAMPLE CONFIG
#
# Variable
# Description: Brief description what the variable is doing.
# Important: Annotation for important things about this variable.
# Example: "Example, i.e. if the value is a string"
# Default: 10 - (Enabled|Comment|Variable name in case of grouped config options)
# 0 - (Disabled|Comment|Variable name in case of grouped config options)
#
# Note to developers:
# - Copy this example to keep the formatting.
# - Line breaks should be at column 100.
###################################################################################################
###################################################################################################
# AUTH SERVER SETTINGS
#
# LogsDir
# Description: Logs directory setting.
# Important: LogsDir needs to be quoted, as the string might contain space characters.
# Logs directory must exists, or log file creation will be disabled.
# Default: "" - (Log files will be stored in the current path)
LogsDir = ""
#
# MaxPingTime
# Description: Time (in minutes) between database pings.
# Default: 30
MaxPingTime = 30
#
# RealmServerPort
# Description: TCP port to reach the auth server.
# Default: 3724
RealmServerPort = 3724
#
#
# BindIP
# Description: Bind auth server to IP/hostname
# Default: "0.0.0.0" - (Bind to all IPs on the system)
BindIP = "0.0.0.0"
#
# PidFile
# Description: Auth server PID file.
# Example: "./authserver.pid" - (Enabled)
# Default: "" - (Disabled)
PidFile = ""
#
# LogLevel
# Description: Server console level of logging
# Default: 0 - (Minimum)
# 1 - (Basic)
# 2 - (Detail)
# 3 - (Full/Debug)
LogLevel = 0
#
# LogFile
# Description: Log file for main server log.
# Default: "Auth.log" - (Enabled)
# "" - (Disabled)
LogFile = "Auth.log"
#
# Debug Log Mask
# Description: Bitmask that determines which debug log output (level 3)
# will be logged.
# Possible flags:
#
# 64 - Anything related to network input/output,
# such as packet handlers and netcode logs
#
# Simply add the values together to create a bitmask.
# For more info see enum DebugLogFilters in Log.h
#
# Default: 0 (nothing)
DebugLogMask = 64
#
# SQLDriverLogFile
# Description: Log file for SQL driver events.
# Example: "SQLDriver.log" - (Enabled)
# Default: "" - (Disabled)
SQLDriverLogFile = ""
#
# SQLDriverQueryLogging
# Description: Log SQL queries to the SQLDriverLogFile and console.
# Default: 0 - (Disabled, Query errors only)
# 1 - (Enabled, Full query logging - may have performance impact)
SQLDriverQueryLogging = 0
#
# LogTimestamp
# Description: Append timestamp to the server log file name.
# Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext
# Default: 0 - (Disabled)
# 1 - (Enabled)
LogTimestamp = 0
#
# LogFileLevel
# Description: Server file level of logging
# Default: 0 - (Minimum)
# 1 - (Basic)
# 2 - (Detail)
# 3 - (Full/Debug)
LogFileLevel = 0
#
# LogColors
# Description: Colors for log messages (Format: "normal basic detail debug").
# Colors: 0 - Black
# 1 - Red
# 2 - Green
# 3 - Brown
# 4 - Blue
# 5 - Magenta
# 6 - Cyan
# 7 - Grey
# 8 - Yellow
# 9 - Lred
# 10 - Lgreen
# 11 - Lblue
# 12 - Lmagenta
# 13 - Lcyan
# 14 - White
# Example: "13 11 9 5" - (Enabled)
# Default: "" - (Disabled)
LogColors = ""
#
# EnableLogDB
# Description: Write log messages to database (LogDatabaseInfo).
# Default: 0 - (Disabled)
# 1 - (Enabled)
EnableLogDB = 0
#
# DBLogLevel
# Description: Log level of databases logging.
# Default: 1 - (Basic)
# 0 - (Minimum)
# 2 - (Detail)
# 3 - (Full/Debug)
DBLogLevel = 1
#
# UseProcessors
# Description: Processors mask for Windows and Linux based multi-processor systems.
# Example: A computer with 2 CPUs:
# 1 - 1st CPU only, 2 - 2nd CPU only, 3 - 1st and 2nd CPU, because 1 | 2 is 3
# Default: 0 - (Selected by OS)
# 1+ - (Bit mask value of selected processors)
UseProcessors = 0
#
# ProcessPriority
# Description: Process priority setting for Windows and Linux based systems.
# Details: On Linux, a nice value of -15 is used. (requires superuser). On Windows, process is set to HIGH class.
# Default: 0 - (Normal)
# 1 - (High)
ProcessPriority = 0
#
# RealmsStateUpdateDelay
# Description: Time (in seconds) between realm list updates.
# Default: 20 - (Enabled)
# 0 - (Disabled)
RealmsStateUpdateDelay = 20
#
# WrongPass.MaxCount
# Description: Number of login attemps with wrong password before the account or IP will be
# banned.
# Default: 0 - (Disabled)
# 1+ - (Enabled)
WrongPass.MaxCount = 0
#
# WrongPass.BanTime
# Description: Time (in seconds) for banning account or IP for invalid login attempts.
# Default: 600 - (10 minutes)
# 0 - (Permanent ban)
WrongPass.BanTime = 600
#
# WrongPass.BanType
# Description: Ban type for invalid login attempts.
# Default: 0 - (Ban IP)
# 1 - (Ban Account)
WrongPass.BanType = 0
#
###################################################################################################
###################################################################################################
# MYSQL SETTINGS
#
# LoginDatabaseInfo
# Description: Database connection settings for the realm server.
# Example: "hostname;port;username;password;database"
# ".;somenumber;username;password;database" - (Use named pipes on Windows
# "enable-named-pipe" to [mysqld]
# section my.ini)
# ".;/path/to/unix_socket;username;password;database" - (use Unix sockets on
# Unix/Linux)
# Default: "127.0.0.1;3306;trinity;trinity;auth"
LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth"
#
# LoginDatabase.WorkerThreads
# Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL
# statements. Each worker thread is mirrored with its own connection to the
# Default: 1
LoginDatabase.WorkerThreads = 1
#
###################################################################################################

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "resource.h"
#include "revision.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h" //"afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APPICON ICON "authserver.ico"
/////////////////////////////////////////////////////////////////////////////
// Neutre (Par défaut système) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifndef _DEBUG
FILEFLAGS 0
#else
#define VER_PRERELEASE VS_FF_PRERELEASE
#define VER_PRIVATEBUILD VS_FF_PRIVATEBUILD
#define VER_DEBUG 0
FILEFLAGS (VER_PRIVATEBUILD|VER_PRERELEASE|VER_DEBUG)
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080004b0"
BEGIN
VALUE "CompanyName", VER_COMPANYNAME_STR
VALUE "FileDescription", "Authentication Server Daemon"
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", "authserver"
VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
VALUE "OriginalFilename", "authserver.exe"
VALUE "ProductName", "Authentication Server"
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x800, 1200
END
END
#endif

View File

@@ -0,0 +1,15 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by SunwellCore.rc
//
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,310 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "BoundingIntervalHierarchy.h"
#ifdef _MSC_VER
#define isnan _isnan
#else
#define isnan std::isnan
#endif
void BIH::buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats)
{
// create space for the first node
tempTree.push_back(uint32(3 << 30)); // dummy leaf
tempTree.insert(tempTree.end(), 2, 0);
//tempTree.add(0);
// seed bbox
AABound gridBox = { bounds.low(), bounds.high() };
AABound nodeBox = gridBox;
// seed subdivide function
subdivide(0, dat.numPrims - 1, tempTree, dat, gridBox, nodeBox, 0, 1, stats);
}
void BIH::subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats)
{
if ((right - left + 1) <= dat.maxPrims || depth >= MAX_STACK_SIZE)
{
// write leaf node
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
// calculate extents
int axis = -1, prevAxis, rightOrig;
float clipL = G3D::fnan(), clipR = G3D::fnan(), prevClip = G3D::fnan();
float split = G3D::fnan(), prevSplit;
bool wasLeft = true;
while (true)
{
prevAxis = axis;
prevSplit = split;
// perform quick consistency checks
G3D::Vector3 d( gridBox.hi - gridBox.lo );
if (d.x < 0 || d.y < 0 || d.z < 0)
throw std::logic_error("negative node extents");
for (int i = 0; i < 3; i++)
{
if (nodeBox.hi[i] < gridBox.lo[i] || nodeBox.lo[i] > gridBox.hi[i])
{
//UI.printError(Module.ACCEL, "Reached tree area in error - discarding node with: %d objects", right - left + 1);
throw std::logic_error("invalid node overlap");
}
}
// find longest axis
axis = d.primaryAxis();
split = 0.5f * (gridBox.lo[axis] + gridBox.hi[axis]);
// partition L/R subsets
clipL = -G3D::inf();
clipR = G3D::inf();
rightOrig = right; // save this for later
float nodeL = G3D::inf();
float nodeR = -G3D::inf();
for (int i = left; i <= right;)
{
int obj = dat.indices[i];
float minb = dat.primBound[obj].low()[axis];
float maxb = dat.primBound[obj].high()[axis];
float center = (minb + maxb) * 0.5f;
if (center <= split)
{
// stay left
i++;
if (clipL < maxb)
clipL = maxb;
}
else
{
// move to the right most
int t = dat.indices[i];
dat.indices[i] = dat.indices[right];
dat.indices[right] = t;
right--;
if (clipR > minb)
clipR = minb;
}
nodeL = std::min(nodeL, minb);
nodeR = std::max(nodeR, maxb);
}
// check for empty space
if (nodeL > nodeBox.lo[axis] && nodeR < nodeBox.hi[axis])
{
float nodeBoxW = nodeBox.hi[axis] - nodeBox.lo[axis];
float nodeNewW = nodeR - nodeL;
// node box is too big compare to space occupied by primitives?
if (1.3f * nodeNewW < nodeBoxW)
{
stats.updateBVH2();
int nextIndex = tempTree.size();
// allocate child
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
// write bvh2 clip node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | (1 << 29) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(nodeL);
tempTree[nodeIndex + 2] = floatToRawIntBits(nodeR);
// update nodebox and recurse
nodeBox.lo[axis] = nodeL;
nodeBox.hi[axis] = nodeR;
subdivide(left, rightOrig, tempTree, dat, gridBox, nodeBox, nextIndex, depth + 1, stats);
return;
}
}
// ensure we are making progress in the subdivision
if (right == rightOrig)
{
// all left
if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split)) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
if (clipL <= split) {
// keep looping on left half
gridBox.hi[axis] = split;
prevClip = clipL;
wasLeft = true;
continue;
}
gridBox.hi[axis] = split;
prevClip = G3D::fnan();
}
else if (left > right)
{
// all right
if (prevAxis == axis && G3D::fuzzyEq(prevSplit, split)) {
// we are stuck here - create a leaf
stats.updateLeaf(depth, right - left + 1);
createNode(tempTree, nodeIndex, left, right);
return;
}
right = rightOrig;
if (clipR >= split) {
// keep looping on right half
gridBox.lo[axis] = split;
prevClip = clipR;
wasLeft = false;
continue;
}
gridBox.lo[axis] = split;
prevClip = G3D::fnan();
}
else
{
// we are actually splitting stuff
if (prevAxis != -1 && !isnan(prevClip))
{
// second time through - lets create the previous split
// since it produced empty space
int nextIndex = tempTree.size();
// allocate child node
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
if (wasLeft) {
// create a node with a left child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(prevClip);
tempTree[nodeIndex + 2] = floatToRawIntBits(G3D::inf());
} else {
// create a node with a right child
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (prevAxis << 30) | (nextIndex - 3);
tempTree[nodeIndex + 1] = floatToRawIntBits(-G3D::inf());
tempTree[nodeIndex + 2] = floatToRawIntBits(prevClip);
}
// count stats for the unused leaf
depth++;
stats.updateLeaf(depth, 0);
// now we keep going as we are, with a new nodeIndex:
nodeIndex = nextIndex;
}
break;
}
}
// compute index of child nodes
int nextIndex = tempTree.size();
// allocate left node
int nl = right - left + 1;
int nr = rightOrig - (right + 1) + 1;
if (nl > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
} else
nextIndex -= 3;
// allocate right node
if (nr > 0) {
tempTree.push_back(0);
tempTree.push_back(0);
tempTree.push_back(0);
}
// write leaf node
stats.updateInner();
tempTree[nodeIndex + 0] = (axis << 30) | nextIndex;
tempTree[nodeIndex + 1] = floatToRawIntBits(clipL);
tempTree[nodeIndex + 2] = floatToRawIntBits(clipR);
// prepare L/R child boxes
AABound gridBoxL(gridBox), gridBoxR(gridBox);
AABound nodeBoxL(nodeBox), nodeBoxR(nodeBox);
gridBoxL.hi[axis] = gridBoxR.lo[axis] = split;
nodeBoxL.hi[axis] = clipL;
nodeBoxR.lo[axis] = clipR;
// recurse
if (nl > 0)
subdivide(left, right, tempTree, dat, gridBoxL, nodeBoxL, nextIndex, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
if (nr > 0)
subdivide(right + 1, rightOrig, tempTree, dat, gridBoxR, nodeBoxR, nextIndex + 3, depth + 1, stats);
else
stats.updateLeaf(depth + 1, 0);
}
bool BIH::writeToFile(FILE* wf) const
{
uint32 treeSize = tree.size();
uint32 check=0, count;
check += fwrite(&bounds.low(), sizeof(float), 3, wf);
check += fwrite(&bounds.high(), sizeof(float), 3, wf);
check += fwrite(&treeSize, sizeof(uint32), 1, wf);
check += fwrite(&tree[0], sizeof(uint32), treeSize, wf);
count = objects.size();
check += fwrite(&count, sizeof(uint32), 1, wf);
check += fwrite(&objects[0], sizeof(uint32), count, wf);
return check == (3 + 3 + 2 + treeSize + count);
}
bool BIH::readFromFile(FILE* rf)
{
uint32 treeSize;
G3D::Vector3 lo, hi;
uint32 check=0, count=0;
check += fread(&lo, sizeof(float), 3, rf);
check += fread(&hi, sizeof(float), 3, rf);
bounds = G3D::AABox(lo, hi);
check += fread(&treeSize, sizeof(uint32), 1, rf);
tree.resize(treeSize);
check += fread(&tree[0], sizeof(uint32), treeSize, rf);
check += fread(&count, sizeof(uint32), 1, rf);
objects.resize(count); // = new uint32[nObjects];
check += fread(&objects[0], sizeof(uint32), count, rf);
return uint64(check) == uint64(3 + 3 + 1 + 1 + uint64(treeSize) + uint64(count));
}
void BIH::BuildStats::updateLeaf(int depth, int n)
{
numLeaves++;
minDepth = std::min(depth, minDepth);
maxDepth = std::max(depth, maxDepth);
sumDepth += depth;
minObjects = std::min(n, minObjects);
maxObjects = std::max(n, maxObjects);
sumObjects += n;
int nl = std::min(n, 5);
++numLeavesN[nl];
}
void BIH::BuildStats::printStats()
{
printf("Tree stats:\n");
printf(" * Nodes: %d\n", numNodes);
printf(" * Leaves: %d\n", numLeaves);
printf(" * Objects: min %d\n", minObjects);
printf(" avg %.2f\n", (float) sumObjects / numLeaves);
printf(" avg(n>0) %.2f\n", (float) sumObjects / (numLeaves - numLeavesN[0]));
printf(" max %d\n", maxObjects);
printf(" * Depth: min %d\n", minDepth);
printf(" avg %.2f\n", (float) sumDepth / numLeaves);
printf(" max %d\n", maxDepth);
printf(" * Leaves w/: N=0 %3d%%\n", 100 * numLeavesN[0] / numLeaves);
printf(" N=1 %3d%%\n", 100 * numLeavesN[1] / numLeaves);
printf(" N=2 %3d%%\n", 100 * numLeavesN[2] / numLeaves);
printf(" N=3 %3d%%\n", 100 * numLeavesN[3] / numLeaves);
printf(" N=4 %3d%%\n", 100 * numLeavesN[4] / numLeaves);
printf(" N>4 %3d%%\n", 100 * numLeavesN[5] / numLeaves);
printf(" * BVH2 nodes: %d (%3d%%)\n", numBVH2, 100 * numBVH2 / (numNodes + numLeaves - 2 * numBVH2));
}

View File

@@ -0,0 +1,400 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _BIH_H
#define _BIH_H
#include "G3D/Vector3.h"
#include "G3D/Ray.h"
#include "G3D/AABox.h"
#include "Define.h"
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <limits>
#include <cmath>
#define MAX_STACK_SIZE 64
static inline uint32 floatToRawIntBits(float f)
{
union
{
uint32 ival;
float fval;
} temp;
temp.fval=f;
return temp.ival;
}
static inline float intBitsToFloat(uint32 i)
{
union
{
uint32 ival;
float fval;
} temp;
temp.ival=i;
return temp.fval;
}
struct AABound
{
G3D::Vector3 lo, hi;
};
/** Bounding Interval Hierarchy Class.
Building and Ray-Intersection functions based on BIH from
Sunflow, a Java Raytracer, released under MIT/X11 License
http://sunflow.sourceforge.net/
Copyright (c) 2003-2007 Christopher Kulla
*/
class BIH
{
private:
void init_empty()
{
tree.clear();
objects.clear();
// create space for the first node
tree.push_back(3u << 30u); // dummy leaf
tree.insert(tree.end(), 2, 0);
}
public:
BIH() { init_empty(); }
template< class BoundsFunc, class PrimArray >
void build(const PrimArray &primitives, BoundsFunc &getBounds, uint32 leafSize = 3, bool printStats=false)
{
if (primitives.size() == 0)
{
init_empty();
return;
}
buildData dat;
dat.maxPrims = leafSize;
dat.numPrims = primitives.size();
dat.indices = new uint32[dat.numPrims];
dat.primBound = new G3D::AABox[dat.numPrims];
getBounds(primitives[0], bounds);
for (uint32 i=0; i<dat.numPrims; ++i)
{
dat.indices[i] = i;
getBounds(primitives[i], dat.primBound[i]);
bounds.merge(dat.primBound[i]);
}
std::vector<uint32> tempTree;
BuildStats stats;
buildHierarchy(tempTree, dat, stats);
if (printStats)
stats.printStats();
objects.resize(dat.numPrims);
for (uint32 i=0; i<dat.numPrims; ++i)
objects[i] = dat.indices[i];
//nObjects = dat.numPrims;
tree = tempTree;
delete[] dat.primBound;
delete[] dat.indices;
}
uint32 primCount() const { return objects.size(); }
template<typename RayCallback>
void intersectRay(const G3D::Ray &r, RayCallback& intersectCallback, float &maxDist, bool stopAtFirstHit) const
{
float intervalMin = -1.f;
float intervalMax = -1.f;
G3D::Vector3 org = r.origin();
G3D::Vector3 dir = r.direction();
G3D::Vector3 invDir;
for (int i=0; i<3; ++i)
{
invDir[i] = 1.f / dir[i];
if (G3D::fuzzyNe(dir[i], 0.0f))
{
float t1 = (bounds.low()[i] - org[i]) * invDir[i];
float t2 = (bounds.high()[i] - org[i]) * invDir[i];
if (t1 > t2)
std::swap(t1, t2);
if (t1 > intervalMin)
intervalMin = t1;
if (t2 < intervalMax || intervalMax < 0.f)
intervalMax = t2;
// intervalMax can only become smaller for other axis,
// and intervalMin only larger respectively, so stop early
if (intervalMax <= 0 || intervalMin >= maxDist)
return;
}
}
if (intervalMin > intervalMax)
return;
intervalMin = std::max(intervalMin, 0.f);
intervalMax = std::min(intervalMax, maxDist);
uint32 offsetFront[3];
uint32 offsetBack[3];
uint32 offsetFront3[3];
uint32 offsetBack3[3];
// compute custom offsets from direction sign bit
for (int i=0; i<3; ++i)
{
offsetFront[i] = floatToRawIntBits(dir[i]) >> 31;
offsetBack[i] = offsetFront[i] ^ 1;
offsetFront3[i] = offsetFront[i] * 3;
offsetBack3[i] = offsetBack[i] * 3;
// avoid always adding 1 during the inner loop
++offsetFront[i];
++offsetBack[i];
}
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
// ray passes between clip zones
if (tf < intervalMin && tb > intervalMax)
break;
int back = offset + offsetBack3[axis];
node = back;
// ray passes through far node only
if (tf < intervalMin) {
intervalMin = (tb >= intervalMin) ? tb : intervalMin;
continue;
}
node = offset + offsetFront3[axis]; // front
// ray passes through near node only
if (tb > intervalMax) {
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
// ray passes through both nodes
// push back node
stack[stackPos].node = back;
stack[stackPos].tnear = (tb >= intervalMin) ? tb : intervalMin;
stack[stackPos].tfar = intervalMax;
stackPos++;
// update ray interval for front node
intervalMax = (tf <= intervalMax) ? tf : intervalMax;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
bool hit = intersectCallback(r, objects[offset], maxDist, stopAtFirstHit);
if (stopAtFirstHit && hit) return;
--n;
++offset;
}
break;
}
}
else
{
if (axis>2)
return; // should not happen
float tf = (intBitsToFloat(tree[node + offsetFront[axis]]) - org[axis]) * invDir[axis];
float tb = (intBitsToFloat(tree[node + offsetBack[axis]]) - org[axis]) * invDir[axis];
node = offset;
intervalMin = (tf >= intervalMin) ? tf : intervalMin;
intervalMax = (tb <= intervalMax) ? tb : intervalMax;
if (intervalMin > intervalMax)
break;
continue;
}
} // traversal loop
do
{
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
intervalMin = stack[stackPos].tnear;
if (maxDist < intervalMin)
continue;
node = stack[stackPos].node;
intervalMax = stack[stackPos].tfar;
break;
} while (true);
}
}
template<typename IsectCallback>
void intersectPoint(const G3D::Vector3 &p, IsectCallback& intersectCallback) const
{
if (!bounds.contains(p))
return;
StackNode stack[MAX_STACK_SIZE];
int stackPos = 0;
int node = 0;
while (true) {
while (true)
{
uint32 tn = tree[node];
uint32 axis = (tn & (3 << 30)) >> 30;
bool BVH2 = tn & (1 << 29);
int offset = tn & ~(7 << 29);
if (!BVH2)
{
if (axis < 3)
{
// "normal" interior node
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
// point is between clip zones
if (tl < p[axis] && tr > p[axis])
break;
int right = offset + 3;
node = right;
// point is in right node only
if (tl < p[axis]) {
continue;
}
node = offset; // left
// point is in left node only
if (tr > p[axis]) {
continue;
}
// point is in both nodes
// push back right node
stack[stackPos].node = right;
stackPos++;
continue;
}
else
{
// leaf - test some objects
int n = tree[node + 1];
while (n > 0) {
intersectCallback(p, objects[offset]); // !!!
--n;
++offset;
}
break;
}
}
else // BVH2 node (empty space cut off left and right)
{
if (axis>2)
return; // should not happen
float tl = intBitsToFloat(tree[node + 1]);
float tr = intBitsToFloat(tree[node + 2]);
node = offset;
if (tl > p[axis] || tr < p[axis])
break;
continue;
}
} // traversal loop
// stack is empty?
if (stackPos == 0)
return;
// move back up the stack
stackPos--;
node = stack[stackPos].node;
}
}
bool writeToFile(FILE* wf) const;
bool readFromFile(FILE* rf);
protected:
std::vector<uint32> tree;
std::vector<uint32> objects;
G3D::AABox bounds;
struct buildData
{
uint32 *indices;
G3D::AABox *primBound;
uint32 numPrims;
int maxPrims;
};
struct StackNode
{
uint32 node;
float tnear;
float tfar;
};
class BuildStats
{
private:
int numNodes;
int numLeaves;
int sumObjects;
int minObjects;
int maxObjects;
int sumDepth;
int minDepth;
int maxDepth;
int numLeavesN[6];
int numBVH2;
public:
BuildStats():
numNodes(0), numLeaves(0), sumObjects(0), minObjects(0x0FFFFFFF),
maxObjects(0xFFFFFFFF), sumDepth(0), minDepth(0x0FFFFFFF),
maxDepth(0xFFFFFFFF), numBVH2(0)
{
for (int i=0; i<6; ++i) numLeavesN[i] = 0;
}
void updateInner() { numNodes++; }
void updateBVH2() { numBVH2++; }
void updateLeaf(int depth, int n);
void printStats();
};
void buildHierarchy(std::vector<uint32> &tempTree, buildData &dat, BuildStats &stats);
void createNode(std::vector<uint32> &tempTree, int nodeIndex, uint32 left, uint32 right) const
{
// write leaf node
tempTree[nodeIndex + 0] = (3 << 30) | left;
tempTree[nodeIndex + 1] = right - left + 1;
}
void subdivide(int left, int right, std::vector<uint32> &tempTree, buildData &dat, AABound &gridBox, AABound &nodeBox, int nodeIndex, int depth, BuildStats &stats);
};
#endif // _BIH_H

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _BIH_WRAP
#define _BIH_WRAP
#include "G3D/Table.h"
#include "G3D/Array.h"
#include "G3D/Set.h"
#include "BoundingIntervalHierarchy.h"
template<class T, class BoundsFunc = BoundsTrait<T> >
class BIHWrap
{
template<class RayCallback>
struct MDLCallback
{
const T* const* objects;
RayCallback& _callback;
uint32 objects_size;
MDLCallback(RayCallback& callback, const T* const* objects_array, uint32 objects_size ) : objects(objects_array), _callback(callback), objects_size(objects_size) { }
/// Intersect ray
bool operator() (const G3D::Ray& ray, uint32 idx, float& maxDist, bool stopAtFirstHit)
{
if (idx >= objects_size)
return false;
if (const T* obj = objects[idx])
return _callback(ray, *obj, maxDist, stopAtFirstHit);
return false;
}
/// Intersect point
void operator() (const G3D::Vector3& p, uint32 idx)
{
if (idx >= objects_size)
return;
if (const T* obj = objects[idx])
_callback(p, *obj);
}
};
typedef G3D::Array<const T*> ObjArray;
BIH m_tree;
ObjArray m_objects;
G3D::Table<const T*, uint32> m_obj2Idx;
G3D::Set<const T*> m_objects_to_push;
int unbalanced_times;
public:
BIHWrap() : unbalanced_times(0) { }
void insert(const T& obj)
{
++unbalanced_times;
m_objects_to_push.insert(&obj);
}
void remove(const T& obj)
{
++unbalanced_times;
uint32 Idx = 0;
const T * temp;
if (m_obj2Idx.getRemove(&obj, temp, Idx))
m_objects[Idx] = NULL;
else
m_objects_to_push.remove(&obj);
}
void balance()
{
if (unbalanced_times == 0)
return;
unbalanced_times = 0;
m_objects.fastClear();
m_obj2Idx.getKeys(m_objects);
m_objects_to_push.getMembers(m_objects);
//assert that m_obj2Idx has all the keys
m_tree.build(m_objects, BoundsFunc::getBounds2);
}
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& maxDist, bool stopAtFirstHit)
{
balance();
MDLCallback<RayCallback> temp_cb(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectRay(ray, temp_cb, maxDist, stopAtFirstHit);
}
template<typename IsectCallback>
void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
{
balance();
MDLCallback<IsectCallback> callback(intersectCallback, m_objects.getCArray(), m_objects.size());
m_tree.intersectPoint(point, callback);
}
};
#endif // _BIH_WRAP

View File

@@ -0,0 +1,93 @@
# Copyright (C)
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
if( USE_COREPCH )
include_directories(${CMAKE_CURRENT_BINARY_DIR})
endif()
file(GLOB_RECURSE sources_Management Management/*.cpp Management/*.h)
file(GLOB_RECURSE sources_Maps Maps/*.cpp Maps/*.h)
file(GLOB_RECURSE sources_Models Models/*.cpp Models/*.h)
file(GLOB sources_localdir *.cpp *.h)
if (USE_COREPCH)
set(collision_STAT_PCH_HDR PrecompiledHeaders/collisionPCH.h)
set(collision_STAT_PCH_SRC PrecompiledHeaders/collisionPCH.cpp)
endif ()
set(collision_STAT_SRCS
${collision_STAT_SRCS}
${sources_Management}
${sources_Maps}
${sources_Models}
${sources_localdir}
)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic
${CMAKE_SOURCE_DIR}/src/server/shared/Dynamic/LinkedReference
${CMAKE_SOURCE_DIR}/src/server/shared/Logging
${CMAKE_SOURCE_DIR}/src/server/shared/Threading
${CMAKE_SOURCE_DIR}/src/server/shared/Packets
${CMAKE_SOURCE_DIR}/src/server/shared/Utilities
${CMAKE_SOURCE_DIR}/src/server/shared/DataStores
${CMAKE_SOURCE_DIR}/src/server/game/Addons
${CMAKE_SOURCE_DIR}/src/server/game/Conditions
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Item
${CMAKE_SOURCE_DIR}/src/server/game/Entities/GameObject
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Creature
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Object
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Object/Updates
${CMAKE_SOURCE_DIR}/src/server/game/Entities/Unit
${CMAKE_SOURCE_DIR}/src/server/game/Combat
${CMAKE_SOURCE_DIR}/src/server/game/Loot
${CMAKE_SOURCE_DIR}/src/server/game/Miscellaneous
${CMAKE_SOURCE_DIR}/src/server/game/Grids
${CMAKE_SOURCE_DIR}/src/server/game/Grids/Cells
${CMAKE_SOURCE_DIR}/src/server/game/Grids/Notifiers
${CMAKE_SOURCE_DIR}/src/server/game/Maps
${CMAKE_SOURCE_DIR}/src/server/game/DataStores
${CMAKE_SOURCE_DIR}/src/server/game/Movement/Waypoints
${CMAKE_SOURCE_DIR}/src/server/game/Movement/Spline
${CMAKE_SOURCE_DIR}/src/server/game/Movement
${CMAKE_SOURCE_DIR}/src/server/game/Server
${CMAKE_SOURCE_DIR}/src/server/game/Server/Protocol
${CMAKE_SOURCE_DIR}/src/server/game/World
${CMAKE_SOURCE_DIR}/src/server/game/Spells
${CMAKE_SOURCE_DIR}/src/server/game/Spells/Auras
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/Management
${CMAKE_CURRENT_SOURCE_DIR}/Maps
${CMAKE_CURRENT_SOURCE_DIR}/Models
${ACE_INCLUDE_DIR}
${MYSQL_INCLUDE_DIR}
)
add_library(collision STATIC
${collision_STAT_SRCS}
${collision_STAT_PCH_SRC}
)
target_link_libraries(collision
shared
)
# Generate precompiled header
if (USE_COREPCH)
add_cxx_pch(collision ${collision_STAT_PCH_HDR} ${collision_STAT_PCH_SRC})
endif ()

View File

@@ -0,0 +1,240 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "DynamicTree.h"
//#include "QuadTree.h"
//#include "RegularGrid.h"
#include "BoundingIntervalHierarchyWrapper.h"
#include "Log.h"
#include "RegularGrid.h"
#include "Timer.h"
#include "GameObjectModel.h"
#include "ModelInstance.h"
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include <G3D/Vector3.h>
using VMAP::ModelInstance;
namespace {
int CHECK_TREE_PERIOD = 200;
} // namespace
template<> struct HashTrait< GameObjectModel>{
static size_t hashCode(const GameObjectModel& g) { return (size_t)(void*)&g; }
};
template<> struct PositionTrait< GameObjectModel> {
static void getPosition(const GameObjectModel& g, G3D::Vector3& p) { p = g.getPosition(); }
};
template<> struct BoundsTrait< GameObjectModel> {
static void getBounds(const GameObjectModel& g, G3D::AABox& out) { out = g.getBounds();}
static void getBounds2(const GameObjectModel* g, G3D::AABox& out) { out = g->getBounds();}
};
/*
static bool operator == (const GameObjectModel& mdl, const GameObjectModel& mdl2){
return &mdl == &mdl2;
}
*/
typedef RegularGrid2D<GameObjectModel, BIHWrap<GameObjectModel> > ParentTree;
struct DynTreeImpl : public ParentTree/*, public Intersectable*/
{
typedef GameObjectModel Model;
typedef ParentTree base;
DynTreeImpl() :
rebalance_timer(CHECK_TREE_PERIOD),
unbalanced_times(0)
{
}
void insert(const Model& mdl)
{
base::insert(mdl);
++unbalanced_times;
}
void remove(const Model& mdl)
{
base::remove(mdl);
++unbalanced_times;
}
void balance()
{
base::balance();
unbalanced_times = 0;
}
void update(uint32 difftime)
{
if (!size())
return;
rebalance_timer.Update(difftime);
if (rebalance_timer.Passed())
{
rebalance_timer.Reset(CHECK_TREE_PERIOD);
if (unbalanced_times > 0)
balance();
}
}
TimeTrackerSmall rebalance_timer;
int unbalanced_times;
};
DynamicMapTree::DynamicMapTree() : impl(new DynTreeImpl()) { }
DynamicMapTree::~DynamicMapTree()
{
delete impl;
}
void DynamicMapTree::insert(const GameObjectModel& mdl)
{
impl->insert(mdl);
}
void DynamicMapTree::remove(const GameObjectModel& mdl)
{
impl->remove(mdl);
}
bool DynamicMapTree::contains(const GameObjectModel& mdl) const
{
return impl->contains(mdl);
}
void DynamicMapTree::balance()
{
impl->balance();
}
int DynamicMapTree::size() const
{
return impl->size();
}
void DynamicMapTree::update(uint32 t_diff)
{
impl->update(t_diff);
}
struct DynamicTreeIntersectionCallback
{
bool did_hit;
uint32 phase_mask;
DynamicTreeIntersectionCallback(uint32 phasemask) : did_hit(false), phase_mask(phasemask) { }
bool operator()(const G3D::Ray& r, const GameObjectModel& obj, float& distance, bool stopAtFirstHit)
{
bool result = obj.intersectRay(r, distance, stopAtFirstHit, phase_mask);
if (result)
did_hit = result;
return result;
}
bool didHit() const { return did_hit;}
};
bool DynamicMapTree::getIntersectionTime(const uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const
{
float distance = maxDist;
DynamicTreeIntersectionCallback callback(phasemask);
impl->intersectRay(ray, callback, distance, endPos, false);
if (callback.didHit())
maxDist = distance;
return callback.didHit();
}
bool DynamicMapTree::getObjectHitPos(const uint32 phasemask, const G3D::Vector3& startPos,
const G3D::Vector3& endPos, G3D::Vector3& resultHit,
float modifyDist) const
{
bool result = false;
float maxDist = (endPos - startPos).magnitude();
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
ASSERT(maxDist < std::numeric_limits<float>::max());
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
{
resultHit = endPos;
return false;
}
G3D::Vector3 dir = (endPos - startPos)/maxDist; // direction with length of 1
G3D::Ray ray(startPos, dir);
float dist = maxDist;
if (getIntersectionTime(phasemask, ray, endPos, dist))
{
resultHit = startPos + dir * dist;
if (modifyDist < 0)
{
if ((resultHit - startPos).magnitude() > -modifyDist)
resultHit = resultHit + dir*modifyDist;
else
resultHit = startPos;
}
else
resultHit = resultHit + dir*modifyDist;
result = true;
}
else
{
resultHit = endPos;
result = false;
}
return result;
}
bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const
{
G3D::Vector3 v1(x1, y1, z1), v2(x2, y2, z2);
float maxDist = (v2 - v1).magnitude();
if (!G3D::fuzzyGt(maxDist, 0) )
return true;
G3D::Ray r(v1, (v2-v1) / maxDist);
DynamicTreeIntersectionCallback callback(phasemask);
impl->intersectRay(r, callback, maxDist, v2, true);
return !callback.did_hit;
}
float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const
{
G3D::Vector3 v(x, y, z + 2.0f);
G3D::Ray r(v, G3D::Vector3(0, 0, -1));
DynamicTreeIntersectionCallback callback(phasemask);
impl->intersectZAllignedRay(r, callback, maxSearchDist);
if (callback.didHit())
return v.z - maxSearchDist;
else
return -G3D::inf();
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _DYNTREE_H
#define _DYNTREE_H
#include "Define.h"
namespace G3D
{
class Ray;
class Vector3;
}
class GameObjectModel;
struct DynTreeImpl;
class DynamicMapTree
{
DynTreeImpl *impl;
public:
DynamicMapTree();
~DynamicMapTree();
bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2,
float z2, uint32 phasemask) const;
bool getIntersectionTime(uint32 phasemask, const G3D::Ray& ray,
const G3D::Vector3& endPos, float& maxDist) const;
bool getObjectHitPos(uint32 phasemask, const G3D::Vector3& pPos1,
const G3D::Vector3& pPos2, G3D::Vector3& pResultHitPos,
float pModifyDist) const;
float getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const;
void insert(const GameObjectModel&);
void remove(const GameObjectModel&);
bool contains(const GameObjectModel&) const;
int size() const;
void balance();
void update(uint32 diff);
};
#endif // _DYNTREE_H

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _IMMAPMANAGER_H
#define _IMMAPMANAGER_H
#include <string>
#include "Define.h"
// Interface for IMMapManger
namespace MMAP
{
enum MMAP_LOAD_RESULT
{
MMAP_LOAD_RESULT_ERROR,
MMAP_LOAD_RESULT_OK,
MMAP_LOAD_RESULT_IGNORED,
};
class IMMapManager
{
private:
bool iEnablePathFinding;
public:
IMMapManager() : iEnablePathFinding(true) {}
virtual ~IMMapManager(void) {}
//Enabled/Disabled Pathfinding
void setEnablePathFinding(bool value) { iEnablePathFinding = value; }
bool isEnablePathFinding() const { return (iEnablePathFinding); }
};
}
#endif

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _IVMAPMANAGER_H
#define _IVMAPMANAGER_H
#include <string>
#include "Define.h"
//===========================================================
/**
This is the minimum interface to the VMapMamager.
*/
namespace VMAP
{
enum VMAP_LOAD_RESULT
{
VMAP_LOAD_RESULT_ERROR,
VMAP_LOAD_RESULT_OK,
VMAP_LOAD_RESULT_IGNORED
};
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
//===========================================================
class IVMapManager
{
private:
bool iEnableLineOfSightCalc;
bool iEnableHeightCalc;
public:
IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) { }
virtual ~IVMapManager(void) { }
virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId) = 0;
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual float getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) = 0;
/**
test if we hit an object. return true if we hit one. rx, ry, rz will hold the hit position or the dest position, if no intersection was found
return a position, that is pReduceDist closer to the origin
*/
virtual bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist) = 0;
/**
send debug commands
*/
virtual bool processCommand(char *pCommand)= 0;
/**
Enable/disable LOS calculation
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
*/
void setEnableLineOfSightCalc(bool pVal) { iEnableLineOfSightCalc = pVal; }
/**
Enable/disable model height calculation
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
*/
void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; }
bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); }
bool isHeightCalcEnabled() const { return(iEnableHeightCalc); }
bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); }
virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0;
/**
Query world model area info.
\param z gets adjusted to the ground height for which this are info is valid
*/
virtual bool getAreaInfo(unsigned int pMapId, float x, float y, float &z, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const=0;
virtual bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float &level, float &floor, uint32 &type) const=0;
};
}
#endif

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "MMapFactory.h"
#include "World.h"
#include <set>
namespace MMAP
{
// ######################## MMapFactory ########################
// our global singleton copy
MMapManager *g_MMapManager = NULL;
bool MMapFactory::forbiddenMaps[1000] = {0};
MMapManager* MMapFactory::createOrGetMMapManager()
{
if (g_MMapManager == NULL)
g_MMapManager = new MMapManager();
return g_MMapManager;
}
bool MMapFactory::IsPathfindingEnabled(const Map* map, bool force)
{
if (!map) return false;
return !forbiddenMaps[map->GetId()] && (sWorld->getBoolConfig(CONFIG_ENABLE_MMAPS) ? true : map->IsBattlegroundOrArena());
}
void MMapFactory::InitializeDisabledMaps()
{
memset(&forbiddenMaps, 0, sizeof(forbiddenMaps));
int32 f[] = {616 /*EoE*/, 649 /*ToC25*/, 650 /*ToC5*/, -1};
uint32 i = 0;
while (f[i] >= 0)
forbiddenMaps[f[i++]] = true;
}
void MMapFactory::clear()
{
if (g_MMapManager)
{
delete g_MMapManager;
g_MMapManager = NULL;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _MMAP_FACTORY_H
#define _MMAP_FACTORY_H
#include "MMapManager.h"
#include "UnorderedMap.h"
#include "DetourAlloc.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "Map.h"
namespace MMAP
{
enum MMAP_LOAD_RESULT
{
MMAP_LOAD_RESULT_ERROR,
MMAP_LOAD_RESULT_OK,
MMAP_LOAD_RESULT_IGNORED,
};
// static class
// holds all mmap global data
// access point to MMapManager singleton
class MMapFactory
{
public:
static MMapManager* createOrGetMMapManager();
static void clear();
static bool IsPathfindingEnabled(const Map* map, bool force = false);
static void InitializeDisabledMaps();
static bool forbiddenMaps[1000];
};
}
#endif

View File

@@ -0,0 +1,356 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "MapManager.h"
#include "MMapManager.h"
#include "Log.h"
namespace MMAP
{
// ######################## MMapManager ########################
MMapManager::~MMapManager()
{
for (MMapDataSet::iterator i = loadedMMaps.begin(); i != loadedMMaps.end(); ++i)
delete i->second;
// by now we should not have maps loaded
// if we had, tiles in MMapData->mmapLoadedTiles, their actual data is lost!
}
bool MMapManager::loadMapData(uint32 mapId)
{
// we already have this map loaded?
if (loadedMMaps.find(mapId) != loadedMMaps.end())
return true;
// load and init dtNavMesh - read parameters from file
uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i.mmap")+1;
char *fileName = new char[pathLen];
snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i.mmap").c_str(), mapId);
FILE* file = fopen(fileName, "rb");
if (!file)
{
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not open mmap file '%s'", fileName);
delete [] fileName;
return false;
}
dtNavMeshParams params;
int count = fread(&params, sizeof(dtNavMeshParams), 1, file);
fclose(file);
if (count != 1)
{
;//TC_LOG_DEBUG(LOG_FILTER_MAPS, "MMAP:loadMapData: Error: Could not read params from file '%s'", fileName);
delete [] fileName;
return false;
}
dtNavMesh* mesh = dtAllocNavMesh();
ASSERT(mesh);
if (DT_SUCCESS != mesh->init(&params))
{
dtFreeNavMesh(mesh);
sLog->outError("MMAP:loadMapData: Failed to initialize dtNavMesh for mmap %03u from file %s", mapId, fileName);
delete [] fileName;
return false;
}
delete [] fileName;
;//sLog->outDetail("MMAP:loadMapData: Loaded %03i.mmap", mapId);
// store inside our map list
MMapData* mmap_data = new MMapData(mesh);
mmap_data->mmapLoadedTiles.clear();
loadedMMaps.insert(std::pair<uint32, MMapData*>(mapId, mmap_data));
return true;
}
uint32 MMapManager::packTileID(int32 x, int32 y)
{
return uint32(x << 16 | y);
}
ACE_RW_Thread_Mutex& MMapManager::GetMMapLock(uint32 mapId)
{
Map* map = sMapMgr->FindBaseMap(mapId);
if (!map)
{
sLog->outMisc("ZOMG! MoveMaps: BaseMap not found!");
return this->MMapLock;
}
return map->GetMMapLock();
}
bool MMapManager::loadMap(uint32 mapId, int32 x, int32 y)
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
// make sure the mmap is loaded and ready to load tiles
if(!loadMapData(mapId))
return false;
// get this mmap data
MMapData* mmap = loadedMMaps[mapId];
ASSERT(mmap->navMesh);
// check if we already have this tile loaded
uint32 packedGridPos = packTileID(x, y);
if (mmap->mmapLoadedTiles.find(packedGridPos) != mmap->mmapLoadedTiles.end())
{
sLog->outError("MMAP:loadMap: Asked to load already loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
return false;
}
// load this tile :: mmaps/MMMXXYY.mmtile
uint32 pathLen = sWorld->GetDataPath().length() + strlen("mmaps/%03i%02i%02i.mmtile")+1;
char *fileName = new char[pathLen];
snprintf(fileName, pathLen, (sWorld->GetDataPath()+"mmaps/%03i%02i%02i.mmtile").c_str(), mapId, x, y);
FILE *file = fopen(fileName, "rb");
if (!file)
{
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:loadMap: Could not open mmtile file '%s'", fileName);
delete [] fileName;
return false;
}
delete [] fileName;
// read header
MmapTileHeader fileHeader;
if (fread(&fileHeader, sizeof(MmapTileHeader), 1, file) != 1 || fileHeader.mmapMagic != MMAP_MAGIC)
{
sLog->outError("MMAP:loadMap: Bad header in mmap %03u%02i%02i.mmtile", mapId, x, y);
fclose(file);
return false;
}
if (fileHeader.mmapVersion != MMAP_VERSION)
{
sLog->outError("MMAP:loadMap: %03u%02i%02i.mmtile was built with generator v%i, expected v%i",
mapId, x, y, fileHeader.mmapVersion, MMAP_VERSION);
fclose(file);
return false;
}
unsigned char* data = (unsigned char*)dtAlloc(fileHeader.size, DT_ALLOC_PERM);
ASSERT(data);
size_t result = fread(data, fileHeader.size, 1, file);
if(!result)
{
sLog->outError("MMAP:loadMap: Bad header or data in mmap %03u%02i%02i.mmtile", mapId, x, y);
fclose(file);
return false;
}
fclose(file);
dtMeshHeader* header = (dtMeshHeader*)data;
dtTileRef tileRef = 0;
dtStatus stat;
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, GetMMapLock(mapId));
stat = mmap->navMesh->addTile(data, fileHeader.size, DT_TILE_FREE_DATA, 0, &tileRef);
}
// memory allocated for data is now managed by detour, and will be deallocated when the tile is removed
if (stat == DT_SUCCESS)
{
mmap->mmapLoadedTiles.insert(std::pair<uint32, dtTileRef>(packedGridPos, tileRef));
++loadedTiles;
;//sLog->outDetail("MMAP:loadMap: Loaded mmtile %03i[%02i,%02i] into %03i[%02i,%02i]", mapId, x, y, mapId, header->x, header->y);
return true;
}
else
{
sLog->outError("MMAP:loadMap: Could not load %03u%02i%02i.mmtile into navmesh", mapId, x, y);
dtFree(data);
return false;
}
return false;
}
bool MMapManager::unloadMap(uint32 mapId, int32 x, int32 y)
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
// check if we have this map loaded
if (loadedMMaps.find(mapId) == loadedMMaps.end())
{
// file may not exist, therefore not loaded
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map. %03u%02i%02i.mmtile", mapId, x, y);
return false;
}
MMapData* mmap = loadedMMaps[mapId];
// check if we have this tile loaded
uint32 packedGridPos = packTileID(x, y);
if (mmap->mmapLoadedTiles.find(packedGridPos) == mmap->mmapLoadedTiles.end())
{
// file may not exist, therefore not loaded
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh tile. %03u%02i%02i.mmtile", mapId, x, y);
return false;
}
dtTileRef tileRef = mmap->mmapLoadedTiles[packedGridPos];
dtStatus status;
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, GetMMapLock(mapId));
status = mmap->navMesh->removeTile(tileRef, NULL, NULL);
}
// unload, and mark as non loaded
if (status != DT_SUCCESS)
{
// this is technically a memory leak
// if the grid is later reloaded, dtNavMesh::addTile will return error but no extra memory is used
// we cannot recover from this error - assert out
sLog->outError("MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
ASSERT(false);
}
else
{
mmap->mmapLoadedTiles.erase(packedGridPos);
--loadedTiles;
;//sLog->outDetail("MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
return true;
}
return false;
}
bool MMapManager::unloadMap(uint32 mapId)
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
if (loadedMMaps.find(mapId) == loadedMMaps.end())
{
// file may not exist, therefore not loaded
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMap: Asked to unload not loaded navmesh map %03u", mapId);
return false;
}
// unload all tiles from given map
MMapData* mmap = loadedMMaps[mapId];
for (MMapTileSet::iterator i = mmap->mmapLoadedTiles.begin(); i != mmap->mmapLoadedTiles.end(); ++i)
{
uint32 x = (i->first >> 16);
uint32 y = (i->first & 0x0000FFFF);
dtStatus status;
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, GetMMapLock(mapId));
status = mmap->navMesh->removeTile(i->second, NULL, NULL);
}
if (status != DT_SUCCESS)
sLog->outError("MMAP:unloadMap: Could not unload %03u%02i%02i.mmtile from navmesh", mapId, x, y);
else
{
--loadedTiles;
;//sLog->outDetail("MMAP:unloadMap: Unloaded mmtile %03i[%02i,%02i] from %03i", mapId, x, y, mapId);
}
}
delete mmap;
loadedMMaps.erase(mapId);
;//sLog->outDetail("MMAP:unloadMap: Unloaded %03i.mmap", mapId);
return true;
}
bool MMapManager::unloadMapInstance(uint32 mapId, uint32 instanceId)
{
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
// check if we have this map loaded
if (loadedMMaps.find(mapId) == loadedMMaps.end())
{
// file may not exist, therefore not loaded
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded navmesh map %03u", mapId);
return false;
}
MMapData* mmap = loadedMMaps[mapId];
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
;//sLog->outDebug(LOG_FILTER_MAPS, "MMAP:unloadMapInstance: Asked to unload not loaded dtNavMeshQuery mapId %03u instanceId %u", mapId, instanceId);
return false;
}
dtNavMeshQuery* query = mmap->navMeshQueries[instanceId];
dtFreeNavMeshQuery(query);
mmap->navMeshQueries.erase(instanceId);
;//sLog->outDetail("MMAP:unloadMapInstance: Unloaded mapId %03u instanceId %u", mapId, instanceId);
return true;
}
dtNavMesh const* MMapManager::GetNavMesh(uint32 mapId)
{
// pussywizard: moved to calling function
//TRINITY_READ_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
if (loadedMMaps.find(mapId) == loadedMMaps.end())
return NULL;
return loadedMMaps[mapId]->navMesh;
}
dtNavMeshQuery const* MMapManager::GetNavMeshQuery(uint32 mapId, uint32 instanceId)
{
// pussywizard: moved to calling function
//TRINITY_READ_GUARD(ACE_RW_Thread_Mutex, MMapManagerLock);
if (loadedMMaps.find(mapId) == loadedMMaps.end())
return NULL;
MMapData* mmap = loadedMMaps[mapId];
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
// pussywizard: different instances of the same map shouldn't access this simultaneously
TRINITY_WRITE_GUARD(ACE_RW_Thread_Mutex, GetMMapLock(mapId));
// check again after acquiring mutex
if (mmap->navMeshQueries.find(instanceId) == mmap->navMeshQueries.end())
{
// allocate mesh query
dtNavMeshQuery* query = dtAllocNavMeshQuery();
ASSERT(query);
if (DT_SUCCESS != query->init(mmap->navMesh, 1024))
{
dtFreeNavMeshQuery(query);
sLog->outError("MMAP:GetNavMeshQuery: Failed to initialize dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
return NULL;
}
;//sLog->outDetail("MMAP:GetNavMeshQuery: created dtNavMeshQuery for mapId %03u instanceId %u", mapId, instanceId);
mmap->navMeshQueries.insert(std::pair<uint32, dtNavMeshQuery*>(instanceId, query));
}
}
return mmap->navMeshQueries[instanceId];
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _MMAP_MANAGER_H
#define _MMAP_MANAGER_H
#include "UnorderedMap.h"
#include "DetourAlloc.h"
#include "DetourNavMesh.h"
#include "DetourNavMeshQuery.h"
#include "World.h"
// memory management
inline void* dtCustomAlloc(int size, dtAllocHint /*hint*/)
{
return (void*)new unsigned char[size];
}
inline void dtCustomFree(void* ptr)
{
delete [] (unsigned char*)ptr;
}
// move map related classes
namespace MMAP
{
typedef UNORDERED_MAP<uint32, dtTileRef> MMapTileSet;
typedef UNORDERED_MAP<uint32, dtNavMeshQuery*> NavMeshQuerySet;
// dummy struct to hold map's mmap data
struct MMapData
{
MMapData(dtNavMesh* mesh) : navMesh(mesh) {}
~MMapData()
{
for (NavMeshQuerySet::iterator i = navMeshQueries.begin(); i != navMeshQueries.end(); ++i)
dtFreeNavMeshQuery(i->second);
if (navMesh)
dtFreeNavMesh(navMesh);
}
dtNavMesh* navMesh;
// we have to use single dtNavMeshQuery for every instance, since those are not thread safe
NavMeshQuerySet navMeshQueries; // instanceId to query
MMapTileSet mmapLoadedTiles; // maps [map grid coords] to [dtTile]
};
typedef UNORDERED_MAP<uint32, MMapData*> MMapDataSet;
// singleton class
// holds all all access to mmap loading unloading and meshes
class MMapManager
{
public:
MMapManager() : loadedTiles(0) {}
~MMapManager();
bool loadMap(uint32 mapId, int32 x, int32 y);
bool unloadMap(uint32 mapId, int32 x, int32 y);
bool unloadMap(uint32 mapId);
bool unloadMapInstance(uint32 mapId, uint32 instanceId);
// the returned [dtNavMeshQuery const*] is NOT threadsafe
dtNavMeshQuery const* GetNavMeshQuery(uint32 mapId, uint32 instanceId);
dtNavMesh const* GetNavMesh(uint32 mapId);
uint32 getLoadedTilesCount() const { return loadedTiles; }
uint32 getLoadedMapsCount() const { return loadedMMaps.size(); }
ACE_RW_Thread_Mutex& GetMMapLock(uint32 mapId);
ACE_RW_Thread_Mutex& GetMMapGeneralLock() { return MMapLock; } // pussywizard: in case a per-map mutex can't be found, should never happen
ACE_RW_Thread_Mutex& GetManagerLock() { return MMapManagerLock; }
private:
bool loadMapData(uint32 mapId);
uint32 packTileID(int32 x, int32 y);
MMapDataSet loadedMMaps;
uint32 loadedTiles;
ACE_RW_Thread_Mutex MMapManagerLock;
ACE_RW_Thread_Mutex MMapLock; // pussywizard: in case a per-map mutex can't be found, should never happen
};
}
#endif

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "VMapFactory.h"
#include "VMapManager2.h"
namespace VMAP
{
IVMapManager* gVMapManager = NULL;
//===============================================
// just return the instance
IVMapManager* VMapFactory::createOrGetVMapManager()
{
if (gVMapManager == 0)
gVMapManager= new VMapManager2(); // should be taken from config ... Please change if you like :-)
return gVMapManager;
}
//===============================================
// delete all internal data structures
void VMapFactory::clear()
{
delete gVMapManager;
gVMapManager = NULL;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _VMAPFACTORY_H
#define _VMAPFACTORY_H
#include "IVMapManager.h"
/**
This is the access point to the VMapManager.
*/
namespace VMAP
{
//===========================================================
class VMapFactory
{
public:
static IVMapManager* createOrGetVMapManager();
static void clear();
};
}
#endif

View File

@@ -0,0 +1,299 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include "VMapManager2.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "WorldModel.h"
#include <G3D/Vector3.h>
#include <ace/Null_Mutex.h>
#include <ace/Singleton.h>
#include "DisableMgr.h"
#include "DBCStores.h"
#include "Log.h"
#include "VMapDefinitions.h"
using G3D::Vector3;
namespace VMAP
{
VMapManager2::VMapManager2()
{
}
VMapManager2::~VMapManager2(void)
{
for (InstanceTreeMap::iterator i = iInstanceMapTrees.begin(); i != iInstanceMapTrees.end(); ++i)
{
delete i->second;
}
for (ModelFileMap::iterator i = iLoadedModelFiles.begin(); i != iLoadedModelFiles.end(); ++i)
{
delete i->second.getModel();
}
}
Vector3 VMapManager2::convertPositionToInternalRep(float x, float y, float z) const
{
Vector3 pos;
const float mid = 0.5f * 64.0f * 533.33333333f;
pos.x = mid - x;
pos.y = mid - y;
pos.z = z;
return pos;
}
// move to MapTree too?
std::string VMapManager2::getMapFileName(unsigned int mapId)
{
std::stringstream fname;
fname.width(3);
fname << std::setfill('0') << mapId << std::string(MAP_FILENAME_EXTENSION2);
return fname.str();
}
int VMapManager2::loadMap(const char* basePath, unsigned int mapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
if (isMapLoadingEnabled())
{
if (_loadMap(mapId, basePath, x, y))
result = VMAP_LOAD_RESULT_OK;
else
result = VMAP_LOAD_RESULT_ERROR;
}
return result;
}
// load one tile (internal use only)
bool VMapManager2::_loadMap(unsigned int mapId, const std::string& basePath, uint32 tileX, uint32 tileY)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree == iInstanceMapTrees.end())
{
std::string mapFileName = getMapFileName(mapId);
StaticMapTree* newTree = new StaticMapTree(mapId, basePath);
if (!newTree->InitMap(mapFileName, this))
{
delete newTree;
return false;
}
instanceTree = iInstanceMapTrees.insert(InstanceTreeMap::value_type(mapId, newTree)).first;
}
return instanceTree->second->LoadMapTile(tileX, tileY, this);
}
void VMapManager2::unloadMap(unsigned int mapId)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMap(this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(mapId);
}
}
}
void VMapManager2::unloadMap(unsigned int mapId, int x, int y)
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
instanceTree->second->UnloadMapTile(x, y, this);
if (instanceTree->second->numLoadedTiles() == 0)
{
delete instanceTree->second;
iInstanceMapTrees.erase(mapId);
}
}
}
bool VMapManager2::isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2)
{
//if (!isLineOfSightCalcEnabled() || DisableMgr::IsDisabledFor(DISABLE_TYPE_VMAP, mapId, NULL, VMAP_DISABLE_LOS)) // pussywizard: optimization
// return true;
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
if (pos1 != pos2)
{
return instanceTree->second->isInLineOfSight(pos1, pos2);
}
}
return true;
}
/**
get the hit position and return true if we hit something
otherwise the result pos will be the dest pos
*/
bool VMapManager2::getObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float modifyDist)
{
//if (isLineOfSightCalcEnabled() && !DisableMgr::IsDisabledFor(DISABLE_TYPE_VMAP, mapId, NULL, VMAP_DISABLE_LOS)) // pussywizard: optimization
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos1 = convertPositionToInternalRep(x1, y1, z1);
Vector3 pos2 = convertPositionToInternalRep(x2, y2, z2);
Vector3 resultPos;
bool result = instanceTree->second->getObjectHitPos(pos1, pos2, resultPos, modifyDist);
resultPos = convertPositionToInternalRep(resultPos.x, resultPos.y, resultPos.z);
rx = resultPos.x;
ry = resultPos.y;
rz = resultPos.z;
return result;
}
}
rx = x2;
ry = y2;
rz = z2;
return false;
}
/**
get height or INVALID_HEIGHT if no height available
*/
float VMapManager2::getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist)
{
//if (isHeightCalcEnabled() && !DisableMgr::IsDisabledFor(DISABLE_TYPE_VMAP, mapId, NULL, VMAP_DISABLE_HEIGHT)) // pussywizard: optimization
{
InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x, y, z);
float height = instanceTree->second->getHeight(pos, maxSearchDist);
if (!(height < G3D::inf()))
return height = VMAP_INVALID_HEIGHT_VALUE; // No height
return height;
}
}
return VMAP_INVALID_HEIGHT_VALUE;
}
bool VMapManager2::getAreaInfo(unsigned int mapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const
{
//if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_VMAP, mapId, NULL, VMAP_DISABLE_AREAFLAG)) // pussywizard: optimization
{
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
Vector3 pos = convertPositionToInternalRep(x, y, z);
bool result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId);
// z is not touched by convertPositionToInternalRep(), so just copy
z = pos.z;
return result;
}
}
return false;
}
bool VMapManager2::GetLiquidLevel(uint32 mapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const
{
//if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_VMAP, mapId, NULL, VMAP_DISABLE_LIQUIDSTATUS)) // pussywizard: optimization
{
InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(mapId);
if (instanceTree != iInstanceMapTrees.end())
{
LocationInfo info;
Vector3 pos = convertPositionToInternalRep(x, y, z);
if (instanceTree->second->GetLocationInfo(pos, info))
{
floor = info.ground_Z;
ASSERT(floor < std::numeric_limits<float>::max());
type = info.hitModel->GetLiquidType(); // entry from LiquidType.dbc
if (reqLiquidType && !(GetLiquidFlags(type) & reqLiquidType))
return false;
if (info.hitInstance->GetLiquidLevel(pos, info, level))
return true;
}
}
}
return false;
}
WorldModel* VMapManager2::acquireModelInstance(const std::string& basepath, const std::string& filename)
{
//! Critical section, thread safe access to iLoadedModelFiles
TRINITY_GUARD(ACE_Thread_Mutex, LoadedModelFilesLock);
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
WorldModel* worldmodel = new WorldModel();
if (!worldmodel->readFile(basepath + filename + ".vmo"))
{
sLog->outError("VMapManager2: could not load '%s%s.vmo'", basepath.c_str(), filename.c_str());
delete worldmodel;
return NULL;
}
;//sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: loading file '%s%s'", basepath.c_str(), filename.c_str());
model = iLoadedModelFiles.insert(std::pair<std::string, ManagedModel>(filename, ManagedModel())).first;
model->second.setModel(worldmodel);
}
//model->second.incRefCount();
return model->second.getModel();
}
void VMapManager2::releaseModelInstance(const std::string &filename)
{
/*//! Critical section, thread safe access to iLoadedModelFiles
TRINITY_GUARD(ACE_Thread_Mutex, LoadedModelFilesLock);
ModelFileMap::iterator model = iLoadedModelFiles.find(filename);
if (model == iLoadedModelFiles.end())
{
sLog->outError("VMapManager2: trying to unload non-loaded file '%s'", filename.c_str());
return;
}
if (model->second.decRefCount() == 0)
{
;//sLog->outDebug(LOG_FILTER_MAPS, "VMapManager2: unloading file '%s'", filename.c_str());
delete model->second.getModel();
iLoadedModelFiles.erase(model);
}*/
}
bool VMapManager2::existsMap(const char* basePath, unsigned int mapId, int x, int y)
{
return StaticMapTree::CanLoadMap(std::string(basePath), mapId, x, y);
}
} // namespace VMAP

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _VMAPMANAGER2_H
#define _VMAPMANAGER2_H
#include "IVMapManager.h"
#include "Dynamic/UnorderedMap.h"
#include "Define.h"
#include <ace/Thread_Mutex.h>
//===========================================================
#define MAP_FILENAME_EXTENSION2 ".vmtree"
#define FILENAMEBUFFER_SIZE 500
/**
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
Each global map or instance has its own dynamic BSP-Tree.
The loaded ModelContainers are included in one of these BSP-Trees.
Additionally a table to match map ids and map names is used.
*/
//===========================================================
namespace G3D
{
class Vector3;
}
namespace VMAP
{
class StaticMapTree;
class WorldModel;
class ManagedModel
{
public:
ManagedModel() : iModel(0), iRefCount(0) { }
void setModel(WorldModel* model) { iModel = model; }
WorldModel* getModel() { return iModel; }
void incRefCount() { ++iRefCount; }
int decRefCount() { return --iRefCount; }
protected:
WorldModel* iModel;
int iRefCount;
};
typedef UNORDERED_MAP<uint32, StaticMapTree*> InstanceTreeMap;
typedef UNORDERED_MAP<std::string, ManagedModel> ModelFileMap;
class VMapManager2 : public IVMapManager
{
protected:
// Tree to check collision
ModelFileMap iLoadedModelFiles;
InstanceTreeMap iInstanceMapTrees;
// Mutex for iLoadedModelFiles
ACE_Thread_Mutex LoadedModelFilesLock;
bool _loadMap(uint32 mapId, const std::string& basePath, uint32 tileX, uint32 tileY);
/* void _unloadMap(uint32 pMapId, uint32 x, uint32 y); */
public:
// public for debug
G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
static std::string getMapFileName(unsigned int mapId);
VMapManager2();
~VMapManager2(void);
int loadMap(const char* pBasePath, unsigned int mapId, int x, int y);
void unloadMap(unsigned int mapId, int x, int y);
void unloadMap(unsigned int mapId);
bool isInLineOfSight(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2) ;
/**
fill the hit pos and return true, if an object was hit
*/
bool getObjectHitPos(unsigned int mapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float& ry, float& rz, float modifyDist);
float getHeight(unsigned int mapId, float x, float y, float z, float maxSearchDist);
bool processCommand(char* /*command*/) { return false; } // for debug and extensions
bool getAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const;
bool GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 reqLiquidType, float& level, float& floor, uint32& type) const;
WorldModel* acquireModelInstance(const std::string& basepath, const std::string& filename);
void releaseModelInstance(const std::string& filename);
// what's the use of this? o.O
virtual std::string getDirFileName(unsigned int mapId, int /*x*/, int /*y*/) const
{
return getMapFileName(mapId);
}
virtual bool existsMap(const char* basePath, unsigned int mapId, int x, int y);
public:
void getInstanceMapTree(InstanceTreeMap &instanceMapTree);
};
}
#endif

View File

@@ -0,0 +1,477 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "MapTree.h"
#include "ModelInstance.h"
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include "Log.h"
#include "Errors.h"
#include <string>
#include <sstream>
#include <iomanip>
#include <limits>
using G3D::Vector3;
namespace VMAP
{
class MapRayCallback
{
public:
MapRayCallback(ModelInstance* val): prims(val), hit(false) {}
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool StopAtFirstHit)
{
bool result = prims[entry].intersectRay(ray, distance, StopAtFirstHit);
if (result)
hit = true;
return result;
}
bool didHit() { return hit; }
protected:
ModelInstance* prims;
bool hit;
};
class AreaInfoCallback
{
public:
AreaInfoCallback(ModelInstance* val): prims(val) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
;//sLog->outDebug(LOG_FILTER_MAPS, "AreaInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
#endif
prims[entry].intersectPoint(point, aInfo);
}
ModelInstance* prims;
AreaInfo aInfo;
};
class LocationInfoCallback
{
public:
LocationInfoCallback(ModelInstance* val, LocationInfo &info): prims(val), locInfo(info), result(false) {}
void operator()(const Vector3& point, uint32 entry)
{
#ifdef VMAP_DEBUG
;//sLog->outDebug(LOG_FILTER_MAPS, "LocationInfoCallback: trying to intersect '%s'", prims[entry].name.c_str());
#endif
if (prims[entry].GetLocationInfo(point, locInfo))
result = true;
}
ModelInstance* prims;
LocationInfo &locInfo;
bool result;
};
//=========================================================
std::string StaticMapTree::getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY)
{
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << std::setw(3) << mapID << '_';
//tilefilename << std::setw(2) << tileX << '_' << std::setw(2) << tileY << ".vmtile";
tilefilename << std::setw(2) << tileY << '_' << std::setw(2) << tileX << ".vmtile";
return tilefilename.str();
}
bool StaticMapTree::getAreaInfo(Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const
{
AreaInfoCallback intersectionCallBack(iTreeValues);
iTree.intersectPoint(pos, intersectionCallBack);
if (intersectionCallBack.aInfo.result)
{
flags = intersectionCallBack.aInfo.flags;
adtId = intersectionCallBack.aInfo.adtId;
rootId = intersectionCallBack.aInfo.rootId;
groupId = intersectionCallBack.aInfo.groupId;
pos.z = intersectionCallBack.aInfo.ground_Z;
return true;
}
return false;
}
bool StaticMapTree::GetLocationInfo(const Vector3 &pos, LocationInfo &info) const
{
LocationInfoCallback intersectionCallBack(iTreeValues, info);
iTree.intersectPoint(pos, intersectionCallBack);
return intersectionCallBack.result;
}
StaticMapTree::StaticMapTree(uint32 mapID, const std::string &basePath)
: iMapID(mapID), iIsTiled(false), iTreeValues(0), iBasePath(basePath)
{
if (iBasePath.length() > 0 && iBasePath[iBasePath.length()-1] != '/' && iBasePath[iBasePath.length()-1] != '\\')
{
iBasePath.push_back('/');
}
}
//=========================================================
//! Make sure to call unloadMap() to unregister acquired model references before destroying
StaticMapTree::~StaticMapTree()
{
delete[] iTreeValues;
}
//=========================================================
/**
If intersection is found within pMaxDist, sets pMaxDist to intersection distance and returns true.
Else, pMaxDist is not modified and returns false;
*/
bool StaticMapTree::getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool StopAtFirstHit) const
{
float distance = pMaxDist;
MapRayCallback intersectionCallBack(iTreeValues);
iTree.intersectRay(pRay, intersectionCallBack, distance, StopAtFirstHit);
if (intersectionCallBack.didHit())
pMaxDist = distance;
return intersectionCallBack.didHit();
}
//=========================================================
bool StaticMapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) const
{
float maxDist = (pos2 - pos1).magnitude();
// return false if distance is over max float, in case of cheater teleporting to the end of the universe
if (maxDist == std::numeric_limits<float>::max() || !myisfinite(maxDist))
return false;
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
ASSERT(maxDist < std::numeric_limits<float>::max());
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
return true;
// direction with length of 1
G3D::Ray ray = G3D::Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist);
if (getIntersectionTime(ray, maxDist, true))
return false;
return true;
}
//=========================================================
/**
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
bool StaticMapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) const
{
bool result=false;
float maxDist = (pPos2 - pPos1).magnitude();
// valid map coords should *never ever* produce float overflow, but this would produce NaNs too
ASSERT(maxDist < std::numeric_limits<float>::max());
// prevent NaN values which can cause BIH intersection to enter infinite loop
if (maxDist < 1e-10f)
{
pResultHitPos = pPos2;
return false;
}
Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1
G3D::Ray ray(pPos1, dir);
float dist = maxDist;
if (getIntersectionTime(ray, dist, false))
{
pResultHitPos = pPos1 + dir * dist;
if (pModifyDist < 0)
{
if ((pResultHitPos - pPos1).magnitude() > -pModifyDist)
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
else
{
pResultHitPos = pPos1;
}
}
else
{
pResultHitPos = pResultHitPos + dir*pModifyDist;
}
result = true;
}
else
{
pResultHitPos = pPos2;
result = false;
}
return result;
}
//=========================================================
float StaticMapTree::getHeight(const Vector3& pPos, float maxSearchDist) const
{
float height = G3D::inf();
Vector3 dir = Vector3(0, 0, -1);
G3D::Ray ray(pPos, dir); // direction with length of 1
float maxDist = maxSearchDist;
if (getIntersectionTime(ray, maxDist, false))
{
height = pPos.z - maxDist;
}
return(height);
}
//=========================================================
bool StaticMapTree::CanLoadMap(const std::string &vmapPath, uint32 mapID, uint32 tileX, uint32 tileY)
{
std::string basePath = vmapPath;
if (basePath.length() > 0 && basePath[basePath.length()-1] != '/' && basePath[basePath.length()-1] != '\\')
basePath.push_back('/');
std::string fullname = basePath + VMapManager2::getMapFileName(mapID);
bool success = true;
FILE* rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
// TODO: check magic number when implemented...
char tiled;
char chunk[8];
if (!readChunk(rf, chunk, VMAP_MAGIC, 8) || fread(&tiled, sizeof(char), 1, rf) != 1)
{
fclose(rf);
return false;
}
if (tiled)
{
std::string tilefile = basePath + getTileFileName(mapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (!tf)
success = false;
else
{
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
success = false;
fclose(tf);
}
}
fclose(rf);
return success;
}
//=========================================================
bool StaticMapTree::InitMap(const std::string &fname, VMapManager2* vm)
{
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : initializing StaticMapTree '%s'", fname.c_str());
bool success = false;
std::string fullname = iBasePath + fname;
FILE* rf = fopen(fullname.c_str(), "rb");
if (!rf)
return false;
char chunk[8];
char tiled = '\0';
if (readChunk(rf, chunk, VMAP_MAGIC, 8) && fread(&tiled, sizeof(char), 1, rf) == 1 &&
readChunk(rf, chunk, "NODE", 4) && iTree.readFromFile(rf))
{
iNTreeValues = iTree.primCount();
iTreeValues = new ModelInstance[iNTreeValues];
success = readChunk(rf, chunk, "GOBJ", 4);
}
iIsTiled = bool(tiled);
// global model spawns
// only non-tiled maps have them, and if so exactly one (so far at least...)
ModelSpawn spawn;
#ifdef VMAP_DEBUG
//TC_LOG_DEBUG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : map isTiled: %u", static_cast<uint32>(iIsTiled));
#endif
if (!iIsTiled && ModelSpawn::readFromFile(rf, spawn))
{
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
//VMAP_DEBUG_LOG(LOG_FILTER_MAPS, "StaticMapTree::InitMap() : loading %s", spawn.name.c_str());
if (model)
{
// assume that global model always is the first and only tree value (could be improved...)
iTreeValues[0] = ModelInstance(spawn, model);
iLoadedSpawns[0] = 1;
}
else
{
success = false;
//VMAP_ERROR_LOG(LOG_FILTER_GENERAL, "StaticMapTree::InitMap() : could not acquire WorldModel pointer for '%s'", spawn.name.c_str());
}
}
fclose(rf);
return success;
}
//=========================================================
void StaticMapTree::UnloadMap(VMapManager2* vm)
{
for (loadedSpawnMap::iterator i = iLoadedSpawns.begin(); i != iLoadedSpawns.end(); ++i)
{
iTreeValues[i->first].setUnloaded();
for (uint32 refCount = 0; refCount < i->second; ++refCount)
vm->releaseModelInstance(iTreeValues[i->first].name);
}
iLoadedSpawns.clear();
iLoadedTiles.clear();
}
//=========================================================
bool StaticMapTree::LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
{
if (!iIsTiled)
{
// currently, core creates grids for all maps, whether it has terrain tiles or not
// so we need "fake" tile loads to know when we can unload map geometry
iLoadedTiles[packTileID(tileX, tileY)] = false;
return true;
}
if (!iTreeValues)
{
sLog->outError("StaticMapTree::LoadMapTile() : tree has not been initialized [%u, %u]", tileX, tileY);
return false;
}
bool result = true;
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
char chunk[8];
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
result = false;
uint32 numSpawns = 0;
if (result && fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// acquire model instance
WorldModel* model = vm->acquireModelInstance(iBasePath, spawn.name);
if (!model)
sLog->outError("StaticMapTree::LoadMapTile() : could not acquire WorldModel pointer [%u, %u]", tileX, tileY);
// update tree
uint32 referencedVal;
if (fread(&referencedVal, sizeof(uint32), 1, tf) == 1)
{
if (!iLoadedSpawns.count(referencedVal))
{
#ifdef VMAP_DEBUG
if (referencedVal > iNTreeValues)
{
;//sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::LoadMapTile() : invalid tree element (%u/%u)", referencedVal, iNTreeValues);
continue;
}
#endif
iTreeValues[referencedVal] = ModelInstance(spawn, model);
iLoadedSpawns[referencedVal] = 1;
}
else
{
++iLoadedSpawns[referencedVal];
#ifdef VMAP_DEBUG
if (iTreeValues[referencedVal].ID != spawn.ID)
;//sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::LoadMapTile() : trying to load wrong spawn in node");
else if (iTreeValues[referencedVal].name != spawn.name)
;//sLog->outDebug(LOG_FILTER_MAPS, "StaticMapTree::LoadMapTile() : name collision on GUID=%u", spawn.ID);
#endif
}
}
else
result = false;
}
}
iLoadedTiles[packTileID(tileX, tileY)] = true;
fclose(tf);
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
return result;
}
//=========================================================
void StaticMapTree::UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm)
{
uint32 tileID = packTileID(tileX, tileY);
loadedTileMap::iterator tile = iLoadedTiles.find(tileID);
if (tile == iLoadedTiles.end())
{
sLog->outError("StaticMapTree::UnloadMapTile() : trying to unload non-loaded tile - Map:%u X:%u Y:%u", iMapID, tileX, tileY);
return;
}
if (tile->second) // file associated with tile
{
std::string tilefile = iBasePath + getTileFileName(iMapID, tileX, tileY);
FILE* tf = fopen(tilefile.c_str(), "rb");
if (tf)
{
bool result=true;
char chunk[8];
if (!readChunk(tf, chunk, VMAP_MAGIC, 8))
result = false;
uint32 numSpawns;
if (fread(&numSpawns, sizeof(uint32), 1, tf) != 1)
result = false;
for (uint32 i=0; i<numSpawns && result; ++i)
{
// read model spawns
ModelSpawn spawn;
result = ModelSpawn::readFromFile(tf, spawn);
if (result)
{
// release model instance
vm->releaseModelInstance(spawn.name);
// update tree
uint32 referencedNode;
if (fread(&referencedNode, sizeof(uint32), 1, tf) != 1)
result = false;
else
{
if (!iLoadedSpawns.count(referencedNode))
sLog->outError("StaticMapTree::UnloadMapTile() : trying to unload non-referenced model '%s' (ID:%u)", spawn.name.c_str(), spawn.ID);
else if (--iLoadedSpawns[referencedNode] == 0)
{
iTreeValues[referencedNode].setUnloaded();
iLoadedSpawns.erase(referencedNode);
}
}
}
}
fclose(tf);
}
}
iLoadedTiles.erase(tile);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _MAPTREE_H
#define _MAPTREE_H
#include "Define.h"
#include "Dynamic/UnorderedMap.h"
#include "BoundingIntervalHierarchy.h"
namespace VMAP
{
class ModelInstance;
class GroupModel;
class VMapManager2;
struct LocationInfo
{
LocationInfo(): hitInstance(0), hitModel(0), ground_Z(-G3D::inf()) { }
const ModelInstance* hitInstance;
const GroupModel* hitModel;
float ground_Z;
};
class StaticMapTree
{
typedef UNORDERED_MAP<uint32, bool> loadedTileMap;
typedef UNORDERED_MAP<uint32, uint32> loadedSpawnMap;
private:
uint32 iMapID;
bool iIsTiled;
BIH iTree;
ModelInstance* iTreeValues; // the tree entries
uint32 iNTreeValues;
// Store all the map tile idents that are loaded for that map
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
// empty tiles have no tile file, hence map with bool instead of just a set (consistency check)
loadedTileMap iLoadedTiles;
// stores <tree_index, reference_count> to invalidate tree values, unload map, and to be able to report errors
loadedSpawnMap iLoadedSpawns;
std::string iBasePath;
private:
bool getIntersectionTime(const G3D::Ray& pRay, float &pMaxDist, bool StopAtFirstHit) const;
//bool containsLoadedMapTile(unsigned int pTileIdent) const { return(iLoadedMapTiles.containsKey(pTileIdent)); }
public:
static std::string getTileFileName(uint32 mapID, uint32 tileX, uint32 tileY);
static uint32 packTileID(uint32 tileX, uint32 tileY) { return tileX<<16 | tileY; }
static void unpackTileID(uint32 ID, uint32 &tileX, uint32 &tileY) { tileX = ID>>16; tileY = ID&0xFF; }
static bool CanLoadMap(const std::string &basePath, uint32 mapID, uint32 tileX, uint32 tileY);
StaticMapTree(uint32 mapID, const std::string &basePath);
~StaticMapTree();
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2) const;
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist) const;
float getHeight(const G3D::Vector3& pPos, float maxSearchDist) const;
bool getAreaInfo(G3D::Vector3 &pos, uint32 &flags, int32 &adtId, int32 &rootId, int32 &groupId) const;
bool GetLocationInfo(const G3D::Vector3 &pos, LocationInfo &info) const;
bool InitMap(const std::string &fname, VMapManager2* vm);
void UnloadMap(VMapManager2* vm);
bool LoadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
void UnloadMapTile(uint32 tileX, uint32 tileY, VMapManager2* vm);
bool isTiled() const { return iIsTiled; }
uint32 numLoadedTiles() const { return iLoadedTiles.size(); }
void getModelInstances(ModelInstance* &models, uint32 &count);
};
struct AreaInfo
{
AreaInfo(): result(false), ground_Z(-G3D::inf()), flags(0), adtId(0),
rootId(0), groupId(0) { }
bool result;
float ground_Z;
uint32 flags;
int32 adtId;
int32 rootId;
int32 groupId;
};
} // VMAP
#endif // _MAPTREE_H

View File

@@ -0,0 +1,540 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "TileAssembler.h"
#include "MapTree.h"
#include "BoundingIntervalHierarchy.h"
#include "VMapDefinitions.h"
#include "SharedDefines.h"
#include <set>
#include <iomanip>
#include <sstream>
#include <iomanip>
using G3D::Vector3;
using G3D::AABox;
using G3D::inf;
using std::pair;
template<> struct BoundsTrait<VMAP::ModelSpawn*>
{
static void getBounds(const VMAP::ModelSpawn* const &obj, G3D::AABox& out) { out = obj->getBounds(); }
};
namespace VMAP
{
bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len)
{
if (fread(dest, sizeof(char), len, rf) != len) return false;
return memcmp(dest, compare, len) == 0;
}
Vector3 ModelPosition::transform(const Vector3& pIn) const
{
Vector3 out = pIn * iScale;
out = iRotation * out;
return(out);
}
//=================================================================
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
: iDestDir(pDestDirName), iSrcDir(pSrcDirName), iFilterMethod(NULL), iCurrentUniqueNameId(0)
{
//mkdir(iDestDir);
//init();
}
TileAssembler::~TileAssembler()
{
//delete iCoordModelMapping;
}
bool TileAssembler::convertWorld2()
{
bool success = readMapSpawns();
if (!success)
return false;
// export Map data
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end() && success; ++map_iter)
{
// build global map tree
std::vector<ModelSpawn*> mapSpawns;
UniqueEntryMap::iterator entry;
printf("Calculating model bounds for map %u...\n", map_iter->first);
for (entry = map_iter->second->UniqueEntries.begin(); entry != map_iter->second->UniqueEntries.end(); ++entry)
{
// M2 models don't have a bound set in WDT/ADT placement data, i still think they're not used for LoS at all on retail
if (entry->second.flags & MOD_M2)
{
if (!calculateTransformedBound(entry->second))
break;
}
else if (entry->second.flags & MOD_WORLDSPAWN) // WMO maps and terrain maps use different origin, so we need to adapt :/
{
/// @todo remove extractor hack and uncomment below line:
//entry->second.iPos += Vector3(533.33333f*32, 533.33333f*32, 0.f);
entry->second.iBound = entry->second.iBound + Vector3(533.33333f*32, 533.33333f*32, 0.f);
}
mapSpawns.push_back(&(entry->second));
spawnedModelFiles.insert(entry->second.name);
}
printf("Creating map tree for map %u...\n", map_iter->first);
BIH pTree;
try
{
pTree.build(mapSpawns, BoundsTrait<ModelSpawn*>::getBounds);
}
catch (std::exception& e)
{
printf("Exception ""%s"" when calling pTree.build", e.what());
return false;
}
// ===> possibly move this code to StaticMapTree class
std::map<uint32, uint32> modelNodeIdx;
for (uint32 i=0; i<mapSpawns.size(); ++i)
modelNodeIdx.insert(pair<uint32, uint32>(mapSpawns[i]->ID, i));
// write map tree file
std::stringstream mapfilename;
mapfilename << iDestDir << '/' << std::setfill('0') << std::setw(3) << map_iter->first << ".vmtree";
FILE* mapfile = fopen(mapfilename.str().c_str(), "wb");
if (!mapfile)
{
success = false;
printf("Cannot open %s\n", mapfilename.str().c_str());
break;
}
//general info
if (success && fwrite(VMAP_MAGIC, 1, 8, mapfile) != 8) success = false;
uint32 globalTileID = StaticMapTree::packTileID(65, 65);
pair<TileMap::iterator, TileMap::iterator> globalRange = map_iter->second->TileEntries.equal_range(globalTileID);
char isTiled = globalRange.first == globalRange.second; // only maps without terrain (tiles) have global WMO
if (success && fwrite(&isTiled, sizeof(char), 1, mapfile) != 1) success = false;
// Nodes
if (success && fwrite("NODE", 4, 1, mapfile) != 1) success = false;
if (success) success = pTree.writeToFile(mapfile);
// global map spawns (WDT), if any (most instances)
if (success && fwrite("GOBJ", 4, 1, mapfile) != 1) success = false;
for (TileMap::iterator glob=globalRange.first; glob != globalRange.second && success; ++glob)
{
success = ModelSpawn::writeToFile(mapfile, map_iter->second->UniqueEntries[glob->second]);
}
fclose(mapfile);
// <====
// write map tile files, similar to ADT files, only with extra BSP tree node info
TileMap &tileEntries = map_iter->second->TileEntries;
TileMap::iterator tile;
for (tile = tileEntries.begin(); tile != tileEntries.end(); ++tile)
{
const ModelSpawn &spawn = map_iter->second->UniqueEntries[tile->second];
if (spawn.flags & MOD_WORLDSPAWN) // WDT spawn, saved as tile 65/65 currently...
continue;
uint32 nSpawns = tileEntries.count(tile->first);
std::stringstream tilefilename;
tilefilename.fill('0');
tilefilename << iDestDir << '/' << std::setw(3) << map_iter->first << '_';
uint32 x, y;
StaticMapTree::unpackTileID(tile->first, x, y);
tilefilename << std::setw(2) << x << '_' << std::setw(2) << y << ".vmtile";
if (FILE* tilefile = fopen(tilefilename.str().c_str(), "wb"))
{
// file header
if (success && fwrite(VMAP_MAGIC, 1, 8, tilefile) != 8) success = false;
// write number of tile spawns
if (success && fwrite(&nSpawns, sizeof(uint32), 1, tilefile) != 1) success = false;
// write tile spawns
for (uint32 s=0; s<nSpawns; ++s)
{
if (s)
++tile;
const ModelSpawn &spawn2 = map_iter->second->UniqueEntries[tile->second];
success = success && ModelSpawn::writeToFile(tilefile, spawn2);
// MapTree nodes to update when loading tile:
std::map<uint32, uint32>::iterator nIdx = modelNodeIdx.find(spawn2.ID);
if (success && fwrite(&nIdx->second, sizeof(uint32), 1, tilefile) != 1) success = false;
}
fclose(tilefile);
}
}
// break; //test, extract only first map; TODO: remvoe this line
}
// add an object models, listed in temp_gameobject_models file
exportGameobjectModels();
// export objects
std::cout << "\nConverting Model Files" << std::endl;
for (std::set<std::string>::iterator mfile = spawnedModelFiles.begin(); mfile != spawnedModelFiles.end(); ++mfile)
{
std::cout << "Converting " << *mfile << std::endl;
if (!convertRawFile(*mfile))
{
std::cout << "error converting " << *mfile << std::endl;
success = false;
break;
}
}
//cleanup:
for (MapData::iterator map_iter = mapData.begin(); map_iter != mapData.end(); ++map_iter)
{
delete map_iter->second;
}
return success;
}
bool TileAssembler::readMapSpawns()
{
std::string fname = iSrcDir + "/dir_bin";
FILE* dirf = fopen(fname.c_str(), "rb");
if (!dirf)
{
printf("Could not read dir_bin file!\n");
return false;
}
printf("Read coordinate mapping...\n");
uint32 mapID, tileX, tileY, check=0;
G3D::Vector3 v1, v2;
ModelSpawn spawn;
while (!feof(dirf))
{
check = 0;
// read mapID, tileX, tileY, Flags, adtID, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
check += fread(&mapID, sizeof(uint32), 1, dirf);
if (check == 0) // EoF...
break;
check += fread(&tileX, sizeof(uint32), 1, dirf);
check += fread(&tileY, sizeof(uint32), 1, dirf);
if (!ModelSpawn::readFromFile(dirf, spawn))
break;
MapSpawns *current;
MapData::iterator map_iter = mapData.find(mapID);
if (map_iter == mapData.end())
{
printf("spawning Map %d\n", mapID);
mapData[mapID] = current = new MapSpawns();
}
else current = (*map_iter).second;
current->UniqueEntries.insert(pair<uint32, ModelSpawn>(spawn.ID, spawn));
current->TileEntries.insert(pair<uint32, uint32>(StaticMapTree::packTileID(tileX, tileY), spawn.ID));
}
bool success = (ferror(dirf) == 0);
fclose(dirf);
return success;
}
bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn)
{
std::string modelFilename(iSrcDir);
modelFilename.push_back('/');
modelFilename.append(spawn.name);
ModelPosition modelPosition;
modelPosition.iDir = spawn.iRot;
modelPosition.iScale = spawn.iScale;
modelPosition.init();
WorldModel_Raw raw_model;
if (!raw_model.Read(modelFilename.c_str()))
return false;
uint32 groups = raw_model.groupsArray.size();
if (groups != 1)
printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str());
AABox modelBound;
bool boundEmpty=true;
for (uint32 g=0; g<groups; ++g) // should be only one for M2 files...
{
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
if (vertices.empty())
{
std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl;
continue;
}
uint32 nvectors = vertices.size();
for (uint32 i = 0; i < nvectors; ++i)
{
Vector3 v = modelPosition.transform(vertices[i]);
if (boundEmpty)
modelBound = AABox(v, v), boundEmpty=false;
else
modelBound.merge(v);
}
}
spawn.iBound = modelBound + spawn.iPos;
spawn.flags |= MOD_HAS_BOUND;
return true;
}
struct WMOLiquidHeader
{
int xverts, yverts, xtiles, ytiles;
float pos_x;
float pos_y;
float pos_z;
short type;
};
//=================================================================
bool TileAssembler::convertRawFile(const std::string& pModelFilename)
{
bool success = true;
std::string filename = iSrcDir;
if (filename.length() >0)
filename.push_back('/');
filename.append(pModelFilename);
WorldModel_Raw raw_model;
if (!raw_model.Read(filename.c_str()))
return false;
// write WorldModel
WorldModel model;
model.setRootWmoID(raw_model.RootWMOID);
if (!raw_model.groupsArray.empty())
{
std::vector<GroupModel> groupsArray;
uint32 groups = raw_model.groupsArray.size();
for (uint32 g = 0; g < groups; ++g)
{
GroupModel_Raw& raw_group = raw_model.groupsArray[g];
groupsArray.push_back(GroupModel(raw_group.mogpflags, raw_group.GroupWMOID, raw_group.bounds ));
groupsArray.back().setMeshData(raw_group.vertexArray, raw_group.triangles);
groupsArray.back().setLiquidData(raw_group.liquid);
}
model.setGroupModels(groupsArray);
}
success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo");
//std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl;
return success;
}
void TileAssembler::exportGameobjectModels()
{
FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb");
if (!model_list)
return;
FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb");
if (!model_list_copy)
{
fclose(model_list);
return;
}
uint32 name_length, displayId;
char buff[500];
while (!feof(model_list))
{
if (fread(&displayId, sizeof(uint32), 1, model_list) != 1
|| fread(&name_length, sizeof(uint32), 1, model_list) != 1
|| name_length >= sizeof(buff)
|| fread(&buff, sizeof(char), name_length, model_list) != name_length)
{
std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl;
break;
}
std::string model_name(buff, name_length);
WorldModel_Raw raw_model;
if ( !raw_model.Read((iSrcDir + "/" + model_name).c_str()) )
continue;
spawnedModelFiles.insert(model_name);
AABox bounds;
bool boundEmpty = true;
for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g)
{
std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray;
uint32 nvectors = vertices.size();
for (uint32 i = 0; i < nvectors; ++i)
{
Vector3& v = vertices[i];
if (boundEmpty)
bounds = AABox(v, v), boundEmpty = false;
else
bounds.merge(v);
}
}
fwrite(&displayId, sizeof(uint32), 1, model_list_copy);
fwrite(&name_length, sizeof(uint32), 1, model_list_copy);
fwrite(&buff, sizeof(char), name_length, model_list_copy);
fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy);
fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy);
}
fclose(model_list);
fclose(model_list_copy);
}
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V, S) if (fread((V), (S), 1, rf) != 1) { \
fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }
#define READ_OR_RETURN_WITH_DELETE(V, S) if (fread((V), (S), 1, rf) != 1) { \
fclose(rf); printf("readfail, op = %i\n", readOperation); delete[] V; return(false); };
#define CMP_OR_RETURN(V, S) if (strcmp((V), (S)) != 0) { \
fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); }
bool GroupModel_Raw::Read(FILE* rf)
{
char blockId[5];
blockId[4] = 0;
int blocksize;
int readOperation = 0;
READ_OR_RETURN(&mogpflags, sizeof(uint32));
READ_OR_RETURN(&GroupWMOID, sizeof(uint32));
Vector3 vec1, vec2;
READ_OR_RETURN(&vec1, sizeof(Vector3));
READ_OR_RETURN(&vec2, sizeof(Vector3));
bounds.set(vec1, vec2);
READ_OR_RETURN(&liquidflags, sizeof(uint32));
// will this ever be used? what is it good for anyway??
uint32 branches;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&branches, sizeof(uint32));
for (uint32 b=0; b<branches; ++b)
{
uint32 indexes;
// indexes for each branch (not used jet)
READ_OR_RETURN(&indexes, sizeof(uint32));
}
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nindexes;
READ_OR_RETURN(&nindexes, sizeof(uint32));
if (nindexes >0)
{
uint16 *indexarray = new uint16[nindexes];
READ_OR_RETURN_WITH_DELETE(indexarray, nindexes*sizeof(uint16));
triangles.reserve(nindexes / 3);
for (uint32 i=0; i<nindexes; i+=3)
triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2]));
delete[] indexarray;
}
// ---- vectors
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
uint32 nvectors;
READ_OR_RETURN(&nvectors, sizeof(uint32));
if (nvectors >0)
{
float *vectorarray = new float[nvectors*3];
READ_OR_RETURN_WITH_DELETE(vectorarray, nvectors*sizeof(float)*3);
for (uint32 i=0; i<nvectors; ++i)
vertexArray.push_back( Vector3(vectorarray + 3*i) );
delete[] vectorarray;
}
// ----- liquid
liquid = 0;
if (liquidflags& 1)
{
WMOLiquidHeader hlq;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "LIQU");
READ_OR_RETURN(&blocksize, sizeof(int));
READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader));
liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type);
uint32 size = hlq.xverts*hlq.yverts;
READ_OR_RETURN(liquid->GetHeightStorage(), size*sizeof(float));
size = hlq.xtiles*hlq.ytiles;
READ_OR_RETURN(liquid->GetFlagsStorage(), size);
}
return true;
}
GroupModel_Raw::~GroupModel_Raw()
{
delete liquid;
}
bool WorldModel_Raw::Read(const char * path)
{
FILE* rf = fopen(path, "rb");
if (!rf)
{
printf("ERROR: Can't open raw model file: %s\n", path);
return false;
}
char ident[9];
ident[8] = '\0';
int readOperation = 0;
READ_OR_RETURN(&ident, 8);
CMP_OR_RETURN(ident, RAW_VMAP_MAGIC);
// we have to read one int. This is needed during the export and we have to skip it here
uint32 tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors));
uint32 groups;
READ_OR_RETURN(&groups, sizeof(uint32));
READ_OR_RETURN(&RootWMOID, sizeof(uint32));
groupsArray.resize(groups);
bool succeed = true;
for (uint32 g = 0; g < groups && succeed; ++g)
succeed = groupsArray[g].Read(rf);
if (succeed) /// rf will be freed inside Read if the function had any errors.
fclose(rf);
return succeed;
}
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _TILEASSEMBLER_H_
#define _TILEASSEMBLER_H_
#include <G3D/Vector3.h>
#include <G3D/Matrix3.h>
#include <map>
#include <set>
#include "ModelInstance.h"
#include "WorldModel.h"
namespace VMAP
{
/**
This Class is used to convert raw vector data into balanced BSP-Trees.
To start the conversion call convertWorld().
*/
//===============================================
class ModelPosition
{
private:
G3D::Matrix3 iRotation;
public:
ModelPosition(): iScale(0.0f) { }
G3D::Vector3 iPos;
G3D::Vector3 iDir;
float iScale;
void init()
{
iRotation = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iDir.y/180.f, G3D::pi()*iDir.x/180.f, G3D::pi()*iDir.z/180.f);
}
G3D::Vector3 transform(const G3D::Vector3& pIn) const;
void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
};
typedef std::map<uint32, ModelSpawn> UniqueEntryMap;
typedef std::multimap<uint32, uint32> TileMap;
struct MapSpawns
{
UniqueEntryMap UniqueEntries;
TileMap TileEntries;
};
typedef std::map<uint32, MapSpawns*> MapData;
//===============================================
struct GroupModel_Raw
{
uint32 mogpflags;
uint32 GroupWMOID;
G3D::AABox bounds;
uint32 liquidflags;
std::vector<MeshTriangle> triangles;
std::vector<G3D::Vector3> vertexArray;
class WmoLiquid* liquid;
GroupModel_Raw() : mogpflags(0), GroupWMOID(0), liquidflags(0),
liquid(NULL) { }
~GroupModel_Raw();
bool Read(FILE* f);
};
struct WorldModel_Raw
{
uint32 RootWMOID;
std::vector<GroupModel_Raw> groupsArray;
bool Read(const char * path);
};
class TileAssembler
{
private:
std::string iDestDir;
std::string iSrcDir;
bool (*iFilterMethod)(char *pName);
G3D::Table<std::string, unsigned int > iUniqueNameIds;
unsigned int iCurrentUniqueNameId;
MapData mapData;
std::set<std::string> spawnedModelFiles;
public:
TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
virtual ~TileAssembler();
bool convertWorld2();
bool readMapSpawns();
bool calculateTransformedBound(ModelSpawn &spawn);
void exportGameobjectModels();
bool convertRawFile(const std::string& pModelFilename);
void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
std::string getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName);
};
} // VMAP
#endif /*_TILEASSEMBLER_H_*/

View File

@@ -0,0 +1,226 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include "WorldModel.h"
#include "GameObjectModel.h"
#include "Log.h"
#include "GameObject.h"
#include "Creature.h"
#include "TemporarySummon.h"
#include "Object.h"
#include "DBCStores.h"
#include "World.h"
using G3D::Vector3;
using G3D::Ray;
using G3D::AABox;
struct GameobjectModelData
{
GameobjectModelData(const std::string& name_, const AABox& box) :
bound(box), name(name_) {}
AABox bound;
std::string name;
};
typedef UNORDERED_MAP<uint32, GameobjectModelData> ModelList;
ModelList model_list;
void LoadGameObjectModelList()
{
//#ifndef NO_CORE_FUNCS
uint32 oldMSTime = getMSTime();
//#endif
FILE* model_list_file = fopen((sWorld->GetDataPath() + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb");
if (!model_list_file)
{
sLog->outError("Unable to open '%s' file.", VMAP::GAMEOBJECT_MODELS);
return;
}
uint32 name_length, displayId;
char buff[500];
while (true)
{
Vector3 v1, v2;
if (fread(&displayId, sizeof(uint32), 1, model_list_file) != 1)
if (feof(model_list_file)) // EOF flag is only set after failed reading attempt
break;
if (fread(&name_length, sizeof(uint32), 1, model_list_file) != 1
|| name_length >= sizeof(buff)
|| fread(&buff, sizeof(char), name_length, model_list_file) != name_length
|| fread(&v1, sizeof(Vector3), 1, model_list_file) != 1
|| fread(&v2, sizeof(Vector3), 1, model_list_file) != 1)
{
sLog->outError("File '%s' seems to be corrupted!", VMAP::GAMEOBJECT_MODELS);
break;
}
model_list.insert
(
ModelList::value_type( displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)) )
);
}
fclose(model_list_file);
sLog->outString(">> Loaded %u GameObject models in %u ms", uint32(model_list.size()), GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
GameObjectModel::~GameObjectModel()
{
if (iModel)
((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->releaseModelInstance(name);
}
bool GameObjectModel::initialize(const GameObject& go, const GameObjectDisplayInfoEntry& info)
{
ModelList::const_iterator it = model_list.find(info.Displayid);
if (it == model_list.end())
return false;
G3D::AABox mdl_box(it->second.bound);
// ignore models with no bounds
if (mdl_box == G3D::AABox::zero())
{
sLog->outError("GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
return false;
}
iModel = ((VMAP::VMapManager2*)VMAP::VMapFactory::createOrGetVMapManager())->acquireModelInstance(sWorld->GetDataPath() + "vmaps/", it->second.name);
if (!iModel)
return false;
name = it->second.name;
//flags = VMAP::MOD_M2;
//adtId = 0;
//ID = 0;
iPos = Vector3(go.GetPositionX(), go.GetPositionY(), go.GetPositionZ());
// pussywizard:
phasemask = (go.GetGoState() == GO_STATE_READY || go.IsTransport()) ? go.GetPhaseMask() : 0;
iScale = go.GetFloatValue(OBJECT_FIELD_SCALE_X);
iInvScale = 1.f / iScale;
G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(go.GetOrientation(), 0, 0);
iInvRot = iRotation.inverse();
// transform bounding box:
mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
AABox rotated_bounds;
for (int i = 0; i < 8; ++i)
rotated_bounds.merge(iRotation * mdl_box.corner(i));
iBound = rotated_bounds + iPos;
#ifdef SPAWN_CORNERS
// test:
for (int i = 0; i < 8; ++i)
{
Vector3 pos(iBound.corner(i));
const_cast<GameObject&>(go).SummonCreature(1, pos.x, pos.y, pos.z, 0, TEMPSUMMON_MANUAL_DESPAWN);
}
#endif
owner = &go;
return true;
}
GameObjectModel* GameObjectModel::Create(const GameObject& go)
{
const GameObjectDisplayInfoEntry* info = sGameObjectDisplayInfoStore.LookupEntry(go.GetDisplayId());
if (!info)
return NULL;
GameObjectModel* mdl = new GameObjectModel();
if (!mdl->initialize(go, *info))
{
delete mdl;
return NULL;
}
return mdl;
}
bool GameObjectModel::intersectRay(const G3D::Ray& ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const
{
if (!(phasemask & ph_mask) || !owner->isSpawned())
return false;
float time = ray.intersectionTime(iBound);
if (time == G3D::inf())
return false;
// child bounds are defined in object space:
Vector3 p = iInvRot * (ray.origin() - iPos) * iInvScale;
Ray modRay(p, iInvRot * ray.direction());
float distance = MaxDist * iInvScale;
bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit);
if (hit)
{
distance *= iScale;
MaxDist = distance;
}
return hit;
}
bool GameObjectModel::UpdatePosition()
{
if (!iModel)
return false;
ModelList::const_iterator it = model_list.find(owner->GetDisplayId());
if (it == model_list.end())
return false;
G3D::AABox mdl_box(it->second.bound);
// ignore models with no bounds
if (mdl_box == G3D::AABox::zero())
{
//VMAP_ERROR_LOG("misc", "GameObject model %s has zero bounds, loading skipped", it->second.name.c_str());
return false;
}
iPos = Vector3(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ());
G3D::Matrix3 iRotation = G3D::Matrix3::fromEulerAnglesZYX(owner->GetOrientation(), 0, 0);
iInvRot = iRotation.inverse();
// transform bounding box:
mdl_box = AABox(mdl_box.low() * iScale, mdl_box.high() * iScale);
AABox rotated_bounds;
for (int i = 0; i < 8; ++i)
rotated_bounds.merge(iRotation * mdl_box.corner(i));
iBound = rotated_bounds + iPos;
#ifdef SPAWN_CORNERS
// test:
for (int i = 0; i < 8; ++i)
{
Vector3 pos(iBound.corner(i));
owner->SummonCreature(1, pos.x, pos.y, pos.z, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 10000);
}
#endif
return true;
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _GAMEOBJECT_MODEL_H
#define _GAMEOBJECT_MODEL_H
#include <G3D/Matrix3.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "Define.h"
namespace VMAP
{
class WorldModel;
}
class GameObject;
struct GameObjectDisplayInfoEntry;
class GameObjectModel /*, public Intersectable*/
{
uint32 phasemask;
G3D::AABox iBound;
G3D::Matrix3 iInvRot;
G3D::Vector3 iPos;
//G3D::Vector3 iRot;
float iInvScale;
float iScale;
VMAP::WorldModel* iModel;
GameObject const* owner;
GameObjectModel() : phasemask(0), iInvScale(0), iScale(0), iModel(NULL), owner(NULL) { }
bool initialize(const GameObject& go, const GameObjectDisplayInfoEntry& info);
public:
std::string name;
const G3D::AABox& getBounds() const { return iBound; }
~GameObjectModel();
const G3D::Vector3& getPosition() const { return iPos;}
/** Enables\disables collision. */
void disable() { phasemask = 0;}
void enable(uint32 ph_mask) { phasemask = ph_mask;}
bool isEnabled() const {return phasemask != 0;}
bool intersectRay(const G3D::Ray& Ray, float& MaxDist, bool StopAtFirstHit, uint32 ph_mask) const;
static GameObjectModel* Create(const GameObject& go);
bool UpdatePosition();
};
#endif // _GAMEOBJECT_MODEL_H

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ModelInstance.h"
#include "WorldModel.h"
#include "MapTree.h"
#include "VMapDefinitions.h"
using G3D::Vector3;
using G3D::Ray;
namespace VMAP
{
ModelInstance::ModelInstance(const ModelSpawn &spawn, WorldModel* model): ModelSpawn(spawn), iModel(model)
{
iInvRot = G3D::Matrix3::fromEulerAnglesZYX(G3D::pi()*iRot.y/180.f, G3D::pi()*iRot.x/180.f, G3D::pi()*iRot.z/180.f).inverse();
iInvScale = 1.f/iScale;
}
bool ModelInstance::intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const
{
if (!iModel)
{
//std::cout << "<object not loaded>\n";
return false;
}
float time = pRay.intersectionTime(iBound);
if (time == G3D::inf())
{
// std::cout << "Ray does not hit '" << name << "'\n";
return false;
}
// std::cout << "Ray crosses bound of '" << name << "'\n";
/* std::cout << "ray from:" << pRay.origin().x << ", " << pRay.origin().y << ", " << pRay.origin().z
<< " dir:" << pRay.direction().x << ", " << pRay.direction().y << ", " << pRay.direction().z
<< " t/tmax:" << time << '/' << pMaxDist;
std::cout << "\nBound lo:" << iBound.low().x << ", " << iBound.low().y << ", " << iBound.low().z << " hi: "
<< iBound.high().x << ", " << iBound.high().y << ", " << iBound.high().z << std::endl; */
// child bounds are defined in object space:
Vector3 p = iInvRot * (pRay.origin() - iPos) * iInvScale;
Ray modRay(p, iInvRot * pRay.direction());
float distance = pMaxDist * iInvScale;
bool hit = iModel->IntersectRay(modRay, distance, StopAtFirstHit);
if (hit)
{
distance *= iScale;
pMaxDist = distance;
}
return hit;
}
void ModelInstance::intersectPoint(const G3D::Vector3& p, AreaInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
std::cout << "<object not loaded>\n";
#endif
return;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return;
if (!iBound.contains(p))
return;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->IntersectPoint(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z)
{
info.ground_Z = world_Z;
info.adtId = adtId;
}
}
}
bool ModelInstance::GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const
{
if (!iModel)
{
#ifdef VMAP_DEBUG
std::cout << "<object not loaded>\n";
#endif
return false;
}
// M2 files don't contain area info, only WMO files
if (flags & MOD_M2)
return false;
if (!iBound.contains(p))
return false;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (iModel->GetLocationInfo(pModel, zDirModel, zDist, info))
{
Vector3 modelGround = pModel + zDist * zDirModel;
// Transform back to world space. Note that:
// Mat * vec == vec * Mat.transpose()
// and for rotation matrices: Mat.inverse() == Mat.transpose()
float world_Z = ((modelGround * iInvRot) * iScale + iPos).z;
if (info.ground_Z < world_Z) // hm...could it be handled automatically with zDist at intersection?
{
info.ground_Z = world_Z;
info.hitInstance = this;
return true;
}
}
return false;
}
bool ModelInstance::GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const
{
// child bounds are defined in object space:
Vector3 pModel = iInvRot * (p - iPos) * iInvScale;
//Vector3 zDirModel = iInvRot * Vector3(0.f, 0.f, -1.f);
float zDist;
if (info.hitModel->GetLiquidLevel(pModel, zDist))
{
// calculate world height (zDist in model coords):
// assume WMO not tilted (wouldn't make much sense anyway)
liqHeight = zDist * iScale + iPos.z;
return true;
}
return false;
}
bool ModelSpawn::readFromFile(FILE* rf, ModelSpawn &spawn)
{
uint32 check = 0, nameLen;
check += fread(&spawn.flags, sizeof(uint32), 1, rf);
// EoF?
if (!check)
{
if (ferror(rf))
std::cout << "Error reading ModelSpawn!\n";
return false;
}
check += fread(&spawn.adtId, sizeof(uint16), 1, rf);
check += fread(&spawn.ID, sizeof(uint32), 1, rf);
check += fread(&spawn.iPos, sizeof(float), 3, rf);
check += fread(&spawn.iRot, sizeof(float), 3, rf);
check += fread(&spawn.iScale, sizeof(float), 1, rf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if (has_bound) // only WMOs have bound in MPQ, only available after computation
{
Vector3 bLow, bHigh;
check += fread(&bLow, sizeof(float), 3, rf);
check += fread(&bHigh, sizeof(float), 3, rf);
spawn.iBound = G3D::AABox(bLow, bHigh);
}
check += fread(&nameLen, sizeof(uint32), 1, rf);
if (check != uint32(has_bound ? 17 : 11))
{
std::cout << "Error reading ModelSpawn!\n";
return false;
}
char nameBuff[500];
if (nameLen > 500) // file names should never be that long, must be file error
{
std::cout << "Error reading ModelSpawn, file name too long!\n";
return false;
}
check = fread(nameBuff, sizeof(char), nameLen, rf);
if (check != nameLen)
{
std::cout << "Error reading ModelSpawn!\n";
return false;
}
spawn.name = std::string(nameBuff, nameLen);
return true;
}
bool ModelSpawn::writeToFile(FILE* wf, const ModelSpawn &spawn)
{
uint32 check=0;
check += fwrite(&spawn.flags, sizeof(uint32), 1, wf);
check += fwrite(&spawn.adtId, sizeof(uint16), 1, wf);
check += fwrite(&spawn.ID, sizeof(uint32), 1, wf);
check += fwrite(&spawn.iPos, sizeof(float), 3, wf);
check += fwrite(&spawn.iRot, sizeof(float), 3, wf);
check += fwrite(&spawn.iScale, sizeof(float), 1, wf);
bool has_bound = (spawn.flags & MOD_HAS_BOUND);
if (has_bound) // only WMOs have bound in MPQ, only available after computation
{
check += fwrite(&spawn.iBound.low(), sizeof(float), 3, wf);
check += fwrite(&spawn.iBound.high(), sizeof(float), 3, wf);
}
uint32 nameLen = spawn.name.length();
check += fwrite(&nameLen, sizeof(uint32), 1, wf);
if (check != uint32(has_bound ? 17 : 11)) return false;
check = fwrite(spawn.name.c_str(), sizeof(char), nameLen, wf);
if (check != nameLen) return false;
return true;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _MODELINSTANCE_H_
#define _MODELINSTANCE_H_
#include <G3D/Matrix3.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "Define.h"
namespace VMAP
{
class WorldModel;
struct AreaInfo;
struct LocationInfo;
enum ModelFlags
{
MOD_M2 = 1,
MOD_WORLDSPAWN = 1<<1,
MOD_HAS_BOUND = 1<<2
};
class ModelSpawn
{
public:
//mapID, tileX, tileY, Flags, ID, Pos, Rot, Scale, Bound_lo, Bound_hi, name
uint32 flags;
uint16 adtId;
uint32 ID;
G3D::Vector3 iPos;
G3D::Vector3 iRot;
float iScale;
G3D::AABox iBound;
std::string name;
bool operator==(const ModelSpawn &other) const { return ID == other.ID; }
//uint32 hashCode() const { return ID; }
// temp?
const G3D::AABox& getBounds() const { return iBound; }
static bool readFromFile(FILE* rf, ModelSpawn &spawn);
static bool writeToFile(FILE* rw, const ModelSpawn &spawn);
};
class ModelInstance: public ModelSpawn
{
public:
ModelInstance(): iInvScale(0.0f), iModel(0) { }
ModelInstance(const ModelSpawn &spawn, WorldModel* model);
void setUnloaded() { iModel = 0; }
bool intersectRay(const G3D::Ray& pRay, float& pMaxDist, bool StopAtFirstHit) const;
void intersectPoint(const G3D::Vector3& p, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3& p, LocationInfo &info) const;
bool GetLiquidLevel(const G3D::Vector3& p, LocationInfo &info, float &liqHeight) const;
protected:
G3D::Matrix3 iInvRot;
float iInvScale;
WorldModel* iModel;
public:
WorldModel* getWorldModel();
};
} // namespace VMAP
#endif // _MODELINSTANCE

View File

@@ -0,0 +1,586 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "WorldModel.h"
#include "ModelInstance.h"
#include "VMapDefinitions.h"
#include "MapTree.h"
using G3D::Vector3;
using G3D::Ray;
template<> struct BoundsTrait<VMAP::GroupModel>
{
static void getBounds(const VMAP::GroupModel& obj, G3D::AABox& out) { out = obj.GetBound(); }
};
namespace VMAP
{
bool IntersectTriangle(const MeshTriangle &tri, std::vector<Vector3>::const_iterator points, const G3D::Ray &ray, float &distance)
{
static const float EPS = 1e-5f;
// See RTR2 ch. 13.7 for the algorithm.
const Vector3 e1 = points[tri.idx1] - points[tri.idx0];
const Vector3 e2 = points[tri.idx2] - points[tri.idx0];
const Vector3 p(ray.direction().cross(e2));
const float a = e1.dot(p);
if (fabs(a) < EPS) {
// Determinant is ill-conditioned; abort early
return false;
}
const float f = 1.0f / a;
const Vector3 s(ray.origin() - points[tri.idx0]);
const float u = f * s.dot(p);
if ((u < 0.0f) || (u > 1.0f)) {
// We hit the plane of the m_geometry, but outside the m_geometry
return false;
}
const Vector3 q(s.cross(e1));
const float v = f * ray.direction().dot(q);
if ((v < 0.0f) || ((u + v) > 1.0f)) {
// We hit the plane of the triangle, but outside the triangle
return false;
}
const float t = f * e2.dot(q);
if ((t > 0.0f) && (t < distance))
{
// This is a new hit, closer than the previous one
distance = t;
/* baryCoord[0] = 1.0 - u - v;
baryCoord[1] = u;
baryCoord[2] = v; */
return true;
}
// This hit is after the previous hit, so ignore it
return false;
}
class TriBoundFunc
{
public:
TriBoundFunc(std::vector<Vector3> &vert): vertices(vert.begin()) { }
void operator()(const MeshTriangle &tri, G3D::AABox &out) const
{
G3D::Vector3 lo = vertices[tri.idx0];
G3D::Vector3 hi = lo;
lo = (lo.min(vertices[tri.idx1])).min(vertices[tri.idx2]);
hi = (hi.max(vertices[tri.idx1])).max(vertices[tri.idx2]);
out = G3D::AABox(lo, hi);
}
protected:
const std::vector<Vector3>::const_iterator vertices;
};
// ===================== WmoLiquid ==================================
WmoLiquid::WmoLiquid(uint32 width, uint32 height, const Vector3 &corner, uint32 type):
iTilesX(width), iTilesY(height), iCorner(corner), iType(type)
{
iHeight = new float[(width+1)*(height+1)];
iFlags = new uint8[width*height];
}
WmoLiquid::WmoLiquid(const WmoLiquid &other): iHeight(0), iFlags(0)
{
*this = other; // use assignment operator...
}
WmoLiquid::~WmoLiquid()
{
delete[] iHeight;
delete[] iFlags;
}
WmoLiquid& WmoLiquid::operator=(const WmoLiquid &other)
{
if (this == &other)
return *this;
iTilesX = other.iTilesX;
iTilesY = other.iTilesY;
iCorner = other.iCorner;
iType = other.iType;
delete iHeight;
delete iFlags;
if (other.iHeight)
{
iHeight = new float[(iTilesX+1)*(iTilesY+1)];
memcpy(iHeight, other.iHeight, (iTilesX+1)*(iTilesY+1)*sizeof(float));
}
else
iHeight = 0;
if (other.iFlags)
{
iFlags = new uint8[iTilesX * iTilesY];
memcpy(iFlags, other.iFlags, iTilesX * iTilesY);
}
else
iFlags = 0;
return *this;
}
bool WmoLiquid::GetLiquidHeight(const Vector3 &pos, float &liqHeight) const
{
float tx_f = (pos.x - iCorner.x)/LIQUID_TILE_SIZE;
uint32 tx = uint32(tx_f);
if (tx_f < 0.0f || tx >= iTilesX)
return false;
float ty_f = (pos.y - iCorner.y)/LIQUID_TILE_SIZE;
uint32 ty = uint32(ty_f);
if (ty_f < 0.0f || ty >= iTilesY)
return false;
// check if tile shall be used for liquid level
// checking for 0x08 *might* be enough, but disabled tiles always are 0x?F:
if ((iFlags[tx + ty*iTilesX] & 0x0F) == 0x0F)
return false;
// (dx, dy) coordinates inside tile, in [0, 1]^2
float dx = tx_f - (float)tx;
float dy = ty_f - (float)ty;
/* Tesselate tile to two triangles (not sure if client does it exactly like this)
^ dy
|
1 x---------x (1, 1)
| (b) / |
| / |
| / |
| / (a) |
x---------x---> dx
0 1
*/
const uint32 rowOffset = iTilesX + 1;
if (dx > dy) // case (a)
{
float sx = iHeight[tx+1 + ty * rowOffset] - iHeight[tx + ty * rowOffset];
float sy = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx+1 + ty * rowOffset];
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
}
else // case (b)
{
float sx = iHeight[tx+1 + (ty+1) * rowOffset] - iHeight[tx + (ty+1) * rowOffset];
float sy = iHeight[tx + (ty+1) * rowOffset] - iHeight[tx + ty * rowOffset];
liqHeight = iHeight[tx + ty * rowOffset] + dx * sx + dy * sy;
}
return true;
}
uint32 WmoLiquid::GetFileSize()
{
return 2 * sizeof(uint32) +
sizeof(Vector3) +
(iTilesX + 1)*(iTilesY + 1) * sizeof(float) +
iTilesX * iTilesY;
}
bool WmoLiquid::writeToFile(FILE* wf)
{
bool result = false;
if (fwrite(&iTilesX, sizeof(uint32), 1, wf) == 1 &&
fwrite(&iTilesY, sizeof(uint32), 1, wf) == 1 &&
fwrite(&iCorner, sizeof(Vector3), 1, wf) == 1 &&
fwrite(&iType, sizeof(uint32), 1, wf) == 1)
{
uint32 size = (iTilesX + 1) * (iTilesY + 1);
if (fwrite(iHeight, sizeof(float), size, wf) == size)
{
size = iTilesX*iTilesY;
result = fwrite(iFlags, sizeof(uint8), size, wf) == size;
}
}
return result;
}
bool WmoLiquid::readFromFile(FILE* rf, WmoLiquid* &out)
{
bool result = false;
WmoLiquid* liquid = new WmoLiquid();
if (fread(&liquid->iTilesX, sizeof(uint32), 1, rf) == 1 &&
fread(&liquid->iTilesY, sizeof(uint32), 1, rf) == 1 &&
fread(&liquid->iCorner, sizeof(Vector3), 1, rf) == 1 &&
fread(&liquid->iType, sizeof(uint32), 1, rf) == 1)
{
uint32 size = (liquid->iTilesX + 1) * (liquid->iTilesY + 1);
liquid->iHeight = new float[size];
if (fread(liquid->iHeight, sizeof(float), size, rf) == size)
{
size = liquid->iTilesX * liquid->iTilesY;
liquid->iFlags = new uint8[size];
result = fread(liquid->iFlags, sizeof(uint8), size, rf) == size;
}
}
if (!result)
delete liquid;
else
out = liquid;
return result;
}
// ===================== GroupModel ==================================
GroupModel::GroupModel(const GroupModel &other):
iBound(other.iBound), iMogpFlags(other.iMogpFlags), iGroupWMOID(other.iGroupWMOID),
vertices(other.vertices), triangles(other.triangles), meshTree(other.meshTree), iLiquid(0)
{
if (other.iLiquid)
iLiquid = new WmoLiquid(*other.iLiquid);
}
void GroupModel::setMeshData(std::vector<Vector3> &vert, std::vector<MeshTriangle> &tri)
{
vertices.swap(vert);
triangles.swap(tri);
TriBoundFunc bFunc(vertices);
meshTree.build(triangles, bFunc);
}
bool GroupModel::writeToFile(FILE* wf)
{
bool result = true;
uint32 chunkSize, count;
if (result && fwrite(&iBound, sizeof(G3D::AABox), 1, wf) != 1) result = false;
if (result && fwrite(&iMogpFlags, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&iGroupWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write vertices
if (result && fwrite("VERT", 1, 4, wf) != 4) result = false;
count = vertices.size();
chunkSize = sizeof(uint32)+ sizeof(Vector3)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result && fwrite(&vertices[0], sizeof(Vector3), count, wf) != count) result = false;
// write triangle mesh
if (result && fwrite("TRIM", 1, 4, wf) != 4) result = false;
count = triangles.size();
chunkSize = sizeof(uint32)+ sizeof(MeshTriangle)*count;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&triangles[0], sizeof(MeshTriangle), count, wf) != count) result = false;
// write mesh BIH
if (result && fwrite("MBIH", 1, 4, wf) != 4) result = false;
if (result) result = meshTree.writeToFile(wf);
// write liquid data
if (result && fwrite("LIQU", 1, 4, wf) != 4) result = false;
if (!iLiquid)
{
chunkSize = 0;
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
return result;
}
chunkSize = iLiquid->GetFileSize();
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result) result = iLiquid->writeToFile(wf);
return result;
}
bool GroupModel::readFromFile(FILE* rf)
{
char chunk[8];
bool result = true;
uint32 chunkSize = 0;
uint32 count = 0;
triangles.clear();
vertices.clear();
delete iLiquid;
iLiquid = NULL;
if (result && fread(&iBound, sizeof(G3D::AABox), 1, rf) != 1) result = false;
if (result && fread(&iMogpFlags, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&iGroupWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read vertices
if (result && !readChunk(rf, chunk, "VERT", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (!count) // models without (collision) geometry end here, unsure if they are useful
return result;
if (result) vertices.resize(count);
if (result && fread(&vertices[0], sizeof(Vector3), count, rf) != count) result = false;
// read triangle mesh
if (result && !readChunk(rf, chunk, "TRIM", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) triangles.resize(count);
if (result && fread(&triangles[0], sizeof(MeshTriangle), count, rf) != count) result = false;
// read mesh BIH
if (result && !readChunk(rf, chunk, "MBIH", 4)) result = false;
if (result) result = meshTree.readFromFile(rf);
// write liquid data
if (result && !readChunk(rf, chunk, "LIQU", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && chunkSize > 0)
result = WmoLiquid::readFromFile(rf, iLiquid);
return result;
}
struct GModelRayCallback
{
GModelRayCallback(const std::vector<MeshTriangle> &tris, const std::vector<Vector3> &vert):
vertices(vert.begin()), triangles(tris.begin()), hit(false) { }
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool /*StopAtFirstHit*/)
{
bool result = IntersectTriangle(triangles[entry], vertices, ray, distance);
if (result) hit=true;
return hit;
}
std::vector<Vector3>::const_iterator vertices;
std::vector<MeshTriangle>::const_iterator triangles;
bool hit;
};
bool GroupModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
if (triangles.empty())
return false;
GModelRayCallback callback(triangles, vertices);
meshTree.intersectRay(ray, callback, distance, stopAtFirstHit);
return callback.hit;
}
bool GroupModel::IsInsideObject(const Vector3 &pos, const Vector3 &down, float &z_dist) const
{
if (triangles.empty() || !iBound.contains(pos))
return false;
GModelRayCallback callback(triangles, vertices);
Vector3 rPos = pos - 0.1f * down;
float dist = G3D::inf();
G3D::Ray ray(rPos, down);
bool hit = IntersectRay(ray, dist, false);
if (hit)
z_dist = dist - 0.1f;
return hit;
}
bool GroupModel::GetLiquidLevel(const Vector3 &pos, float &liqHeight) const
{
if (iLiquid)
return iLiquid->GetLiquidHeight(pos, liqHeight);
return false;
}
uint32 GroupModel::GetLiquidType() const
{
if (iLiquid)
return iLiquid->GetType();
return 0;
}
// ===================== WorldModel ==================================
void WorldModel::setGroupModels(std::vector<GroupModel> &models)
{
groupModels.swap(models);
groupTree.build(groupModels, BoundsTrait<GroupModel>::getBounds, 1);
}
struct WModelRayCallBack
{
WModelRayCallBack(const std::vector<GroupModel> &mod): models(mod.begin()), hit(false) { }
bool operator()(const G3D::Ray& ray, uint32 entry, float& distance, bool StopAtFirstHit)
{
bool result = models[entry].IntersectRay(ray, distance, StopAtFirstHit);
if (result) hit=true;
return hit;
}
std::vector<GroupModel>::const_iterator models;
bool hit;
};
bool WorldModel::IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const
{
// small M2 workaround, maybe better make separate class with virtual intersection funcs
// in any case, there's no need to use a bound tree if we only have one submodel
if (groupModels.size() == 1)
return groupModels[0].IntersectRay(ray, distance, stopAtFirstHit);
WModelRayCallBack isc(groupModels);
groupTree.intersectRay(ray, isc, distance, stopAtFirstHit);
return isc.hit;
}
class WModelAreaCallback {
public:
WModelAreaCallback(const std::vector<GroupModel> &vals, const Vector3 &down):
prims(vals.begin()), hit(vals.end()), minVol(G3D::inf()), zDist(G3D::inf()), zVec(down) { }
std::vector<GroupModel>::const_iterator prims;
std::vector<GroupModel>::const_iterator hit;
float minVol;
float zDist;
Vector3 zVec;
void operator()(const Vector3& point, uint32 entry)
{
float group_Z;
//float pVol = prims[entry].GetBound().volume();
//if (pVol < minVol)
//{
/* if (prims[entry].iBound.contains(point)) */
if (prims[entry].IsInsideObject(point, zVec, group_Z))
{
//minVol = pVol;
//hit = prims + entry;
if (group_Z < zDist)
{
zDist = group_Z;
hit = prims + entry;
}
#ifdef VMAP_DEBUG
const GroupModel &gm = prims[entry];
printf("%10u %8X %7.3f, %7.3f, %7.3f | %7.3f, %7.3f, %7.3f | z=%f, p_z=%f\n", gm.GetWmoID(), gm.GetMogpFlags(),
gm.GetBound().low().x, gm.GetBound().low().y, gm.GetBound().low().z,
gm.GetBound().high().x, gm.GetBound().high().y, gm.GetBound().high().z, group_Z, point.z);
#endif
}
//}
//std::cout << "trying to intersect '" << prims[entry].name << "'\n";
}
};
bool WorldModel::IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const
{
if (groupModels.empty())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.rootId = RootWMOID;
info.groupId = callback.hit->GetWmoID();
info.flags = callback.hit->GetMogpFlags();
info.result = true;
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const
{
if (groupModels.empty())
return false;
WModelAreaCallback callback(groupModels, down);
groupTree.intersectPoint(p, callback);
if (callback.hit != groupModels.end())
{
info.hitModel = &(*callback.hit);
dist = callback.zDist;
return true;
}
return false;
}
bool WorldModel::writeFile(const std::string &filename)
{
FILE* wf = fopen(filename.c_str(), "wb");
if (!wf)
return false;
uint32 chunkSize, count;
bool result = fwrite(VMAP_MAGIC, 1, 8, wf) == 8;
if (result && fwrite("WMOD", 1, 4, wf) != 4) result = false;
chunkSize = sizeof(uint32) + sizeof(uint32);
if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&RootWMOID, sizeof(uint32), 1, wf) != 1) result = false;
// write group models
count=groupModels.size();
if (count)
{
if (result && fwrite("GMOD", 1, 4, wf) != 4) result = false;
//chunkSize = sizeof(uint32)+ sizeof(GroupModel)*count;
//if (result && fwrite(&chunkSize, sizeof(uint32), 1, wf) != 1) result = false;
if (result && fwrite(&count, sizeof(uint32), 1, wf) != 1) result = false;
for (uint32 i=0; i<groupModels.size() && result; ++i)
result = groupModels[i].writeToFile(wf);
// write group BIH
if (result && fwrite("GBIH", 1, 4, wf) != 4) result = false;
if (result) result = groupTree.writeToFile(wf);
}
fclose(wf);
return result;
}
bool WorldModel::readFile(const std::string &filename)
{
FILE* rf = fopen(filename.c_str(), "rb");
if (!rf)
return false;
bool result = true;
uint32 chunkSize = 0;
uint32 count = 0;
char chunk[8]; // Ignore the added magic header
if (!readChunk(rf, chunk, VMAP_MAGIC, 8)) result = false;
if (result && !readChunk(rf, chunk, "WMOD", 4)) result = false;
if (result && fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&RootWMOID, sizeof(uint32), 1, rf) != 1) result = false;
// read group models
if (result && readChunk(rf, chunk, "GMOD", 4))
{
//if (fread(&chunkSize, sizeof(uint32), 1, rf) != 1) result = false;
if (result && fread(&count, sizeof(uint32), 1, rf) != 1) result = false;
if (result) groupModels.resize(count);
//if (result && fread(&groupModels[0], sizeof(GroupModel), count, rf) != count) result = false;
for (uint32 i=0; i<count && result; ++i)
result = groupModels[i].readFromFile(rf);
// read group BIH
if (result && !readChunk(rf, chunk, "GBIH", 4)) result = false;
if (result) result = groupTree.readFromFile(rf);
}
fclose(rf);
return result;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _WORLDMODEL_H
#define _WORLDMODEL_H
#include <G3D/HashTrait.h>
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Ray.h>
#include "BoundingIntervalHierarchy.h"
#include "Define.h"
namespace VMAP
{
class TreeNode;
struct AreaInfo;
struct LocationInfo;
class MeshTriangle
{
public:
MeshTriangle() : idx0(0), idx1(0), idx2(0) { }
MeshTriangle(uint32 na, uint32 nb, uint32 nc): idx0(na), idx1(nb), idx2(nc) { }
uint32 idx0;
uint32 idx1;
uint32 idx2;
};
class WmoLiquid
{
public:
WmoLiquid(uint32 width, uint32 height, const G3D::Vector3 &corner, uint32 type);
WmoLiquid(const WmoLiquid &other);
~WmoLiquid();
WmoLiquid& operator=(const WmoLiquid &other);
bool GetLiquidHeight(const G3D::Vector3 &pos, float &liqHeight) const;
uint32 GetType() const { return iType; }
float *GetHeightStorage() { return iHeight; }
uint8 *GetFlagsStorage() { return iFlags; }
uint32 GetFileSize();
bool writeToFile(FILE* wf);
static bool readFromFile(FILE* rf, WmoLiquid* &liquid);
private:
WmoLiquid(): iTilesX(0), iTilesY(0), iType(0), iHeight(0), iFlags(0) { }
uint32 iTilesX; //!< number of tiles in x direction, each
uint32 iTilesY;
G3D::Vector3 iCorner; //!< the lower corner
uint32 iType; //!< liquid type
float *iHeight; //!< (tilesX + 1)*(tilesY + 1) height values
uint8 *iFlags; //!< info if liquid tile is used
public:
void getPosInfo(uint32 &tilesX, uint32 &tilesY, G3D::Vector3 &corner) const;
};
/*! holding additional info for WMO group files */
class GroupModel
{
public:
GroupModel(): iMogpFlags(0), iGroupWMOID(0), iLiquid(0) { }
GroupModel(const GroupModel &other);
GroupModel(uint32 mogpFlags, uint32 groupWMOID, const G3D::AABox &bound):
iBound(bound), iMogpFlags(mogpFlags), iGroupWMOID(groupWMOID), iLiquid(0) { }
~GroupModel() { delete iLiquid; }
//! pass mesh data to object and create BIH. Passed vectors get get swapped with old geometry!
void setMeshData(std::vector<G3D::Vector3> &vert, std::vector<MeshTriangle> &tri);
void setLiquidData(WmoLiquid*& liquid) { iLiquid = liquid; liquid = NULL; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IsInsideObject(const G3D::Vector3 &pos, const G3D::Vector3 &down, float &z_dist) const;
bool GetLiquidLevel(const G3D::Vector3 &pos, float &liqHeight) const;
uint32 GetLiquidType() const;
bool writeToFile(FILE* wf);
bool readFromFile(FILE* rf);
const G3D::AABox& GetBound() const { return iBound; }
uint32 GetMogpFlags() const { return iMogpFlags; }
uint32 GetWmoID() const { return iGroupWMOID; }
protected:
G3D::AABox iBound;
uint32 iMogpFlags;// 0x8 outdor; 0x2000 indoor
uint32 iGroupWMOID;
std::vector<G3D::Vector3> vertices;
std::vector<MeshTriangle> triangles;
BIH meshTree;
WmoLiquid* iLiquid;
public:
void getMeshData(std::vector<G3D::Vector3> &vertices, std::vector<MeshTriangle> &triangles, WmoLiquid* &liquid);
};
/*! Holds a model (converted M2 or WMO) in its original coordinate space */
class WorldModel
{
public:
WorldModel(): RootWMOID(0) { }
//! pass group models to WorldModel and create BIH. Passed vector is swapped with old geometry!
void setGroupModels(std::vector<GroupModel> &models);
void setRootWmoID(uint32 id) { RootWMOID = id; }
bool IntersectRay(const G3D::Ray &ray, float &distance, bool stopAtFirstHit) const;
bool IntersectPoint(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, AreaInfo &info) const;
bool GetLocationInfo(const G3D::Vector3 &p, const G3D::Vector3 &down, float &dist, LocationInfo &info) const;
bool writeFile(const std::string &filename);
bool readFile(const std::string &filename);
protected:
uint32 RootWMOID;
std::vector<GroupModel> groupModels;
BIH groupTree;
public:
void getGroupModels(std::vector<GroupModel> &groupModels);
};
} // namespace VMAP
#endif // _WORLDMODEL_H

View File

@@ -0,0 +1 @@
#include "collisionPCH.h"

View File

@@ -0,0 +1,9 @@
#include "Define.h"
#include "VMapDefinitions.h"
#include "MapTree.h"
#include "WorldModel.h"
#include "ModelInstance.h"
#include "BoundingIntervalHierarchy.h"
#include "RegularGrid.h"
#include "BoundingIntervalHierarchyWrapper.h"
#include "GameObjectModel.h"

View File

@@ -0,0 +1,264 @@
#ifndef _REGULAR_GRID_H
#define _REGULAR_GRID_H
#include <G3D/Ray.h>
#include <G3D/Table.h>
#include <G3D/BoundsTrait.h>
#include <G3D/PositionTrait.h>
#include "Errors.h"
template <class Node>
class NodeArray
{
public:
explicit NodeArray() { memset(&_nodes, 0, sizeof(_nodes)); }
void AddNode(Node* n)
{
for (uint8 i=0; i<9; ++i)
if (_nodes[i] == 0)
{
_nodes[i] = n;
return;
}
else if (_nodes[i] == n)
return;
}
Node* _nodes[9];
};
template<class Node>
struct NodeCreator{
static Node * makeNode(int /*x*/, int /*y*/) { return new Node();}
};
template<class T,
class Node,
class NodeCreatorFunc = NodeCreator<Node>,
/*class BoundsFunc = BoundsTrait<T>,*/
class PositionFunc = PositionTrait<T>
>
class RegularGrid2D
{
public:
enum{
CELL_NUMBER = 64,
};
#define HGRID_MAP_SIZE (533.33333f * 64.f) // shouldn't be changed
#define CELL_SIZE float(HGRID_MAP_SIZE/(float)CELL_NUMBER)
typedef G3D::Table<const T*, NodeArray<Node> > MemberTable;
MemberTable memberTable;
Node* nodes[CELL_NUMBER][CELL_NUMBER];
RegularGrid2D(){
memset(nodes, 0, sizeof(nodes));
}
~RegularGrid2D(){
for (int x = 0; x < CELL_NUMBER; ++x)
for (int y = 0; y < CELL_NUMBER; ++y)
delete nodes[x][y];
}
void insert(const T& value)
{
G3D::Vector3 pos[9];
pos[0] = value.getBounds().corner(0);
pos[1] = value.getBounds().corner(1);
pos[2] = value.getBounds().corner(2);
pos[3] = value.getBounds().corner(3);
pos[4] = (pos[0] + pos[1])/2.0f;
pos[5] = (pos[1] + pos[2])/2.0f;
pos[6] = (pos[2] + pos[3])/2.0f;
pos[7] = (pos[3] + pos[0])/2.0f;
pos[8] = (pos[0] + pos[2])/2.0f;
NodeArray<Node> na;
for (uint8 i=0; i<9; ++i)
{
Cell c = Cell::ComputeCell(pos[i].x, pos[i].y);
if (!c.isValid())
continue;
Node& node = getGridFor(pos[i].x, pos[i].y);
na.AddNode(&node);
}
for (uint8 i=0; i<9; ++i)
{
if (na._nodes[i])
na._nodes[i]->insert(value);
else
break;
}
memberTable.set(&value, na);
}
void remove(const T& value)
{
NodeArray<Node>& na = memberTable[&value];
for (uint8 i=0; i<9; ++i)
{
if (na._nodes[i])
na._nodes[i]->remove(value);
else
break;
}
// Remove the member
memberTable.remove(&value);
}
void balance()
{
for (int x = 0; x < CELL_NUMBER; ++x)
for (int y = 0; y < CELL_NUMBER; ++y)
if (Node* n = nodes[x][y])
n->balance();
}
bool contains(const T& value) const { return memberTable.containsKey(&value); }
int size() const { return memberTable.size(); }
struct Cell
{
int x, y;
bool operator == (const Cell& c2) const { return x == c2.x && y == c2.y;}
static Cell ComputeCell(float fx, float fy)
{
Cell c = { int(fx * (1.f/CELL_SIZE) + (CELL_NUMBER/2)), int(fy * (1.f/CELL_SIZE) + (CELL_NUMBER/2)) };
return c;
}
bool isValid() const { return x >= 0 && x < CELL_NUMBER && y >= 0 && y < CELL_NUMBER;}
};
Node& getGridFor(float fx, float fy)
{
Cell c = Cell::ComputeCell(fx, fy);
return getGrid(c.x, c.y);
}
Node& getGrid(int x, int y)
{
ASSERT(x < CELL_NUMBER && y < CELL_NUMBER);
if (!nodes[x][y])
nodes[x][y] = NodeCreatorFunc::makeNode(x, y);
return *nodes[x][y];
}
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float max_dist, bool stopAtFirstHit)
{
intersectRay(ray, intersectCallback, max_dist, ray.origin() + ray.direction() * max_dist, stopAtFirstHit);
}
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist, const G3D::Vector3& end, bool stopAtFirstHit)
{
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
if (!cell.isValid())
return;
Cell last_cell = Cell::ComputeCell(end.x, end.y);
if (cell == last_cell)
{
if (Node* node = nodes[cell.x][cell.y])
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
return;
}
float voxel = (float)CELL_SIZE;
float kx_inv = ray.invDirection().x, bx = ray.origin().x;
float ky_inv = ray.invDirection().y, by = ray.origin().y;
int stepX, stepY;
float tMaxX, tMaxY;
if (kx_inv >= 0)
{
stepX = 1;
float x_border = (cell.x+1) * voxel;
tMaxX = (x_border - bx) * kx_inv;
}
else
{
stepX = -1;
float x_border = (cell.x-1) * voxel;
tMaxX = (x_border - bx) * kx_inv;
}
if (ky_inv >= 0)
{
stepY = 1;
float y_border = (cell.y+1) * voxel;
tMaxY = (y_border - by) * ky_inv;
}
else
{
stepY = -1;
float y_border = (cell.y-1) * voxel;
tMaxY = (y_border - by) * ky_inv;
}
//int Cycles = std::max((int)ceilf(max_dist/tMaxX),(int)ceilf(max_dist/tMaxY));
//int i = 0;
float tDeltaX = voxel * fabs(kx_inv);
float tDeltaY = voxel * fabs(ky_inv);
do
{
if (Node* node = nodes[cell.x][cell.y])
{
//float enterdist = max_dist;
node->intersectRay(ray, intersectCallback, max_dist, stopAtFirstHit);
}
if (cell == last_cell)
break;
if (tMaxX < tMaxY)
{
tMaxX += tDeltaX;
cell.x += stepX;
}
else
{
tMaxY += tDeltaY;
cell.y += stepY;
}
//++i;
} while (cell.isValid());
}
template<typename IsectCallback>
void intersectPoint(const G3D::Vector3& point, IsectCallback& intersectCallback)
{
Cell cell = Cell::ComputeCell(point.x, point.y);
if (!cell.isValid())
return;
if (Node* node = nodes[cell.x][cell.y])
node->intersectPoint(point, intersectCallback);
}
// Optimized verson of intersectRay function for rays with vertical directions
template<typename RayCallback>
void intersectZAllignedRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& max_dist)
{
Cell cell = Cell::ComputeCell(ray.origin().x, ray.origin().y);
if (!cell.isValid())
return;
if (Node* node = nodes[cell.x][cell.y])
node->intersectRay(ray, intersectCallback, max_dist, false);
}
};
#undef CELL_SIZE
#undef HGRID_MAP_SIZE
#endif

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _VMAPDEFINITIONS_H
#define _VMAPDEFINITIONS_H
#include <cstring>
#define LIQUID_TILE_SIZE (533.333f / 128.f)
namespace VMAP
{
const char VMAP_MAGIC[] = "VMAP_4.1";
const char RAW_VMAP_MAGIC[] = "VMAP041"; // used in extracted vmap files with raw data
const char GAMEOBJECT_MODELS[] = "GameObjectModels.dtree";
// defined in TileAssembler.cpp currently...
bool readChunk(FILE* rf, char *dest, const char *compare, uint32 len);
}
#endif

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _VMAPTOOLS_H
#define _VMAPTOOLS_H
#include <G3D/CollisionDetection.h>
#include <G3D/AABox.h>
#include "NodeValueAccess.h"
/**
The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure.
This is an iterator that helps us analysing the BSP-Trees.
The collision detection is modified to return true, if we are inside an object.
*/
namespace VMAP
{
template<class TValue>
class IntersectionCallBack {
public:
TValue* closestEntity;
G3D::Vector3 hitLocation;
G3D::Vector3 hitNormal;
void operator()(const G3D::Ray& ray, const TValue* entity, bool StopAtFirstHit, float& distance) {
entity->intersect(ray, distance, StopAtFirstHit, hitLocation, hitNormal);
}
};
//==============================================================
//==============================================================
//==============================================================
class MyCollisionDetection
{
private:
public:
static bool collisionLocationForMovingPointFixedAABox(
const G3D::Vector3& origin,
const G3D::Vector3& dir,
const G3D::AABox& box,
G3D::Vector3& location,
bool& Inside)
{
// Integer representation of a floating-point value.
#define IR(x) (reinterpret_cast<G3D::uint32 const&>(x))
Inside = true;
const G3D::Vector3& MinB = box.low();
const G3D::Vector3& MaxB = box.high();
G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f);
// Find candidate planes.
for (int i = 0; i < 3; ++i)
{
if (origin[i] < MinB[i])
{
location[i] = MinB[i];
Inside = false;
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
MaxT[i] = (MinB[i] - origin[i]) / dir[i];
}
}
else if (origin[i] > MaxB[i])
{
location[i] = MaxB[i];
Inside = false;
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
}
}
}
if (Inside)
{
// definite hit
location = origin;
return true;
}
// Get largest of the maxT's for final choice of intersection
int WhichPlane = 0;
if (MaxT[1] > MaxT[WhichPlane])
{
WhichPlane = 1;
}
if (MaxT[2] > MaxT[WhichPlane])
{
WhichPlane = 2;
}
// Check final candidate actually inside box
if (IR(MaxT[WhichPlane]) & 0x80000000)
{
// Miss the box
return false;
}
for (int i = 0; i < 3; ++i)
{
if (i != WhichPlane)
{
location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
if ((location[i] < MinB[i]) ||
(location[i] > MaxB[i]))
{
// On this plane we're outside the box extents, so
// we miss the box
return false;
}
}
}
/*
// Choose the normal to be the plane normal facing into the ray
normal = G3D::Vector3::zero();
normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
*/
return true;
#undef IR
}
};
}
#endif

View File

@@ -0,0 +1,323 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "CombatAI.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "Vehicle.h"
#include "ObjectAccessor.h"
#include "Player.h"
/////////////////
// AggressorAI
/////////////////
int AggressorAI::Permissible(const Creature* creature)
{
// have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
if (!creature->IsCivilian() && !creature->IsNeutralToAll())
return PERMIT_BASE_PROACTIVE;
return PERMIT_BASE_NO;
}
void AggressorAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
/////////////////
// CombatAI
/////////////////
void CombatAI::InitializeAI()
{
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
if (me->m_spells[i] && sSpellMgr->GetSpellInfo(me->m_spells[i]))
spells.push_back(me->m_spells[i]);
CreatureAI::InitializeAI();
}
void CombatAI::Reset()
{
events.Reset();
}
void CombatAI::JustDied(Unit* killer)
{
for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
if (AISpellInfo[*i].condition == AICOND_DIE)
me->CastSpell(killer, *i, true);
}
void CombatAI::EnterCombat(Unit* who)
{
for (SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
{
if (AISpellInfo[*i].condition == AICOND_AGGRO)
me->CastSpell(who, *i, false);
else if (AISpellInfo[*i].condition == AICOND_COMBAT)
events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown);
}
}
void CombatAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + rand()%AISpellInfo[spellId].cooldown);
}
else
DoMeleeAttackIfReady();
}
/////////////////
// CasterAI
/////////////////
void CasterAI::InitializeAI()
{
CombatAI::InitializeAI();
m_attackDist = 30.0f;
for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr)
if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange)
m_attackDist = GetAISpellInfo(*itr)->maxRange;
if (m_attackDist == 30.0f)
m_attackDist = MELEE_RANGE;
}
void CasterAI::EnterCombat(Unit* who)
{
if (spells.empty())
return;
uint32 spell = rand()%spells.size();
uint32 count = 0;
for (SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count)
{
if (AISpellInfo[*itr].condition == AICOND_AGGRO)
me->CastSpell(who, *itr, false);
else if (AISpellInfo[*itr].condition == AICOND_COMBAT)
{
uint32 cooldown = GetAISpellInfo(*itr)->realCooldown;
if (count == spell)
{
DoCast(spells[spell]);
cooldown += me->GetCurrentSpellCastTime(*itr);
}
events.ScheduleEvent(*itr, cooldown);
}
}
}
void CasterAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me))
{
me->InterruptNonMeleeSpells(false);
return;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
uint32 casttime = me->GetCurrentSpellCastTime(spellId);
events.ScheduleEvent(spellId, (casttime ? casttime : 500) + GetAISpellInfo(spellId)->realCooldown);
}
}
//////////////
// ArcherAI
//////////////
ArcherAI::ArcherAI(Creature* c) : CreatureAI(c)
{
if (!me->m_spells[0])
sLog->outError("ArcherAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->m_spells[0]);
m_minRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
if (!m_minRange)
m_minRange = MELEE_RANGE;
me->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
me->m_SightDistance = me->m_CombatDistance;
}
void ArcherAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->IsWithinCombatRange(who, m_minRange))
{
if (me->Attack(who, true) && !who->IsFlying())
me->GetMotionMaster()->MoveChase(who);
}
else
{
if (me->Attack(who, false) && !who->IsFlying())
me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance);
}
if (who->IsFlying())
me->GetMotionMaster()->MoveIdle();
}
void ArcherAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
if (!me->IsWithinCombatRange(me->GetVictim(), m_minRange))
DoSpellAttackIfReady(me->m_spells[0]);
else
DoMeleeAttackIfReady();
}
//////////////
// TurretAI
//////////////
TurretAI::TurretAI(Creature* c) : CreatureAI(c)
{
if (!me->m_spells[0])
sLog->outError("TurretAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry());
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->m_spells[0]);
m_minRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
me->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
me->m_SightDistance = me->m_CombatDistance;
}
bool TurretAI::CanAIAttack(const Unit* /*who*/) const
{
// TODO: use one function to replace it
if (!me->IsWithinCombatRange(me->GetVictim(), me->m_CombatDistance)
|| (m_minRange && me->IsWithinCombatRange(me->GetVictim(), m_minRange)))
return false;
return true;
}
void TurretAI::AttackStart(Unit* who)
{
if (who)
me->Attack(who, false);
}
void TurretAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
if( me->m_spells[0] )
DoSpellAttackIfReady(me->m_spells[0]);
}
//////////////
// VehicleAI
//////////////
VehicleAI::VehicleAI(Creature* c) : CreatureAI(c), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME)
{
LoadConditions();
m_DoDismiss = false;
m_DismissTimer = VEHICLE_DISMISS_TIME;
}
//NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted
void VehicleAI::UpdateAI(uint32 diff)
{
CheckConditions(diff);
if (m_DoDismiss)
{
if (m_DismissTimer < diff)
{
m_DoDismiss = false;
me->DespawnOrUnsummon();
}
else
m_DismissTimer -= diff;
}
}
void VehicleAI::OnCharmed(bool apply)
{
if (!me->GetVehicleKit()->IsVehicleInUse() && !apply && !conditions.empty()) // was used and has conditions
m_DoDismiss = true; // needs reset
else if (apply)
m_DoDismiss = false; // in use again
m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer
}
void VehicleAI::LoadConditions()
{
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
//if (!conditions.empty())
;//sLog->outDebug(LOG_FILTER_CONDITIONSYS, "VehicleAI::LoadConditions: loaded %u conditions", uint32(conditions.size()));
}
void VehicleAI::CheckConditions(uint32 diff)
{
if (m_ConditionsTimer < diff)
{
if (!conditions.empty())
{
if (Vehicle* vehicleKit = me->GetVehicleKit())
for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
{
if (Player* player = passenger->ToPlayer())
{
if (!sConditionMgr->IsObjectMeetToConditions(player, me, conditions))
{
player->ExitVehicle();
return; // check other pessanger in next tick
}
}
}
}
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
}
else
m_ConditionsTimer -= diff;
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_COMBATAI_H
#define TRINITY_COMBATAI_H
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "ConditionMgr.h"
class Creature;
class AggressorAI : public CreatureAI
{
public:
explicit AggressorAI(Creature* c) : CreatureAI(c) {}
void UpdateAI(uint32);
static int Permissible(const Creature*);
};
typedef std::vector<uint32> SpellVct;
class CombatAI : public CreatureAI
{
public:
explicit CombatAI(Creature* c) : CreatureAI(c) {}
void InitializeAI();
void Reset();
void EnterCombat(Unit* who);
void JustDied(Unit* killer);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
EventMap events;
SpellVct spells;
};
class CasterAI : public CombatAI
{
public:
explicit CasterAI(Creature* c) : CombatAI(c) { m_attackDist = MELEE_RANGE; }
void InitializeAI();
void AttackStart(Unit* victim) { AttackStartCaster(victim, m_attackDist); }
void UpdateAI(uint32 diff);
void EnterCombat(Unit* /*who*/);
private:
float m_attackDist;
};
struct ArcherAI : public CreatureAI
{
public:
explicit ArcherAI(Creature* c);
void AttackStart(Unit* who);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
float m_minRange;
};
struct TurretAI : public CreatureAI
{
public:
explicit TurretAI(Creature* c);
bool CanAIAttack(const Unit* who) const;
void AttackStart(Unit* who);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
protected:
float m_minRange;
};
#define VEHICLE_CONDITION_CHECK_TIME 1000
#define VEHICLE_DISMISS_TIME 5000
struct VehicleAI : public CreatureAI
{
public:
explicit VehicleAI(Creature* creature);
void UpdateAI(uint32 diff);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void OnCharmed(bool apply);
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
private:
void LoadConditions();
void CheckConditions(uint32 diff);
ConditionList conditions;
uint32 m_ConditionsTimer;
bool m_DoDismiss;
uint32 m_DismissTimer;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "GameObjectAI.h"
//GameObjectAI::GameObjectAI(GameObject* g) : go(g) {}
int GameObjectAI::Permissible(const GameObject* go)
{
if (go->GetAIName() == "GameObjectAI")
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
NullGameObjectAI::NullGameObjectAI(GameObject* g) : GameObjectAI(g) {}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_GAMEOBJECTAI_H
#define TRINITY_GAMEOBJECTAI_H
#include "Define.h"
#include <list>
#include "Object.h"
#include "QuestDef.h"
#include "GameObject.h"
#include "CreatureAI.h"
class GameObjectAI
{
protected:
GameObject* const go;
public:
explicit GameObjectAI(GameObject* g) : go(g) {}
virtual ~GameObjectAI() {}
virtual void UpdateAI(uint32 /*diff*/) {}
virtual void InitializeAI() { Reset(); }
virtual void Reset() { }
// Pass parameters between AI
virtual void DoAction(int32 /*param = 0 */) {}
virtual void SetGUID(uint64 /*guid*/, int32 /*id = 0 */) {}
virtual uint64 GetGUID(int32 /*id = 0 */) const { return 0; }
static int Permissible(GameObject const* go);
virtual bool GossipHello(Player* /*player*/, bool reportUse) { return false; }
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { return false; }
virtual bool QuestAccept(Player* /*player*/, Quest const* /*quest*/) { return false; }
virtual bool QuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) { return false; }
virtual uint32 GetDialogStatus(Player* /*player*/) { return DIALOG_STATUS_SCRIPTED_NO_STATUS; }
virtual void Destroyed(Player* /*player*/, uint32 /*eventId*/) {}
virtual uint32 GetData(uint32 /*id*/) const { return 0; }
virtual void SetData64(uint32 /*id*/, uint64 /*value*/) {}
virtual uint64 GetData64(uint32 /*id*/) const { return 0; }
virtual void SetData(uint32 /*id*/, uint32 /*value*/) {}
virtual void OnGameEvent(bool /*start*/, uint16 /*eventId*/) {}
virtual void OnStateChanged(uint32 /*state*/, Unit* /*unit*/) {}
virtual void EventInform(uint32 /*eventId*/) {}
virtual void SpellHit(Unit* unit, const SpellInfo* spellInfo) {}
};
class NullGameObjectAI : public GameObjectAI
{
public:
explicit NullGameObjectAI(GameObject* g);
void UpdateAI(uint32 /*diff*/) {}
static int Permissible(GameObject const* /*go*/) { return PERMIT_BASE_IDLE; }
};
#endif

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "GuardAI.h"
#include "Errors.h"
#include "Player.h"
#include "ObjectAccessor.h"
#include "World.h"
#include "CreatureAIImpl.h"
int GuardAI::Permissible(Creature const* creature)
{
if (creature->IsGuard())
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
GuardAI::GuardAI(Creature* creature) : ScriptedAI(creature)
{
}
void GuardAI::Reset()
{
ScriptedAI::Reset();
me->CastSpell(me, 18950 /*SPELL_INVISIBILITY_AND_STEALTH_DETECTION*/, true);
}
void GuardAI::EnterEvadeMode()
{
if (!me->IsAlive())
{
me->GetMotionMaster()->MoveIdle();
me->CombatStop(true);
me->DeleteThreatList();
return;
}
;//sLog->outDebug(LOG_FILTER_UNITS, "Guard entry: %u enters evade mode.", me->GetEntry());
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveTargetedHome();
}
void GuardAI::JustDied(Unit* killer)
{
if (Player* player = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
me->SendZoneUnderAttackMessage(player);
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_GUARDAI_H
#define TRINITY_GUARDAI_H
#include "ScriptedCreature.h"
class Creature;
class GuardAI : public ScriptedAI
{
public:
explicit GuardAI(Creature* creature);
static int Permissible(Creature const* creature);
void Reset();
void EnterEvadeMode();
void JustDied(Unit* killer);
};
#endif

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "PassiveAI.h"
#include "Creature.h"
#include "TemporarySummon.h"
PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
void PassiveAI::UpdateAI(uint32)
{
if (me->IsInCombat() && me->getAttackers().empty())
EnterEvadeMode();
}
void PossessedAI::AttackStart(Unit* target)
{
me->Attack(target, true);
}
void PossessedAI::UpdateAI(uint32 /*diff*/)
{
if (me->GetVictim())
{
if (!me->IsValidAttackTarget(me->GetVictim()))
me->AttackStop();
else
DoMeleeAttackIfReady();
}
}
void PossessedAI::JustDied(Unit* /*u*/)
{
// We died while possessed, disable our loot
me->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
void PossessedAI::KilledUnit(Unit* victim)
{
// We killed a creature, disable victim's loot
//if (victim->GetTypeId() == TYPEID_UNIT)
// victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
void CritterAI::DamageTaken(Unit*, uint32&, DamageEffectType, SpellSchoolMask)
{
if (!me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(true, UNIT_STATE_FLEEING);
_combatTimer = 1;
}
void CritterAI::EnterEvadeMode()
{
if (me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(false, UNIT_STATE_FLEEING);
CreatureAI::EnterEvadeMode();
_combatTimer = 0;
}
void CritterAI::UpdateAI(uint32 diff)
{
if (me->IsInCombat())
{
_combatTimer += diff;
if (_combatTimer >= 5000)
EnterEvadeMode();
}
}
void TriggerAI::IsSummonedBy(Unit* summoner)
{
if (me->m_spells[0])
me->CastSpell(me, me->m_spells[0], false, 0, 0, summoner ? summoner->GetGUID() : 0);
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_PASSIVEAI_H
#define TRINITY_PASSIVEAI_H
#include "CreatureAI.h"
//#include "CreatureAIImpl.h"
class PassiveAI : public CreatureAI
{
public:
explicit PassiveAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void UpdateAI(uint32);
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class PossessedAI : public CreatureAI
{
public:
explicit PossessedAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit* target);
void UpdateAI(uint32);
void EnterEvadeMode() {}
void JustDied(Unit*);
void KilledUnit(Unit* victim);
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class NullCreatureAI : public CreatureAI
{
public:
explicit NullCreatureAI(Creature* c);
void MoveInLineOfSight(Unit*) {}
void AttackStart(Unit*) {}
void UpdateAI(uint32) {}
void EnterEvadeMode() {}
void OnCharmed(bool /*apply*/) {}
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
};
class CritterAI : public PassiveAI
{
public:
explicit CritterAI(Creature* c) : PassiveAI(c) { _combatTimer = 0; }
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask);
void EnterEvadeMode();
void UpdateAI(uint32);
// Xinef: Added
private:
uint32 _combatTimer;
};
class TriggerAI : public NullCreatureAI
{
public:
explicit TriggerAI(Creature* c) : NullCreatureAI(c) {}
void IsSummonedBy(Unit* summoner);
};
#endif

View File

@@ -0,0 +1,742 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "PetAI.h"
#include "Errors.h"
#include "Pet.h"
#include "Player.h"
#include "DBCStores.h"
#include "Spell.h"
#include "ObjectAccessor.h"
#include "SpellMgr.h"
#include "Creature.h"
#include "World.h"
#include "Util.h"
#include "Group.h"
#include "SpellInfo.h"
#include "SpellAuraEffects.h"
#include "WorldSession.h"
int PetAI::Permissible(const Creature* creature)
{
if (creature->IsPet())
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK)
{
UpdateAllies();
}
bool PetAI::_needToStop()
{
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
if (me->IsCharmed() && me->GetVictim() == me->GetCharmer())
return true;
// xinef: dont allow to follow targets out of visibility range
if (me->GetExactDist(me->GetVictim()) > me->GetVisibilityRange()-5.0f)
return true;
// dont allow pets to follow targets far away from owner
if (Unit* owner = me->GetCharmerOrOwner())
if (owner->GetExactDist(me) >= (owner->GetVisibilityRange()-10.0f))
return true;
if (!me->_CanDetectFeignDeathOf(me->GetVictim()))
return true;
if (me->isTargetNotAcceptableByMMaps(me->GetVictim()->GetGUID(), sWorld->GetGameTime(), me->GetVictim()))
return true;
return !me->CanCreatureAttack(me->GetVictim());
}
void PetAI::_stopAttack()
{
if (!me->IsAlive())
{
;//sLog->outStaticDebug("Creature stoped attacking cuz his dead [guid=%u]", me->GetGUIDLow());
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
me->CombatStop();
me->getHostileRefManager().deleteReferences();
return;
}
me->AttackStop();
me->InterruptNonMeleeSpells(false);
me->GetCharmInfo()->SetIsCommandAttack(false);
ClearCharmInfoFlags();
HandleReturnMovement();
}
void PetAI::_doMeleeAttack()
{
// Xinef: Imps cannot attack with melee
if (!_canMeleeAttack())
return;
DoMeleeAttackIfReady();
}
bool PetAI::_canMeleeAttack() const
{
return me->GetEntry() != 416 /*ENTRY_IMP*/ &&
me->GetEntry() != 510 /*ENTRY_WATER_ELEMENTAL*/ &&
me->GetEntry() != 37994 /*ENTRY_WATER_ELEMENTAL_PERM*/;
}
void PetAI::UpdateAI(uint32 diff)
{
if (!me->IsAlive() || !me->GetCharmInfo())
return;
Unit* owner = me->GetCharmerOrOwner();
if (m_updateAlliesTimer <= diff)
// UpdateAllies self set update timer
UpdateAllies();
else
m_updateAlliesTimer -= diff;
if (me->GetVictim() && me->GetVictim()->IsAlive())
{
// is only necessary to stop casting, the pet must not exit combat
if (me->GetVictim()->HasBreakableByDamageCrowdControlAura(me))
{
me->InterruptNonMeleeSpells(false);
return;
}
if (_needToStop())
{
;//sLog->outStaticDebug("Pet AI stopped attacking [guid=%u]", me->GetGUIDLow());
_stopAttack();
return;
}
// Check before attacking to prevent pets from leaving stay position
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim())))
_doMeleeAttack();
}
else
_doMeleeAttack();
}
else if (!me->GetCharmInfo() || (!me->GetCharmInfo()->GetForcedSpell() && !me->HasUnitState(UNIT_STATE_CASTING)))
{
if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay())
{
// Every update we need to check targets only in certain cases
// Aggressive - Allow auto select if owner or pet don't have a target
// Stay - Only pick from pet or owner targets / attackers so targets won't run by
// while chasing our owner. Don't do auto select.
// All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc.
Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE));
if (nextTarget)
AttackStart(nextTarget);
else
HandleReturnMovement();
}
else
HandleReturnMovement();
}
// xinef: charm info must be always available
if (!me->GetCharmInfo())
return;
// Autocast (casted only in combat or persistent spells in any state)
if (!me->HasUnitState(UNIT_STATE_CASTING))
{
if (owner && owner->GetTypeId() == TYPEID_PLAYER && me->GetCharmInfo()->GetForcedSpell() && me->GetCharmInfo()->GetForcedTarget())
{
owner->ToPlayer()->GetSession()->HandlePetActionHelper(me, me->GetGUID(), abs(me->GetCharmInfo()->GetForcedSpell()), ACT_ENABLED, me->GetCharmInfo()->GetForcedTarget());
// xinef: if spell was casted properly and we are in passive mode, handle return
if (!me->GetCharmInfo()->GetForcedSpell() && me->HasReactState(REACT_PASSIVE))
{
if (me->HasUnitState(UNIT_STATE_CASTING))
{
me->GetMotionMaster()->Clear(false);
me->StopMoving();
}
else
_stopAttack();
}
return;
}
// xinef: dont allow ghouls to cast spells below 75 energy
if (me->IsPet() && me->ToPet()->IsPetGhoul() && me->GetPower(POWER_ENERGY) < 75)
return;
typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
TargetSpellList targetSpellStore;
for (uint8 i = 0; i < me->GetPetAutoSpellSize(); ++i)
{
uint32 spellID = me->GetPetAutoSpellOnPos(i);
if (!spellID)
continue;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID);
if (!spellInfo)
continue;
if (me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
continue;
// check spell cooldown, this should be checked in CheckCast...
if (me->HasSpellCooldown(spellInfo->Id))
continue;
if (spellInfo->IsPositive())
{
if (spellInfo->CanBeUsedInCombat())
{
// Check if we're in combat or commanded to attack
if (!me->IsInCombat() && !me->GetCharmInfo()->IsCommandAttack())
continue;
}
Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0);
spell->LoadScripts(); // xinef: load for CanAutoCast (calling CheckPetCast)
bool spellUsed = false;
// Some spells can target enemy or friendly (DK Ghoul's Leap)
// Check for enemy first (pet then owner)
Unit* target = me->getAttackerForHelper();
if (!target && owner)
target = owner->getAttackerForHelper();
if (target)
{
if (CanAttack(target) && spell->CanAutoCast(target))
{
targetSpellStore.push_back(std::make_pair(target, spell));
spellUsed = true;
}
}
// No enemy, check friendly
if (!spellUsed)
{
for (std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
{
Unit* ally = ObjectAccessor::GetUnit(*me, *tar);
//only buff targets that are in combat, unless the spell can only be cast while out of combat
if (!ally)
continue;
if (spell->CanAutoCast(ally))
{
targetSpellStore.push_back(std::make_pair(ally, spell));
spellUsed = true;
break;
}
}
}
// No valid targets at all
if (!spellUsed)
delete spell;
}
else if (me->GetVictim() && CanAttack(me->GetVictim(), spellInfo) && spellInfo->CanBeUsedInCombat())
{
Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE, 0);
if (spell->CanAutoCast(me->GetVictim()))
targetSpellStore.push_back(std::make_pair(me->GetVictim(), spell));
else
delete spell;
}
}
//found units to cast on to
if (!targetSpellStore.empty())
{
uint32 index = urand(0, targetSpellStore.size() - 1);
Spell* spell = targetSpellStore[index].second;
Unit* target = targetSpellStore[index].first;
targetSpellStore.erase(targetSpellStore.begin() + index);
SpellCastTargets targets;
targets.SetUnitTarget(target);
if (!me->HasInArc(M_PI, target))
{
me->SetInFront(target);
if (target && target->GetTypeId() == TYPEID_PLAYER)
me->SendUpdateToPlayer(target->ToPlayer());
if (owner && owner->GetTypeId() == TYPEID_PLAYER)
me->SendUpdateToPlayer(owner->ToPlayer());
}
me->AddSpellCooldown(spell->m_spellInfo->Id, 0, 0);
spell->prepare(&targets);
}
// deleted cached Spell objects
for (TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
delete itr->second;
}
}
void PetAI::UpdateAllies()
{
Unit* owner = me->GetCharmerOrOwner();
Group* group = NULL;
m_updateAlliesTimer = 10*IN_MILLISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance
if (!owner)
return;
else if (owner->GetTypeId() == TYPEID_PLAYER)
group = owner->ToPlayer()->GetGroup();
//only pet and owner/not in group->ok
if (m_AllySet.size() == 2 && !group)
return;
//owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
if (group && !group->isRaidGroup() && m_AllySet.size() == (group->GetMembersCount() + 2))
return;
m_AllySet.clear();
m_AllySet.insert(me->GetGUID());
if (group) //add group
{
for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* Target = itr->GetSource();
if (!Target || !Target->IsInMap(owner) || !group->SameSubGroup(owner->ToPlayer(), Target))
continue;
if (Target->GetGUID() == owner->GetGUID())
continue;
m_AllySet.insert(Target->GetGUID());
}
}
else //remove group
m_AllySet.insert(owner->GetGUID());
}
void PetAI::KilledUnit(Unit* victim)
{
// Called from Unit::Kill() in case where pet or owner kills something
// if owner killed this victim, pet may still be attacking something else
if (me->GetVictim() && me->GetVictim() != victim)
return;
// Xinef: if pet is channeling a spell and owner killed something different, dont interrupt it
if (me->HasUnitState(UNIT_STATE_CASTING) && me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT) && me->GetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT) != victim->GetGUID())
return;
// Clear target just in case. May help problem where health / focus / mana
// regen gets stuck. Also resets attack command.
// Can't use _stopAttack() because that activates movement handlers and ignores
// next target selection
me->AttackStop();
me->InterruptNonMeleeSpells(false);
// Before returning to owner, see if there are more things to attack
if (Unit* nextTarget = SelectNextTarget(false))
AttackStart(nextTarget);
else
HandleReturnMovement(); // Return
}
void PetAI::AttackStart(Unit* target)
{
// Overrides Unit::AttackStart to correctly evaluate Pet states
// Check all pet states to decide if we can attack this target
if (!CanAttack(target))
return;
// Only chase if not commanded to stay or if stay but commanded to attack
DoAttack(target, (!me->GetCharmInfo()->HasCommandState(COMMAND_STAY) || me->GetCharmInfo()->IsCommandAttack()));
}
void PetAI::OwnerAttackedBy(Unit* attacker)
{
// Called when owner takes damage. This function helps keep pets from running off
// simply due to owner gaining aggro.
if (!attacker)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(attacker);
}
void PetAI::OwnerAttacked(Unit* target)
{
// Called when owner attacks something. Allows defensive pets to know
// that they need to assist
// Target might be NULL if called from spell with invalid cast targets
if (!target)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(target);
}
Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
{
// Provides next target selection after current target death.
// This function should only be called internally by the AI
// Targets are not evaluated here for being valid targets, that is done in _CanAttack()
// The parameter: allowAutoSelect lets us disable aggressive pet auto targeting for certain situations
// Passive pets don't do next target selection
if (me->HasReactState(REACT_PASSIVE))
return NULL;
// Check pet attackers first so we don't drag a bunch of targets to the owner
if (Unit* myAttacker = me->getAttackerForHelper())
if (!myAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(myAttacker) && me->CanCreatureAttack(myAttacker) && !me->isTargetNotAcceptableByMMaps(myAttacker->GetGUID(), sWorld->GetGameTime(), myAttacker))
return myAttacker;
// Check pet's attackers first to prevent dragging mobs back to owner
if (me->HasAuraType(SPELL_AURA_MOD_TAUNT))
{
const Unit::AuraEffectList& tauntAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT);
if (!tauntAuras.empty())
for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr)
if (Unit* caster = (*itr)->GetCaster())
if (me->_CanDetectFeignDeathOf(caster) && me->CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, me->GetGUID()))
return caster;
}
// Not sure why we wouldn't have an owner but just in case...
Unit* owner = me->GetCharmerOrOwner();
if (!owner)
return NULL;
// Check owner attackers
if (Unit* ownerAttacker = owner->getAttackerForHelper())
if (!ownerAttacker->HasBreakableByDamageCrowdControlAura() && me->_CanDetectFeignDeathOf(ownerAttacker) && me->CanCreatureAttack(ownerAttacker) && !me->isTargetNotAcceptableByMMaps(ownerAttacker->GetGUID(), sWorld->GetGameTime(), ownerAttacker))
return ownerAttacker;
// Check owner victim
// 3.0.2 - Pets now start attacking their owners victim in defensive mode as soon as the hunter does
if (Unit* ownerVictim = owner->GetVictim())
if (me->_CanDetectFeignDeathOf(ownerVictim) && me->CanCreatureAttack(ownerVictim) && !me->isTargetNotAcceptableByMMaps(ownerVictim->GetGUID(), sWorld->GetGameTime(), ownerVictim))
return ownerVictim;
// Neither pet or owner had a target and aggressive pets can pick any target
// To prevent aggressive pets from chain selecting targets and running off, we
// only select a random target if certain conditions are met.
if (allowAutoSelect)
if (!me->GetCharmInfo()->IsReturning() || me->GetCharmInfo()->IsFollowing() || me->GetCharmInfo()->IsAtStay())
if (Unit* nearTarget = me->ToCreature()->SelectNearestTargetInAttackDistance(MAX_AGGRO_RADIUS))
return nearTarget;
// Default - no valid targets
return NULL;
}
void PetAI::HandleReturnMovement()
{
// Handles moving the pet back to stay or owner
// Prevent activating movement when under control of spells
// such as "Eyes of the Beast"
if (me->isPossessed())
return;
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
{
// Return to previous position where stay was clicked
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
float x, y, z;
me->GetCharmInfo()->GetStayPosition(x, y, z);
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z);
}
}
}
else // COMMAND_FOLLOW
{
if (!me->GetCharmInfo()->IsFollowing() && !me->GetCharmInfo()->IsReturning())
{
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE)
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle());
}
}
}
me->GetCharmInfo()->SetForcedSpell(0);
me->GetCharmInfo()->SetForcedTargetGUID(0);
// xinef: remember that npcs summoned by npcs can also be pets
me->DeleteThreatList();
me->ClearInPetCombat();
}
void PetAI::SpellHit(Unit* caster, const SpellInfo* spellInfo)
{
// Xinef: taunt behavior code
if (spellInfo->HasAura(SPELL_AURA_MOD_TAUNT) && !me->HasReactState(REACT_PASSIVE))
{
me->GetCharmInfo()->SetForcedSpell(0);
me->GetCharmInfo()->SetForcedTargetGUID(0);
AttackStart(caster);
}
}
void PetAI::DoAttack(Unit* target, bool chase)
{
// Handles attack with or without chase and also resets flags
// for next update / creature kill
if (me->Attack(target, true))
{
// xinef: properly fix fake combat after pet is sent to attack
if (Unit* owner = me->GetOwner())
owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
// Play sound to let the player know the pet is attacking something it picked on its own
if (me->HasReactState(REACT_AGGRESSIVE) && !me->GetCharmInfo()->IsCommandAttack())
me->SendPetAIReaction(me->GetGUID());
if (CharmInfo* ci = me->GetCharmInfo())
{
ci->SetIsAtStay(false);
ci->SetIsCommandFollow(false);
ci->SetIsFollowing(false);
ci->SetIsReturning(false);
}
if (chase)
{
me->GetMotionMaster()->MoveChase(target, !_canMeleeAttack() ? 20.0f: 0.0f, me->GetAngle(target));
}
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
{
me->GetCharmInfo()->SetIsAtStay(true);
me->GetMotionMaster()->MovementExpiredOnSlot(MOTION_SLOT_ACTIVE, false);
me->GetMotionMaster()->MoveIdle();
}
}
}
void PetAI::MovementInform(uint32 moveType, uint32 data)
{
// Receives notification when pet reaches stay or follow owner
switch (moveType)
{
case POINT_MOTION_TYPE:
{
// Pet is returning to where stay was clicked. data should be
// pet's GUIDLow since we set that as the waypoint ID
if (data == me->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsAtStay(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
break;
}
case FOLLOW_MOTION_TYPE:
{
// If data is owner's GUIDLow then we've reached follow point,
// otherwise we're probably chasing a creature
if (me->GetCharmerOrOwner() && me->GetCharmInfo() && data == me->GetCharmerOrOwner()->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsFollowing(true);
}
break;
}
default:
break;
}
}
bool PetAI::CanAttack(Unit* target, const SpellInfo* spellInfo)
{
// Evaluates wether a pet can attack a specific target based on CommandState, ReactState and other flags
// IMPORTANT: The order in which things are checked is important, be careful if you add or remove checks
// Hmmm...
if (!target)
return false;
if (!target->IsAlive())
{
// xinef: if target is invalid, pet should evade automaticly
// Clear target to prevent getting stuck on dead targets
//me->AttackStop();
//me->InterruptNonMeleeSpells(false);
return false;
}
// xinef: check unit states of pet
if (me->HasUnitState(UNIT_STATE_LOST_CONTROL))
return false;
// xinef: pets of mounted players have stunned flag only, check this also
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED))
return false;
// pussywizard: ZOMG! TEMP!
if (!me->GetCharmInfo())
{
sLog->outMisc("PetAI::CanAttack (A1) - %u, %u", me->GetEntry(), GUID_LOPART(me->GetOwnerGUID()));
return false;
}
// Passive - passive pets can attack if told to
if (me->HasReactState(REACT_PASSIVE))
return me->GetCharmInfo()->IsCommandAttack();
// CC - mobs under crowd control can be attacked if owner commanded
if (target->HasBreakableByDamageCrowdControlAura() && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)))
return me->GetCharmInfo()->IsCommandAttack();
// Returning - pets ignore attacks only if owner clicked follow
if (me->GetCharmInfo()->IsReturning())
return !me->GetCharmInfo()->IsCommandFollow();
// Stay - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
return (me->IsWithinMeleeRange(target) || me->GetCharmInfo()->IsCommandAttack());
// Pets attacking something (or chasing) should only switch targets if owner tells them to
if (me->GetVictim() && me->GetVictim() != target)
{
// Check if our owner selected this target and clicked "attack"
Unit* ownerTarget = NULL;
if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
ownerTarget = owner->GetSelectedUnit();
else
ownerTarget = me->GetCharmerOrOwner()->GetVictim();
if (ownerTarget && me->GetCharmInfo()->IsCommandAttack())
return (target->GetGUID() == ownerTarget->GetGUID());
}
// Follow
if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
return !me->GetCharmInfo()->IsReturning();
// default, though we shouldn't ever get here
return false;
}
void PetAI::ReceiveEmote(Player* player, uint32 emote)
{
if (me->GetOwnerGUID() && me->GetOwnerGUID() == player->GetGUID())
switch (emote)
{
case TEXT_EMOTE_COWER:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(/*EMOTE_ONESHOT_ROAR*/EMOTE_ONESHOT_OMNICAST_GHOUL);
break;
case TEXT_EMOTE_ANGRY:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(/*EMOTE_ONESHOT_COWER*/EMOTE_STATE_STUN);
break;
case TEXT_EMOTE_GLARE:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(EMOTE_STATE_STUN);
break;
case TEXT_EMOTE_SOOTHE:
if (me->IsPet() && me->ToPet()->IsPetGhoul())
me->HandleEmoteCommand(EMOTE_ONESHOT_OMNICAST_GHOUL);
break;
}
}
void PetAI::ClearCharmInfoFlags()
{
// Quick access to set all flags to FALSE
CharmInfo* ci = me->GetCharmInfo();
if (ci)
{
ci->SetIsAtStay(false);
ci->SetIsCommandAttack(false);
ci->SetIsCommandFollow(false);
ci->SetIsFollowing(false);
ci->SetIsReturning(false);
}
}
void PetAI::AttackedBy(Unit* attacker)
{
// Called when pet takes damage. This function helps keep pets from running off
// simply due to gaining aggro.
if (!attacker)
return;
// Passive pets don't do anything
if (me->HasReactState(REACT_PASSIVE))
return;
// Prevent pet from disengaging from current target
if (me->GetVictim() && me->GetVictim()->IsAlive())
return;
// Continue to evaluate and attack if necessary
AttackStart(attacker);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_PETAI_H
#define TRINITY_PETAI_H
#include "CreatureAI.h"
#include "Timer.h"
class Creature;
class Spell;
class PetAI : public CreatureAI
{
public:
explicit PetAI(Creature* c);
void UpdateAI(uint32);
static int Permissible(const Creature*);
void KilledUnit(Unit* /*victim*/);
void AttackStart(Unit* target);
void MovementInform(uint32 moveType, uint32 data);
void OwnerAttackedBy(Unit* attacker);
void OwnerAttacked(Unit* target);
void AttackedBy(Unit* attacker);
void ReceiveEmote(Player* player, uint32 textEmote);
// The following aren't used by the PetAI but need to be defined to override
// default CreatureAI functions which interfere with the PetAI
//
void MoveInLineOfSight(Unit* /*who*/) {} // CreatureAI interferes with returning pets
void MoveInLineOfSight_Safe(Unit* /*who*/) {} // CreatureAI interferes with returning pets
void EnterEvadeMode() {} // For fleeing, pets don't use this type of Evade mechanic
void SpellHit(Unit* caster, const SpellInfo* spellInfo);
private:
bool _isVisible(Unit*) const;
bool _needToStop(void);
void _stopAttack(void);
void _doMeleeAttack();
bool _canMeleeAttack() const;
void UpdateAllies();
TimeTracker i_tracker;
std::set<uint64> m_AllySet;
uint32 m_updateAlliesTimer;
Unit* SelectNextTarget(bool allowAutoSelect) const;
void HandleReturnMovement();
void DoAttack(Unit* target, bool chase);
bool CanAttack(Unit* target, const SpellInfo* spellInfo = NULL);
void ClearCharmInfoFlags();
};
#endif

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "ByteBuffer.h"
#include "ReactorAI.h"
#include "Errors.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "CreatureAIImpl.h"
int ReactorAI::Permissible(const Creature* creature)
{
if (creature->IsCivilian() || creature->IsNeutralToAll())
return PERMIT_BASE_REACTIVE;
return PERMIT_BASE_NO;
}
void ReactorAI::UpdateAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_REACTORAI_H
#define TRINITY_REACTORAI_H
#include "CreatureAI.h"
class Unit;
class ReactorAI : public CreatureAI
{
public:
explicit ReactorAI(Creature* c) : CreatureAI(c) {}
void MoveInLineOfSight(Unit*) {}
void UpdateAI(uint32 diff);
static int Permissible(const Creature*);
};
#endif

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "TotemAI.h"
#include "Totem.h"
#include "Creature.h"
#include "DBCStores.h"
#include "ObjectAccessor.h"
#include "SpellMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
int TotemAI::Permissible(Creature const* creature)
{
if (creature->IsTotem())
return PERMIT_BASE_PROACTIVE;
return PERMIT_BASE_NO;
}
TotemAI::TotemAI(Creature* c) : CreatureAI(c), i_victimGuid(0)
{
ASSERT(c->IsTotem());
}
void TotemAI::SpellHit(Unit* /*caster*/, const SpellInfo* spellInfo)
{
}
void TotemAI::DoAction(int32 param)
{
}
void TotemAI::MoveInLineOfSight(Unit* /*who*/)
{
}
void TotemAI::EnterEvadeMode()
{
me->CombatStop(true);
}
void TotemAI::UpdateAI(uint32 /*diff*/)
{
if (me->ToTotem()->GetTotemType() != TOTEM_ACTIVE)
return;
if (!me->IsAlive() || me->IsNonMeleeSpellCast(false))
return;
// Search spell
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(me->ToTotem()->GetSpell());
if (!spellInfo)
return;
// Get spell range
float max_range = spellInfo->GetMaxRange(false);
// SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems
// pointer to appropriate target if found any
Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(*me, i_victimGuid) : NULL;
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
if (!victim ||
!victim->isTargetableForAttack(true, me) || !me->IsWithinDistInMap(victim, max_range) ||
me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim))
{
victim = NULL;
Trinity::NearestAttackableUnitInObjectRangeCheck u_check(me, me, max_range);
Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(me, victim, u_check);
me->VisitNearbyObject(max_range, checker);
}
// If have target
if (victim)
{
// remember
i_victimGuid = victim->GetGUID();
// attack
me->SetInFront(victim); // client change orientation by self
me->CastSpell(victim, me->ToTotem()->GetSpell(), false);
}
else
i_victimGuid = 0;
}
void TotemAI::AttackStart(Unit* /*victim*/)
{
// Sentry totem sends ping on attack
if (me->GetEntry() == SENTRY_TOTEM_ENTRY && me->GetOwner()->GetTypeId() == TYPEID_PLAYER)
{
WorldPacket data(MSG_MINIMAP_PING, (8+4+4));
data << me->GetGUID();
data << me->GetPositionX();
data << me->GetPositionY();
me->GetOwner()->ToPlayer()->GetSession()->SendPacket(&data);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_TOTEMAI_H
#define TRINITY_TOTEMAI_H
#include "CreatureAI.h"
#include "Timer.h"
class Creature;
class Totem;
class TotemAI : public CreatureAI
{
public:
explicit TotemAI(Creature* c);
void MoveInLineOfSight(Unit* who);
void AttackStart(Unit* victim);
void EnterEvadeMode();
void SpellHit(Unit* /*caster*/, const SpellInfo* /*spellInfo*/);
void DoAction(int32 param);
void UpdateAI(uint32 diff);
static int Permissible(Creature const* creature);
private:
uint64 i_victimGuid;
};
class KillMagnetEvent : public BasicEvent
{
public:
KillMagnetEvent(Unit& self) : _self(self) { }
bool Execute(uint64 e_time, uint32 p_time)
{
_self.setDeathState(JUST_DIED);
return true;
}
protected:
Unit& _self;
};
#endif

View File

@@ -0,0 +1,336 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "UnitAI.h"
#include "Player.h"
#include "Creature.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "Spell.h"
#include "CreatureAIImpl.h"
void UnitAI::AttackStart(Unit* victim)
{
if (victim && me->Attack(victim, true))
me->GetMotionMaster()->MoveChase(victim);
}
void UnitAI::AttackStartCaster(Unit* victim, float dist)
{
if (victim && me->Attack(victim, false))
me->GetMotionMaster()->MoveChase(victim, dist);
}
void UnitAI::DoMeleeAttackIfReady()
{
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
Unit *victim = me->GetVictim();
if (!victim || !victim->IsInWorld())
return;
if (!me->IsWithinMeleeRange(victim))
return;
//Make sure our attack is ready and we aren't currently casting before checking distance
if (me->isAttackReady())
{
// xinef: prevent base and off attack in same time, delay attack at 0.2 sec
if (me->haveOffhandWeapon())
if (me->getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
me->setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
me->AttackerStateUpdate(victim);
me->resetAttackTimer();
}
if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK))
{
// xinef: delay main hand attack if both will hit at the same time (players code)
if (me->getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
me->setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
me->AttackerStateUpdate(victim, OFF_ATTACK);
me->resetAttackTimer(OFF_ATTACK);
}
}
bool UnitAI::DoSpellAttackIfReady(uint32 spell)
{
if (me->HasUnitState(UNIT_STATE_CASTING) || !me->isAttackReady())
return true;
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell))
{
if (me->IsWithinCombatRange(me->GetVictim(), spellInfo->GetMaxRange(false)))
{
me->CastSpell(me->GetVictim(), spell, false);
me->resetAttackTimer();
return true;
}
}
return false;
}
Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura)
{
return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, aura));
}
void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura)
{
SelectTargetList(targetList, DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
}
float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
return spellInfo ? spellInfo->GetMaxRange(positive) : 0;
}
void UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid)
{
if (me->IsInCombat())
{
ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (unit->GetTypeId() == TYPEID_PLAYER)
me->AddAura(spellid, unit);
}
}
}
void UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered)
{
if (me->IsInCombat())
{
ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (unit->GetTypeId() == TYPEID_PLAYER)
me->CastSpell(unit, spellid, triggered);
}
}
}
void UnitAI::DoCast(uint32 spellId)
{
Unit* target = NULL;
//sLog->outError("aggre %u %u", spellId, (uint32)AISpellInfo[spellId].target);
switch (AISpellInfo[spellId].target)
{
default:
case AITARGET_SELF: target = me; break;
case AITARGET_VICTIM: target = me->GetVictim(); break;
case AITARGET_ENEMY:
{
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
//float range = GetSpellMaxRange(spellInfo, false);
target = SelectTarget(SELECT_TARGET_RANDOM, 0, spellInfo->GetMaxRange(false), playerOnly);
break;
}
case AITARGET_ALLY: target = me; break;
case AITARGET_BUFF: target = me; break;
case AITARGET_DEBUFF:
{
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
float range = spellInfo->GetMaxRange(false);
DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId);
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
&& targetSelector(me->GetVictim()))
target = me->GetVictim();
else
target = SelectTarget(SELECT_TARGET_RANDOM, 0, targetSelector);
break;
}
}
if (target)
me->CastSpell(target, spellId, false);
}
void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
{
if (!victim || (me->HasUnitState(UNIT_STATE_CASTING) && !triggered))
return;
me->CastSpell(victim, spellId, triggered);
}
void UnitAI::DoCastVictim(uint32 spellId, bool triggered)
{
if (!me->GetVictim() || (me->HasUnitState(UNIT_STATE_CASTING) && !triggered))
return;
me->CastSpell(me->GetVictim(), spellId, triggered);
}
void UnitAI::DoCastAOE(uint32 spellId, bool triggered)
{
if (!triggered && me->HasUnitState(UNIT_STATE_CASTING))
return;
me->CastSpell((Unit*)NULL, spellId, triggered);
}
#define UPDATE_TARGET(a) {if (AIInfo->target<a) AIInfo->target=a;}
void UnitAI::FillAISpellInfo()
{
AISpellInfo = new AISpellInfoType[sSpellMgr->GetSpellInfoStoreSize()];
AISpellInfoType* AIInfo = AISpellInfo;
const SpellInfo* spellInfo;
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i, ++AIInfo)
{
spellInfo = sSpellMgr->GetSpellInfo(i);
if (!spellInfo)
continue;
if (spellInfo->HasAttribute(SPELL_ATTR0_CASTABLE_WHILE_DEAD))
AIInfo->condition = AICOND_DIE;
else if (spellInfo->IsPassive() || spellInfo->GetDuration() == -1)
AIInfo->condition = AICOND_AGGRO;
else
AIInfo->condition = AICOND_COMBAT;
if (AIInfo->cooldown < spellInfo->RecoveryTime)
AIInfo->cooldown = spellInfo->RecoveryTime;
if (!spellInfo->GetMaxRange(false))
UPDATE_TARGET(AITARGET_SELF)
else
{
for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
uint32 targetType = spellInfo->Effects[j].TargetA.GetTarget();
if (targetType == TARGET_UNIT_TARGET_ENEMY
|| targetType == TARGET_DEST_TARGET_ENEMY)
UPDATE_TARGET(AITARGET_VICTIM)
else if (targetType == TARGET_UNIT_DEST_AREA_ENEMY)
UPDATE_TARGET(AITARGET_ENEMY)
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_APPLY_AURA)
{
if (targetType == TARGET_UNIT_TARGET_ENEMY)
UPDATE_TARGET(AITARGET_DEBUFF)
else if (spellInfo->IsPositive())
UPDATE_TARGET(AITARGET_BUFF)
}
}
}
AIInfo->realCooldown = spellInfo->RecoveryTime + spellInfo->StartRecoveryTime;
AIInfo->maxRange = spellInfo->GetMaxRange(false) * 3 / 4;
}
}
//Enable PlayerAI when charmed
void PlayerAI::OnCharmed(bool apply)
{
me->IsAIEnabled = apply;
}
void SimpleCharmedAI::UpdateAI(uint32 /*diff*/)
{
Creature* charmer = me->GetCharmer()->ToCreature();
//kill self if charm aura has infinite duration
if (charmer->IsInEvadeMode())
{
Unit::AuraEffectList const& auras = me->GetAuraEffectsByType(SPELL_AURA_MOD_CHARM);
for (Unit::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent())
{
Unit::Kill(charmer, me);
return;
}
}
if (!charmer->IsInCombat())
me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle());
Unit* target = me->GetVictim();
if (!target || !charmer->IsValidAttackTarget(target))
AttackStart(charmer->SelectNearestTargetInAttackDistance(ATTACK_DISTANCE));
}
SpellTargetSelector::SpellTargetSelector(Unit* caster, uint32 spellId) :
_caster(caster), _spellInfo(sSpellMgr->GetSpellForDifficultyFromSpell(sSpellMgr->GetSpellInfo(spellId), caster))
{
ASSERT(_spellInfo);
}
bool SpellTargetSelector::operator()(Unit const* target) const
{
if (!target)
return false;
if (_spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK)
return false;
// copypasta from Spell::CheckRange
uint32 range_type = _spellInfo->RangeEntry ? _spellInfo->RangeEntry->type : 0;
float max_range = _caster->GetSpellMaxRangeForTarget(target, _spellInfo);
float min_range = _caster->GetSpellMinRangeForTarget(target, _spellInfo);
if (target && target != _caster)
{
if (range_type == SPELL_RANGE_MELEE)
{
// Because of lag, we can not check too strictly here.
if (!_caster->IsWithinMeleeRange(target, max_range))
return false;
}
else if (!_caster->IsWithinCombatRange(target, max_range))
return false;
if (range_type == SPELL_RANGE_RANGED)
{
if (_caster->IsWithinMeleeRange(target))
return false;
}
else if (min_range && _caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
return false;
}
return true;
}
bool NonTankTargetSelector::operator()(Unit const* target) const
{
if (!target)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
return target != _source->GetVictim();
}

View File

@@ -0,0 +1,340 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_UNITAI_H
#define TRINITY_UNITAI_H
#include "Define.h"
#include "Unit.h"
#include "Containers.h"
#include <list>
class Player;
class Quest;
class Unit;
struct AISpellInfoType;
//Selection method used by SelectTarget
enum SelectAggroTarget
{
SELECT_TARGET_RANDOM = 0, //Just selects a random target
SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom
SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top
SELECT_TARGET_NEAREST,
SELECT_TARGET_FARTHEST,
};
// default predicate function to select target based on distance, player and/or aura criteria
struct DefaultTargetSelector : public std::unary_function<Unit*, bool>
{
const Unit* me;
float m_dist;
bool m_playerOnly;
int32 m_aura;
// unit: the reference unit
// dist: if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit
// playerOnly: self explaining
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, int32 aura) : me(unit), m_dist(dist), m_playerOnly(playerOnly), m_aura(aura) {}
bool operator()(Unit const* target) const
{
if (!me)
return false;
if (!target)
return false;
if (m_playerOnly && (target->GetTypeId() != TYPEID_PLAYER))
return false;
if (m_dist > 0.0f && !me->IsWithinCombatRange(target, m_dist))
return false;
if (m_dist < 0.0f && me->IsWithinCombatRange(target, -m_dist))
return false;
if (m_aura)
{
if (m_aura > 0)
{
if (!target->HasAura(m_aura))
return false;
}
else
{
if (target->HasAura(-m_aura))
return false;
}
}
return true;
}
};
// Target selector for spell casts checking range, auras and attributes
// TODO: Add more checks from Spell::CheckCast
struct SpellTargetSelector : public std::unary_function<Unit*, bool>
{
public:
SpellTargetSelector(Unit* caster, uint32 spellId);
bool operator()(Unit const* target) const;
private:
Unit const* _caster;
SpellInfo const* _spellInfo;
};
// Very simple target selector, will just skip main target
// NOTE: When passing to UnitAI::SelectTarget remember to use 0 as position for random selection
// because tank will not be in the temporary list
struct NonTankTargetSelector : public std::unary_function<Unit*, bool>
{
public:
NonTankTargetSelector(Creature* source, bool playerOnly = true) : _source(source), _playerOnly(playerOnly) { }
bool operator()(Unit const* target) const;
private:
Creature const* _source;
bool _playerOnly;
};
// Simple selector for units using mana
struct PowerUsersSelector : public std::unary_function<Unit*, bool>
{
Unit const* _me;
float const _dist;
bool const _playerOnly;
Powers const _power;
PowerUsersSelector(Unit const* unit, Powers power, float dist, bool playerOnly) : _me(unit), _power(power), _dist(dist), _playerOnly(playerOnly) { }
bool operator()(Unit const* target) const
{
if (!_me || !target)
return false;
if (target->getPowerType() != _power)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
return false;
if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
return false;
return true;
}
};
struct FarthestTargetSelector : public std::unary_function<Unit*, bool>
{
FarthestTargetSelector(Unit const* unit, float dist, bool playerOnly, bool inLos) : _me(unit), _dist(dist), _playerOnly(playerOnly), _inLos(inLos) {}
bool operator()(Unit const* target) const
{
if (!_me || !target)
return false;
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
return false;
if (_inLos && !_me->IsWithinLOSInMap(target))
return false;
return true;
}
private:
const Unit* _me;
float _dist;
bool _playerOnly;
bool _inLos;
};
class UnitAI
{
protected:
Unit* const me;
public:
explicit UnitAI(Unit* unit) : me(unit) {}
virtual ~UnitAI() {}
virtual bool CanAIAttack(Unit const* /*target*/) const { return true; }
virtual void AttackStart(Unit* /*target*/);
virtual void UpdateAI(uint32 diff) = 0;
virtual void InitializeAI() { if (!me->isDead()) Reset(); }
virtual void Reset() {};
// Called when unit is charmed
virtual void OnCharmed(bool apply) = 0;
// Pass parameters between AI
virtual void DoAction(int32 /*param*/) {}
virtual uint32 GetData(uint32 /*id = 0*/) const { return 0; }
virtual void SetData(uint32 /*id*/, uint32 /*value*/) {}
virtual void SetGUID(uint64 /*guid*/, int32 /*id*/ = 0) {}
virtual uint64 GetGUID(int32 /*id*/ = 0) const { return 0; }
Unit* SelectTarget(SelectAggroTarget targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
// Select the targets satifying the predicate.
// predicate shall extend std::unary_function<Unit*, bool>
template <class PREDICATE> Unit* SelectTarget(SelectAggroTarget targetType, uint32 position, PREDICATE const& predicate)
{
ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList();
if (position >= threatlist.size())
return NULL;
std::list<Unit*> targetList;
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
if (predicate((*itr)->getTarget()))
targetList.push_back((*itr)->getTarget());
if (position >= targetList.size())
return NULL;
if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
targetList.sort(Trinity::ObjectDistanceOrderPred(me));
switch (targetType)
{
case SELECT_TARGET_NEAREST:
case SELECT_TARGET_TOPAGGRO:
{
std::list<Unit*>::iterator itr = targetList.begin();
std::advance(itr, position);
return *itr;
}
case SELECT_TARGET_FARTHEST:
case SELECT_TARGET_BOTTOMAGGRO:
{
std::list<Unit*>::reverse_iterator ritr = targetList.rbegin();
std::advance(ritr, position);
return *ritr;
}
case SELECT_TARGET_RANDOM:
{
std::list<Unit*>::iterator itr = targetList.begin();
std::advance(itr, urand(position, targetList.size() - 1));
return *itr;
}
default:
break;
}
return NULL;
}
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
// Select the targets satifying the predicate.
// predicate shall extend std::unary_function<Unit*, bool>
template <class PREDICATE> void SelectTargetList(std::list<Unit*>& targetList, PREDICATE const& predicate, uint32 maxTargets, SelectAggroTarget targetType)
{
ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList();
if (threatlist.empty())
return;
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
if (predicate((*itr)->getTarget()))
targetList.push_back((*itr)->getTarget());
if (targetList.size() < maxTargets)
return;
if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
targetList.sort(Trinity::ObjectDistanceOrderPred(me));
if (targetType == SELECT_TARGET_FARTHEST || targetType == SELECT_TARGET_BOTTOMAGGRO)
targetList.reverse();
if (targetType == SELECT_TARGET_RANDOM)
Trinity::Containers::RandomResizeList(targetList, maxTargets);
else
targetList.resize(maxTargets);
}
// Called at any Damage to any victim (before damage apply)
virtual void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/) { }
// Called at any Damage from any attacker (before damage apply)
// Note: it for recalculation damage or special reaction at damage
// for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/ ) {}
// Called when the creature receives heal
virtual void HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/) {}
// Called when the unit heals
virtual void HealDone(Unit* /*done_to*/, uint32& /*addhealth*/) {}
void AttackStartCaster(Unit* victim, float dist);
void DoAddAuraToAllHostilePlayers(uint32 spellid);
void DoCast(uint32 spellId);
void DoCast(Unit* victim, uint32 spellId, bool triggered = false);
void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false);
void DoCastVictim(uint32 spellId, bool triggered = false);
void DoCastAOE(uint32 spellId, bool triggered = false);
float DoGetSpellMaxRange(uint32 spellId, bool positive = false);
void DoMeleeAttackIfReady();
bool DoSpellAttackIfReady(uint32 spell);
static AISpellInfoType* AISpellInfo;
static void FillAISpellInfo();
virtual void sGossipHello(Player* /*player*/) {}
virtual void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) {}
virtual void sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) {}
virtual void sQuestAccept(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestSelect(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestComplete(Player* /*player*/, Quest const* /*quest*/) {}
virtual void sQuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) {}
virtual void sOnGameEvent(bool /*start*/, uint16 /*eventId*/) {}
};
class PlayerAI : public UnitAI
{
protected:
Player* const me;
public:
explicit PlayerAI(Player* player) : UnitAI((Unit*)player), me(player) {}
void OnCharmed(bool apply);
};
class SimpleCharmedAI : public PlayerAI
{
public:
void UpdateAI(uint32 diff);
SimpleCharmedAI(Player* player): PlayerAI(player) {}
};
#endif

View File

@@ -0,0 +1,268 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "Creature.h"
#include "World.h"
#include "SpellMgr.h"
#include "Vehicle.h"
#include "Log.h"
#include "MapReference.h"
#include "Player.h"
#include "CreatureTextMgr.h"
//Disable CreatureAI when charmed
void CreatureAI::OnCharmed(bool /*apply*/)
{
//me->IsAIEnabled = !apply;*/
me->NeedChangeAI = true;
me->IsAIEnabled = false;
}
AISpellInfoType* UnitAI::AISpellInfo;
AISpellInfoType* GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; }
void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= NULL*/)
{
sCreatureTextMgr->SendChat(me, id, whisperTarget);
}
void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/)
{
if (!creature)
creature = me;
if (!creature->CanHaveThreatList())
return;
Map* map = creature->GetMap();
if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
{
sLog->outError("DoZoneInCombat call for map that isn't an instance (creature entry = %d)", creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0);
return;
}
// Xinef: Skip creatures in evade mode
if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim() && !creature->IsInEvadeMode())
{
if (Unit* nearTarget = creature->SelectNearestTarget(maxRangeToNearestTarget))
creature->AI()->AttackStart(nearTarget);
else if (creature->IsSummon())
{
if (Unit* summoner = creature->ToTempSummon()->GetSummoner())
{
Unit* target = summoner->getAttackerForHelper();
if (!target && summoner->CanHaveThreatList() && !summoner->getThreatManager().isThreatListEmpty())
target = summoner->getThreatManager().getHostilTarget();
if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target)))
creature->AI()->AttackStart(target);
}
}
}
if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim())
{
sLog->outError("DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry());
return;
}
Map::PlayerList const& playerList = map->GetPlayers();
if (playerList.isEmpty())
return;
for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr)
{
if (Player* player = itr->GetSource())
{
if (player->IsGameMaster())
continue;
if (player->IsAlive())
{
creature->SetInCombatWith(player);
player->SetInCombatWith(creature);
creature->AddThreat(player, 0.0f);
}
/* Causes certain things to never leave the threat list (Priest Lightwell, etc):
for (Unit::ControlSet::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr)
{
creature->SetInCombatWith(*itr);
(*itr)->SetInCombatWith(creature);
creature->AddThreat(*itr, 0.0f);
}*/
}
}
}
// scripts does not take care about MoveInLineOfSight loops
// MoveInLineOfSight can be called inside another MoveInLineOfSight and cause stack overflow
void CreatureAI::MoveInLineOfSight_Safe(Unit* who)
{
if (m_MoveInLineOfSight_locked == true)
return;
m_MoveInLineOfSight_locked = true;
MoveInLineOfSight(who);
m_MoveInLineOfSight_locked = false;
}
void CreatureAI::MoveInLineOfSight(Unit* who)
{
if (me->GetVictim())
return;
// pussywizard: civilian, non-combat pet or any other NOT HOSTILE TO ANYONE (!)
if (me->IsMoveInLineOfSightDisabled())
if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET || // nothing more to do, return
!who->IsInCombat() || // if not in combat, nothing more to do
!me->IsWithinDist(who, ATTACK_DISTANCE)) // if in combat and in dist - neutral to all can actually assist other creatures
return;
if (me->CanStartAttack(who))
AttackStart(who);
}
void CreatureAI::EnterEvadeMode()
{
if (!_EnterEvadeMode())
return;
;//sLog->outDebug(LOG_FILTER_UNITS, "Creature %u enters evade mode.", me->GetEntry());
if (!me->GetVehicle()) // otherwise me will be in evade mode forever
{
if (Unit* owner = me->GetCharmerOrOwner())
{
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE);
}
else
{
// Required to prevent attacking creatures that are evading and cause them to reenter combat
// Does not apply to MoveFollow
me->AddUnitState(UNIT_STATE_EVADE);
me->GetMotionMaster()->MoveTargetedHome();
}
}
Reset();
if (me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons!
me->GetVehicleKit()->Reset(true);
}
/*void CreatureAI::AttackedBy(Unit* attacker)
{
if (!me->GetVictim())
AttackStart(attacker);
}*/
void CreatureAI::SetGazeOn(Unit* target)
{
if (me->IsValidAttackTarget(target))
{
AttackStart(target);
me->SetReactState(REACT_PASSIVE);
}
}
bool CreatureAI::UpdateVictimWithGaze()
{
if (!me->IsInCombat())
return false;
if (me->HasReactState(REACT_PASSIVE))
{
if (me->GetVictim())
return true;
else
me->SetReactState(REACT_AGGRESSIVE);
}
if (Unit* victim = me->SelectVictim())
AttackStart(victim);
return me->GetVictim();
}
bool CreatureAI::UpdateVictim()
{
if (!me->IsInCombat())
return false;
if (!me->HasReactState(REACT_PASSIVE))
{
if (Unit* victim = me->SelectVictim())
AttackStart(victim);
return me->GetVictim();
}
// xinef: if we have any victim, just return true
else if (me->GetVictim() && me->GetExactDist(me->GetVictim()) < 30.0f)
return true;
else if (me->getThreatManager().isThreatListEmpty())
{
EnterEvadeMode();
return false;
}
return true;
}
bool CreatureAI::_EnterEvadeMode()
{
if (!me->IsAlive())
return false;
// don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
// don't remove clone caster on evade (to be verified)
me->RemoveEvadeAuras();
// sometimes bosses stuck in combat?
me->DeleteThreatList();
me->CombatStop(true);
me->LoadCreaturesAddon(true);
me->SetLootRecipient(NULL);
me->ResetPlayerDamageReq();
me->SetLastDamagedTime(0);
if (me->IsInEvadeMode())
return false;
return true;
}
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
{
return me->SummonCreature(entry, pos, summonType, despawnTime);
}
Creature* CreatureAI::DoSummon(uint32 entry, WorldObject* obj, float radius, uint32 despawnTime, TempSummonType summonType)
{
Position pos;
obj->GetRandomNearPosition(pos, radius);
return me->SummonCreature(entry, pos, summonType, despawnTime);
}
Creature* CreatureAI::DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius, uint32 despawnTime, TempSummonType summonType)
{
Position pos;
obj->GetRandomNearPosition(pos, radius);
pos.m_positionZ += flightZ;
return me->SummonCreature(entry, pos, summonType, despawnTime);
}

View File

@@ -0,0 +1,193 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CREATUREAI_H
#define TRINITY_CREATUREAI_H
#include "Creature.h"
#include "UnitAI.h"
#include "Common.h"
class WorldObject;
class Unit;
class Creature;
class Player;
class SpellInfo;
#define TIME_INTERVAL_LOOK 5000
#define VISIBILITY_RANGE 10000
//Spell targets used by SelectSpell
enum SelectTargetType
{
SELECT_TARGET_DONTCARE = 0, //All target types allowed
SELECT_TARGET_SELF, //Only Self casting
SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy
SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy
SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy
SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend
SELECT_TARGET_AOE_FRIEND, //Only AoE Friend
SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend
};
//Spell Effects used by SelectSpell
enum SelectEffect
{
SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed
SELECT_EFFECT_DAMAGE, //Spell does damage
SELECT_EFFECT_HEALING, //Spell does healing
SELECT_EFFECT_AURA, //Spell applies an aura
};
enum SCEquip
{
EQUIP_NO_CHANGE = -1,
EQUIP_UNEQUIP = 0
};
class CreatureAI : public UnitAI
{
protected:
Creature* const me;
bool UpdateVictim();
bool UpdateVictimWithGaze();
void SetGazeOn(Unit* target);
Creature* DoSummon(uint32 entry, Position const& pos, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
public:
void Talk(uint8 id, WorldObject const* whisperTarget = NULL);
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), m_MoveInLineOfSight_locked(false) {}
virtual ~CreatureAI() {}
/// == Reactions At =================================
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
void MoveInLineOfSight_Safe(Unit* who);
// Called in Creature::Update when deathstate = DEAD. Inherited classes may maniuplate the ability to respawn based on scripted events.
virtual bool CanRespawn() { return true; }
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode();
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
virtual void EnterCombat(Unit* /*victim*/) {}
// Called when the creature is killed
virtual void JustDied(Unit* /*killer*/) {}
// Called when the creature kills a unit
virtual void KilledUnit(Unit* /*victim*/) {}
// Called when the creature summon successfully other creature
virtual void JustSummoned(Creature* /*summon*/) {}
virtual void IsSummonedBy(Unit* /*summoner*/) {}
virtual void SummonedCreatureDespawn(Creature* /*summon*/) {}
virtual void SummonedCreatureDies(Creature* /*summon*/, Unit* /*killer*/) {}
// Called when hit by a spell
virtual void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) {}
// Called when spell hits a target
virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
// Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
virtual void AttackedBy(Unit* /*attacker*/) {}
virtual bool IsEscorted() { return false; }
// Called when creature is spawned or respawned (for reseting variables)
virtual void JustRespawned() { Reset(); }
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*type*/, uint32 /*id*/) {}
void OnCharmed(bool apply);
// Called at reaching home after evade
virtual void JustReachedHome() {}
void DoZoneInCombat(Creature* creature = NULL, float maxRangeToNearestTarget = 50.0f);
// Called at text emote receive from player
virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) {}
// Called when owner takes damage
virtual void OwnerAttackedBy(Unit* /*attacker*/) {}
// Called when owner attacks something
virtual void OwnerAttacked(Unit* /*target*/) {}
/// == Triggered Actions Requested ==================
// Called when creature attack expected (if creature can and no have current victim)
// Note: for reaction at hostile action must be called AttackedBy function.
//virtual void AttackStart(Unit*) {}
// Called at World update tick
//virtual void UpdateAI(uint32 /*diff*/) {}
/// == State checks =================================
// Is unit visible for MoveInLineOfSight
//virtual bool IsVisible(Unit*) const { return false; }
// called when the corpse of this creature gets removed
virtual void CorpseRemoved(uint32& /*respawnDelay*/) {}
// Called when victim entered water and creature can not enter water
//virtual bool CanReachByRangeAttack(Unit*) { return false; }
/// == Fields =======================================
virtual void PassengerBoarded(Unit* /*passenger*/, int8 /*seatId*/, bool /*apply*/) {}
virtual void OnSpellClick(Unit* /*clicker*/, bool& /*result*/) { }
virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; }
virtual bool CanBeSeen(Player const* /*seer*/) { return true; }
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
bool _EnterEvadeMode();
private:
bool m_MoveInLineOfSight_locked;
};
enum Permitions
{
PERMIT_BASE_NO = -1,
PERMIT_BASE_IDLE = 1,
PERMIT_BASE_REACTIVE = 100,
PERMIT_BASE_PROACTIVE = 200,
PERMIT_BASE_FACTION_SPECIFIC = 400,
PERMIT_BASE_SPECIAL = 800
};
#endif

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CREATUREAIFACTORY_H
#define TRINITY_CREATUREAIFACTORY_H
//#include "Policies/Singleton.h"
#include "ObjectRegistry.h"
#include "FactoryHolder.h"
#include "GameObjectAI.h"
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
{
SelectableAI(const char* id) : FactoryHolder<CreatureAI>(id) {}
};
template<class REAL_AI>
struct CreatureAIFactory : public SelectableAI
{
CreatureAIFactory(const char* name) : SelectableAI(name) {}
CreatureAI* Create(void*) const;
int Permit(const Creature* c) const { return REAL_AI::Permissible(c); }
};
template<class REAL_AI>
inline CreatureAI*
CreatureAIFactory<REAL_AI>::Create(void* data) const
{
Creature* creature = reinterpret_cast<Creature*>(data);
return (new REAL_AI(creature));
}
typedef FactoryHolder<CreatureAI> CreatureAICreator;
typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
typedef FactoryHolder<CreatureAI>::FactoryHolderRepository CreatureAIRepository;
//GO
struct SelectableGameObjectAI : public FactoryHolder<GameObjectAI>, public Permissible<GameObject>
{
SelectableGameObjectAI(const char* id) : FactoryHolder<GameObjectAI>(id) {}
};
template<class REAL_GO_AI>
struct GameObjectAIFactory : public SelectableGameObjectAI
{
GameObjectAIFactory(const char* name) : SelectableGameObjectAI(name) {}
GameObjectAI* Create(void*) const;
int Permit(const GameObject* g) const { return REAL_GO_AI::Permissible(g); }
};
template<class REAL_GO_AI>
inline GameObjectAI*
GameObjectAIFactory<REAL_GO_AI>::Create(void* data) const
{
GameObject* go = reinterpret_cast<GameObject*>(data);
return (new REAL_GO_AI(go));
}
typedef FactoryHolder<GameObjectAI> GameObjectAICreator;
typedef FactoryHolder<GameObjectAI>::FactoryHolderRegistry GameObjectAIRegistry;
typedef FactoryHolder<GameObjectAI>::FactoryHolderRepository GameObjectAIRepository;
#endif

View File

@@ -0,0 +1,347 @@
/*
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CREATUREAIIMPL_H
#define CREATUREAIIMPL_H
#include "Common.h"
#include "Define.h"
#include "TemporarySummon.h"
#include "CreatureAI.h"
#include "SpellMgr.h"
template<class T>
inline
const T& RAND(const T& v1, const T& v2)
{
return (urand(0, 1)) ? v1 : v2;
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3)
{
switch (urand(0, 2))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4)
{
switch (urand(0, 3))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
{
switch (urand(0, 4))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6)
{
switch (urand(0, 5))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7)
{
switch (urand(0, 6))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8)
{
switch (urand(0, 7))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9)
{
switch (urand(0, 8))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10)
{
switch (urand(0, 9))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11)
{
switch (urand(0, 10))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12)
{
switch (urand(0, 11))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13)
{
switch (urand(0, 12))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14)
{
switch (urand(0, 13))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15)
{
switch (urand(0, 14))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
case 14: return v15;
}
}
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
const T& v9, const T& v10, const T& v11, const T& v12, const T& v13, const T& v14, const T& v15, const T& v16)
{
switch (urand(0, 15))
{
default:
case 0: return v1;
case 1: return v2;
case 2: return v3;
case 3: return v4;
case 4: return v5;
case 5: return v6;
case 6: return v7;
case 7: return v8;
case 8: return v9;
case 9: return v10;
case 10: return v11;
case 11: return v12;
case 12: return v13;
case 13: return v14;
case 14: return v15;
case 15: return v16;
}
}
enum AITarget
{
AITARGET_SELF,
AITARGET_VICTIM,
AITARGET_ENEMY,
AITARGET_ALLY,
AITARGET_BUFF,
AITARGET_DEBUFF,
};
enum AICondition
{
AICOND_AGGRO,
AICOND_COMBAT,
AICOND_DIE,
};
#define AI_DEFAULT_COOLDOWN 5000
struct AISpellInfoType
{
AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT)
, cooldown(AI_DEFAULT_COOLDOWN), realCooldown(0), maxRange(0.0f){}
AITarget target;
AICondition condition;
uint32 cooldown;
uint32 realCooldown;
float maxRange;
};
AISpellInfoType* GetAISpellInfo(uint32 i);
#endif

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "PassiveAI.h"
#include "ReactorAI.h"
#include "CombatAI.h"
#include "GuardAI.h"
#include "PetAI.h"
#include "TotemAI.h"
#include "RandomMovementGenerator.h"
#include "MovementGeneratorImpl.h"
#include "CreatureAIRegistry.h"
#include "WaypointMovementGenerator.h"
#include "CreatureAIFactory.h"
#include "SmartAI.h"
//#include "CreatureAIImpl.h"
namespace AIRegistry
{
void Initialize()
{
(new CreatureAIFactory<NullCreatureAI>("NullCreatureAI"))->RegisterSelf();
(new CreatureAIFactory<TriggerAI>("TriggerAI"))->RegisterSelf();
(new CreatureAIFactory<AggressorAI>("AggressorAI"))->RegisterSelf();
(new CreatureAIFactory<ReactorAI>("ReactorAI"))->RegisterSelf();
(new CreatureAIFactory<PassiveAI>("PassiveAI"))->RegisterSelf();
(new CreatureAIFactory<CritterAI>("CritterAI"))->RegisterSelf();
(new CreatureAIFactory<GuardAI>("GuardAI"))->RegisterSelf();
(new CreatureAIFactory<PetAI>("PetAI"))->RegisterSelf();
(new CreatureAIFactory<TotemAI>("TotemAI"))->RegisterSelf();
(new CreatureAIFactory<CombatAI>("CombatAI"))->RegisterSelf();
(new CreatureAIFactory<ArcherAI>("ArcherAI"))->RegisterSelf();
(new CreatureAIFactory<TurretAI>("TurretAI"))->RegisterSelf();
(new CreatureAIFactory<VehicleAI>("VehicleAI"))->RegisterSelf();
(new CreatureAIFactory<SmartAI>("SmartAI"))->RegisterSelf();
(new GameObjectAIFactory<GameObjectAI>("GameObjectAI"))->RegisterSelf();
(new GameObjectAIFactory<SmartGameObjectAI>("SmartGameObjectAI"))->RegisterSelf();
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CREATUREAIREGISTRY_H
#define TRINITY_CREATUREAIREGISTRY_H
namespace AIRegistry
{
void Initialize(void);
}
#endif

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Creature.h"
#include "CreatureAISelector.h"
#include "PassiveAI.h"
#include "MovementGenerator.h"
#include "Pet.h"
#include "TemporarySummon.h"
#include "CreatureAIFactory.h"
#include "ScriptMgr.h"
namespace FactorySelector
{
CreatureAI* selectAI(Creature* creature)
{
const CreatureAICreator* ai_factory = NULL;
CreatureAIRegistry& ai_registry(*CreatureAIRepository::instance());
// xinef: if we have controlable guardian, define petai for players as they can steer him, otherwise db / normal ai
// xinef: dont remember why i changed this qq commented out as may break some quests
if (creature->IsPet()/* || (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)*/)
ai_factory = ai_registry.GetRegistryItem("PetAI");
//scriptname in db
if (!ai_factory)
if (CreatureAI* scriptedAI = sScriptMgr->GetCreatureAI(creature))
return scriptedAI;
// AIname in db
std::string ainame=creature->GetAIName();
if (!ai_factory && !ainame.empty())
ai_factory = ai_registry.GetRegistryItem(ainame);
// select by NPC flags
if (!ai_factory)
{
if (creature->IsVehicle())
ai_factory = ai_registry.GetRegistryItem("VehicleAI");
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)
ai_factory = ai_registry.GetRegistryItem("PetAI");
else if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
else if (creature->IsGuard())
ai_factory = ai_registry.GetRegistryItem("GuardAI");
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
ai_factory = ai_registry.GetRegistryItem("PetAI");
else if (creature->IsTotem())
ai_factory = ai_registry.GetRegistryItem("TotemAI");
else if (creature->IsTrigger())
{
if (creature->m_spells[0])
ai_factory = ai_registry.GetRegistryItem("TriggerAI");
else
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
}
else if (creature->IsCritter() && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
ai_factory = ai_registry.GetRegistryItem("CritterAI");
}
// select by permit check
if (!ai_factory)
{
int best_val = -1;
typedef CreatureAIRegistry::RegistryMapType RMT;
RMT const& l = ai_registry.GetRegisteredItems();
for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
{
const CreatureAICreator* factory = iter->second;
const SelectableAI* p = dynamic_cast<const SelectableAI*>(factory);
ASSERT(p);
int val = p->Permit(creature);
if (val > best_val)
{
best_val = val;
ai_factory = p;
}
}
}
// select NullCreatureAI if not another cases
// xinef: unused
// ainame = (ai_factory == NULL) ? "NullCreatureAI" : ai_factory->key();
;//sLog->outDebug(LOG_FILTER_TSCR, "Creature %u used AI is %s.", creature->GetGUIDLow(), ainame.c_str());
return (ai_factory == NULL ? new NullCreatureAI(creature) : ai_factory->Create(creature));
}
MovementGenerator* selectMovementGenerator(Creature* creature)
{
MovementGeneratorRegistry& mv_registry(*MovementGeneratorRepository::instance());
ASSERT(creature->GetCreatureTemplate());
const MovementGeneratorCreator* mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType());
/* if (mv_factory == NULL)
{
int best_val = -1;
StringVector l;
mv_registry.GetRegisteredItems(l);
for (StringVector::iterator iter = l.begin(); iter != l.end(); ++iter)
{
const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
ASSERT(p != NULL);
int val = p->Permit(creature);
if (val > best_val)
{
best_val = val;
mv_factory = p;
}
}
}*/
return (mv_factory == NULL ? NULL : mv_factory->Create(creature));
}
GameObjectAI* SelectGameObjectAI(GameObject* go)
{
const GameObjectAICreator* ai_factory = NULL;
GameObjectAIRegistry& ai_registry(*GameObjectAIRepository::instance());
if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
return scriptedAI;
ai_factory = ai_registry.GetRegistryItem(go->GetAIName());
//future goAI types go here
// xinef: unused
//std::string ainame = (ai_factory == NULL || go->GetScriptId()) ? "NullGameObjectAI" : ai_factory->key();
;//sLog->outDebug(LOG_FILTER_TSCR, "GameObject %u used AI is %s.", go->GetGUIDLow(), ainame.c_str());
return (ai_factory == NULL ? new NullGameObjectAI(go) : ai_factory->Create(go));
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_CREATUREAISELECTOR_H
#define TRINITY_CREATUREAISELECTOR_H
class CreatureAI;
class Creature;
class MovementGenerator;
class GameObjectAI;
class GameObject;
namespace FactorySelector
{
CreatureAI* selectAI(Creature*);
MovementGenerator* selectMovementGenerator(Creature*);
GameObjectAI* SelectGameObjectAI(GameObject*);
}
#endif

View File

@@ -0,0 +1,668 @@
/*
* Copyright (C)
*
*
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#include "ScriptedCreature.h"
#include "Item.h"
#include "Spell.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Cell.h"
#include "CellImpl.h"
#include "ObjectMgr.h"
#include "TemporarySummon.h"
// Spell summary for ScriptedAI::SelectSpell
struct TSpellSummary
{
uint8 Targets; // set of enum SelectTarget
uint8 Effects; // set of enum SelectEffect
} extern* SpellSummary;
void SummonList::DoZoneInCombat(uint32 entry)
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
++i;
if (summon && summon->IsAIEnabled
&& (!entry || summon->GetEntry() == entry))
{
summon->AI()->DoZoneInCombat();
}
}
}
void SummonList::DespawnEntry(uint32 entry)
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (!summon)
i = storage_.erase(i);
else if (summon->GetEntry() == entry)
{
i = storage_.erase(i);
summon->DespawnOrUnsummon();
}
else
++i;
}
}
void SummonList::DespawnAll()
{
while (!storage_.empty())
{
Creature* summon = ObjectAccessor::GetCreature(*me, storage_.front());
storage_.pop_front();
if (summon)
summon->DespawnOrUnsummon();
}
}
void SummonList::RemoveNotExisting()
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
if (ObjectAccessor::GetCreature(*me, *i))
++i;
else
i = storage_.erase(i);
}
}
bool SummonList::HasEntry(uint32 entry) const
{
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon && summon->GetEntry() == entry)
return true;
}
return false;
}
uint32 SummonList::GetEntryCount(uint32 entry) const
{
uint32 count = 0;
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon && summon->GetEntry() == entry)
++count;
}
return count;
}
void SummonList::Respawn()
{
for (StorageType::iterator i = storage_.begin(); i != storage_.end();)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
{
summon->Respawn(true);
++i;
}
else
i = storage_.erase(i);
}
}
Creature* SummonList::GetCreatureWithEntry(uint32 entry) const
{
for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
if (summon->GetEntry() == entry)
return summon;
}
return NULL;
}
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
me(creature),
IsFleeing(false),
_evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
{
_isHeroic = me->GetMap()->IsHeroic();
_difficulty = Difficulty(me->GetMap()->GetSpawnMode());
}
void ScriptedAI::AttackStartNoMove(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
DoStartNoMovement(who);
}
void ScriptedAI::AttackStart(Unit* who)
{
if (IsCombatMovementAllowed())
CreatureAI::AttackStart(who);
else
AttackStartNoMove(who);
}
void ScriptedAI::UpdateAI(uint32 /*diff*/)
{
//Check if we have a current target
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle)
{
if (victim)
me->GetMotionMaster()->MoveChase(victim, distance, angle);
}
void ScriptedAI::DoStartNoMovement(Unit* victim)
{
if (!victim)
return;
me->GetMotionMaster()->MoveIdle();
}
void ScriptedAI::DoStopAttack()
{
if (me->GetVictim())
me->AttackStop();
}
void ScriptedAI::DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered)
{
if (!target || me->IsNonMeleeSpellCast(false))
return;
me->StopMoving();
me->CastSpell(target, spellInfo, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
}
void ScriptedAI::DoPlaySoundToSet(WorldObject* source, uint32 soundId)
{
if (!source)
return;
if (!sSoundEntriesStore.LookupEntry(soundId))
{
sLog->outError("TSCR: Invalid soundId %u used in DoPlaySoundToSet (Source: TypeId %u, GUID %u)", soundId, source->GetTypeId(), source->GetGUIDLow());
return;
}
source->PlayDirectSound(soundId);
}
Creature* ScriptedAI::DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime)
{
return me->SummonCreature(entry, me->GetPositionX() + offsetX, me->GetPositionY() + offsetY, me->GetPositionZ() + offsetZ, angle, TempSummonType(type), despawntime);
}
SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effects)
{
//No target so we can't cast
if (!target)
return NULL;
//Silenced so we can't cast
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
return NULL;
//Using the extended script system we first create a list of viable spells
SpellInfo const* apSpell[CREATURE_MAX_SPELLS];
memset(apSpell, 0, CREATURE_MAX_SPELLS * sizeof(SpellInfo*));
uint32 spellCount = 0;
SpellInfo const* tempSpell = NULL;
//Check if each spell is viable(set it to null if not)
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++)
{
tempSpell = sSpellMgr->GetSpellInfo(me->m_spells[i]);
//This spell doesn't exist
if (!tempSpell)
continue;
// Targets and Effects checked first as most used restrictions
//Check the spell targets if specified
if (targets && !(SpellSummary[me->m_spells[i]].Targets & (1 << (targets-1))))
continue;
//Check the type of spell if we are looking for a specific spell type
if (effects && !(SpellSummary[me->m_spells[i]].Effects & (1 << (effects-1))))
continue;
//Check for school if specified
if (school && (tempSpell->SchoolMask & school) == 0)
continue;
//Check for spell mechanic if specified
if (mechanic && tempSpell->Mechanic != mechanic)
continue;
//Make sure that the spell uses the requested amount of power
if (powerCostMin && tempSpell->ManaCost < powerCostMin)
continue;
if (powerCostMax && tempSpell->ManaCost > powerCostMax)
continue;
//Continue if we don't have the mana to actually cast this spell
if (tempSpell->ManaCost > me->GetPower(Powers(tempSpell->PowerType)))
continue;
//Check if the spell meets our range requirements
if (rangeMin && me->GetSpellMinRangeForTarget(target, tempSpell) < rangeMin)
continue;
if (rangeMax && me->GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax)
continue;
//Check if our target is in range
if (me->IsWithinDistInMap(target, float(me->GetSpellMinRangeForTarget(target, tempSpell))) || !me->IsWithinDistInMap(target, float(me->GetSpellMaxRangeForTarget(target, tempSpell))))
continue;
//All good so lets add it to the spell list
apSpell[spellCount] = tempSpell;
++spellCount;
}
//We got our usable spells so now lets randomly pick one
if (!spellCount)
return NULL;
return apSpell[urand(0, spellCount - 1)];
}
void ScriptedAI::DoResetThreat()
{
if (!me->CanHaveThreatList() || me->getThreatManager().isThreatListEmpty())
{
sLog->outError("DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = %d)", me->GetEntry());
return;
}
me->getThreatManager().resetAllAggro();
}
float ScriptedAI::DoGetThreat(Unit* unit)
{
if (!unit)
return 0.0f;
return me->getThreatManager().getThreat(unit);
}
void ScriptedAI::DoModifyThreatPercent(Unit* unit, int32 pct)
{
if (!unit)
return;
me->getThreatManager().modifyThreatPercent(unit, pct);
}
void ScriptedAI::DoTeleportPlayer(Unit* unit, float x, float y, float z, float o)
{
if (!unit)
return;
if (Player* player = unit->ToPlayer())
player->TeleportTo(unit->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
else
sLog->outError("TSCR: Creature " UI64FMTD " (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: " UI64FMTD ") to x: %f y:%f z: %f o: %f. Aborted.", me->GetGUID(), me->GetEntry(), unit->GetTypeId(), unit->GetGUID(), x, y, z, o);
}
void ScriptedAI::DoTeleportAll(float x, float y, float z, float o)
{
Map* map = me->GetMap();
if (!map->IsDungeon())
return;
Map::PlayerList const& PlayerList = map->GetPlayers();
for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
if (Player* player = itr->GetSource())
if (player->IsAlive())
player->TeleportTo(me->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT);
}
Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff)
{
Unit* unit = NULL;
Trinity::MostHPMissingInRange u_check(me, range, minHPDiff);
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check);
me->VisitNearbyObject(range, searcher);
return unit;
}
std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float range)
{
std::list<Creature*> list;
Trinity::FriendlyCCedInRange u_check(me, range);
Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange> searcher(me, list, u_check);
me->VisitNearbyObject(range, searcher);
return list;
}
std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 uiSpellid)
{
std::list<Creature*> list;
Trinity::FriendlyMissingBuffInRange u_check(me, range, uiSpellid);
Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange> searcher(me, list, u_check);
me->VisitNearbyObject(range, searcher);
return list;
}
Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange)
{
Player* player = NULL;
CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
Cell cell(pair);
cell.SetNoCreate();
Trinity::PlayerAtMinimumRangeAway check(me, minimumRange);
Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway> searcher(me, player, check);
TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway>, GridTypeMapContainer> visitor(searcher);
cell.Visit(pair, visitor, *me->GetMap(), *me, minimumRange);
return player;
}
void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO_CHANGE*/, int32 offHand /*= EQUIP_NO_CHANGE*/, int32 ranged /*= EQUIP_NO_CHANGE*/)
{
if (loadDefault)
{
me->LoadEquipment(me->GetOriginalEquipmentId(), true);
return;
}
if (mainHand >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(mainHand));
if (offHand >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(offHand));
if (ranged >= 0)
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(ranged));
}
void ScriptedAI::SetCombatMovement(bool allowMovement)
{
_isCombatMovementAllowed = allowMovement;
}
enum eNPCs
{
NPC_BROODLORD = 12017,
NPC_JAN_ALAI = 23578,
NPC_SARTHARION = 28860,
NPC_FREYA = 32906,
};
bool ScriptedAI::EnterEvadeIfOutOfCombatArea()
{
if (me->IsInEvadeMode() || !me->IsInCombat())
return false;
if (_evadeCheckCooldown == time(NULL))
return false;
_evadeCheckCooldown = time(NULL);
if (!CheckEvadeIfOutOfCombatArea())
return false;
EnterEvadeMode();
return true;
}
Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura, bool mustBeInLOS) const
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
std::vector<Player*> tList;
for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
{
if (me->GetDistance(itr->GetSource()) > maxdist || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster())
continue;
if (excludeAura && itr->GetSource()->HasAura(excludeAura))
continue;
if (mustBeInLOS && !me->IsWithinLOSInMap(itr->GetSource()))
continue;
tList.push_back(itr->GetSource());
}
if (!tList.empty())
return tList[urand(0,tList.size()-1)];
else
return NULL;
}
// BossAI - for instanced bosses
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_boundary(instance ? instance->GetBossBoundary(bossId) : NULL),
_bossId(bossId)
{
}
void BossAI::_Reset()
{
if (!me->IsAlive())
return;
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
if (instance)
instance->SetBossState(_bossId, NOT_STARTED);
}
void BossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
if (instance)
{
instance->SetBossState(_bossId, DONE);
instance->SaveToDB();
}
}
void BossAI::_EnterCombat()
{
me->setActive(true);
DoZoneInCombat();
if (instance)
{
// bosses do not respawn, check only on enter combat
if (!instance->CheckRequiredBosses(_bossId))
{
EnterEvadeMode();
return;
}
instance->SetBossState(_bossId, IN_PROGRESS);
}
}
void BossAI::TeleportCheaters()
{
float x, y, z;
me->GetPosition(x, y, z);
ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->GetTypeId() == TYPEID_PLAYER && !CheckBoundary(target))
target->NearTeleportTo(x, y, z, 0);
}
bool BossAI::CheckBoundary(Unit* who)
{
if (!GetBoundary() || !who)
return true;
for (BossBoundaryMap::const_iterator itr = GetBoundary()->begin(); itr != GetBoundary()->end(); ++itr)
{
switch (itr->first)
{
case BOUNDARY_N:
if (who->GetPositionX() > itr->second)
return false;
break;
case BOUNDARY_S:
if (who->GetPositionX() < itr->second)
return false;
break;
case BOUNDARY_E:
if (who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_W:
if (who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_NW:
if (who->GetPositionX() + who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SE:
if (who->GetPositionX() + who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_NE:
if (who->GetPositionX() - who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SW:
if (who->GetPositionX() - who->GetPositionY() < itr->second)
return false;
break;
default:
break;
}
}
return true;
}
void BossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
if (me->IsInCombat())
DoZoneInCombat(summon);
}
void BossAI::SummonedCreatureDespawn(Creature* summon)
{
summons.Despawn(summon);
}
void BossAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
ExecuteEvent(eventId);
DoMeleeAttackIfReady();
}
// WorldBossAI - for non-instanced bosses
WorldBossAI::WorldBossAI(Creature* creature) :
ScriptedAI(creature),
summons(creature)
{
}
void WorldBossAI::_Reset()
{
if (!me->IsAlive())
return;
events.Reset();
summons.DespawnAll();
}
void WorldBossAI::_JustDied()
{
events.Reset();
summons.DespawnAll();
}
void WorldBossAI::_EnterCombat()
{
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
if (target)
AttackStart(target);
}
void WorldBossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true);
if (target)
summon->AI()->AttackStart(target);
}
void WorldBossAI::SummonedCreatureDespawn(Creature* summon)
{
summons.Despawn(summon);
}
void WorldBossAI::UpdateAI(uint32 diff)
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
ExecuteEvent(eventId);
DoMeleeAttackIfReady();
}
// SD2 grid searchers.
Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/)
{
return source->FindNearestCreature(entry, maxSearchRange, alive);
}
GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange)
{
return source->FindNearestGameObject(entry, maxSearchRange);
}
void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetCreatureListWithEntryInGrid(list, entry, maxSearchRange);
}
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange)
{
source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange);
}

View File

@@ -0,0 +1,457 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTEDCREATURE_H_
#define SCRIPTEDCREATURE_H_
#include "Creature.h"
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "InstanceScript.h"
#define CAST_AI(a, b) (dynamic_cast<a*>(b))
class InstanceScript;
class SummonList
{
public:
typedef std::list<uint64> StorageType;
typedef StorageType::iterator iterator;
typedef StorageType::const_iterator const_iterator;
typedef StorageType::size_type size_type;
typedef StorageType::value_type value_type;
explicit SummonList(Creature* creature)
: me(creature)
{ }
// And here we see a problem of original inheritance approach. People started
// to exploit presence of std::list members, so I have to provide wrappers
iterator begin()
{
return storage_.begin();
}
const_iterator begin() const
{
return storage_.begin();
}
iterator end()
{
return storage_.end();
}
const_iterator end() const
{
return storage_.end();
}
iterator erase(iterator i)
{
return storage_.erase(i);
}
bool empty() const
{
return storage_.empty();
}
size_type size() const
{
return storage_.size();
}
void clear()
{
storage_.clear();
}
void Summon(Creature const* summon) { storage_.push_back(summon->GetGUID()); }
void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); }
void DespawnEntry(uint32 entry);
void DespawnAll();
template <typename T>
void DespawnIf(T const &predicate)
{
storage_.remove_if(predicate);
}
void DoAction(int32 info, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
StorageType listCopy = storage_;
for (StorageType::const_iterator i = listCopy.begin(); i != listCopy.end(); ++i)
{
if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
if (summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
}
template <class Predicate>
void DoAction(int32 info, Predicate& predicate, uint16 max = 0)
{
if (max)
RemoveNotExisting(); // pussywizard: when max is set, non existing can be chosen and nothing will happen
// We need to use a copy of SummonList here, otherwise original SummonList would be modified
StorageType listCopy = storage_;
Trinity::Containers::RandomResizeList<uint64, Predicate>(listCopy, predicate, max);
for (StorageType::iterator i = listCopy.begin(); i != listCopy.end(); ++i)
{
Creature* summon = ObjectAccessor::GetCreature(*me, *i);
if (summon)
{
if (summon->IsAIEnabled)
summon->AI()->DoAction(info);
}
else
storage_.remove(*i);
}
}
void DoZoneInCombat(uint32 entry = 0);
void RemoveNotExisting();
bool HasEntry(uint32 entry) const;
uint32 GetEntryCount(uint32 entry) const;
void Respawn();
Creature* GetCreatureWithEntry(uint32 entry) const;
private:
Creature* me;
StorageType storage_;
};
class EntryCheckPredicate
{
public:
EntryCheckPredicate(uint32 entry) : _entry(entry) {}
bool operator()(uint64 guid) { return GUID_ENPART(guid) == _entry; }
private:
uint32 _entry;
};
class PlayerOrPetCheck
{
public:
bool operator() (WorldObject* unit) const
{
if (unit->GetTypeId() != TYPEID_PLAYER)
if (!IS_PLAYER_GUID(unit->ToUnit()->GetOwnerGUID()))
return true;
return false;
}
};
struct ScriptedAI : public CreatureAI
{
explicit ScriptedAI(Creature* creature);
virtual ~ScriptedAI() {}
// *************
//CreatureAI Functions
// *************
void AttackStartNoMove(Unit* target);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) {}
//Called at World update tick
virtual void UpdateAI(uint32 diff);
//Called at creature death
void JustDied(Unit* /*killer*/) {}
//Called at creature killing another unit
void KilledUnit(Unit* /*victim*/) {}
// Called when the creature summon successfully other creature
void JustSummoned(Creature* /*summon*/) {}
// Called when a summoned creature is despawned
void SummonedCreatureDespawn(Creature* /*summon*/) {}
// Called when hit by a spell
void SpellHit(Unit* /*caster*/, SpellInfo const* /*spell*/) {}
// Called when spell hits a target
void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
//Called at waypoint reached or PointMovement end
void MovementInform(uint32 /*type*/, uint32 /*id*/) {}
// Called when AI is temporarily replaced or put back when possess is applied or removed
void OnPossess(bool /*apply*/) {}
// *************
// Variables
// *************
//Pointer to creature we are manipulating
Creature* me;
//For fleeing
bool IsFleeing;
// *************
//Pure virtual functions
// *************
//Called at creature reset either by death or evade
void Reset() {}
//Called at creature aggro either by MoveInLOS or Attack Start
void EnterCombat(Unit* /*victim*/) {}
// Called before EnterCombat even before the creature is in combat.
void AttackStart(Unit* /*target*/);
// *************
//AI Helper Functions
// *************
//Start movement toward victim
void DoStartMovement(Unit* target, float distance = 0.0f, float angle = 0.0f);
//Start no movement on victim
void DoStartNoMovement(Unit* target);
//Stop attack of current victim
void DoStopAttack();
//Cast spell by spell info
void DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered = false);
//Plays a sound to all nearby players
void DoPlaySoundToSet(WorldObject* source, uint32 soundId);
//Drops all threat to 0%. Does not remove players from the threat list
void DoResetThreat();
float DoGetThreat(Unit* unit);
void DoModifyThreatPercent(Unit* unit, int32 pct);
//Teleports a player without dropping threat (only teleports to same map)
void DoTeleportPlayer(Unit* unit, float x, float y, float z, float o);
void DoTeleportAll(float x, float y, float z, float o);
//Returns friendly unit with the most amount of hp missing from max hp
Unit* DoSelectLowestHpFriendly(float range, uint32 minHPDiff = 1);
//Returns a list of friendly CC'd units within range
std::list<Creature*> DoFindFriendlyCC(float range);
//Returns a list of all friendly units missing a specific buff within range
std::list<Creature*> DoFindFriendlyMissingBuff(float range, uint32 spellId);
//Return a player with at least minimumRange from me
Player* GetPlayerAtMinimumRange(float minRange);
//Spawns a creature relative to me
Creature* DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime);
bool HealthBelowPct(uint32 pct) const { return me->HealthBelowPct(pct); }
bool HealthAbovePct(uint32 pct) const { return me->HealthAbovePct(pct); }
//Returns spells that meet the specified criteria from the creatures spell list
SpellInfo const* SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effect);
void SetEquipmentSlots(bool loadDefault, int32 mainHand = EQUIP_NO_CHANGE, int32 offHand = EQUIP_NO_CHANGE, int32 ranged = EQUIP_NO_CHANGE);
// Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
// NOTE: If you use SetCombatMovement while the creature is in combat, it will do NOTHING - This only affects AttackStart
// You should make the necessary to make it happen so.
// Remember that if you modified _isCombatMovementAllowed (e.g: using SetCombatMovement) it will not be reset at Reset().
// It will keep the last value you set.
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
bool EnterEvadeIfOutOfCombatArea();
virtual bool CheckEvadeIfOutOfCombatArea() const { return false; }
// return true for heroic mode. i.e.
// - for dungeon in mode 10-heroic,
// - for raid in mode 10-Heroic
// - for raid in mode 25-heroic
// DO NOT USE to check raid in mode 25-normal.
bool IsHeroic() const { return _isHeroic; }
// return the dungeon or raid difficulty
Difficulty GetDifficulty() const { return _difficulty; }
// return true for 25 man or 25 man heroic mode
bool Is25ManRaid() const { return _difficulty & RAID_DIFFICULTY_MASK_25MAN; }
template<class T> inline
const T& DUNGEON_MODE(const T& normal5, const T& heroic10) const
{
switch (_difficulty)
{
case DUNGEON_DIFFICULTY_NORMAL:
return normal5;
case DUNGEON_DIFFICULTY_HEROIC:
return heroic10;
default:
break;
}
return heroic10;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
default:
break;
}
return normal25;
}
template<class T> inline
const T& RAID_MODE(const T& normal10, const T& normal25, const T& heroic10, const T& heroic25) const
{
switch (_difficulty)
{
case RAID_DIFFICULTY_10MAN_NORMAL:
return normal10;
case RAID_DIFFICULTY_25MAN_NORMAL:
return normal25;
case RAID_DIFFICULTY_10MAN_HEROIC:
return heroic10;
case RAID_DIFFICULTY_25MAN_HEROIC:
return heroic25;
default:
break;
}
return heroic25;
}
Player* SelectTargetFromPlayerList(float maxdist, uint32 excludeAura = 0, bool mustBeInLOS = false) const;
private:
Difficulty _difficulty;
uint32 _evadeCheckCooldown;
bool _isCombatMovementAllowed;
bool _isHeroic;
};
class BossAI : public ScriptedAI
{
public:
BossAI(Creature* creature, uint32 bossId);
virtual ~BossAI() {}
InstanceScript* const instance;
BossBoundaryMap const* GetBoundary() const { return _boundary; }
void JustSummoned(Creature* summon);
void SummonedCreatureDespawn(Creature* summon);
virtual void UpdateAI(uint32 diff);
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
void Reset() { _Reset(); }
void EnterCombat(Unit* /*who*/) { _EnterCombat(); }
void JustDied(Unit* /*killer*/) { _JustDied(); }
void JustReachedHome() { _JustReachedHome(); }
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
bool CheckInRoom()
{
if (CheckBoundary(me))
return true;
EnterEvadeMode();
return false;
}
bool CheckBoundary(Unit* who);
void TeleportCheaters();
EventMap events;
SummonList summons;
private:
BossBoundaryMap const* const _boundary;
uint32 const _bossId;
};
class WorldBossAI : public ScriptedAI
{
public:
WorldBossAI(Creature* creature);
virtual ~WorldBossAI() {}
void JustSummoned(Creature* summon);
void SummonedCreatureDespawn(Creature* summon);
virtual void UpdateAI(uint32 diff);
// Hook used to execute events scheduled into EventMap without the need
// to override UpdateAI
// note: You must re-schedule the event within this method if the event
// is supposed to run more than once
virtual void ExecuteEvent(uint32 /*eventId*/) { }
void Reset() { _Reset(); }
void EnterCombat(Unit* /*who*/) { _EnterCombat(); }
void JustDied(Unit* /*killer*/) { _JustDied(); }
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
EventMap events;
SummonList summons;
};
// SD2 grid searchers.
Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive = true);
GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange);
void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange);
#endif // SCRIPTEDCREATURE_H_

View File

@@ -0,0 +1,584 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
/* ScriptData
SDName: Npc_EscortAI
SD%Complete: 100
SDComment:
SDCategory: Npc
EndScriptData */
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "Group.h"
#include "Player.h"
enum ePoints
{
POINT_LAST_POINT = 0xFFFFFF,
POINT_HOME = 0xFFFFFE
};
npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
m_uiPlayerGUID(0),
m_uiWPWaitTimer(1000),
m_uiPlayerCheckTimer(0),
m_uiEscortState(STATE_ESCORT_NONE),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
m_pQuestForEscort(NULL),
m_bIsActiveAttacker(true),
m_bIsRunning(false),
m_bCanInstantRespawn(false),
m_bCanReturnToStart(false),
DespawnAtEnd(true),
DespawnAtFar(true),
ScriptWP(false),
HasImmuneToNPCFlags(false)
{}
void npc_escortAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
{
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
if (type == ESCORT_MOTION_TYPE || type == POINT_MOTION_TYPE)
{
me->GetMotionMaster()->MovementExpired();
//me->DisableSpline();
me->StopMoving();
}
if (IsCombatMovementAllowed())
me->GetMotionMaster()->MoveChase(who);
}
}
//see followerAI
bool npc_escortAI::AssistPlayerInCombat(Unit* who)
{
if (!who || !who->GetVictim())
return false;
//experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
return false;
//not a player
if (!who->GetVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
//never attack friendly
if (!me->IsValidAttackTarget(who))
return false;
//too far away and no free sight?
if (me->IsWithinDistInMap(who, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who))
{
AttackStart(who);
return true;
}
return false;
}
void npc_escortAI::MoveInLineOfSight(Unit* who)
{
if (me->GetVictim())
return;
if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack(true, me) && who->isInAccessiblePlaceFor(me))
if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(who))
return;
if (me->CanStartAttack(who))
AttackStart(who);
}
void npc_escortAI::JustDied(Unit* /*killer*/)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
return;
if (Player* player = GetPlayerForEscort())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
if (Player* member = groupRef->GetSource())
if (member->IsInMap(player) && member->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
member->FailQuest(m_pQuestForEscort->GetQuestId());
}
else
{
if (player->GetQuestStatus(m_pQuestForEscort->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(m_pQuestForEscort->GetQuestId());
}
}
}
void npc_escortAI::JustRespawned()
{
RemoveEscortState(STATE_ESCORT_ESCORTING|STATE_ESCORT_RETURNING|STATE_ESCORT_PAUSED);
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
//add a small delay before going to first waypoint, normal in near all cases
m_uiWPWaitTimer = 1000;
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
Reset();
}
void npc_escortAI::ReturnToLastPoint()
{
float x, y, z, o;
me->SetWalk(false);
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
void npc_escortAI::EnterEvadeMode()
{
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
me->SetLootRecipient(NULL);
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
AddEscortState(STATE_ESCORT_RETURNING);
ReturnToLastPoint();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has left combat and is now returning to last point");
}
else
{
me->GetMotionMaster()->MoveTargetedHome();
if (HasImmuneToNPCFlags)
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
Reset();
}
}
bool npc_escortAI::IsPlayerOrGroupInRange()
{
if (Player* player = GetPlayerForEscort())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
if (Player* member = groupRef->GetSource())
if (me->IsWithinDistInMap(member, GetMaxPlayerDistance()))
return true;
}
else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance()))
return true;
}
return false;
}
void npc_escortAI::UpdateAI(uint32 diff)
{
if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING))
{
if (m_uiWPWaitTimer <= diff)
{
if (CurrentWP == WaypointList.end())
{
if (DespawnAtEnd)
{
if (m_bCanReturnToStart)
{
float fRetX, fRetY, fRetZ;
me->GetRespawnPosition(fRetX, fRetY, fRetZ);
me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
m_uiWPWaitTimer = 0;
return;
}
if (m_bCanInstantRespawn)
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
else
me->DespawnOrUnsummon();
}
// xinef: remove escort state, escort was finished (lack of this line resulted in skipping UpdateEscortAI calls after finished escort)
RemoveEscortState(STATE_ESCORT_ESCORTING);
return;
}
if (!HasEscortState(STATE_ESCORT_PAUSED))
{
// xinef, start escort if there is no spline active
if (me->movespline->Finalized())
{
Movement::PointsArray pathPoints;
GenerateWaypointArray(&pathPoints);
me->GetMotionMaster()->MoveSplinePath(&pathPoints);
}
WaypointStart(CurrentWP->id);
m_uiWPWaitTimer = 0;
}
}
else
m_uiWPWaitTimer -= diff;
}
//Check if player or any member of his group is within range
if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
m_uiPlayerCheckTimer += diff;
if (m_uiPlayerCheckTimer > 1000)
{
if (DespawnAtFar && !IsPlayerOrGroupInRange())
{
if (m_bCanInstantRespawn)
{
me->setDeathState(JUST_DIED);
me->Respawn();
}
else
me->DespawnOrUnsummon();
return;
}
m_uiPlayerCheckTimer = 0;
}
}
UpdateEscortAI(diff);
}
void npc_escortAI::UpdateEscortAI(uint32 /*diff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId)
{
// xinef: no action allowed if there is no escort
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
if (moveType == POINT_MOTION_TYPE)
{
//Combat start position reached, continue waypoint movement
if (pointId == POINT_LAST_POINT)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has returned to original position before combat");
me->SetWalk(!m_bIsRunning);
RemoveEscortState(STATE_ESCORT_RETURNING);
if (!m_uiWPWaitTimer)
m_uiWPWaitTimer = 1;
}
else if (pointId == POINT_HOME)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI has returned to original home location and will continue from beginning of waypoint list.");
CurrentWP = WaypointList.begin();
m_uiWPWaitTimer = 1;
}
}
else if (moveType == ESCORT_MOTION_TYPE)
{
if (m_uiWPWaitTimer <= 1 && !HasEscortState(STATE_ESCORT_PAUSED) && CurrentWP != WaypointList.end())
{
//Call WP function
me->SetPosition(CurrentWP->x, CurrentWP->y, CurrentWP->z, me->GetOrientation());
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
WaypointReached(CurrentWP->id);
m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
++CurrentWP;
if (m_uiWPWaitTimer > 1 || HasEscortState(STATE_ESCORT_PAUSED))
{
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->StopMovingOnCurrentPos();
me->GetMotionMaster()->MoveIdle();
}
}
}
}
/*
void npc_escortAI::OnPossess(bool apply)
{
// We got possessed in the middle of being escorted, store the point
// where we left off to come back to when possess is removed
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
if (apply)
me->GetPosition(LastPos.x, LastPos.y, LastPos.z);
else
{
Returning = true;
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z);
}
}
}
*/
void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime)
{
Escort_Waypoint t(id, x, y, z, waitTime);
WaypointList.push_back(t);
// i think SD2 no longer uses this function
ScriptWP = true;
/*PointMovement wp;
wp.m_uiCreatureEntry = me->GetEntry();
wp.m_uiPointId = id;
wp.m_fX = x;
wp.m_fY = y;
wp.m_fZ = z;
wp.m_uiWaitTime = WaitTimeMs;
PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
}
void npc_escortAI::FillPointMovementListForCreature()
{
ScriptPointVector const& movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
if (movePoints.empty())
return;
ScriptPointVector::const_iterator itrEnd = movePoints.end();
for (ScriptPointVector::const_iterator itr = movePoints.begin(); itr != itrEnd; ++itr)
{
Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
WaypointList.push_back(point);
}
}
void npc_escortAI::SetRun(bool on)
{
if (on)
{
if (!m_bIsRunning)
me->SetWalk(false);
else
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI attempt to set run mode, but is already running.");
}
else
{
if (m_bIsRunning)
me->SetWalk(true);
else
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI attempt to set walk mode, but is already walking.");
}
m_bIsRunning = on;
}
//TODO: get rid of this many variables passed in function.
void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, uint64 playerGUID /* = 0 */, Quest const* quest /* = NULL */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
{
if (me->GetVictim())
{
sLog->outError("TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry());
return;
}
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
sLog->outError("TSCR: EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry());
return;
}
if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does
{
if (!WaypointList.empty())
WaypointList.clear();
FillPointMovementListForCreature();
}
if (WaypointList.empty())
{
sLog->outErrorDb("TSCR: EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).",
me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0);
return;
}
//set variables
m_bIsActiveAttacker = isActiveAttacker;
m_bIsRunning = run;
m_uiPlayerGUID = playerGUID;
m_pQuestForEscort = quest;
m_bCanInstantRespawn = instantRespawn;
m_bCanReturnToStart = canLoopPath;
//if (m_bCanReturnToStart && m_bCanInstantRespawn)
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveIdle();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
//disable npcflags
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC))
{
HasImmuneToNPCFlags = true;
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC);
}
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = " UI64FMTD "", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID);
CurrentWP = WaypointList.begin();
//Set initial speed
if (m_bIsRunning)
me->SetWalk(false);
else
me->SetWalk(true);
AddEscortState(STATE_ESCORT_ESCORTING);
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->DisableSpline();
me->GetMotionMaster()->MoveIdle();
}
void npc_escortAI::SetEscortPaused(bool on)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
if (on)
AddEscortState(STATE_ESCORT_PAUSED);
else
RemoveEscortState(STATE_ESCORT_PAUSED);
}
bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation)
{
me->UpdatePosition(x, y, z, orientation);
return SetNextWaypoint(pointId, false);
}
bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition)
{
if (!WaypointList.empty())
WaypointList.clear();
FillPointMovementListForCreature();
if (WaypointList.empty())
return false;
size_t const size = WaypointList.size();
Escort_Waypoint waypoint(0, 0, 0, 0, 0);
for (CurrentWP = WaypointList.begin(); CurrentWP != WaypointList.end(); ++CurrentWP)
{
if (CurrentWP->id == pointId)
{
if (setPosition)
me->UpdatePosition(CurrentWP->x, CurrentWP->y, CurrentWP->z, me->GetOrientation());
return true;
}
}
return false;
}
bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z)
{
ScriptPointVector const& waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry());
if (waypoints.empty())
return false;
for (ScriptPointVector::const_iterator itr = waypoints.begin(); itr != waypoints.end(); ++itr)
{
if (itr->uiPointId == pointId)
{
x = itr->fX;
y = itr->fY;
z = itr->fZ;
return true;
}
}
return false;
}
void npc_escortAI::GenerateWaypointArray(Movement::PointsArray* points)
{
if (WaypointList.empty())
return;
uint32 startingWaypointId = CurrentWP->id;
// Flying unit, just fill array
if (me->m_movementInfo.HasMovementFlag((MovementFlags)(MOVEMENTFLAG_CAN_FLY|MOVEMENTFLAG_DISABLE_GRAVITY)))
{
// xinef: first point in vector is unit real position
points->clear();
points->push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
for (std::list<Escort_Waypoint>::const_iterator itr = CurrentWP; itr != WaypointList.end(); ++itr)
points->push_back(G3D::Vector3(itr->x, itr->y, itr->z));
}
else
{
for (float size = 1.0f; size; size *= 0.5f)
{
std::vector<G3D::Vector3> pVector;
// xinef: first point in vector is unit real position
pVector.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
uint32 length = (WaypointList.size() - startingWaypointId)*size;
uint32 cnt = 0;
for (std::list<Escort_Waypoint>::const_iterator itr = CurrentWP; itr != WaypointList.end() && cnt <= length; ++itr, ++cnt)
pVector.push_back(G3D::Vector3(itr->x, itr->y, itr->z));
if (pVector.size() > 2) // more than source + dest
{
G3D::Vector3 middle = (pVector[0] + pVector[pVector.size()-1]) / 2.f;
G3D::Vector3 offset;
bool continueLoop = false;
for (uint32 i = 1; i < pVector.size()-1; ++i)
{
offset = middle - pVector[i];
if (fabs(offset.x) >= 0xFF || fabs(offset.y) >= 0xFF || fabs(offset.z) >= 0x7F)
{
// offset is too big, split points
continueLoop = true;
break;
}
}
if (continueLoop)
continue;
}
// everything ok
*points = pVector;
break;
}
}
}

View File

@@ -0,0 +1,128 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_ESCORTAI_H
#define SC_ESCORTAI_H
#include "ScriptSystem.h"
#define DEFAULT_MAX_PLAYER_DISTANCE 50
struct Escort_Waypoint
{
Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
{
id = _id;
x = _x;
y = _y;
z = _z;
WaitTimeMs = _w;
}
uint32 id;
float x;
float y;
float z;
uint32 WaitTimeMs;
};
enum eEscortState
{
STATE_ESCORT_NONE = 0x000, //nothing in progress
STATE_ESCORT_ESCORTING = 0x001, //escort are in progress
STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
struct npc_escortAI : public ScriptedAI
{
public:
explicit npc_escortAI(Creature* creature);
~npc_escortAI() {}
// CreatureAI functions
void AttackStart(Unit* who);
void MoveInLineOfSight(Unit* who);
void JustDied(Unit*);
void JustRespawned();
void ReturnToLastPoint();
void EnterEvadeMode();
void UpdateAI(uint32 diff); //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); //used when it's needed to add code in update (abilities, scripted events, etc)
void MovementInform(uint32, uint32);
// EscortAI functions
void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms
//this will set the current position to x/y/z/o, and the current WP to pointId.
bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation);
//this will set the current position to WP start position (if setPosition == true),
//and the current WP to pointId
bool SetNextWaypoint(uint32 pointId, bool setPosition = true);
bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z);
void GenerateWaypointArray(Movement::PointsArray* points);
virtual void WaypointReached(uint32 pointId) = 0;
virtual void WaypointStart(uint32 /*pointId*/) {}
void Start(bool isActiveAttacker = true, bool run = false, uint64 playerGUID = 0, Quest const* quest = NULL, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true);
void SetRun(bool on = true);
void SetEscortPaused(bool on);
bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState); }
virtual bool IsEscorted() { return (m_uiEscortState & STATE_ESCORT_ESCORTING); }
void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
float GetMaxPlayerDistance() { return MaxPlayerDistance; }
void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
uint64 GetEventStarterGUID() { return m_uiPlayerGUID; }
void AddEscortState(uint32 escortState) { m_uiEscortState |= escortState; }
void RemoveEscortState(uint32 escortState) { m_uiEscortState &= ~escortState; }
protected:
Player* GetPlayerForEscort() { return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID); }
private:
bool AssistPlayerInCombat(Unit* who);
bool IsPlayerOrGroupInRange();
void FillPointMovementListForCreature();
uint64 m_uiPlayerGUID;
uint32 m_uiWPWaitTimer;
uint32 m_uiPlayerCheckTimer;
uint32 m_uiEscortState;
float MaxPlayerDistance;
Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script.
std::list<Escort_Waypoint> WaypointList;
std::list<Escort_Waypoint>::iterator CurrentWP;
bool m_bIsActiveAttacker; //obsolete, determined by faction.
bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests.
bool DespawnAtEnd;
bool DespawnAtFar;
bool ScriptWP;
bool HasImmuneToNPCFlags;
};
#endif

View File

@@ -0,0 +1,362 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
/* ScriptData
SDName: FollowerAI
SD%Complete: 50
SDComment: This AI is under development
SDCategory: Npc
EndScriptData */
#include "ScriptedCreature.h"
#include "ScriptedFollowerAI.h"
#include "Group.h"
#include "Player.h"
const float MAX_PLAYER_DISTANCE = 100.0f;
enum ePoints
{
POINT_COMBAT_START = 0xFFFFFF
};
FollowerAI::FollowerAI(Creature* creature) : ScriptedAI(creature),
m_uiLeaderGUID(0),
m_uiUpdateFollowTimer(2500),
m_uiFollowState(STATE_FOLLOW_NONE),
m_pQuestForFollow(NULL)
{}
void FollowerAI::AttackStart(Unit* who)
{
if (!who)
return;
if (me->Attack(who, true))
{
// This is done in Unit::Attack function which wont bug npcs by not adding threat upon combat start...
//me->AddThreat(who, 0.0f);
//me->SetInCombatWith(who);
//who->SetInCombatWith(me);
if (me->HasUnitState(UNIT_STATE_FOLLOW))
me->ClearUnitState(UNIT_STATE_FOLLOW);
if (IsCombatMovementAllowed())
me->GetMotionMaster()->MoveChase(who);
}
}
//This part provides assistance to a player that are attacked by who, even if out of normal aggro range
//It will cause me to attack who that are attacking _any_ player (which has been confirmed may happen also on offi)
//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
bool FollowerAI::AssistPlayerInCombat(Unit* who)
{
if (!who || !who->GetVictim())
return false;
//experimental (unknown) flag not present
if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS))
return false;
//not a player
if (!who->GetVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
//never attack friendly
if (me->IsFriendlyTo(who))
return false;
//too far away and no free sight?
if (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && me->IsWithinLOSInMap(who))
{
AttackStart(who);
return true;
}
return false;
}
void FollowerAI::MoveInLineOfSight(Unit* who)
{
if (me->GetVictim())
return;
if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack(true, me) && who->isInAccessiblePlaceFor(me))
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who))
return;
if (me->CanStartAttack(who))
AttackStart(who);
}
void FollowerAI::JustDied(Unit* /*pKiller*/)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
return;
//TODO: need a better check for quests with time limit.
if (Player* player = GetLeaderForFollower())
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
if (Player* member = groupRef->GetSource())
{
if (member->IsInMap(player) && member->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
member->FailQuest(m_pQuestForFollow->GetQuestId());
}
}
}
else
{
if (player->GetQuestStatus(m_pQuestForFollow->GetQuestId()) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(m_pQuestForFollow->GetQuestId());
}
}
}
void FollowerAI::JustRespawned()
{
m_uiFollowState = STATE_FOLLOW_NONE;
if (!IsCombatMovementAllowed())
SetCombatMovement(true);
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->setFaction(me->GetCreatureTemplate()->faction);
Reset();
}
void FollowerAI::EnterEvadeMode()
{
me->RemoveAllAuras();
me->DeleteThreatList();
me->CombatStop(true);
me->SetLootRecipient(NULL);
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI left combat, returning to CombatStartPosition.");
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
{
float fPosX, fPosY, fPosZ;
me->GetPosition(fPosX, fPosY, fPosZ);
me->GetMotionMaster()->MovePoint(POINT_COMBAT_START, fPosX, fPosY, fPosZ);
}
}
else
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->MoveTargetedHome();
}
Reset();
}
void FollowerAI::UpdateAI(uint32 uiDiff)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !me->GetVictim())
{
if (m_uiUpdateFollowTimer <= uiDiff)
{
if (HasFollowState(STATE_FOLLOW_COMPLETE) && !HasFollowState(STATE_FOLLOW_POSTEVENT))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is set completed, despawns.");
me->DespawnOrUnsummon();
return;
}
bool bIsMaxRangeExceeded = true;
if (Player* player = GetLeaderForFollower())
{
if (HasFollowState(STATE_FOLLOW_RETURNING))
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI is returning to leader.");
RemoveFollowState(STATE_FOLLOW_RETURNING);
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
return;
}
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
Player* member = groupRef->GetSource();
if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE))
{
bIsMaxRangeExceeded = false;
break;
}
}
}
else
{
if (me->IsWithinDistInMap(player, MAX_PLAYER_DISTANCE))
bIsMaxRangeExceeded = false;
}
}
if (bIsMaxRangeExceeded)
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI failed because player/group was to far away or not found");
me->DespawnOrUnsummon();
return;
}
m_uiUpdateFollowTimer = 1000;
}
else
m_uiUpdateFollowTimer -= uiDiff;
}
UpdateFollowerAI(uiDiff);
}
void FollowerAI::UpdateFollowerAI(uint32 /*uiDiff*/)
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
void FollowerAI::MovementInform(uint32 motionType, uint32 pointId)
{
if (motionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
return;
if (pointId == POINT_COMBAT_START)
{
if (GetLeaderForFollower())
{
if (!HasFollowState(STATE_FOLLOW_PAUSED))
AddFollowState(STATE_FOLLOW_RETURNING);
}
else
me->DespawnOrUnsummon();
}
}
void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, const Quest* quest)
{
if (me->GetVictim())
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI attempt to StartFollow while in combat.");
return;
}
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
sLog->outError("TSCR: FollowerAI attempt to StartFollow while already following.");
return;
}
//set variables
m_uiLeaderGUID = player->GetGUID();
if (factionForFollower)
me->setFaction(factionForFollower);
m_pQuestForFollow = quest;
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
}
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
AddFollowState(STATE_FOLLOW_INPROGRESS);
me->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI start follow %s (GUID " UI64FMTD ")", player->GetName().c_str(), m_uiLeaderGUID);
}
Player* FollowerAI::GetLeaderForFollower()
{
if (Player* player = ObjectAccessor::GetPlayer(*me, m_uiLeaderGUID))
{
if (player->IsAlive())
return player;
else
{
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != NULL; groupRef = groupRef->next())
{
Player* member = groupRef->GetSource();
if (member && me->IsWithinDistInMap(member, MAX_PLAYER_DISTANCE) && member->IsAlive())
{
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader changed and returned new leader.");
m_uiLeaderGUID = member->GetGUID();
return member;
}
}
}
}
}
;//sLog->outDebug(LOG_FILTER_TSCR, "TSCR: FollowerAI GetLeader can not find suitable leader.");
return NULL;
}
void FollowerAI::SetFollowComplete(bool bWithEndEvent)
{
if (me->HasUnitState(UNIT_STATE_FOLLOW))
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
if (bWithEndEvent)
AddFollowState(STATE_FOLLOW_POSTEVENT);
else
{
if (HasFollowState(STATE_FOLLOW_POSTEVENT))
RemoveFollowState(STATE_FOLLOW_POSTEVENT);
}
AddFollowState(STATE_FOLLOW_COMPLETE);
}
void FollowerAI::SetFollowPaused(bool paused)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
return;
if (paused)
{
AddFollowState(STATE_FOLLOW_PAUSED);
if (me->HasUnitState(UNIT_STATE_FOLLOW))
{
me->ClearUnitState(UNIT_STATE_FOLLOW);
me->StopMoving();
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
}
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
if (Player* leader = GetLeaderForFollower())
me->GetMotionMaster()->MoveFollow(leader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
}

View File

@@ -0,0 +1,67 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_FOLLOWERAI_H
#define SC_FOLLOWERAI_H
#include "ScriptSystem.h"
enum eFollowState
{
STATE_FOLLOW_NONE = 0x000,
STATE_FOLLOW_INPROGRESS = 0x001, //must always have this state for any follow
STATE_FOLLOW_RETURNING = 0x002, //when returning to combat start after being in combat
STATE_FOLLOW_PAUSED = 0x004, //disables following
STATE_FOLLOW_COMPLETE = 0x008, //follow is completed and may end
STATE_FOLLOW_PREEVENT = 0x010, //not implemented (allow pre event to run, before follow is initiated)
STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run
};
class FollowerAI : public ScriptedAI
{
public:
explicit FollowerAI(Creature* creature);
~FollowerAI() {}
//virtual void WaypointReached(uint32 uiPointId) = 0;
void MovementInform(uint32 motionType, uint32 pointId);
void AttackStart(Unit*);
void MoveInLineOfSight(Unit*);
void EnterEvadeMode();
void JustDied(Unit*);
void JustRespawned();
void UpdateAI(uint32); //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
void StartFollow(Player* player, uint32 factionForFollower = 0, const Quest* quest = NULL);
void SetFollowPaused(bool bPaused); //if special event require follow mode to hold/resume during the follow
void SetFollowComplete(bool bWithEndEvent = false);
bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); }
protected:
Player* GetLeaderForFollower();
private:
void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; }
bool AssistPlayerInCombat(Unit* who);
uint64 m_uiLeaderGUID;
uint32 m_uiUpdateFollowTimer;
uint32 m_uiFollowState;
const Quest* m_pQuestForFollow; //normally we have a quest
};
#endif

View File

@@ -0,0 +1,90 @@
/* Copyright (C)
*
*
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
#ifndef SC_GOSSIP_H
#define SC_GOSSIP_H
#include "GossipDef.h"
#include "QuestDef.h"
// Gossip Item Text
#define GOSSIP_TEXT_BROWSE_GOODS "I'd like to browse your goods."
#define GOSSIP_TEXT_TRAIN "Train me!"
enum eTradeskill
{
// Skill defines
TRADESKILL_ALCHEMY = 1,
TRADESKILL_BLACKSMITHING = 2,
TRADESKILL_COOKING = 3,
TRADESKILL_ENCHANTING = 4,
TRADESKILL_ENGINEERING = 5,
TRADESKILL_FIRSTAID = 6,
TRADESKILL_HERBALISM = 7,
TRADESKILL_LEATHERWORKING = 8,
TRADESKILL_POISONS = 9,
TRADESKILL_TAILORING = 10,
TRADESKILL_MINING = 11,
TRADESKILL_FISHING = 12,
TRADESKILL_SKINNING = 13,
TRADESKILL_JEWLCRAFTING = 14,
TRADESKILL_INSCRIPTION = 15,
TRADESKILL_LEVEL_NONE = 0,
TRADESKILL_LEVEL_APPRENTICE = 1,
TRADESKILL_LEVEL_JOURNEYMAN = 2,
TRADESKILL_LEVEL_EXPERT = 3,
TRADESKILL_LEVEL_ARTISAN = 4,
TRADESKILL_LEVEL_MASTER = 5,
TRADESKILL_LEVEL_GRAND_MASTER = 6,
// Gossip defines
GOSSIP_ACTION_TRADE = 1,
GOSSIP_ACTION_TRAIN = 2,
GOSSIP_ACTION_TAXI = 3,
GOSSIP_ACTION_GUILD = 4,
GOSSIP_ACTION_BATTLE = 5,
GOSSIP_ACTION_BANK = 6,
GOSSIP_ACTION_INN = 7,
GOSSIP_ACTION_HEAL = 8,
GOSSIP_ACTION_TABARD = 9,
GOSSIP_ACTION_AUCTION = 10,
GOSSIP_ACTION_INN_INFO = 11,
GOSSIP_ACTION_UNLEARN = 12,
GOSSIP_ACTION_INFO_DEF = 1000,
GOSSIP_SENDER_MAIN = 1,
GOSSIP_SENDER_INN_INFO = 2,
GOSSIP_SENDER_INFO = 3,
GOSSIP_SENDER_SEC_PROFTRAIN = 4,
GOSSIP_SENDER_SEC_CLASSTRAIN = 5,
GOSSIP_SENDER_SEC_BATTLEINFO = 6,
GOSSIP_SENDER_SEC_BANK = 7,
GOSSIP_SENDER_SEC_INN = 8,
GOSSIP_SENDER_SEC_MAILBOX = 9,
GOSSIP_SENDER_SEC_STABLEMASTER = 10
};
// Defined fuctions to use with player.
// This fuction add's a menu item,
// a - Icon Id
// b - Text
// c - Sender(this is to identify the current Menu with this item)
// d - Action (identifys this Menu Item)
// e - Text to be displayed in pop up box
// f - Money value in pop up box
#define ADD_GOSSIP_ITEM(a, b, c, d) PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, a, b, c, d, "", 0)
#define ADD_GOSSIP_ITEM_EXTENDED(a, b, c, d, e, f, g) PlayerTalkClass->GetGossipMenu().AddMenuItem(-1, a, b, c, d, e, f, g)
// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32), b - npc guid(uint64)
#define SEND_GOSSIP_MENU(a, b) PlayerTalkClass->SendGossipMenu(a, b)
// Closes the Menu
#define CLOSE_GOSSIP_MENU() PlayerTalkClass->SendCloseGossip()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_SMARTAI_H
#define TRINITY_SMARTAI_H
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Unit.h"
#include "Spell.h"
#include "SmartScript.h"
#include "SmartScriptMgr.h"
#include "GameObjectAI.h"
enum SmartEscortState
{
SMART_ESCORT_NONE = 0x000, //nothing in progress
SMART_ESCORT_ESCORTING = 0x001, //escort is in progress
SMART_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
SMART_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
enum SmartEscortVars
{
SMART_ESCORT_MAX_PLAYER_DIST = 60,
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
};
class SmartAI : public CreatureAI
{
public:
~SmartAI(){};
explicit SmartAI(Creature* c);
// Start moving to the desired MovePoint
void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = NULL);
bool LoadPath(uint32 entry);
void PausePath(uint32 delay, bool forced = false);
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
void EndPath(bool fail = false);
void ResumePath();
WayPoint* GetNextWayPoint();
void GenerateWayPointArray(Movement::PointsArray* points);
bool HasEscortState(uint32 uiEscortState) { return (mEscortState & uiEscortState); }
void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; }
virtual bool IsEscorted() { return (mEscortState & SMART_ESCORT_ESCORTING); }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on);
bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; }
bool IsEscortInvokerInRange();
// Called when creature is spawned or respawned
void JustRespawned();
// Called at reaching home after evade, InitializeAI(), EnterEvadeMode() for resetting variables
void JustReachedHome();
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
void EnterCombat(Unit* enemy);
// Called for reaction at stopping attack at no attackers or targets
void EnterEvadeMode();
// Called when the creature is killed
void JustDied(Unit* killer);
// Called when the creature kills a unit
void KilledUnit(Unit* victim);
// Called when the creature summon successfully other creature
void JustSummoned(Creature* creature);
// Tell creature to attack and follow the victim
void AttackStart(Unit* who);
// Called if IsVisible(Unit* who) is true at each *who move, reaction at visibility zone enter
void MoveInLineOfSight(Unit* who);
// Called when hit by a spell
void SpellHit(Unit* unit, const SpellInfo* spellInfo);
// Called when spell hits a target
void SpellHitTarget(Unit* target, const SpellInfo* spellInfo);
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* done_by, uint32 &damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask);
// Called when the creature receives heal
void HealReceived(Unit* doneBy, uint32& addhealth);
// Called at World update tick
void UpdateAI(uint32 diff);
// Called at text emote receive from player
void ReceiveEmote(Player* player, uint32 textEmote);
// Called at waypoint reached or point movement finished
void MovementInform(uint32 MovementType, uint32 Data);
// Called when creature is summoned by another unit
void IsSummonedBy(Unit* summoner);
// Called at any Damage to any victim (before damage apply)
void DamageDealt(Unit* doneTo, uint32& damage, DamageEffectType damagetyp);
// Called when a summoned creature dissapears (UnSommoned)
void SummonedCreatureDespawn(Creature* unit);
// called when the corpse of this creature gets removed
void CorpseRemoved(uint32& respawnDelay);
// Called at World update tick if creature is charmed
void UpdateAIWhileCharmed(const uint32 diff);
// Called when a Player/Creature enters the creature (vehicle)
void PassengerBoarded(Unit* who, int8 seatId, bool apply);
// Called when gets initialized, when creature is added to world
void InitializeAI();
// Called when creature gets charmed by another unit
void OnCharmed(bool apply);
// Called when victim is in line of sight
bool CanAIAttack(const Unit* who) const;
// Used in scripts to share variables
void DoAction(int32 param = 0);
// Used in scripts to share variables
uint32 GetData(uint32 id = 0) const;
// Used in scripts to share variables
void SetData(uint32 id, uint32 value);
// Used in scripts to share variables
void SetGUID(uint64 guid, int32 id = 0);
// Used in scripts to share variables
uint64 GetGUID(int32 id = 0) const;
//core related
static int32 Permissible(const Creature*);
// Called at movepoint reached
void MovepointReached(uint32 id);
// Makes the creature run/walk
void SetRun(bool run = true);
void SetFly(bool fly = true);
void SetSwim(bool swim = true);
void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; }
void sGossipHello(Player* player);
void sGossipSelect(Player* player, uint32 sender, uint32 action);
void sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code);
void sQuestAccept(Player* player, Quest const* quest);
//void sQuestSelect(Player* player, Quest const* quest);
//void sQuestComplete(Player* player, Quest const* quest);
void sQuestReward(Player* player, Quest const* quest, uint32 opt);
void sOnGameEvent(bool start, uint16 eventId);
uint32 mEscortQuestID;
void SetDespawnTime (uint32 t)
{
mDespawnTime = t;
mDespawnState = t ? 1 : 0;
}
void StartDespawn() { mDespawnState = 2; }
void OnSpellClick(Unit* clicker, bool& result);
// Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
void SetForcedCombatMove(float dist);
private:
uint32 mFollowCreditType;
uint32 mFollowArrivedTimer;
uint32 mFollowCredit;
uint32 mFollowArrivedEntry;
bool mFollowArrivedAlive;
uint64 mFollowGuid;
float mFollowDist;
float mFollowAngle;
void ReturnToLastOOCPos();
void UpdatePath(const uint32 diff);
SmartScript mScript;
WPPath* mWayPoints;
uint32 mEscortState;
uint32 mCurrentWPID;
bool mWPReached;
bool mOOCReached;
uint32 mWPPauseTimer;
WayPoint* mLastWP;
uint32 mEscortNPCFlags;
uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; }
bool mCanRepeatPath;
bool mRun;
bool mCanAutoAttack;
bool mCanCombatMove;
bool mForcedPaused;
uint32 mInvincibilityHpLevel;
bool AssistPlayerInCombat(Unit* who);
uint32 mDespawnTime;
uint32 mDespawnState;
void UpdateDespawn(const uint32 diff);
uint32 mEscortInvokerCheckTimer;
bool mJustReset;
// Xinef: Vehicle conditions
void CheckConditions(const uint32 diff);
ConditionList conditions;
uint32 m_ConditionsTimer;
};
class SmartGameObjectAI : public GameObjectAI
{
public:
SmartGameObjectAI(GameObject* g) : GameObjectAI(g) {}
~SmartGameObjectAI() {}
void UpdateAI(uint32 diff);
void InitializeAI();
void Reset();
SmartScript* GetScript() { return &mScript; }
static int32 Permissible(const GameObject* g);
bool GossipHello(Player* player, bool reportUse);
bool GossipSelect(Player* player, uint32 sender, uint32 action);
bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/);
bool QuestAccept(Player* player, Quest const* quest);
bool QuestReward(Player* player, Quest const* quest, uint32 opt);
void Destroyed(Player* player, uint32 eventId);
void SetData(uint32 id, uint32 value);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
void OnGameEvent(bool start, uint16 eventId);
void OnStateChanged(uint32 state, Unit* unit);
void EventInform(uint32 eventId);
void SpellHit(Unit* unit, const SpellInfo* spellInfo);
protected:
SmartScript mScript;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,343 @@
/*
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_SMARTSCRIPT_H
#define TRINITY_SMARTSCRIPT_H
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Unit.h"
#include "Spell.h"
#include "GridNotifiers.h"
#include "SmartScriptMgr.h"
//#include "SmartAI.h"
class SmartScript
{
public:
SmartScript();
~SmartScript();
void OnInitialize(WorldObject* obj, AreaTriggerEntry const* at = NULL);
void GetScript();
void FillScript(SmartAIEventList e, WorldObject* obj, AreaTriggerEntry const* at);
void ProcessEventsFor(SMART_EVENT e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
void ProcessEvent(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
bool CheckTimer(SmartScriptHolder const& e) const;
void RecalcTimer(SmartScriptHolder& e, uint32 min, uint32 max);
void UpdateTimer(SmartScriptHolder& e, uint32 const diff);
void InitTimer(SmartScriptHolder& e);
void ProcessAction(SmartScriptHolder& e, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
void ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit = NULL, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, const SpellInfo* spell = NULL, GameObject* gob = NULL);
ObjectList* GetTargets(SmartScriptHolder const& e, Unit* invoker = NULL);
ObjectList* GetWorldObjectsInDist(float dist);
void InstallTemplate(SmartScriptHolder const& e);
SmartScriptHolder CreateEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
void AddEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 phaseMask = 0);
void SetPathId(uint32 id) { mPathId = id; }
uint32 GetPathId() const { return mPathId; }
WorldObject* GetBaseObject()
{
WorldObject* obj = NULL;
if (me)
obj = me;
else if (go)
obj = go;
return obj;
}
bool IsUnit(WorldObject* obj)
{
return obj && obj->IsInWorld() && (obj->GetTypeId() == TYPEID_UNIT || obj->GetTypeId() == TYPEID_PLAYER);
}
bool IsPlayer(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_PLAYER;
}
bool IsCreature(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_UNIT;
}
bool IsGameObject(WorldObject* obj)
{
return obj && obj->IsInWorld() && obj->GetTypeId() == TYPEID_GAMEOBJECT;
}
void OnUpdate(const uint32 diff);
void OnMoveInLineOfSight(Unit* who);
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
void DoFindFriendlyMissingBuff(std::list<Creature*>& list, float range, uint32 spellid);
Unit* DoFindClosestFriendlyInRange(float range, bool playerOnly);
void StoreTargetList(ObjectList* targets, uint32 id)
{
if (!targets)
return;
if (mTargetStorage->find(id) != mTargetStorage->end())
{
// check if already stored
if ((*mTargetStorage)[id]->Equals(targets))
return;
delete (*mTargetStorage)[id];
}
(*mTargetStorage)[id] = new ObjectGuidList(targets, GetBaseObject());
}
bool IsSmart(Creature* c = NULL)
{
bool smart = true;
if (c && c->GetAIName() != "SmartAI")
smart = false;
if (!me || me->GetAIName() != "SmartAI")
smart = false;
if (!smart)
sLog->outErrorDb("SmartScript: Action target Creature(entry: %u) is not using SmartAI, action skipped to prevent crash.", c ? c->GetEntry() : (me ? me->GetEntry() : 0));
return smart;
}
bool IsSmartGO(GameObject* g = NULL)
{
bool smart = true;
if (g && g->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!go || go->GetAIName() != "SmartGameObjectAI")
smart = false;
if (!smart)
sLog->outErrorDb("SmartScript: Action target GameObject(entry: %u) is not using SmartGameObjectAI, action skipped to prevent crash.", g ? g->GetEntry() : (go ? go->GetEntry() : 0));
return smart;
}
ObjectList* GetTargetList(uint32 id)
{
ObjectListMap::iterator itr = mTargetStorage->find(id);
if (itr != mTargetStorage->end())
return (*itr).second->GetObjectList();
return NULL;
}
void StoreCounter(uint32 id, uint32 value, uint32 reset)
{
CounterMap::iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
{
if (reset == 0)
itr->second += value;
else
itr->second = value;
}
else
mCounterList.insert(std::make_pair(id, value));
ProcessEventsFor(SMART_EVENT_COUNTER_SET, NULL, id);
}
uint32 GetCounterValue(uint32 id)
{
CounterMap::iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
return itr->second;
return 0;
}
GameObject* FindGameObjectNear(WorldObject* searchObject, uint32 guid) const
{
GameObject* gameObject = NULL;
CellCoord p(Trinity::ComputeCellCoord(searchObject->GetPositionX(), searchObject->GetPositionY()));
Cell cell(p);
Trinity::GameObjectWithDbGUIDCheck goCheck(*searchObject, guid);
Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck> checker(searchObject, gameObject, goCheck);
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > objectChecker(checker);
cell.Visit(p, objectChecker, *searchObject->GetMap(), *searchObject, searchObject->GetVisibilityRange());
return gameObject;
}
Creature* FindCreatureNear(WorldObject* searchObject, uint32 guid) const
{
Creature* creature = NULL;
CellCoord p(Trinity::ComputeCellCoord(searchObject->GetPositionX(), searchObject->GetPositionY()));
Cell cell(p);
Trinity::CreatureWithDbGUIDCheck target_check(searchObject, guid);
Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(searchObject, creature, target_check);
TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker);
cell.Visit(p, unit_checker, *searchObject->GetMap(), *searchObject, searchObject->GetVisibilityRange());
return creature;
}
ObjectListMap* mTargetStorage;
void OnReset();
void ResetBaseObject()
{
if (meOrigGUID)
{
if (Creature* m = HashMapHolder<Creature>::Find(meOrigGUID))
{
me = m;
go = NULL;
}
}
if (goOrigGUID)
{
if (GameObject* o = HashMapHolder<GameObject>::Find(goOrigGUID))
{
me = NULL;
go = o;
}
}
goOrigGUID = 0;
meOrigGUID = 0;
}
//TIMED_ACTIONLIST (script type 9 aka script9)
void SetScript9(SmartScriptHolder& e, uint32 entry);
Unit* GetLastInvoker(Unit* invoker = NULL);
uint64 mLastInvoker;
typedef UNORDERED_MAP<uint32, uint32> CounterMap;
CounterMap mCounterList;
// Xinef: Fix Combat Movement
void SetActualCombatDist(uint32 dist) { mActualCombatDist = dist; }
void RestoreMaxCombatDist() { mActualCombatDist = mMaxCombatDist; }
uint32 GetActualCombatDist() const { return mActualCombatDist; }
uint32 GetMaxCombatDist() const { return mMaxCombatDist; }
// Xinef: SmartCasterAI, replace above
void SetCasterActualDist(float dist) { smartCasterActualDist = dist; }
void RestoreCasterMaxDist() { smartCasterActualDist = smartCasterMaxDist; }
Powers GetCasterPowerType() const { return smartCasterPowerType; }
float GetCasterActualDist() const { return smartCasterActualDist; }
float GetCasterMaxDist() const { return smartCasterMaxDist; }
bool AllowPhaseReset() const { return _allowPhaseReset; }
void SetPhaseReset(bool allow) { _allowPhaseReset = allow; }
private:
void IncPhase(uint32 p)
{
// Xinef: protect phase from overflowing
mEventPhase = std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p);
}
void DecPhase(uint32 p)
{
if (p >= mEventPhase)
mEventPhase = 0;
else
mEventPhase -= p;
}
bool IsInPhase(uint32 p) const
{
if (mEventPhase == 0)
return false;
return (1 << (mEventPhase - 1)) & p;
}
void SetPhase(uint32 p = 0) { mEventPhase = p; }
SmartAIEventList mEvents;
SmartAIEventList mInstallEvents;
SmartAIEventList mTimedActionList;
bool isProcessingTimedActionList;
Creature* me;
uint64 meOrigGUID;
GameObject* go;
uint64 goOrigGUID;
AreaTriggerEntry const* trigger;
SmartScriptType mScriptType;
uint32 mEventPhase;
UNORDERED_MAP<int32, int32> mStoredDecimals;
uint32 mPathId;
SmartAIEventStoredList mStoredEvents;
std::list<uint32> mRemIDs;
uint32 mTextTimer;
uint32 mLastTextID;
uint32 mTalkerEntry;
bool mUseTextTimer;
// Xinef: Fix Combat Movement
uint32 mActualCombatDist;
uint32 mMaxCombatDist;
// Xinef: SmartCasterAI, replace above in future
uint32 smartCasterActualDist;
uint32 smartCasterMaxDist;
Powers smartCasterPowerType;
// Xinef: misc
bool _allowPhaseReset;
SMARTAI_TEMPLATE mTemplate;
void InstallEvents();
void RemoveStoredEvent (uint32 id)
{
if (!mStoredEvents.empty())
{
for (SmartAIEventStoredList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
{
if (i->event_id == id)
{
mStoredEvents.erase(i);
return;
}
}
}
}
SmartScriptHolder FindLinkedEvent (uint32 link)
{
if (!mEvents.empty())
{
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
{
if (i->event_id == link)
{
return (*i);
}
}
}
SmartScriptHolder s;
return s;
}
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "AccountMgr.h"
#include "DatabaseEnv.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "Util.h"
#include "SHA1.h"
#include "WorldSession.h"
namespace AccountMgr
{
uint32 GetId(std::string const& username)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->setString(0, username);
PreparedQueryResult result = LoginDatabase.Query(stmt);
return (result) ? (*result)[0].GetUInt32() : 0;
}
uint32 GetSecurity(uint32 accountId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER);
}
uint32 GetSecurity(uint32 accountId, int32 realmId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
stmt->setUInt32(0, accountId);
stmt->setInt32(1, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
return (result) ? (*result)[0].GetUInt8() : uint32(SEC_PLAYER);
}
bool GetName(uint32 accountId, std::string& name)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (result)
{
name = (*result)[0].GetString();
return true;
}
return false;
}
bool CheckPassword(uint32 accountId, std::string password)
{
std::string username;
if (!GetName(accountId, username))
return false;
normalizeString(username);
normalizeString(password);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD);
stmt->setUInt32(0, accountId);
stmt->setString(1, CalculateShaPassHash(username, password));
PreparedQueryResult result = LoginDatabase.Query(stmt);
return (result) ? true : false;
}
uint32 GetCharactersCount(uint32 accountId)
{
// check character count
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
return (result) ? (*result)[0].GetUInt64() : 0;
}
bool normalizeString(std::string& utf8String)
{
wchar_t buffer[MAX_ACCOUNT_STR+1];
size_t maxLength = MAX_ACCOUNT_STR;
if (!Utf8toWStr(utf8String, buffer, maxLength))
return false;
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif
std::transform(&buffer[0], buffer+maxLength, &buffer[0], wcharToUpperOnlyLatin);
#ifdef _MSC_VER
#pragma warning(default: 4996)
#endif
return WStrToUtf8(buffer, maxLength, utf8String);
}
std::string CalculateShaPassHash(std::string const& name, std::string const& password)
{
SHA1Hash sha;
sha.Initialize();
sha.UpdateData(name);
sha.UpdateData(":");
sha.UpdateData(password);
sha.Finalize();
return ByteArrayToHexStr(sha.GetDigest(), sha.GetLength());
}
bool IsPlayerAccount(uint32 gmlevel)
{
return gmlevel == SEC_PLAYER;
}
bool IsGMAccount(uint32 gmlevel)
{
return gmlevel >= SEC_GAMEMASTER && gmlevel <= SEC_CONSOLE;
}
bool IsAdminAccount(uint32 gmlevel)
{
return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE;
}
bool IsConsoleAccount(uint32 gmlevel)
{
return gmlevel == SEC_CONSOLE;
}
} // Namespace AccountMgr

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _ACCMGR_H
#define _ACCMGR_H
#include "Define.h"
#include <string>
enum AccountOpResult
{
AOR_OK,
AOR_NAME_TOO_LONG,
AOR_PASS_TOO_LONG,
AOR_NAME_ALREDY_EXIST,
AOR_NAME_NOT_EXIST,
AOR_DB_INTERNAL_ERROR
};
#define MAX_ACCOUNT_STR 20
namespace AccountMgr
{
bool CheckPassword(uint32 accountId, std::string password);
uint32 GetId(std::string const& username);
uint32 GetSecurity(uint32 accountId);
uint32 GetSecurity(uint32 accountId, int32 realmId);
bool GetName(uint32 accountId, std::string& name);
uint32 GetCharactersCount(uint32 accountId);
std::string CalculateShaPassHash(std::string const& name, std::string const& password);
bool normalizeString(std::string& utf8String);
bool IsPlayerAccount(uint32 gmlevel);
bool IsGMAccount(uint32 gmlevel);
bool IsAdminAccount(uint32 gmlevel);
bool IsConsoleAccount(uint32 gmlevel);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,403 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __TRINITY_ACHIEVEMENTMGR_H
#define __TRINITY_ACHIEVEMENTMGR_H
#include <map>
#include <string>
#include "Common.h"
#include <ace/Singleton.h>
#include "DatabaseEnv.h"
#include "DBCEnums.h"
#include "DBCStores.h"
typedef std::list<AchievementCriteriaEntry const*> AchievementCriteriaEntryList;
typedef std::list<AchievementEntry const*> AchievementEntryList;
typedef UNORDERED_MAP<uint32, AchievementCriteriaEntryList> AchievementCriteriaListByAchievement;
typedef std::map<uint32, AchievementEntryList> AchievementListByReferencedId;
struct CriteriaProgress
{
uint32 counter;
time_t date; // latest update time.
bool changed;
};
enum AchievementCriteriaDataType
{ // value1 value2 comment
ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1, // creature_id 0
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2, // class_id race_id
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH= 3, // health_percent 0
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4, // own_team 0 not corpse (not released body), own_team == false if enemy team expected
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5, // spell_id effect_idx
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6, // area id 0
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7, // spell_id effect_idx
ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8, // minvalue value provided with achievement update must be not less that limit
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9, // minlevel minlevel of target
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10, // gender 0=male; 1=female
ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT = 11, // scripted requirement
ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12, // difficulty normal/heroic difficulty for current event map
ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13, // count "with less than %u people in the zone"
ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14, // team HORDE(67), ALLIANCE(469)
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15, // drunken_state 0 (enum DrunkenState) of player
ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16, // holiday_id 0 event in holiday time
ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17, // min_score max_score player's team win bg and opposition team have team score in range
ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT = 18, // 0 0 maker instance script call for check current criteria requirements fit
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19, // item_level item_quality for equipped item in slot to check item level and quality
ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID = 20, // map_id 0 player must be on map with id in map_id
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21, // class_id race_id
ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY = 22, // N login on day of N-th Birthday
ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE = 23 // title_id known (pvp) title, values from dbc
};
#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 24 // maximum value in AchievementCriteriaDataType enum
class Player;
class Unit;
struct AchievementCriteriaData
{
AchievementCriteriaDataType dataType;
union
{
// ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0 (no data)
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE = 1
struct
{
uint32 id;
} creature;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE = 2
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE = 21
struct
{
uint32 class_id;
uint32 race_id;
} classRace;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH = 3
struct
{
uint32 percent;
} health;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_DEAD = 4
struct
{
uint32 own_team_flag;
} player_dead;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA = 5
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA = 7
struct
{
uint32 spell_id;
uint32 effect_idx;
} aura;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AREA = 6
struct
{
uint32 id;
} area;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE = 8
struct
{
uint32 value;
uint32 compType;
} value;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL = 9
struct
{
uint32 minlevel;
} level;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER = 10
struct
{
uint32 gender;
} gender;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT = 11 (no data)
// ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_DIFFICULTY = 12
struct
{
uint32 difficulty;
} difficulty;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT = 13
struct
{
uint32 maxcount;
} map_players;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM = 14
struct
{
uint32 team;
} team;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK = 15
struct
{
uint32 state;
} drunk;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16
struct
{
uint32 id;
} holiday;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE= 17
struct
{
uint32 min_score;
uint32 max_score;
} bg_loss_team_score;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT = 18 (no data)
// ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM = 19
struct
{
uint32 item_level;
uint32 item_quality;
} equipped_item;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_ID = 20
struct
{
uint32 mapId;
} map_id;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_NTH_BIRTHDAY = 21
struct
{
uint32 nth_birthday;
} birthday_login;
// ACHIEVEMENT_CRITERIA_DATA_TYPE_KNOWN_TITLE = 22
struct
{
uint32 title_id;
} known_title;
// ...
struct
{
uint32 value1;
uint32 value2;
} raw;
};
uint32 ScriptId;
AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
{
raw.value1 = 0;
raw.value2 = 0;
ScriptId = 0;
}
AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2, uint32 _scriptId) : dataType(AchievementCriteriaDataType(_dataType))
{
raw.value1 = _value1;
raw.value2 = _value2;
ScriptId = _scriptId;
}
bool IsValid(AchievementCriteriaEntry const* criteria);
bool Meets(uint32 criteria_id, Player const* source, Unit const* target, uint32 miscvalue1 = 0) const;
};
struct AchievementCriteriaDataSet
{
AchievementCriteriaDataSet() : criteria_id(0) {}
typedef std::vector<AchievementCriteriaData> Storage;
void Add(AchievementCriteriaData const& data) { storage.push_back(data); }
bool Meets(Player const* source, Unit const* target, uint32 miscvalue = 0) const;
void SetCriteriaId(uint32 id) {criteria_id = id;}
private:
uint32 criteria_id;
Storage storage;
};
typedef std::map<uint32, AchievementCriteriaDataSet> AchievementCriteriaDataMap;
struct AchievementReward
{
uint32 titleId[2];
uint32 itemId;
uint32 sender;
std::string subject;
std::string text;
uint32 mailTemplate;
};
typedef std::map<uint32, AchievementReward> AchievementRewards;
struct AchievementRewardLocale
{
StringVector subject;
StringVector text;
};
typedef std::map<uint32, AchievementRewardLocale> AchievementRewardLocales;
struct CompletedAchievementData
{
time_t date;
bool changed;
};
typedef UNORDERED_MAP<uint32, CriteriaProgress> CriteriaProgressMap;
typedef UNORDERED_MAP<uint32, CompletedAchievementData> CompletedAchievementMap;
class Unit;
class Player;
class WorldPacket;
class AchievementMgr
{
public:
AchievementMgr(Player* player);
~AchievementMgr();
void Reset();
static void DeleteFromDB(uint32 lowguid);
void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult);
void SaveToDB(SQLTransaction& trans);
void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint32 value, bool evenIfCriteriaComplete = false);
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = NULL);
void CompletedAchievement(AchievementEntry const* entry);
void CheckAllAchievementCriteria();
void SendAllAchievementData() const;
void SendRespondInspectAchievements(Player* player) const;
bool HasAchieved(uint32 achievementId) const;
Player* GetPlayer() const { return m_player; }
void UpdateTimedAchievements(uint32 timeDiff);
void StartTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry, uint32 timeLost = 0);
void RemoveTimedAchievement(AchievementCriteriaTimedTypes type, uint32 entry); // used for quest and scripted timed achievements
void RemoveCriteriaProgress(AchievementCriteriaEntry const* entry);
private:
enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST, PROGRESS_RESET };
void SendAchievementEarned(AchievementEntry const* achievement) const;
void SendCriteriaUpdate(AchievementCriteriaEntry const* entry, CriteriaProgress const* progress, uint32 timeElapsed, bool timedCompleted) const;
CriteriaProgress* GetCriteriaProgress(AchievementCriteriaEntry const* entry);
void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET);
void CompletedCriteriaFor(AchievementEntry const* achievement);
bool IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement);
bool IsCompletedAchievement(AchievementEntry const* entry);
bool CanUpdateCriteria(AchievementCriteriaEntry const* criteria, AchievementEntry const* achievement);
void BuildAllDataPacket(WorldPacket* data, bool inspect = false) const;
Player* m_player;
CriteriaProgressMap m_criteriaProgress;
CompletedAchievementMap m_completedAchievements;
typedef std::map<uint32, uint32> TimedAchievementMap;
TimedAchievementMap m_timedAchievements; // Criteria id/time left in MS
};
class AchievementGlobalMgr
{
friend class ACE_Singleton<AchievementGlobalMgr, ACE_Null_Mutex>;
AchievementGlobalMgr() {}
~AchievementGlobalMgr() {}
public:
AchievementCriteriaEntryList const* GetAchievementCriteriaByType(AchievementCriteriaTypes type) const
{
return &m_AchievementCriteriasByType[type];
}
AchievementCriteriaEntryList const* GetSpecialAchievementCriteriaByType(AchievementCriteriaTypes type, uint32 val)
{
if (m_SpecialList[type].find(val) != m_SpecialList[type].end())
return &m_SpecialList[type][val];
return NULL;
}
AchievementCriteriaEntryList const* GetAchievementCriteriaByCondition(AchievementCriteriaCondition condition, uint32 val)
{
if (m_AchievementCriteriasByCondition[condition].find(val) != m_AchievementCriteriasByCondition[condition].end())
return &m_AchievementCriteriasByCondition[condition][val];
return NULL;
}
AchievementCriteriaEntryList const& GetTimedAchievementCriteriaByType(AchievementCriteriaTimedTypes type) const
{
return m_AchievementCriteriasByTimedType[type];
}
AchievementCriteriaEntryList const* GetAchievementCriteriaByAchievement(uint32 id) const
{
AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id);
return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL;
}
AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const
{
AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id);
return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL;
}
AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const
{
AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID);
return iter != m_achievementRewards.end() ? &iter->second : NULL;
}
AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const
{
AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID);
return iter != m_achievementRewardLocales.end() ? &iter->second : NULL;
}
AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const* achievementCriteria) const
{
AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID);
return iter != m_criteriaDataMap.end() ? &iter->second : NULL;
}
bool IsRealmCompleted(AchievementEntry const* achievement) const
{
return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end();
}
void SetRealmCompleted(AchievementEntry const* achievement)
{
m_allCompletedAchievements.insert(achievement->ID);
}
void LoadAchievementCriteriaList();
void LoadAchievementCriteriaData();
void LoadAchievementReferenceList();
void LoadCompletedAchievements();
void LoadRewards();
void LoadRewardLocales();
private:
AchievementCriteriaDataMap m_criteriaDataMap;
// store achievement criterias by type to speed up lookup
AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
AchievementCriteriaEntryList m_AchievementCriteriasByTimedType[ACHIEVEMENT_TIMED_TYPE_MAX];
// store achievement criterias by achievement to speed up lookup
AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement;
// store achievements by referenced achievement id to speed up lookup
AchievementListByReferencedId m_AchievementListByReferencedId;
typedef std::set<uint32> AllCompletedAchievements;
AllCompletedAchievements m_allCompletedAchievements;
AchievementRewards m_achievementRewards;
AchievementRewardLocales m_achievementRewardLocales;
// pussywizard:
std::map<uint32, AchievementCriteriaEntryList> m_SpecialList[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
std::map<uint32, AchievementCriteriaEntryList> m_AchievementCriteriasByCondition[ACHIEVEMENT_CRITERIA_CONDITION_TOTAL];
};
#define sAchievementMgr ACE_Singleton<AchievementGlobalMgr, ACE_Null_Mutex>::instance()
#endif

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "AddonMgr.h"
#include "DatabaseEnv.h"
#include "Log.h"
#include "Timer.h"
#include <list>
namespace AddonMgr
{
// Anonymous namespace ensures file scope of all the stuff inside it, even
// if you add something more to this namespace somewhere else.
namespace
{
// List of saved addons (in DB).
typedef std::list<SavedAddon> SavedAddonsList;
SavedAddonsList m_knownAddons;
}
void LoadFromDB()
{
uint32 oldMSTime = getMSTime();
QueryResult result = CharacterDatabase.Query("SELECT name, crc FROM addons");
if (!result)
{
sLog->outString(">> Loaded 0 known addons. DB table `addons` is empty!");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
std::string name = fields[0].GetString();
uint32 crc = fields[1].GetUInt32();
m_knownAddons.push_back(SavedAddon(name, crc));
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u known addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void SaveAddon(AddonInfo const& addon)
{
std::string name = addon.Name;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ADDON);
stmt->setString(0, name);
stmt->setUInt32(1, addon.CRC);
CharacterDatabase.Execute(stmt);
m_knownAddons.push_back(SavedAddon(addon.Name, addon.CRC));
}
SavedAddon const* GetAddonInfo(const std::string& name)
{
for (SavedAddonsList::const_iterator it = m_knownAddons.begin(); it != m_knownAddons.end(); ++it)
{
SavedAddon const& addon = (*it);
if (addon.Name == name)
return &addon;
}
return NULL;
}
} // Namespace

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _ADDONMGR_H
#define _ADDONMGR_H
#include "Define.h"
#include <string>
struct AddonInfo
{
AddonInfo(const std::string& name, uint8 enabled, uint32 crc, uint8 state, bool crcOrPubKey)
: Name(name), Enabled(enabled), CRC(crc), State(state), UsePublicKeyOrCRC(crcOrPubKey) {}
std::string Name;
uint8 Enabled;
uint32 CRC;
uint8 State;
bool UsePublicKeyOrCRC;
};
struct SavedAddon
{
SavedAddon(const std::string& name, uint32 crc) : Name(name)
{
CRC = crc;
}
std::string Name;
uint32 CRC;
};
#define STANDARD_ADDON_CRC 0x4c1c776d
namespace AddonMgr
{
void LoadFromDB();
void SaveAddon(AddonInfo const& addon);
SavedAddon const* GetAddonInfo(const std::string& name);
}
#endif

View File

@@ -0,0 +1,217 @@
#ifndef SUNWELLCORE_ARENASPECTATOR_H
#define SUNWELLCORE_ARENASPECTATOR_H
#include "Player.h"
#include "World.h"
#include "Map.h"
#include "Battleground.h"
#include "Pet.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "Chat.h"
#include "LFGMgr.h"
#define SPECTATOR_ADDON_VERSION 27
#define SPECTATOR_BUFFER_LEN 150
#define SPECTATOR_ADDON_PREFIX "ASSUN\x09"
#define SPECTATOR_COOLDOWN_MIN 20
#define SPECTATOR_COOLDOWN_MAX 900
#define SPECTATOR_SPELL_BINDSIGHT 6277
#define SPECTATOR_SPELL_SPEED 1557
namespace ArenaSpectator
{
template<class T> inline void SendCommand(T* o, const char* format, ...) ATTR_PRINTF(2, 3);
inline void CreatePacket(WorldPacket& data, const char* m);
inline void SendPacketTo(const Player* p, const char* m);
inline void SendPacketTo(const Map* map, const char* m);
inline void HandleResetCommand(Player* p);
inline bool ShouldSendAura(Aura* aura, uint8 effMask, uint64 targetGUID, bool remove);
template<class T> inline void SendCommand_String(T* p, uint64 targetGUID, const char* prefix, const std::string& c);
template<class T> inline void SendCommand_UInt32Value(T* o, uint64 targetGUID, const char* prefix, uint32 t);
template<class T> inline void SendCommand_GUID(T* o, uint64 targetGUID, const char* prefix, uint64 t);
template<class T> inline void SendCommand_Spell(T* o, uint64 targetGUID, const char* prefix, uint32 id, int32 casttime);
template<class T> inline void SendCommand_Cooldown(T* o, uint64 targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur);
template<class T> inline void SendCommand_Aura(T* o, uint64 targetGUID, const char* prefix, uint64 caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove);
bool HandleSpectatorSpectateCommand(ChatHandler* handler, char const* args);
bool HandleSpectatorWatchCommand(ChatHandler* handler, char const* args);
// definitions below:
template<class T>
void SendCommand(T* o, const char* format, ...)
{
if (!format)
return;
char buffer[SPECTATOR_BUFFER_LEN];
va_list ap;
va_start(ap, format);
vsnprintf(buffer, SPECTATOR_BUFFER_LEN, format, ap);
va_end(ap);
SendPacketTo(o, buffer);
}
void CreatePacket(WorldPacket& data, const char* m)
{
size_t len = strlen(m);
data.Initialize(SMSG_MESSAGECHAT, 1+4+8+4+8+4+1+len+1);
data << uint8(CHAT_MSG_WHISPER);
data << uint32(LANG_ADDON);
data << uint64(0);
data << uint32(0);
data << uint64(0);
data << uint32(len + 1);
data << m;
data << uint8(0);
}
void SendPacketTo(const Player* p, const char* m)
{
WorldPacket data;
CreatePacket(data, m);
p->GetSession()->SendPacket(&data);
}
void SendPacketTo(const Map* map, const char* m)
{
if (!map->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)map)->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
WorldPacket data;
CreatePacket(data, m);
bg->SpectatorsSendPacket(data);
}
template<class T>
void SendCommand_String(T* o, uint64 targetGUID, const char* prefix, const char* c)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
SendCommand(o, "%s0x%016llX;%s=%s;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, c);
}
template<class T>
void SendCommand_UInt32Value(T* o, uint64 targetGUID, const char* prefix, uint32 t)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
SendCommand(o, "%s0x%016llX;%s=%u;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, t);
}
template<class T>
void SendCommand_GUID(T* o, uint64 targetGUID, const char* prefix, uint64 t)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
SendCommand(o, "%s0x%016llX;%s=0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, t);
}
template<class T>
void SendCommand_Spell(T* o, uint64 targetGUID, const char* prefix, uint32 id, int32 casttime)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
SendCommand(o, "%s0x%016llX;%s=%u,%i;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, id, casttime);
}
template<class T>
void SendCommand_Cooldown(T* o, uint64 targetGUID, const char* prefix, uint32 id, uint32 dur, uint32 maxdur)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
if (const SpellInfo* si = sSpellMgr->GetSpellInfo(id))
if (si->SpellIconID == 1)
return;
SendCommand(o, "%s0x%016llX;%s=%u,%u,%u;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, id, dur, maxdur);
}
template<class T>
void SendCommand_Aura(T* o, uint64 targetGUID, const char* prefix, uint64 caster, uint32 id, bool isDebuff, uint32 dispel, int32 dur, int32 maxdur, uint32 stack, bool remove)
{
if (!IS_PLAYER_GUID(targetGUID))
return;
SendCommand(o, "%s0x%016llX;%s=%u,%u,%i,%i,%u,%u,%u,0x%016llX;", SPECTATOR_ADDON_PREFIX, targetGUID, prefix, remove ? 1 : 0, stack, dur, maxdur, id, dispel, isDebuff ? 1 : 0, caster);
}
void HandleResetCommand(Player* p)
{
if (!p->FindMap() || !p->IsInWorld() || !p->FindMap()->IsBattleArena())
return;
Battleground* bg = ((BattlegroundMap*)p->FindMap())->GetBG();
if (!bg || bg->GetStatus() != STATUS_IN_PROGRESS)
return;
Battleground::BattlegroundPlayerMap const& pl = bg->GetPlayers();
for (Battleground::BattlegroundPlayerMap::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
{
if (p->HasReceivedSpectatorResetFor(GUID_LOPART(itr->first)))
continue;
Player* plr = itr->second;
p->AddReceivedSpectatorResetFor(GUID_LOPART(itr->first));
SendCommand_String(p, itr->first, "NME", plr->GetName().c_str());
// Xinef: addon compatibility
SendCommand_UInt32Value(p, itr->first, "TEM", plr->GetBgTeamId() == TEAM_ALLIANCE ? ALLIANCE : HORDE);
SendCommand_UInt32Value(p, itr->first, "CLA", plr->getClass());
SendCommand_UInt32Value(p, itr->first, "MHP", plr->GetMaxHealth());
SendCommand_UInt32Value(p, itr->first, "CHP", plr->GetHealth());
SendCommand_UInt32Value(p, itr->first, "STA", plr->IsAlive() ? 1 : 0);
Powers ptype = plr->getPowerType();
SendCommand_UInt32Value(p, itr->first, "PWT", ptype);
SendCommand_UInt32Value(p, itr->first, "MPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetMaxPower(ptype)/10 : plr->GetMaxPower(ptype));
SendCommand_UInt32Value(p, itr->first, "CPW", ptype == POWER_RAGE || ptype == POWER_RUNIC_POWER ? plr->GetPower(ptype)/10 : plr->GetPower(ptype));
Pet* pet = plr->GetPet();
SendCommand_UInt32Value(p, itr->first, "PHP", pet && pet->GetCreatureTemplate()->family ? (uint32)pet->GetHealthPct() : 0);
SendCommand_UInt32Value(p, itr->first, "PET", pet ? pet->GetCreatureTemplate()->family : 0);
SendCommand_GUID(p, itr->first, "TRG", plr->GetTarget());
SendCommand_UInt32Value(p, itr->first, "RES", 1);
SendCommand_UInt32Value(p, itr->first, "CDC", 1);
SendCommand_UInt32Value(p, itr->first, "TIM", (bg->GetStartTime() < 46*MINUTE*IN_MILLISECONDS) ? (46*MINUTE*IN_MILLISECONDS-bg->GetStartTime())/IN_MILLISECONDS : 0);
// "SPE" not here (only possible to send starting a new cast)
// send all "CD"
SpellCooldowns const& sc = plr->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itrc = sc.begin(); itrc != sc.end(); ++itrc)
if (itrc->second.sendToSpectator && itrc->second.maxduration >= SPECTATOR_COOLDOWN_MIN*IN_MILLISECONDS && itrc->second.maxduration <= SPECTATOR_COOLDOWN_MAX*IN_MILLISECONDS)
if (uint32 cd = (getMSTimeDiff(World::GetGameTimeMS(), itrc->second.end)/1000))
SendCommand_Cooldown(p, itr->first, "ACD", itrc->first, cd, itrc->second.maxduration/1000);
// send all visible "AUR"
Unit::VisibleAuraMap const *visibleAuras = plr->GetVisibleAuras();
for (Unit::VisibleAuraMap::const_iterator aitr = visibleAuras->begin(); aitr != visibleAuras->end(); ++aitr)
{
Aura *aura = aitr->second->GetBase();
if (ShouldSendAura(aura, aitr->second->GetEffectMask(), plr->GetGUID(), false))
SendCommand_Aura(p, itr->first, "AUR", aura->GetCasterGUID(), aura->GetSpellInfo()->Id, aura->GetSpellInfo()->IsPositive(), aura->GetSpellInfo()->Dispel, aura->GetDuration(), aura->GetMaxDuration(), (aura->GetCharges() > 1 ? aura->GetCharges() : aura->GetStackAmount()), false);
}
}
}
bool ShouldSendAura(Aura* aura, uint8 effMask, uint64 targetGUID, bool remove)
{
if (aura->GetSpellInfo()->SpellIconID == 1 || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR1_DONT_DISPLAY_IN_AURA_BAR))
return false;
if (remove || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_AURA_CC) || aura->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_GENERIC)
return true;
for(uint8 i=EFFECT_0; i<MAX_SPELL_EFFECTS; ++i)
{
if (effMask & (1<<i))
{
AuraType at = aura->GetEffect(i)->GetAuraType();
if (aura->GetEffect(i)->GetAmount() && (aura->GetSpellInfo()->IsPositive() || targetGUID != aura->GetCasterGUID()) ||
at == SPELL_AURA_MECHANIC_IMMUNITY || at == SPELL_AURA_EFFECT_IMMUNITY || at == SPELL_AURA_STATE_IMMUNITY || at == SPELL_AURA_SCHOOL_IMMUNITY || at == SPELL_AURA_DISPEL_IMMUNITY)
return true;
}
}
return false;
}
}
#endif

View File

@@ -0,0 +1,820 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
#include "ScriptMgr.h"
#include "AccountMgr.h"
#include "AuctionHouseMgr.h"
#include "Item.h"
#include "Language.h"
#include "Logging/Log.h"
#include <vector>
#include "AvgDiffTracker.h"
#include "AsyncAuctionListing.h"
enum eAuctionHouse
{
AH_MINIMUM_DEPOSIT = 100,
};
AuctionHouseMgr::AuctionHouseMgr()
{
}
AuctionHouseMgr::~AuctionHouseMgr()
{
for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
delete itr->second;
}
AuctionHouseObject* AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId)
{
// team have linked auction houses
FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId);
if (!u_entry)
return &mNeutralAuctions;
else if (u_entry->ourMask & FACTION_MASK_ALLIANCE)
return &mAllianceAuctions;
else if (u_entry->ourMask & FACTION_MASK_HORDE)
return &mHordeAuctions;
else
return &mNeutralAuctions;
}
uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count)
{
uint32 MSV = pItem->GetTemplate()->SellPrice;
if (MSV <= 0)
return AH_MINIMUM_DEPOSIT;
float multiplier = CalculatePct(float(entry->depositPercent), 3);
uint32 timeHr = (((time / 60) / 60) / 12);
uint32 deposit = uint32(((multiplier * MSV * count / 3) * timeHr * 3) * sWorld->getRate(RATE_AUCTION_DEPOSIT));
;//sLog->outDebug(LOG_FILTER_AUCTIONHOUSE, "MSV: %u", MSV);
;//sLog->outDebug(LOG_FILTER_AUCTIONHOUSE, "Items: %u", count);
;//sLog->outDebug(LOG_FILTER_AUCTIONHOUSE, "Multiplier: %f", multiplier);
;//sLog->outDebug(LOG_FILTER_AUCTIONHOUSE, "Deposit: %u", deposit);
if (deposit < AH_MINIMUM_DEPOSIT)
return AH_MINIMUM_DEPOSIT;
else
return deposit;
}
//does not clear ram
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans)
{
Item* pItem = GetAItem(auction->item_guidlow);
if (!pItem)
return;
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
uint32 bidder_accId = 0;
Player* bidder = ObjectAccessor::FindPlayerInOrOutOfWorld(bidder_guid);
if (bidder)
bidder_accId = bidder->GetSession()->GetAccountId();
else
bidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(bidder_guid);
// receiver exist
if (bidder || bidder_accId)
{
// set owner to bidder (to prevent delete item with sender char deleting)
// owner in `data` will set at mail receive and item extracting
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
stmt->setUInt32(0, auction->bidder);
stmt->setUInt32(1, pItem->GetGUIDLow());
trans->Append(stmt);
if (bidder)
{
bidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template);
// FIXME: for offline player need also
bidder->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1);
}
MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, 0, 0))
.AddItem(pItem)
.SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
else
sAuctionMgr->RemoveAItem(auction->item_guidlow, true);
}
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans)
{
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player* owner = ObjectAccessor::FindPlayerInOrOutOfWorld(owner_guid);
uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid);
// owner exist (online or offline)
if (owner || owner_accId)
MailDraft(auction->BuildAuctionMailSubject(AUCTION_SALE_PENDING), AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut()))
.SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED);
}
//call this method to send mail to auction owner, when auction is successful, it does not clear ram
void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans)
{
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player* owner = ObjectAccessor::FindPlayerInOrOutOfWorld(owner_guid);
uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid);
// owner exist
if (owner || owner_accId)
{
uint32 profit = auction->bid + auction->deposit - auction->GetAuctionCut();
if (owner)
{
owner->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS, profit);
owner->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid);
owner->GetSession()->SendAuctionOwnerNotification(auction);
}
MailDraft(auction->BuildAuctionMailSubject(AUCTION_SUCCESSFUL), AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut()))
.AddMoney(profit)
.SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY));
if (auction->bid >= 500*GOLD)
if (const GlobalPlayerData* gpd = sWorld->GetGlobalPlayerData(auction->bidder))
{
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
Player* bidder = ObjectAccessor::FindPlayerInOrOutOfWorld(bidder_guid);
std::string owner_name = "";
uint8 owner_level = 0;
if (const GlobalPlayerData* gpd_owner = sWorld->GetGlobalPlayerData(auction->owner))
{
owner_name = gpd_owner->name;
owner_level = gpd_owner->level;
}
CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<AH> profit: %ug, bidder: %s %u lvl (guid: %u), seller: %s %u lvl (guid: %u), item %u (%u)\", NOW())", gpd->accountId, auction->bidder, gpd->name.c_str(), bidder ? bidder->GetSession()->GetRemoteAddress().c_str() : "", owner_accId, owner_name.c_str(), auction->bid, (profit/GOLD), gpd->name.c_str(), gpd->level, auction->bidder, owner_name.c_str(), owner_level, auction->owner, auction->item_template, auction->itemCount);
}
}
}
//does not clear ram
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans)
{
//return an item in auction to its owner by mail
Item* pItem = GetAItem(auction->item_guidlow);
if (!pItem)
return;
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player* owner = ObjectAccessor::FindPlayerInOrOutOfWorld(owner_guid);
uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(owner_guid);
// owner exist
if (owner || owner_accId)
{
if (owner)
owner->GetSession()->SendAuctionOwnerNotification(auction);
MailDraft(auction->BuildAuctionMailSubject(AUCTION_EXPIRED), AuctionEntry::BuildAuctionMailBody(0, 0, auction->buyout, auction->deposit, 0))
.AddItem(pItem)
.SendMailTo(trans, MailReceiver(owner, auction->owner), auction, MAIL_CHECK_MASK_COPIED, 0);
}
else
sAuctionMgr->RemoveAItem(auction->item_guidlow, true);
}
//this function sends mail to old bidder
void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans)
{
uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
Player* oldBidder = ObjectAccessor::FindPlayerInOrOutOfWorld(oldBidder_guid);
uint32 oldBidder_accId = 0;
if (!oldBidder)
oldBidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(oldBidder_guid);
// old bidder exist
if (oldBidder || oldBidder_accId)
{
if (oldBidder && newBidder)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, newBidder->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
MailDraft(auction->BuildAuctionMailSubject(AUCTION_OUTBIDDED), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut()))
.AddMoney(auction->bid)
.SendMailTo(trans, MailReceiver(oldBidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
}
//this function sends mail, when auction is cancelled to old bidder
void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans)
{
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
Player* bidder = ObjectAccessor::FindPlayerInOrOutOfWorld(bidder_guid);
uint32 bidder_accId = 0;
if (!bidder)
bidder_accId = sObjectMgr->GetPlayerAccountIdByGUID(bidder_guid);
// bidder exist
if (bidder || bidder_accId)
MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELLED_TO_BIDDER), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, 0))
.AddMoney(auction->bid)
.SendMailTo(trans, MailReceiver(bidder, auction->bidder), auction, MAIL_CHECK_MASK_COPIED);
}
void AuctionHouseMgr::LoadAuctionItems()
{
uint32 oldMSTime = getMSTime();
// need to clear in case we are reloading
if (!mAitems.empty())
{
for (ItemMap::iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
delete itr->second;
mAitems.clear();
}
// data needs to be at first place for Item::LoadFromDB
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
sLog->outString(">> Loaded 0 auction items. DB table `auctionhouse` or `item_instance` is empty!");
sLog->outString();
return;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 item_guid = fields[11].GetUInt32();
uint32 item_template = fields[12].GetUInt32();
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item_template);
if (!proto)
{
sLog->outError("AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid, item_template);
continue;
}
Item* item = NewItemOrBag(proto);
if (!item->LoadFromDB(item_guid, 0, fields, item_template))
{
delete item;
continue;
}
AddAItem(item);
++count;
}
while (result->NextRow());
sLog->outString(">> Loaded %u auction items in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void AuctionHouseMgr::LoadAuctions()
{
uint32 oldMSTime = getMSTime();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTIONS);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
{
sLog->outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
sLog->outString();
return;
}
uint32 count = 0;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
do
{
Field* fields = result->Fetch();
AuctionEntry* aItem = new AuctionEntry();
if (!aItem->LoadFromDB(fields))
{
aItem->DeleteFromDB(trans);
delete aItem;
continue;
}
GetAuctionsMap(aItem->factionTemplateId)->AddAuction(aItem);
count++;
} while (result->NextRow());
CharacterDatabase.CommitTransaction(trans);
sLog->outString(">> Loaded %u auctions in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
sLog->outString();
}
void AuctionHouseMgr::AddAItem(Item* it)
{
ASSERT(it);
ASSERT(mAitems.find(it->GetGUIDLow()) == mAitems.end());
mAitems[it->GetGUIDLow()] = it;
}
bool AuctionHouseMgr::RemoveAItem(uint32 id, bool deleteFromDB)
{
ItemMap::iterator i = mAitems.find(id);
if (i == mAitems.end())
return false;
if (deleteFromDB)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
i->second->FSetState(ITEM_REMOVED);
i->second->SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
mAitems.erase(i);
return true;
}
void AuctionHouseMgr::Update()
{
mHordeAuctions.Update();
mAllianceAuctions.Update();
mNeutralAuctions.Update();
}
AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId)
{
uint32 houseid = 7; // goblin auction house
//if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
{
//FIXME: found way for proper auctionhouse selection by another way
// AuctionHouse.dbc have faction field with _player_ factions associated with auction house races.
// but no easy way convert creature faction to player race faction for specific city
switch (factionTemplateId)
{
case 12: houseid = 1; break; // human
case 29: houseid = 6; break; // orc, and generic for horde
case 55: houseid = 2; break; // dwarf, and generic for alliance
case 68: houseid = 4; break; // undead
case 80: houseid = 3; break; // n-elf
case 104: houseid = 5; break; // trolls
case 120: houseid = 7; break; // booty bay, neutral
case 474: houseid = 7; break; // gadgetzan, neutral
case 855: houseid = 7; break; // everlook, neutral
case 1604: houseid = 6; break; // b-elfs,
default: // for unknown case
{
FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId);
if (!u_entry)
houseid = 7; // goblin auction house
else if (u_entry->ourMask & FACTION_MASK_ALLIANCE)
houseid = 1; // human auction house
else if (u_entry->ourMask & FACTION_MASK_HORDE)
houseid = 6; // orc auction house
else
houseid = 7; // goblin auction house
break;
}
}
}
return sAuctionHouseStore.LookupEntry(houseid);
}
void AuctionHouseObject::AddAuction(AuctionEntry* auction)
{
ASSERT(auction);
AuctionsMap[auction->Id] = auction;
sScriptMgr->OnAuctionAdd(this, auction);
}
bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction)
{
bool wasInMap = AuctionsMap.erase(auction->Id) ? true : false;
sScriptMgr->OnAuctionRemove(this, auction);
// we need to delete the entry, it is not referenced any more
delete auction;
auction = NULL;
return wasInMap;
}
void AuctionHouseObject::Update()
{
time_t checkTime = sWorld->GetGameTime() + 60;
///- Handle expired auctions
// If storage is empty, no need to update. next == NULL in this case.
if (AuctionsMap.empty())
return;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
for (AuctionEntryMap::iterator itr, iter = AuctionsMap.begin(); iter != AuctionsMap.end(); )
{
itr = iter++;
AuctionEntry* auction = (*itr).second;
if (auction->expire_time > checkTime)
continue;
///- Either cancel the auction if there was no bidder
if (auction->bidder == 0)
{
sAuctionMgr->SendAuctionExpiredMail(auction, trans);
sScriptMgr->OnAuctionExpire(this, auction);
}
///- Or perform the transaction
else
{
//we should send an "item sold" message if the seller is online
//we send the item to the winner
//we send the money to the seller
sAuctionMgr->SendAuctionSuccessfulMail(auction, trans);
sAuctionMgr->SendAuctionWonMail(auction, trans);
sScriptMgr->OnAuctionSuccessful(this, auction);
}
///- In any case clear the auction
auction->DeleteFromDB(trans);
sAuctionMgr->RemoveAItem(auction->item_guidlow);
RemoveAuction(auction);
}
CharacterDatabase.CommitTransaction(trans);
}
void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
{
AuctionEntry* Aentry = itr->second;
if (Aentry && Aentry->bidder == player->GetGUIDLow())
{
if (itr->second->BuildAuctionInfo(data))
++count;
++totalcount;
}
}
}
void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
{
AuctionEntry* Aentry = itr->second;
if (Aentry && Aentry->owner == player->GetGUIDLow())
{
if (Aentry->BuildAuctionInfo(data))
++count;
++totalcount;
}
}
}
bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount, uint8 getAll)
{
uint32 itrcounter = 0;
// pussywizard: optimization, this is a simplified case
if (itemClass == 0xffffffff && itemSubClass == 0xffffffff && inventoryType == 0xffffffff && quality == 0xffffffff && levelmin == 0x00 && levelmax == 0x00 && usable == 0x00 && wsearchedname.empty())
{
totalcount = Getcount();
if (listfrom < totalcount)
{
AuctionEntryMap::iterator itr = AuctionsMap.begin();
std::advance(itr, listfrom);
for (; itr != AuctionsMap.end(); ++itr)
{
itr->second->BuildAuctionInfo(data);
if ((++count) >= 50)
break;
}
}
return true;
}
time_t curTime = sWorld->GetGameTime();
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
{
if (AsyncAuctionListingMgr::IsAuctionListingAllowed() == false) // pussywizard: World::Update is waiting for us...
if ((itrcounter++) % 100 == 0) // check condition every 100 iterations
if (avgDiffTracker.getAverage() >= 30 || getMSTimeDiff(World::GetGameTimeMS(), getMSTime()) >= 10) // pussywizard: stop immediately if diff is high or waiting too long
return false;
AuctionEntry* Aentry = itr->second;
// Skip expired auctions
if (Aentry->expire_time < curTime)
continue;
Item* item = sAuctionMgr->GetAItem(Aentry->item_guidlow);
if (!item)
continue;
ItemTemplate const* proto = item->GetTemplate();
if (itemClass != 0xffffffff && proto->Class != itemClass)
continue;
if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass)
continue;
if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType)
{
// xinef: exception, robes are counted as chests
if (inventoryType != INVTYPE_CHEST || proto->InventoryType != INVTYPE_ROBE)
continue;
}
if (quality != 0xffffffff && proto->Quality != quality)
continue;
if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax)))
continue;
if (usable != 0x00)
{
if (player->CanUseItem(item) != EQUIP_ERR_OK)
continue;
// xinef: check already learded recipes and pets
if (proto->Spells[1].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID && player->HasSpell(proto->Spells[1].SpellId))
continue;
}
// Allow search by suffix (ie: of the Monkey) or partial name (ie: Monkey)
// No need to do any of this if no search term was entered
if (!wsearchedname.empty())
{
std::string name = proto->Name1;
if (name.empty())
continue;
// DO NOT use GetItemEnchantMod(proto->RandomProperty) as it may return a result
// that matches the search but it may not equal item->GetItemRandomPropertyId()
// used in BuildAuctionInfo() which then causes wrong items to be listed
int32 propRefID = item->GetItemRandomPropertyId();
if (propRefID)
{
// Append the suffix to the name (ie: of the Monkey) if one exists
// These are found in ItemRandomSuffix.dbc and ItemRandomProperties.dbc
// even though the DBC name seems misleading
char* const* suffix = NULL;
if (propRefID < 0)
{
const ItemRandomSuffixEntry* itemRandEntry = sItemRandomSuffixStore.LookupEntry(-item->GetItemRandomPropertyId());
if (itemRandEntry)
suffix = itemRandEntry->nameSuffix;
}
else
{
const ItemRandomPropertiesEntry* itemRandEntry = sItemRandomPropertiesStore.LookupEntry(item->GetItemRandomPropertyId());
if (itemRandEntry)
suffix = itemRandEntry->nameSuffix;
}
// dbc local name
if (suffix)
{
// Append the suffix (ie: of the Monkey) to the name using localization
// or default enUS if localization is invalid
name += ' ';
name += suffix[LOCALE_enUS];
}
}
// Perform the search (with or without suffix)
if (!Utf8FitTo(name, wsearchedname))
continue;
}
// Add the item if no search term or if entered search term was found
if (count < 50 && totalcount >= listfrom)
{
++count;
Aentry->BuildAuctionInfo(data);
}
++totalcount;
}
return true;
}
//this function inserts to WorldPacket auction's data
bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const
{
Item* item = sAuctionMgr->GetAItem(item_guidlow);
if (!item)
{
sLog->outError("AuctionEntry::BuildAuctionInfo: Auction %u has a non-existent item: %u", Id, item_guidlow);
return false;
}
data << uint32(Id);
data << uint32(item->GetEntry());
for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; ++i)
{
data << uint32(item->GetEnchantmentId(EnchantmentSlot(i)));
data << uint32(item->GetEnchantmentDuration(EnchantmentSlot(i)));
data << uint32(item->GetEnchantmentCharges(EnchantmentSlot(i)));
}
data << int32(item->GetItemRandomPropertyId()); // Random item property id
data << uint32(item->GetItemSuffixFactor()); // SuffixFactor
data << uint32(item->GetCount()); // item->count
data << uint32(item->GetSpellCharges()); // item->charge FFFFFFF
data << uint32(0); // Unknown
data << uint64(owner); // Auction->owner
data << uint32(startbid); // Auction->startbid (not sure if useful)
data << uint32(bid ? GetAuctionOutBid() : 0);
// Minimal outbid
data << uint32(buyout); // Auction->buyout
data << uint32((expire_time - time(NULL)) * IN_MILLISECONDS); // time left
data << uint64(bidder); // auction->bidder current
data << uint32(bid); // current bid
return true;
}
uint32 AuctionEntry::GetAuctionCut() const
{
int32 cut = int32(CalculatePct(bid, auctionHouseEntry->cutPercent) * sWorld->getRate(RATE_AUCTION_CUT));
return std::max(cut, 0);
}
/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
uint32 AuctionEntry::GetAuctionOutBid() const
{
uint32 outbid = CalculatePct(bid, 5);
return outbid ? outbid : 1;
}
void AuctionEntry::DeleteFromDB(SQLTransaction& trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION);
stmt->setUInt32(0, Id);
trans->Append(stmt);
}
void AuctionEntry::SaveToDB(SQLTransaction& trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION);
stmt->setUInt32(0, Id);
stmt->setUInt32(1, auctioneer);
stmt->setUInt32(2, item_guidlow);
stmt->setUInt32(3, owner);
stmt->setUInt32 (4, buyout);
stmt->setUInt32(5, uint32(expire_time));
stmt->setUInt32(6, bidder);
stmt->setUInt32 (7, bid);
stmt->setUInt32 (8, startbid);
stmt->setUInt32 (9, deposit);
trans->Append(stmt);
}
bool AuctionEntry::LoadFromDB(Field* fields)
{
Id = fields[0].GetUInt32();
auctioneer = fields[1].GetUInt32();
item_guidlow = fields[2].GetUInt32();
item_template = fields[3].GetUInt32();
itemCount = fields[4].GetUInt32();
owner = fields[5].GetUInt32();
buyout = fields[6].GetUInt32();
expire_time = fields[7].GetUInt32();
bidder = fields[8].GetUInt32();
bid = fields[9].GetUInt32();
startbid = fields[10].GetUInt32();
deposit = fields[11].GetUInt32();
CreatureData const* auctioneerData = sObjectMgr->GetCreatureData(auctioneer);
if (!auctioneerData)
{
sLog->outError("Auction %u has not a existing auctioneer (GUID : %u)", Id, auctioneer);
return false;
}
CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo)
{
sLog->outError("Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", Id, auctioneer, auctioneerData->id);
return false;
}
factionTemplateId = auctioneerInfo->faction;
auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(factionTemplateId);
if (!auctionHouseEntry)
{
sLog->outError("Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u", Id, auctioneer, auctioneerData->id, factionTemplateId);
return false;
}
// check if sold item exists for guid
// and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
if (!sAuctionMgr->GetAItem(item_guidlow))
{
sLog->outError("Auction %u has not a existing item : %u", Id, item_guidlow);
return false;
}
return true;
}
bool AuctionEntry::LoadFromFieldList(Field* fields)
{
// Loads an AuctionEntry item from a field list. Unlike "LoadFromDB()", this one
// does not require the AuctionEntryMap to have been loaded with items. It simply
// acts as a wrapper to fill out an AuctionEntry struct from a field list
Id = fields[0].GetUInt32();
auctioneer = fields[1].GetUInt32();
item_guidlow = fields[2].GetUInt32();
item_template = fields[3].GetUInt32();
itemCount = fields[4].GetUInt32();
owner = fields[5].GetUInt32();
buyout = fields[6].GetUInt32();
expire_time = fields[7].GetUInt32();
bidder = fields[8].GetUInt32();
bid = fields[9].GetUInt32();
startbid = fields[10].GetUInt32();
deposit = fields[11].GetUInt32();
CreatureData const* auctioneerData = sObjectMgr->GetCreatureData(auctioneer);
if (!auctioneerData)
{
sLog->outError("AuctionEntry::LoadFromFieldList() - Auction %u has not a existing auctioneer (GUID : %u)", Id, auctioneer);
return false;
}
CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo)
{
sLog->outError("AuctionEntry::LoadFromFieldList() - Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", Id, auctioneer, auctioneerData->id);
return false;
}
factionTemplateId = auctioneerInfo->faction;
auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(factionTemplateId);
if (!auctionHouseEntry)
{
sLog->outError("AuctionEntry::LoadFromFieldList() - Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u", Id, auctioneer, auctioneerData->id, factionTemplateId);
return false;
}
return true;
}
std::string AuctionEntry::BuildAuctionMailSubject(MailAuctionAnswers response) const
{
std::ostringstream strm;
strm << item_template << ":0:" << response << ':' << Id << ':' << itemCount;
return strm.str();
}
std::string AuctionEntry::BuildAuctionMailBody(uint32 lowGuid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut)
{
std::ostringstream strm;
strm.width(16);
strm << std::right << std::hex << MAKE_NEW_GUID(lowGuid, 0, HIGHGUID_PLAYER); // HIGHGUID_PLAYER always present, even for empty guids
strm << std::dec << ':' << bid << ':' << buyout;
strm << ':' << deposit << ':' << cut;
return strm.str();
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef _AUCTION_HOUSE_MGR_H
#define _AUCTION_HOUSE_MGR_H
#include <ace/Singleton.h>
#include "Common.h"
#include "DatabaseEnv.h"
#include "DBCStructure.h"
#include "EventProcessor.h"
#include "WorldPacket.h"
class Item;
class Player;
#define MIN_AUCTION_TIME (12*HOUR)
#define MAX_AUCTION_ITEMS 160
enum AuctionError
{
ERR_AUCTION_OK = 0,
ERR_AUCTION_INVENTORY = 1,
ERR_AUCTION_DATABASE_ERROR = 2,
ERR_AUCTION_NOT_ENOUGHT_MONEY = 3,
ERR_AUCTION_ITEM_NOT_FOUND = 4,
ERR_AUCTION_HIGHER_BID = 5,
ERR_AUCTION_BID_INCREMENT = 7,
ERR_AUCTION_BID_OWN = 10,
ERR_AUCTION_RESTRICTED_ACCOUNT = 13
};
enum AuctionAction
{
AUCTION_SELL_ITEM = 0,
AUCTION_CANCEL = 1,
AUCTION_PLACE_BID = 2
};
enum MailAuctionAnswers
{
AUCTION_OUTBIDDED = 0,
AUCTION_WON = 1,
AUCTION_SUCCESSFUL = 2,
AUCTION_EXPIRED = 3,
AUCTION_CANCELLED_TO_BIDDER = 4,
AUCTION_CANCELED = 5,
AUCTION_SALE_PENDING = 6
};
struct AuctionEntry
{
uint32 Id;
uint32 auctioneer; // creature low guid
uint32 item_guidlow;
uint32 item_template;
uint32 itemCount;
uint32 owner;
uint32 startbid; //maybe useless
uint32 bid;
uint32 buyout;
time_t expire_time;
uint32 bidder;
uint32 deposit; //deposit can be calculated only when creating auction
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
uint32 factionTemplateId;
// helpers
uint32 GetHouseId() const { return auctionHouseEntry->houseId; }
uint32 GetHouseFaction() const { return auctionHouseEntry->faction; }
uint32 GetAuctionCut() const;
uint32 GetAuctionOutBid() const;
bool BuildAuctionInfo(WorldPacket & data) const;
void DeleteFromDB(SQLTransaction& trans) const;
void SaveToDB(SQLTransaction& trans) const;
bool LoadFromDB(Field* fields);
bool LoadFromFieldList(Field* fields);
std::string BuildAuctionMailSubject(MailAuctionAnswers response) const;
static std::string BuildAuctionMailBody(uint32 lowGuid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut);
};
//this class is used as auctionhouse instance
class AuctionHouseObject
{
public:
// Initialize storage
AuctionHouseObject() { next = AuctionsMap.begin(); }
~AuctionHouseObject()
{
for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
delete itr->second;
}
typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
uint32 Getcount() const { return AuctionsMap.size(); }
AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();}
AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();}
AuctionEntry* GetAuction(uint32 id) const
{
AuctionEntryMap::const_iterator itr = AuctionsMap.find(id);
return itr != AuctionsMap.end() ? itr->second : NULL;
}
void AddAuction(AuctionEntry* auction);
bool RemoveAuction(AuctionEntry* auction);
void Update();
void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
bool BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount, uint8 getAll);
private:
AuctionEntryMap AuctionsMap;
// storage for "next" auction item for next Update()
AuctionEntryMap::const_iterator next;
};
class AuctionHouseMgr
{
friend class ACE_Singleton<AuctionHouseMgr, ACE_Null_Mutex>;
private:
AuctionHouseMgr();
~AuctionHouseMgr();
public:
typedef UNORDERED_MAP<uint32, Item*> ItemMap;
AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId);
AuctionHouseObject* GetBidsMap(uint32 factionTemplateId);
Item* GetAItem(uint32 id)
{
ItemMap::const_iterator itr = mAitems.find(id);
if (itr != mAitems.end())
return itr->second;
return NULL;
}
//auction messages
void SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans);
void SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans);
void SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans);
void SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans);
void SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans);
void SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans);
static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count);
static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId);
public:
//load first auction items, because of check if item exists, when loading
void LoadAuctionItems();
void LoadAuctions();
void AddAItem(Item* it);
bool RemoveAItem(uint32 id, bool deleteFromDB = false);
void Update();
private:
AuctionHouseObject mHordeAuctions;
AuctionHouseObject mAllianceAuctions;
AuctionHouseObject mNeutralAuctions;
ItemMap mAitems;
};
#define sAuctionMgr ACE_Singleton<AuctionHouseMgr, ACE_Null_Mutex>::instance()
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef BATTLEFIELD_H_
#define BATTLEFIELD_H_
#include "Utilities/Util.h"
#include "SharedDefines.h"
#include "ZoneScript.h"
#include "WorldPacket.h"
#include "GameObject.h"
#include "Battleground.h"
#include "ObjectAccessor.h"
enum BattlefieldTypes
{
BATTLEFIELD_WG, // Wintergrasp
BATTLEFIELD_TB, // Tol Barad (cataclysm)
};
enum BattlefieldIDs
{
BATTLEFIELD_BATTLEID_WG = 1, // Wintergrasp battle
};
enum BattlefieldObjectiveStates
{
BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL = 0,
BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE,
BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE,
BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_ALLIANCE_CHALLENGE,
BF_CAPTUREPOINT_OBJECTIVESTATE_NEUTRAL_HORDE_CHALLENGE,
BF_CAPTUREPOINT_OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE,
BF_CAPTUREPOINT_OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE,
};
enum BattlefieldSounds
{
BF_HORDE_WINS = 8454,
BF_ALLIANCE_WINS = 8455,
BF_START = 3439
};
enum BattlefieldTimers
{
BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL = 1000
};
const uint32 BattlefieldFactions[BG_TEAMS_COUNT] =
{
1732, // Alliance
1735 // Horde
};
// some class predefs
class Player;
class GameObject;
class WorldPacket;
class Creature;
class Unit;
class Battlefield;
class BfGraveyard;
typedef UNORDERED_SET<uint64> GuidSet;
typedef std::vector<BfGraveyard*> GraveyardVect;
typedef std::map<uint64, time_t> PlayerTimerMap;
class BfCapturePoint
{
public:
BfCapturePoint(Battlefield* bf);
virtual void FillInitialWorldStates(WorldPacket& /*data*/) {}
// Send world state update to all players present
void SendUpdateWorldState(uint32 field, uint32 value);
// Send kill notify to players in the controlling faction
void SendObjectiveComplete(uint32 id, uint64 guid);
// Used when player is activated/inactivated in the area
virtual bool HandlePlayerEnter(Player* player);
virtual GuidSet::iterator HandlePlayerLeave(Player* player);
//virtual void HandlePlayerActivityChanged(Player* player);
// Checks if player is in range of a capture credit marker
bool IsInsideObjective(Player* player) const;
// Returns true if the state of the objective has changed, in this case, the OutdoorPvP must send a world state ui update.
virtual bool Update(uint32 diff);
virtual void ChangeTeam(TeamId /*oldTeam*/) {}
virtual void SendChangePhase();
bool SetCapturePointData(GameObject* capturePoint);
GameObject* GetCapturePointGo() { return ObjectAccessor::GetObjectInWorld(m_capturePoint, (GameObject*)NULL); }
GameObject* GetCapturePointGo(WorldObject* obj) { return ObjectAccessor::GetGameObject(*obj, m_capturePoint); }
TeamId GetTeamId() { return m_team; }
protected:
bool DelCapturePoint();
// active Players in the area of the objective, 0 - alliance, 1 - horde
GuidSet m_activePlayers[2];
// Total shift needed to capture the objective
float m_maxValue;
float m_minValue;
// Maximum speed of capture
float m_maxSpeed;
// The status of the objective
float m_value;
TeamId m_team;
// Objective states
BattlefieldObjectiveStates m_OldState;
BattlefieldObjectiveStates m_State;
// Neutral value on capture bar
uint32 m_neutralValuePct;
// Pointer to the Battlefield this objective belongs to
Battlefield* m_Bf;
// Capture point entry
uint32 m_capturePointEntry;
// Gameobject related to that capture point
uint64 m_capturePoint;
};
class BfGraveyard
{
public:
BfGraveyard(Battlefield* Bf);
// Method to changing who controls the graveyard
void GiveControlTo(TeamId team);
TeamId GetControlTeamId() const { return m_ControlTeam; }
// Find the nearest graveyard to a player
float GetDistance(Player* player);
// Initialize the graveyard
void Initialize(TeamId startcontrol, uint32 gy);
// Set spirit service for the graveyard
void SetSpirit(Creature* spirit, TeamId team);
// Add a player to the graveyard
void AddPlayer(uint64 player_guid);
// Remove a player from the graveyard
void RemovePlayer(uint64 player_guid);
// Resurrect players
void Resurrect();
// Move players waiting to that graveyard on the nearest one
void RelocateDeadPlayers();
// Check if this graveyard has a spirit guide
bool HasNpc(uint64 guid)
{
if (!m_SpiritGuide[0] && !m_SpiritGuide[1])
return false;
// performance
/*if (!ObjectAccessor::FindUnit(m_SpiritGuide[0]) &&
!ObjectAccessor::FindUnit(m_SpiritGuide[1]))
return false;*/
return (m_SpiritGuide[0] == guid || m_SpiritGuide[1] == guid);
}
// Check if a player is in this graveyard's resurrect queue
bool HasPlayer(uint64 guid) const { return m_ResurrectQueue.find(guid) != m_ResurrectQueue.end(); }
// Get the graveyard's ID.
uint32 GetGraveyardId() const { return m_GraveyardId; }
protected:
TeamId m_ControlTeam;
uint32 m_GraveyardId;
uint64 m_SpiritGuide[2];
GuidSet m_ResurrectQueue;
Battlefield* m_Bf;
};
class Battlefield : public ZoneScript
{
friend class BattlefieldMgr;
public:
/// Constructor
Battlefield();
/// Destructor
virtual ~Battlefield();
/// typedef of map witch store capturepoint and the associate gameobject entry
typedef std::map<uint32 /*lowguid */, BfCapturePoint*> BfCapturePointMap;
/// Call this to init the Battlefield
virtual bool SetupBattlefield() { return true; }
/// Update data of a worldstate to all players present in zone
void SendUpdateWorldState(uint32 field, uint32 value);
/**
* \brief Called every time for update bf data and time
* - Update timer for start/end battle
* - Invite player in zone to queue m_StartGroupingTimer minutes before start
* - Kick Afk players
* \param diff : time ellapsed since last call (in ms)
*/
virtual bool Update(uint32 diff);
/// Invite all players in zone to join the queue, called x minutes before battle start in Update()
void InvitePlayersInZoneToQueue();
/// Invite all players in queue to join battle on battle start
void InvitePlayersInQueueToWar();
/// Invite all players in zone to join battle on battle start
void InvitePlayersInZoneToWar();
/// Called when a Unit is kill in battlefield zone
virtual void HandleKill(Player* /*killer*/, Unit* /*killed*/) {};
uint32 GetTypeId() { return m_TypeId; }
uint32 GetZoneId() { return m_ZoneId; }
void TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2 = 0);
/// Return true if battle is start, false if battle is not started
bool IsWarTime() { return m_isActive; }
/// Enable or Disable battlefield
void ToggleBattlefield(bool enable) { m_IsEnabled = enable; }
/// Return if battlefield is enable
bool IsEnabled() { return m_IsEnabled; }
/**
* \brief Kick player from battlefield and teleport him to kick-point location
* \param guid : guid of player who must be kick
*/
void KickPlayerFromBattlefield(uint64 guid);
/// Called when player (player) enter in zone
void HandlePlayerEnterZone(Player* player, uint32 zone);
/// Called when player (player) leave the zone
void HandlePlayerLeaveZone(Player* player, uint32 zone);
// All-purpose data storage 64 bit
virtual uint64 GetData64(uint32 dataId) const { return m_Data64[dataId]; }
virtual void SetData64(uint32 dataId, uint64 value) { m_Data64[dataId] = value; }
// All-purpose data storage 32 bit
virtual uint32 GetData(uint32 dataId) const { return m_Data32[dataId]; }
virtual void SetData(uint32 dataId, uint32 value) { m_Data32[dataId] = value; }
virtual void UpdateData(uint32 index, int32 pad) { m_Data32[index] += pad; }
// Battlefield - generic methods
TeamId GetDefenderTeam() { return m_DefenderTeam; }
TeamId GetAttackerTeam() { return TeamId(1 - m_DefenderTeam); }
TeamId GetOtherTeam(TeamId team) { return (team == TEAM_HORDE ? TEAM_ALLIANCE : TEAM_HORDE); }
void SetDefenderTeam(TeamId team) { m_DefenderTeam = team; }
// Group methods
/**
* \brief Find a not full battlefield group, if there is no, create one
* \param TeamId : Id of player team for who we search a group (player->GetTeamId())
*/
Group* GetFreeBfRaid(TeamId TeamId);
/// Return battlefield group where player is.
Group* GetGroupPlayer(uint64 guid, TeamId TeamId);
/// Force player to join a battlefield group
bool AddOrSetPlayerToCorrectBfGroup(Player* player);
// Graveyard methods
// Find which graveyard the player must be teleported to to be resurrected by spiritguide
WorldSafeLocsEntry const * GetClosestGraveyard(Player* player);
virtual void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid);
void RemovePlayerFromResurrectQueue(uint64 player_guid);
void SetGraveyardNumber(uint32 number) { m_GraveyardList.resize(number); }
BfGraveyard* GetGraveyardById(uint32 id) const;
// Misc methods
Creature* SpawnCreature(uint32 entry, float x, float y, float z, float o, TeamId teamId);
Creature* SpawnCreature(uint32 entry, Position pos, TeamId teamId);
GameObject* SpawnGameObject(uint32 entry, float x, float y, float z, float o);
// Script-methods
/// Called on start
virtual void OnBattleStart() {};
/// Called at the end of battle
virtual void OnBattleEnd(bool /*endByTimer*/) {};
/// Called x minutes before battle start when player in zone are invite to join queue
virtual void OnStartGrouping() {};
/// Called when a player accept to join the battle
virtual void OnPlayerJoinWar(Player* /*player*/) {};
/// Called when a player leave the battle
virtual void OnPlayerLeaveWar(Player* /*player*/) {};
/// Called when a player leave battlefield zone
virtual void OnPlayerLeaveZone(Player* /*player*/) {};
/// Called when a player enter in battlefield zone
virtual void OnPlayerEnterZone(Player* /*player*/) {};
void SendWarningToAllInZone(uint32 entry);
void SendWarningToPlayer(Player* player, uint32 entry);
void PlayerAcceptInviteToQueue(Player* player);
void PlayerAcceptInviteToWar(Player* player);
uint32 GetBattleId() { return m_BattleId; }
void AskToLeaveQueue(Player* player);
//virtual void DoCompleteOrIncrementAchievement(uint32 /*achievement*/, Player* /*player*/, uint8 /*incrementNumber = 1*/) {};
/// Send all worldstate data to all player in zone.
virtual void SendInitWorldStatesToAll() = 0;
virtual void FillInitialWorldStates(WorldPacket& /*data*/) = 0;
/// Return if we can use mount in battlefield
bool CanFlyIn() { return !m_isActive; }
void SendAreaSpiritHealerQueryOpcode(Player* player, const uint64 & guid);
void StartBattle();
void EndBattle(bool endByTimer);
void HideNpc(Creature* creature);
void ShowNpc(Creature* creature, bool aggressive);
GraveyardVect GetGraveyardVector() { return m_GraveyardList; }
uint32 GetTimer() { return m_Timer; }
void SetTimer(uint32 timer) { m_Timer = timer; }
void DoPlaySoundToAll(uint32 SoundID);
void InvitePlayerToQueue(Player* player);
void InvitePlayerToWar(Player* player);
void InitStalker(uint32 entry, float x, float y, float z, float o);
protected:
uint64 StalkerGuid;
uint32 m_Timer; // Global timer for event
bool m_IsEnabled;
bool m_isActive;
TeamId m_DefenderTeam;
// Map of the objectives belonging to this OutdoorPvP
BfCapturePointMap m_capturePoints;
// Players info maps
GuidSet m_players[BG_TEAMS_COUNT]; // Players in zone
GuidSet m_PlayersInQueue[BG_TEAMS_COUNT]; // Players in the queue
GuidSet m_PlayersInWar[BG_TEAMS_COUNT]; // Players in WG combat
PlayerTimerMap m_InvitedPlayers[BG_TEAMS_COUNT];
PlayerTimerMap m_PlayersWillBeKick[BG_TEAMS_COUNT];
// Variables that must exist for each battlefield
uint32 m_TypeId; // See enum BattlefieldTypes
uint32 m_BattleId; // BattleID (for packet)
uint32 m_ZoneId; // ZoneID of Wintergrasp = 4197
uint32 m_MapId; // MapId where is Battlefield
uint32 m_MaxPlayer; // Maximum number of player that participated to Battlefield
uint32 m_MinPlayer; // Minimum number of player for Battlefield start
uint32 m_MinLevel; // Required level to participate at Battlefield
uint32 m_BattleTime; // Length of a battle
uint32 m_NoWarBattleTime; // Time between two battles
uint32 m_RestartAfterCrash; // Delay to restart Wintergrasp if the server crashed during a running battle.
uint32 m_TimeForAcceptInvite;
uint32 m_uiKickDontAcceptTimer;
WorldLocation KickPosition; // Position where players are teleported if they switch to afk during the battle or if they don't accept invitation
uint32 m_uiKickAfkPlayersTimer; // Timer for check Afk in war
// Graveyard variables
GraveyardVect m_GraveyardList; // Vector witch contain the different GY of the battle
uint32 m_LastResurectTimer; // Timer for resurect player every 30 sec
uint32 m_StartGroupingTimer; // Timer for invite players in area 15 minute before start battle
bool m_StartGrouping; // bool for know if all players in area has been invited
GuidSet m_Groups[BG_TEAMS_COUNT]; // Contain different raid group
std::vector<uint64> m_Data64;
std::vector<uint32> m_Data32;
void KickAfkPlayers();
// use for switch off all worldstate for client
virtual void SendRemoveWorldStates(Player* /*player*/) {}
// use for send a packet for all player list
void BroadcastPacketToZone(WorldPacket& data) const;
void BroadcastPacketToQueue(WorldPacket& data) const;
void BroadcastPacketToWar(WorldPacket& data) const;
// CapturePoint system
void AddCapturePoint(BfCapturePoint* cp, GameObject* go) { m_capturePoints[go->GetEntry()] = cp; }
BfCapturePoint* GetCapturePoint(uint32 lowguid) const
{
Battlefield::BfCapturePointMap::const_iterator itr = m_capturePoints.find(lowguid);
if (itr != m_capturePoints.end())
return itr->second;
return NULL;
}
void RegisterZone(uint32 zoneid);
bool HasPlayer(Player* player) const;
void TeamCastSpell(TeamId team, int32 spellId);
};
#endif

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "Common.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
#include "Opcodes.h"
#include "Player.h"
//This send to player windows for invite player to join the war
//Param1:(BattleId) the BattleId of Bf
//Param2:(ZoneId) the zone where the battle is (4197 for wg)
//Param3:(time) Time in second that the player have for accept
void WorldSession::SendBfInvitePlayerToWar(uint32 BattleId, uint32 ZoneId, uint32 p_time)
{
//Send packet
WorldPacket data(SMSG_BATTLEFIELD_MGR_ENTRY_INVITE, 12);
data << uint32(BattleId);
data << uint32(ZoneId);
data << uint32((time(NULL) + p_time));
//Sending the packet to player
SendPacket(&data);
}
//This send invitation to player to join the queue
//Param1:(BattleId) the BattleId of Bf
void WorldSession::SendBfInvitePlayerToQueue(uint32 BattleId)
{
WorldPacket data(SMSG_BATTLEFIELD_MGR_QUEUE_INVITE, 5);
data << uint32(BattleId);
data << uint8(1); //warmup ? used ?
//Sending packet to player
SendPacket(&data);
}
//This send packet for inform player that he join queue
//Param1:(BattleId) the BattleId of Bf
//Param2:(ZoneId) the zone where the battle is (4197 for wg)
//Param3:(CanQueue) if able to queue
//Param4:(Full) on log in is full
void WorldSession::SendBfQueueInviteResponse(uint32 BattleId,uint32 ZoneId, bool CanQueue, bool Full)
{
WorldPacket data(SMSG_BATTLEFIELD_MGR_QUEUE_REQUEST_RESPONSE, 11);
data << uint32(BattleId);
data << uint32(ZoneId);
data << uint8((CanQueue ? 1 : 0)); //Accepted //0 you cannot queue wg //1 you are queued
data << uint8((Full ? 0 : 1)); //Logging In //0 wg full //1 queue for upcoming
data << uint8(1); //Warmup
SendPacket(&data);
}
//This is call when player accept to join war
//Param1:(BattleId) the BattleId of Bf
void WorldSession::SendBfEntered(uint32 BattleId)
{
// m_PlayerInWar[player->GetTeamId()].insert(player->GetGUID());
WorldPacket data(SMSG_BATTLEFIELD_MGR_ENTERED, 7);
data << uint32(BattleId);
data << uint8(1); //unk
data << uint8(1); //unk
data << uint8(_player->isAFK() ? 1 : 0); //Clear AFK
SendPacket(&data);
}
void WorldSession::SendBfLeaveMessage(uint32 BattleId, BFLeaveReason reason)
{
WorldPacket data(SMSG_BATTLEFIELD_MGR_EJECTED, 7);
data << uint32(BattleId);
data << uint8(reason);//byte Reason
data << uint8(2);//byte BattleStatus
data << uint8(0);//bool Relocated
SendPacket(&data);
}
//Send by client when he click on accept for queue
void WorldSession::HandleBfQueueInviteResponse(WorldPacket & recvData)
{
uint32 BattleId;
uint8 Accepted;
recvData >> BattleId >> Accepted;
//sLog->outError("HandleQueueInviteResponse: BattleID:%u Accepted:%u", BattleId, Accepted);
Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId);
if (!Bf)
return;
if (Accepted)
{
Bf->PlayerAcceptInviteToQueue(_player);
}
}
//Send by client on clicking in accept or refuse of invitation windows for join game
void WorldSession::HandleBfEntryInviteResponse(WorldPacket & recvData)
{
uint32 BattleId;
uint8 Accepted;
recvData >> BattleId >> Accepted;
//sLog->outError("HandleBattlefieldInviteResponse: BattleID:%u Accepted:%u", BattleId, Accepted);
Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId);
if (!Bf)
return;
//If player accept invitation
if (Accepted)
{
Bf->PlayerAcceptInviteToWar(_player);
}
else
{
if (_player->GetZoneId() == Bf->GetZoneId())
Bf->KickPlayerFromBattlefield(_player->GetGUID());
}
}
void WorldSession::HandleBfExitRequest(WorldPacket & recvData)
{
uint32 BattleId;
recvData >> BattleId;
//sLog->outError("HandleBfExitRequest: BattleID:%u ", BattleId);
Battlefield* Bf = sBattlefieldMgr->GetBattlefieldByBattleId(BattleId);
if (!Bf)
return;
Bf->AskToLeaveQueue(_player);
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C)
* Copyright (C)
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "BattlefieldMgr.h"
#include "Zones/BattlefieldWG.h"
#include "ObjectMgr.h"
#include "Player.h"
BattlefieldMgr::BattlefieldMgr()
{
m_UpdateTimer = 0;
//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Instantiating BattlefieldMgr");
}
BattlefieldMgr::~BattlefieldMgr()
{
//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Deleting BattlefieldMgr");
for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr)
delete *itr;
}
void BattlefieldMgr::InitBattlefield()
{
Battlefield* pBf = new BattlefieldWG;
// respawn, init variables
if (!pBf->SetupBattlefield())
{
sLog->outString();
sLog->outString("Battlefield : Wintergrasp init failed.");
delete pBf;
}
else
{
m_BattlefieldSet.push_back(pBf);
sLog->outString();
sLog->outString("Battlefield : Wintergrasp successfully initiated.");
}
/* For Cataclysm: Tol Barad
pBf = new BattlefieldTB;
// respawn, init variables
if(!pBf->SetupBattlefield())
{
;//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Battlefield : Tol Barad init failed.");
delete pBf;
}
else
{
m_BattlefieldSet.push_back(pBf);
;//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Battlefield : Tol Barad successfully initiated.");
} */
}
void BattlefieldMgr::AddZone(uint32 zoneid, Battlefield *handle)
{
m_BattlefieldMap[zoneid] = handle;
}
void BattlefieldMgr::HandlePlayerEnterZone(Player * player, uint32 zoneid)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid);
if (itr == m_BattlefieldMap.end())
return;
if (itr->second->HasPlayer(player) || !itr->second->IsEnabled())
return;
itr->second->HandlePlayerEnterZone(player, zoneid);
;//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Player %u entered outdoorpvp id %u", player->GetGUIDLow(), itr->second->GetTypeId());
}
void BattlefieldMgr::HandlePlayerLeaveZone(Player * player, uint32 zoneid)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid);
if (itr == m_BattlefieldMap.end())
return;
// teleport: remove once in removefromworld, once in updatezone
if (!itr->second->HasPlayer(player))
return;
itr->second->HandlePlayerLeaveZone(player, zoneid);
;//sLog->outDebug(LOG_FILTER_BATTLEFIELD, "Player %u left outdoorpvp id %u", player->GetGUIDLow(), itr->second->GetTypeId());
}
Battlefield *BattlefieldMgr::GetBattlefieldToZoneId(uint32 zoneid)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneid);
if (itr == m_BattlefieldMap.end())
{
// no handle for this zone, return
return NULL;
}
if (!itr->second->IsEnabled())
return NULL;
return itr->second;
}
Battlefield *BattlefieldMgr::GetBattlefieldByBattleId(uint32 battleid)
{
for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr)
{
if ((*itr)->GetBattleId() == battleid)
return (*itr);
}
return NULL;
}
void BattlefieldMgr::Update(uint32 diff)
{
m_UpdateTimer += diff;
if (m_UpdateTimer > BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL)
{
for (BattlefieldSet::iterator itr = m_BattlefieldSet.begin(); itr != m_BattlefieldSet.end(); ++itr)
//if ((*itr)->IsEnabled())
(*itr)->Update(m_UpdateTimer);
m_UpdateTimer = 0;
}
}
ZoneScript *BattlefieldMgr::GetZoneScript(uint32 zoneId)
{
BattlefieldMap::iterator itr = m_BattlefieldMap.find(zoneId);
if (itr != m_BattlefieldMap.end())
return itr->second;
else
return NULL;
}

Some files were not shown because too many files have changed in this diff Show More