2022-03-05 12:44:06 +02:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "constants.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
#include "desc.h"
|
|
|
|
|
#include "char.h"
|
|
|
|
|
#include "char_manager.h"
|
|
|
|
|
#include "mob_manager.h"
|
|
|
|
|
#include "party.h"
|
|
|
|
|
#include "regen.h"
|
|
|
|
|
#include "p2p.h"
|
|
|
|
|
#include "dungeon.h"
|
|
|
|
|
#include "db.h"
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#include "xmas_event.h"
|
|
|
|
|
#include "questmanager.h"
|
|
|
|
|
#include "questlua.h"
|
|
|
|
|
#include "locale_service.h"
|
|
|
|
|
|
|
|
|
|
#ifndef __GNUC__
|
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
CHARACTER_MANAGER::CHARACTER_MANAGER() :
|
|
|
|
|
m_iVIDCount(0),
|
|
|
|
|
m_pkChrSelectedStone(NULL),
|
|
|
|
|
m_bUsePendingDestroy(false)
|
|
|
|
|
{
|
|
|
|
|
RegisterRaceNum(xmas::MOB_XMAS_FIRWORK_SELLER_VNUM);
|
|
|
|
|
RegisterRaceNum(xmas::MOB_SANTA_VNUM);
|
|
|
|
|
RegisterRaceNum(xmas::MOB_XMAS_TREE_VNUM);
|
|
|
|
|
|
|
|
|
|
m_iMobItemRate = 100;
|
|
|
|
|
m_iMobDamageRate = 100;
|
|
|
|
|
m_iMobGoldAmountRate = 100;
|
|
|
|
|
m_iMobGoldDropRate = 100;
|
|
|
|
|
m_iMobExpRate = 100;
|
|
|
|
|
|
|
|
|
|
m_iMobItemRatePremium = 100;
|
|
|
|
|
m_iMobGoldAmountRatePremium = 100;
|
|
|
|
|
m_iMobGoldDropRatePremium = 100;
|
|
|
|
|
m_iMobExpRatePremium = 100;
|
|
|
|
|
|
|
|
|
|
m_iUserDamageRate = 100;
|
|
|
|
|
m_iUserDamageRatePremium = 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CHARACTER_MANAGER::~CHARACTER_MANAGER()
|
|
|
|
|
{
|
|
|
|
|
Destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::Destroy()
|
|
|
|
|
{
|
|
|
|
|
itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.begin();
|
|
|
|
|
while (it != m_map_pkChrByVID.end()) {
|
|
|
|
|
LPCHARACTER ch = it->second;
|
|
|
|
|
M2_DESTROY_CHARACTER(ch); // m_map_pkChrByVID is changed here
|
|
|
|
|
it = m_map_pkChrByVID.begin();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::GracefulShutdown()
|
|
|
|
|
{
|
|
|
|
|
NAME_MAP::iterator it = m_map_pkPCChr.begin();
|
|
|
|
|
|
|
|
|
|
while (it != m_map_pkPCChr.end())
|
|
|
|
|
(it++)->second->Disconnect("GracefulShutdown");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD CHARACTER_MANAGER::AllocVID()
|
|
|
|
|
{
|
|
|
|
|
++m_iVIDCount;
|
|
|
|
|
return m_iVIDCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::CreateCharacter(const char * name, DWORD dwPID)
|
|
|
|
|
{
|
|
|
|
|
DWORD dwVID = AllocVID();
|
|
|
|
|
|
|
|
|
|
#ifdef M2_USE_POOL
|
|
|
|
|
LPCHARACTER ch = pool_.Construct();
|
|
|
|
|
#else
|
|
|
|
|
LPCHARACTER ch = M2_NEW CHARACTER;
|
|
|
|
|
#endif
|
|
|
|
|
ch->Create(name, dwVID, dwPID ? true : false);
|
|
|
|
|
|
|
|
|
|
m_map_pkChrByVID.insert(std::make_pair(dwVID, ch));
|
|
|
|
|
|
|
|
|
|
if (dwPID)
|
|
|
|
|
{
|
|
|
|
|
char szName[CHARACTER_NAME_MAX_LEN + 1];
|
|
|
|
|
str_lower(name, szName, sizeof(szName));
|
|
|
|
|
|
|
|
|
|
m_map_pkPCChr.insert(NAME_MAP::value_type(szName, ch));
|
|
|
|
|
m_map_pkChrByPID.insert(std::make_pair(dwPID, ch));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef DEBUG_ALLOC
|
|
|
|
|
void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch)
|
|
|
|
|
#else
|
|
|
|
|
void CHARACTER_MANAGER::DestroyCharacter(LPCHARACTER ch, const char* file, size_t line)
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
if (!ch)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// <Factor> Check whether it has been already deleted or not.
|
|
|
|
|
itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(ch->GetVID());
|
|
|
|
|
if (it == m_map_pkChrByVID.end()) {
|
2022-03-12 11:39:41 +02:00
|
|
|
|
sys_err("[CHARACTER_MANAGER::DestroyCharacter] <Factor> %d not found", (int)(ch->GetVID()));
|
2022-03-05 12:44:06 +02:00
|
|
|
|
return; // prevent duplicated destrunction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>Ҽӵ<D2BC> <20><><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD>.
|
|
|
|
|
if (ch->IsNPC() && !ch->IsPet() && ch->GetRider() == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (ch->GetDungeon())
|
|
|
|
|
{
|
|
|
|
|
ch->GetDungeon()->DeadCharacter(ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_bUsePendingDestroy)
|
|
|
|
|
{
|
|
|
|
|
m_set_pkChrPendingDestroy.insert(ch);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_map_pkChrByVID.erase(it);
|
|
|
|
|
|
|
|
|
|
if (true == ch->IsPC())
|
|
|
|
|
{
|
|
|
|
|
char szName[CHARACTER_NAME_MAX_LEN + 1];
|
|
|
|
|
|
|
|
|
|
str_lower(ch->GetName(), szName, sizeof(szName));
|
|
|
|
|
|
|
|
|
|
NAME_MAP::iterator it = m_map_pkPCChr.find(szName);
|
|
|
|
|
|
|
|
|
|
if (m_map_pkPCChr.end() != it)
|
|
|
|
|
m_map_pkPCChr.erase(it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (0 != ch->GetPlayerID())
|
|
|
|
|
{
|
|
|
|
|
itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(ch->GetPlayerID());
|
|
|
|
|
|
|
|
|
|
if (m_map_pkChrByPID.end() != it)
|
|
|
|
|
{
|
|
|
|
|
m_map_pkChrByPID.erase(it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UnregisterRaceNumMap(ch);
|
|
|
|
|
|
|
|
|
|
RemoveFromStateList(ch);
|
|
|
|
|
|
|
|
|
|
#ifdef M2_USE_POOL
|
|
|
|
|
pool_.Destroy(ch);
|
|
|
|
|
#else
|
|
|
|
|
#ifndef DEBUG_ALLOC
|
|
|
|
|
M2_DELETE(ch);
|
|
|
|
|
#else
|
|
|
|
|
M2_DELETE_EX(ch, file, line);
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::Find(DWORD dwVID)
|
|
|
|
|
{
|
|
|
|
|
itertype(m_map_pkChrByVID) it = m_map_pkChrByVID.find(dwVID);
|
|
|
|
|
|
|
|
|
|
if (m_map_pkChrByVID.end() == it)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
// <Factor> Added sanity check
|
|
|
|
|
LPCHARACTER found = it->second;
|
|
|
|
|
if (found != NULL && dwVID != (DWORD)found->GetVID()) {
|
|
|
|
|
sys_err("[CHARACTER_MANAGER::Find] <Factor> %u != %u", dwVID, (DWORD)found->GetVID());
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::Find(const VID & vid)
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER tch = Find((DWORD) vid);
|
|
|
|
|
|
|
|
|
|
if (!tch || tch->GetVID() != vid)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return tch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::FindByPID(DWORD dwPID)
|
|
|
|
|
{
|
|
|
|
|
itertype(m_map_pkChrByPID) it = m_map_pkChrByPID.find(dwPID);
|
|
|
|
|
|
|
|
|
|
if (m_map_pkChrByPID.end() == it)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
// <Factor> Added sanity check
|
|
|
|
|
LPCHARACTER found = it->second;
|
|
|
|
|
if (found != NULL && dwPID != found->GetPlayerID()) {
|
|
|
|
|
sys_err("[CHARACTER_MANAGER::FindByPID] <Factor> %u != %u", dwPID, found->GetPlayerID());
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::FindPC(const char * name)
|
|
|
|
|
{
|
|
|
|
|
char szName[CHARACTER_NAME_MAX_LEN + 1];
|
|
|
|
|
str_lower(name, szName, sizeof(szName));
|
|
|
|
|
NAME_MAP::iterator it = m_map_pkPCChr.find(szName);
|
|
|
|
|
|
|
|
|
|
if (it == m_map_pkPCChr.end())
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
// <Factor> Added sanity check
|
|
|
|
|
LPCHARACTER found = it->second;
|
|
|
|
|
if (found != NULL && strncasecmp(szName, found->GetName(), CHARACTER_NAME_MAX_LEN) != 0) {
|
|
|
|
|
sys_err("[CHARACTER_MANAGER::FindPC] <Factor> %s != %s", name, found->GetName());
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::SpawnMobRandomPosition(DWORD dwVnum, int lMapIndex)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
// <20>ֱ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20>ְ<EFBFBD><D6B0><EFBFBD>
|
|
|
|
|
{
|
|
|
|
|
if (dwVnum == 5001 && !quest::CQuestManager::instance().GetEventFlag("japan_regen"))
|
|
|
|
|
{
|
|
|
|
|
sys_log(1, "WAEGU[5001] regen disabled.");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD>¸<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20>ְ<EFBFBD> <20><>
|
|
|
|
|
{
|
|
|
|
|
if (dwVnum == 5002 && !quest::CQuestManager::instance().GetEventFlag("newyear_mob"))
|
|
|
|
|
{
|
|
|
|
|
sys_log(1, "HAETAE (new-year-mob) [5002] regen disabled.");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>̺<EFBFBD>Ʈ
|
|
|
|
|
{
|
|
|
|
|
if (dwVnum == 5004 && !quest::CQuestManager::instance().GetEventFlag("independence_day"))
|
|
|
|
|
{
|
|
|
|
|
sys_log(1, "INDEPENDECE DAY [5004] regen disabled.");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CMob * pkMob = CMobManager::instance().Get(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (!pkMob)
|
|
|
|
|
{
|
|
|
|
|
sys_err("no mob data for vnum %u", dwVnum);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!map_allow_find(lMapIndex))
|
|
|
|
|
{
|
|
|
|
|
sys_err("not allowed map %u", lMapIndex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPSECTREE_MAP pkSectreeMap = SECTREE_MANAGER::instance().GetMap(lMapIndex);
|
|
|
|
|
if (pkSectreeMap == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int i;
|
2022-03-12 11:39:41 +02:00
|
|
|
|
int x, y;
|
2022-03-05 12:44:06 +02:00
|
|
|
|
for (i=0; i<2000; i++)
|
|
|
|
|
{
|
2022-11-27 14:36:04 +02:00
|
|
|
|
x = Random::get(1, (pkSectreeMap->m_setting.iWidth / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseX;
|
|
|
|
|
y = Random::get(1, (pkSectreeMap->m_setting.iHeight / 100) - 1) * 100 + pkSectreeMap->m_setting.iBaseY;
|
2022-03-05 12:44:06 +02:00
|
|
|
|
//LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
|
|
|
|
|
LPSECTREE tree = pkSectreeMap->Find(x, y);
|
|
|
|
|
|
|
|
|
|
if (!tree)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
DWORD dwAttr = tree->GetAttribute(x, y);
|
|
|
|
|
|
|
|
|
|
if (IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (IS_SET(dwAttr, ATTR_BANPK))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == 2000)
|
|
|
|
|
{
|
|
|
|
|
sys_err("cannot find valid location");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
|
|
|
|
|
|
|
|
|
|
if (!sectree)
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "SpawnMobRandomPosition: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName);
|
|
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "SpawnMobRandomPosition: cannot create new character");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ch->SetProto(pkMob);
|
|
|
|
|
|
|
|
|
|
// if mob is npc with no empire assigned, assign to empire of map
|
|
|
|
|
if (pkMob->m_table.bType == CHAR_TYPE_NPC)
|
|
|
|
|
if (ch->GetEmpire() == 0)
|
|
|
|
|
ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex));
|
|
|
|
|
|
2022-11-27 14:36:04 +02:00
|
|
|
|
ch->SetRotation(Random::get(0, 360));
|
2022-03-05 12:44:06 +02:00
|
|
|
|
|
|
|
|
|
if (!ch->Show(lMapIndex, x, y, 0, false))
|
|
|
|
|
{
|
|
|
|
|
M2_DESTROY_CHARACTER(ch);
|
|
|
|
|
sys_err(0, "SpawnMobRandomPosition: cannot show monster");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char buf[512+1];
|
2022-03-12 11:39:41 +02:00
|
|
|
|
int local_x = x - pkSectreeMap->m_setting.iBaseX;
|
|
|
|
|
int local_y = y - pkSectreeMap->m_setting.iBaseY;
|
|
|
|
|
snprintf(buf, sizeof(buf), "spawn %s[%d] random position at %d %d %d %d (time: %ld)", ch->GetName(), dwVnum, x, y, local_x, local_y, get_global_time());
|
2022-03-05 12:44:06 +02:00
|
|
|
|
|
|
|
|
|
if (test_server)
|
|
|
|
|
SendNotice(buf);
|
|
|
|
|
|
|
|
|
|
sys_log(0, buf);
|
|
|
|
|
return (ch);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::SpawnMob(DWORD dwVnum, int lMapIndex, int x, int y, int z, bool bSpawnMotion, int iRot, bool bShow)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
const CMob * pkMob = CMobManager::instance().Get(dwVnum);
|
|
|
|
|
if (!pkMob)
|
|
|
|
|
{
|
|
|
|
|
sys_err("SpawnMob: no mob data for vnum %u", dwVnum);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(pkMob->m_table.bType == CHAR_TYPE_NPC || pkMob->m_table.bType == CHAR_TYPE_WARP || pkMob->m_table.bType == CHAR_TYPE_GOTO) || mining::IsVeinOfOre (dwVnum))
|
|
|
|
|
{
|
|
|
|
|
LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
|
|
|
|
|
|
|
|
|
|
if (!tree)
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "no sectree for spawn at %d %d mobvnum %d mapindex %d", x, y, dwVnum, lMapIndex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD dwAttr = tree->GetAttribute(x, y);
|
|
|
|
|
|
|
|
|
|
bool is_set = false;
|
|
|
|
|
|
|
|
|
|
if ( mining::IsVeinOfOre (dwVnum) ) is_set = IS_SET(dwAttr, ATTR_BLOCK);
|
|
|
|
|
else is_set = IS_SET(dwAttr, ATTR_BLOCK | ATTR_OBJECT);
|
|
|
|
|
|
|
|
|
|
if ( is_set )
|
|
|
|
|
{
|
|
|
|
|
// SPAWN_BLOCK_LOG
|
|
|
|
|
static bool s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log");
|
|
|
|
|
static DWORD s_nextTime=get_global_time()+10000;
|
|
|
|
|
|
|
|
|
|
DWORD curTime=get_global_time();
|
|
|
|
|
|
|
|
|
|
if (curTime>s_nextTime)
|
|
|
|
|
{
|
|
|
|
|
s_nextTime=curTime;
|
|
|
|
|
s_isLog=quest::CQuestManager::instance().GetEventFlag("spawn_block_log");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s_isLog)
|
|
|
|
|
sys_log(0, "SpawnMob: BLOCKED position for spawn %s %u at %d %d (attr %u)", pkMob->m_table.szName, dwVnum, x, y, dwAttr);
|
|
|
|
|
// END_OF_SPAWN_BLOCK_LOG
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IS_SET(dwAttr, ATTR_BANPK))
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "SpawnMob: BAN_PK position for mob spawn %s %u at %d %d", pkMob->m_table.szName, dwVnum, x, y);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPSECTREE sectree = SECTREE_MANAGER::instance().Get(lMapIndex, x, y);
|
|
|
|
|
|
|
|
|
|
if (!sectree)
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "SpawnMob: cannot create monster at non-exist sectree %d x %d (map %d)", x, y, lMapIndex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER ch = CHARACTER_MANAGER::instance().CreateCharacter(pkMob->m_table.szLocaleName);
|
|
|
|
|
|
|
|
|
|
if (!ch)
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "SpawnMob: cannot create new character");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iRot == -1)
|
2022-11-27 14:36:04 +02:00
|
|
|
|
iRot = Random::get(0, 360);
|
2022-03-05 12:44:06 +02:00
|
|
|
|
|
|
|
|
|
ch->SetProto(pkMob);
|
|
|
|
|
|
|
|
|
|
// if mob is npc with no empire assigned, assign to empire of map
|
|
|
|
|
if (pkMob->m_table.bType == CHAR_TYPE_NPC)
|
|
|
|
|
if (ch->GetEmpire() == 0)
|
|
|
|
|
ch->SetEmpire(SECTREE_MANAGER::instance().GetEmpireFromMapIndex(lMapIndex));
|
|
|
|
|
|
|
|
|
|
ch->SetRotation(iRot);
|
|
|
|
|
|
|
|
|
|
if (bShow && !ch->Show(lMapIndex, x, y, z, bSpawnMotion))
|
|
|
|
|
{
|
|
|
|
|
M2_DESTROY_CHARACTER(ch);
|
|
|
|
|
sys_log(0, "SpawnMob: cannot show monster");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (ch);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::SpawnMobRange(DWORD dwVnum, int lMapIndex, int sx, int sy, int ex, int ey, bool bIsException, bool bSpawnMotion, bool bAggressive )
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
const CMob * pkMob = CMobManager::instance().Get(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (!pkMob)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if (pkMob->m_table.bType == CHAR_TYPE_STONE) // <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> SPAWN <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>ִ<EFBFBD>.
|
|
|
|
|
bSpawnMotion = true;
|
|
|
|
|
|
|
|
|
|
int i = 16;
|
|
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
|
{
|
2022-11-27 14:36:04 +02:00
|
|
|
|
int x = Random::get(sx, ex);
|
|
|
|
|
int y = Random::get(sy, ey);
|
2022-03-05 12:44:06 +02:00
|
|
|
|
/*
|
|
|
|
|
if (bIsException)
|
|
|
|
|
if (is_regen_exception(x, y))
|
|
|
|
|
continue;
|
|
|
|
|
*/
|
|
|
|
|
LPCHARACTER ch = SpawnMob(dwVnum, lMapIndex, x, y, 0, bSpawnMotion);
|
|
|
|
|
|
|
|
|
|
if (ch)
|
|
|
|
|
{
|
|
|
|
|
sys_log(1, "MOB_SPAWN: %s(%d) %dx%d", ch->GetName(), (DWORD) ch->GetVID(), ch->GetX(), ch->GetY());
|
|
|
|
|
if ( bAggressive )
|
|
|
|
|
ch->SetAggressive();
|
|
|
|
|
return (ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::SelectStone(LPCHARACTER pkChr)
|
|
|
|
|
{
|
|
|
|
|
m_pkChrSelectedStone = pkChr;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
bool CHARACTER_MANAGER::SpawnMoveGroup(DWORD dwVnum, int lMapIndex, int sx, int sy, int ex, int ey, int tx, int ty, LPREGEN pkRegen, bool bAggressive_)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (!pkGroup)
|
|
|
|
|
{
|
|
|
|
|
sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER pkChrMaster = NULL;
|
|
|
|
|
LPPARTY pkParty = NULL;
|
|
|
|
|
|
|
|
|
|
const std::vector<DWORD> & c_rdwMembers = pkGroup->GetMemberVector();
|
|
|
|
|
|
|
|
|
|
bool bSpawnedByStone = false;
|
|
|
|
|
bool bAggressive = bAggressive_;
|
|
|
|
|
|
|
|
|
|
if (m_pkChrSelectedStone)
|
|
|
|
|
{
|
|
|
|
|
bSpawnedByStone = true;
|
|
|
|
|
if (m_pkChrSelectedStone->GetDungeon())
|
|
|
|
|
bAggressive = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (DWORD i = 0; i < c_rdwMembers.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone);
|
|
|
|
|
|
|
|
|
|
if (!tch)
|
|
|
|
|
{
|
|
|
|
|
if (i == 0) // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͱ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>쿡<EFBFBD><ECBFA1> <20>׳<EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 14:36:04 +02:00
|
|
|
|
sx = tch->GetX() - Random::get(300, 500);
|
|
|
|
|
sy = tch->GetY() - Random::get(300, 500);
|
|
|
|
|
ex = tch->GetX() + Random::get(300, 500);
|
|
|
|
|
ey = tch->GetY() + Random::get(300, 500);
|
2022-03-05 12:44:06 +02:00
|
|
|
|
|
|
|
|
|
if (m_pkChrSelectedStone)
|
|
|
|
|
tch->SetStone(m_pkChrSelectedStone);
|
|
|
|
|
else if (pkParty)
|
|
|
|
|
{
|
|
|
|
|
pkParty->Join(tch->GetVID());
|
|
|
|
|
pkParty->Link(tch);
|
|
|
|
|
}
|
|
|
|
|
else if (!pkChrMaster)
|
|
|
|
|
{
|
|
|
|
|
pkChrMaster = tch;
|
|
|
|
|
pkChrMaster->SetRegen(pkRegen);
|
|
|
|
|
|
|
|
|
|
pkParty = CPartyManager::instance().CreateParty(pkChrMaster);
|
|
|
|
|
}
|
|
|
|
|
if (bAggressive)
|
|
|
|
|
tch->SetAggressive();
|
|
|
|
|
|
|
|
|
|
if (tch->Goto(tx, ty))
|
|
|
|
|
tch->SendMovePacket(FUNC_WAIT, 0, 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
bool CHARACTER_MANAGER::SpawnGroupGroup(DWORD dwVnum, int lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
const DWORD dwGroupID = CMobManager::Instance().GetGroupFromGroupGroup(dwVnum);
|
|
|
|
|
|
|
|
|
|
if( dwGroupID != 0 )
|
|
|
|
|
{
|
|
|
|
|
return SpawnGroup(dwGroupID, lMapIndex, sx, sy, ex, ey, pkRegen, bAggressive_, pDungeon);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sys_err( "NOT_EXIST_GROUP_GROUP_VNUM(%u) MAP(%ld)", dwVnum, lMapIndex );
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::SpawnGroup(DWORD dwVnum, int lMapIndex, int sx, int sy, int ex, int ey, LPREGEN pkRegen, bool bAggressive_, LPDUNGEON pDungeon)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
CMobGroup * pkGroup = CMobManager::Instance().GetGroup(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (!pkGroup)
|
|
|
|
|
{
|
|
|
|
|
sys_err("NOT_EXIST_GROUP_VNUM(%u) Map(%u) ", dwVnum, lMapIndex);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER pkChrMaster = NULL;
|
|
|
|
|
LPPARTY pkParty = NULL;
|
|
|
|
|
|
|
|
|
|
const std::vector<DWORD> & c_rdwMembers = pkGroup->GetMemberVector();
|
|
|
|
|
|
|
|
|
|
bool bSpawnedByStone = false;
|
|
|
|
|
bool bAggressive = bAggressive_;
|
|
|
|
|
|
|
|
|
|
if (m_pkChrSelectedStone)
|
|
|
|
|
{
|
|
|
|
|
bSpawnedByStone = true;
|
|
|
|
|
|
|
|
|
|
if (m_pkChrSelectedStone->GetDungeon())
|
|
|
|
|
bAggressive = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LPCHARACTER chLeader = NULL;
|
|
|
|
|
|
|
|
|
|
for (DWORD i = 0; i < c_rdwMembers.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER tch = SpawnMobRange(c_rdwMembers[i], lMapIndex, sx, sy, ex, ey, true, bSpawnedByStone);
|
|
|
|
|
|
|
|
|
|
if (!tch)
|
|
|
|
|
{
|
|
|
|
|
if (i == 0) // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>Ͱ<EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>쿡<EFBFBD><ECBFA1> <20>׳<EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i == 0)
|
|
|
|
|
chLeader = tch;
|
|
|
|
|
|
|
|
|
|
tch->SetDungeon(pDungeon);
|
|
|
|
|
|
2022-11-27 14:36:04 +02:00
|
|
|
|
sx = tch->GetX() - Random::get(300, 500);
|
|
|
|
|
sy = tch->GetY() - Random::get(300, 500);
|
|
|
|
|
ex = tch->GetX() + Random::get(300, 500);
|
|
|
|
|
ey = tch->GetY() + Random::get(300, 500);
|
2022-03-05 12:44:06 +02:00
|
|
|
|
|
|
|
|
|
if (m_pkChrSelectedStone)
|
|
|
|
|
tch->SetStone(m_pkChrSelectedStone);
|
|
|
|
|
else if (pkParty)
|
|
|
|
|
{
|
|
|
|
|
pkParty->Join(tch->GetVID());
|
|
|
|
|
pkParty->Link(tch);
|
|
|
|
|
}
|
|
|
|
|
else if (!pkChrMaster)
|
|
|
|
|
{
|
|
|
|
|
pkChrMaster = tch;
|
|
|
|
|
pkChrMaster->SetRegen(pkRegen);
|
|
|
|
|
|
|
|
|
|
pkParty = CPartyManager::instance().CreateParty(pkChrMaster);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bAggressive)
|
|
|
|
|
tch->SetAggressive();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chLeader;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct FuncUpdateAndResetChatCounter
|
|
|
|
|
{
|
|
|
|
|
void operator () (LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
ch->ResetChatCounter();
|
|
|
|
|
ch->CFSM::Update();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::Update(int iPulse)
|
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
using namespace __gnu_cxx;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
BeginPendingDestroy();
|
|
|
|
|
|
|
|
|
|
// PC ij<><C4B3><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ
|
|
|
|
|
{
|
|
|
|
|
if (!m_map_pkPCChr.empty())
|
|
|
|
|
{
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>̳<EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
CHARACTER_VECTOR v;
|
|
|
|
|
v.reserve(m_map_pkPCChr.size());
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), select2nd<NAME_MAP::value_type>());
|
|
|
|
|
#else
|
|
|
|
|
transform(m_map_pkPCChr.begin(), m_map_pkPCChr.end(), back_inserter(v), boost::bind(&NAME_MAP::value_type::second, _1));
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (0 == (iPulse % PASSES_PER_SEC(5)))
|
|
|
|
|
{
|
|
|
|
|
FuncUpdateAndResetChatCounter f;
|
|
|
|
|
for_each(v.begin(), v.end(), f);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//for_each(v.begin(), v.end(), mem_fun(&CFSM::Update));
|
|
|
|
|
for_each(v.begin(), v.end(), bind2nd(mem_fun(&CHARACTER::UpdateCharacter), iPulse));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for_each_pc(bind2nd(mem_fun(&CHARACTER::UpdateCharacter), iPulse));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ
|
|
|
|
|
{
|
|
|
|
|
if (!m_set_pkChrState.empty())
|
|
|
|
|
{
|
|
|
|
|
CHARACTER_VECTOR v;
|
|
|
|
|
v.reserve(m_set_pkChrState.size());
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
transform(m_set_pkChrState.begin(), m_set_pkChrState.end(), back_inserter(v), identity<CHARACTER_SET::value_type>());
|
|
|
|
|
#else
|
|
|
|
|
v.insert(v.end(), m_set_pkChrState.begin(), m_set_pkChrState.end());
|
|
|
|
|
#endif
|
|
|
|
|
for_each(v.begin(), v.end(), bind2nd(mem_fun(&CHARACTER::UpdateStateMachine), iPulse));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><>Ÿ <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ʈ
|
|
|
|
|
{
|
|
|
|
|
CharacterVectorInteractor i;
|
|
|
|
|
|
|
|
|
|
if (CHARACTER_MANAGER::instance().GetCharactersByRaceNum(xmas::MOB_SANTA_VNUM, i))
|
|
|
|
|
{
|
|
|
|
|
for_each(i.begin(), i.end(),
|
|
|
|
|
bind2nd(mem_fun(&CHARACTER::UpdateStateMachine), iPulse));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1<>ð<EFBFBD><C3B0><EFBFBD> <20>ѹ<EFBFBD><D1B9><EFBFBD> <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
if (0 == (iPulse % PASSES_PER_SEC(3600)))
|
|
|
|
|
{
|
|
|
|
|
for (itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.begin(); it != m_map_dwMobKillCount.end(); ++it)
|
|
|
|
|
DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second);
|
|
|
|
|
|
|
|
|
|
m_map_dwMobKillCount.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// <20><EFBFBD>Ʈ <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 60<36>ʸ<EFBFBD><CAB8><EFBFBD> ij<><C4B3><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
if (test_server && 0 == (iPulse % PASSES_PER_SEC(60)))
|
|
|
|
|
sys_log(0, "CHARACTER COUNT vid %zu pid %zu", m_map_pkChrByVID.size(), m_map_pkChrByPID.size());
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> DestroyCharacter <20>ϱ<EFBFBD>
|
|
|
|
|
FlushPendingDestroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::ProcessDelayedSave()
|
|
|
|
|
{
|
|
|
|
|
CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.begin();
|
|
|
|
|
|
|
|
|
|
while (it != m_set_pkChrForDelayedSave.end())
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER pkChr = *it++;
|
|
|
|
|
pkChr->SaveReal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_set_pkChrForDelayedSave.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHARACTER_MANAGER::AddToStateList(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
assert(ch != NULL);
|
|
|
|
|
|
|
|
|
|
CHARACTER_SET::iterator it = m_set_pkChrState.find(ch);
|
|
|
|
|
|
|
|
|
|
if (it == m_set_pkChrState.end())
|
|
|
|
|
{
|
|
|
|
|
m_set_pkChrState.insert(ch);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::RemoveFromStateList(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
CHARACTER_SET::iterator it = m_set_pkChrState.find(ch);
|
|
|
|
|
|
|
|
|
|
if (it != m_set_pkChrState.end())
|
|
|
|
|
{
|
|
|
|
|
//sys_log(0, "RemoveFromStateList %p", ch);
|
|
|
|
|
m_set_pkChrState.erase(it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::DelayedSave(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
m_set_pkChrForDelayedSave.insert(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHARACTER_MANAGER::FlushDelayedSave(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
CHARACTER_SET::iterator it = m_set_pkChrForDelayedSave.find(ch);
|
|
|
|
|
|
|
|
|
|
if (it == m_set_pkChrForDelayedSave.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
m_set_pkChrForDelayedSave.erase(it);
|
|
|
|
|
ch->SaveReal();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::RegisterForMonsterLog(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
m_set_pkChrMonsterLog.insert(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::UnregisterForMonsterLog(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
m_set_pkChrMonsterLog.erase(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::PacketMonsterLog(LPCHARACTER ch, const void* buf, int size)
|
|
|
|
|
{
|
|
|
|
|
itertype(m_set_pkChrMonsterLog) it;
|
|
|
|
|
|
|
|
|
|
for (it = m_set_pkChrMonsterLog.begin(); it!=m_set_pkChrMonsterLog.end();++it)
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER c = *it;
|
|
|
|
|
|
|
|
|
|
if (ch && DISTANCE_APPROX(c->GetX()-ch->GetX(), c->GetY()-ch->GetY())>6000)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
LPDESC d = c->GetDesc();
|
|
|
|
|
|
|
|
|
|
if (d)
|
|
|
|
|
d->Packet(buf, size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::KillLog(DWORD dwVnum)
|
|
|
|
|
{
|
|
|
|
|
const DWORD SEND_LIMIT = 10000;
|
|
|
|
|
|
|
|
|
|
itertype(m_map_dwMobKillCount) it = m_map_dwMobKillCount.find(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (it == m_map_dwMobKillCount.end())
|
|
|
|
|
m_map_dwMobKillCount.insert(std::make_pair(dwVnum, 1));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
++it->second;
|
|
|
|
|
|
|
|
|
|
if (it->second > SEND_LIMIT)
|
|
|
|
|
{
|
|
|
|
|
DBManager::instance().SendMoneyLog(MONEY_LOG_MONSTER_KILL, it->first, it->second);
|
|
|
|
|
m_map_dwMobKillCount.erase(it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::RegisterRaceNum(DWORD dwVnum)
|
|
|
|
|
{
|
|
|
|
|
m_set_dwRegisteredRaceNum.insert(dwVnum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::RegisterRaceNumMap(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
DWORD dwVnum = ch->GetRaceNum();
|
|
|
|
|
|
|
|
|
|
if (m_set_dwRegisteredRaceNum.find(dwVnum) != m_set_dwRegisteredRaceNum.end()) // <20><><EFBFBD>ϵ<EFBFBD> <20><>ȣ <20≯<EFBFBD>
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "RegisterRaceNumMap %s %u", ch->GetName(), dwVnum);
|
|
|
|
|
m_map_pkChrByRaceNum[dwVnum].insert(ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::UnregisterRaceNumMap(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
DWORD dwVnum = ch->GetRaceNum();
|
|
|
|
|
|
|
|
|
|
itertype(m_map_pkChrByRaceNum) it = m_map_pkChrByRaceNum.find(dwVnum);
|
|
|
|
|
|
|
|
|
|
if (it != m_map_pkChrByRaceNum.end())
|
|
|
|
|
it->second.erase(ch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHARACTER_MANAGER::GetCharactersByRaceNum(DWORD dwRaceNum, CharacterVectorInteractor & i)
|
|
|
|
|
{
|
|
|
|
|
std::map<DWORD, CHARACTER_SET>::iterator it = m_map_pkChrByRaceNum.find(dwRaceNum);
|
|
|
|
|
|
|
|
|
|
if (it == m_map_pkChrByRaceNum.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>̳<EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
i = it->second;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define FIND_JOB_WARRIOR_0 (1 << 3)
|
|
|
|
|
#define FIND_JOB_WARRIOR_1 (1 << 4)
|
|
|
|
|
#define FIND_JOB_WARRIOR_2 (1 << 5)
|
|
|
|
|
#define FIND_JOB_WARRIOR (FIND_JOB_WARRIOR_0 | FIND_JOB_WARRIOR_1 | FIND_JOB_WARRIOR_2)
|
|
|
|
|
#define FIND_JOB_ASSASSIN_0 (1 << 6)
|
|
|
|
|
#define FIND_JOB_ASSASSIN_1 (1 << 7)
|
|
|
|
|
#define FIND_JOB_ASSASSIN_2 (1 << 8)
|
|
|
|
|
#define FIND_JOB_ASSASSIN (FIND_JOB_ASSASSIN_0 | FIND_JOB_ASSASSIN_1 | FIND_JOB_ASSASSIN_2)
|
|
|
|
|
#define FIND_JOB_SURA_0 (1 << 9)
|
|
|
|
|
#define FIND_JOB_SURA_1 (1 << 10)
|
|
|
|
|
#define FIND_JOB_SURA_2 (1 << 11)
|
|
|
|
|
#define FIND_JOB_SURA (FIND_JOB_SURA_0 | FIND_JOB_SURA_1 | FIND_JOB_SURA_2)
|
|
|
|
|
#define FIND_JOB_SHAMAN_0 (1 << 12)
|
|
|
|
|
#define FIND_JOB_SHAMAN_1 (1 << 13)
|
|
|
|
|
#define FIND_JOB_SHAMAN_2 (1 << 14)
|
|
|
|
|
#define FIND_JOB_SHAMAN (FIND_JOB_SHAMAN_0 | FIND_JOB_SHAMAN_1 | FIND_JOB_SHAMAN_2)
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// (job+1)*3+(skill_group)
|
|
|
|
|
//
|
2022-03-12 11:39:41 +02:00
|
|
|
|
LPCHARACTER CHARACTER_MANAGER::FindSpecifyPC(unsigned int uiJobFlag, int lMapIndex, LPCHARACTER except, int iMinLevel, int iMaxLevel)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
LPCHARACTER chFind = NULL;
|
|
|
|
|
itertype(m_map_pkChrByPID) it;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
for (it = m_map_pkChrByPID.begin(); it != m_map_pkChrByPID.end(); ++it)
|
|
|
|
|
{
|
|
|
|
|
LPCHARACTER ch = it->second;
|
|
|
|
|
|
|
|
|
|
if (ch == except)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (ch->GetLevel() < iMinLevel)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (ch->GetLevel() > iMaxLevel)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (ch->GetMapIndex() != lMapIndex)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (uiJobFlag)
|
|
|
|
|
{
|
|
|
|
|
unsigned int uiChrJob = (1 << ((ch->GetJob() + 1) * 3 + ch->GetSkillGroup()));
|
|
|
|
|
|
|
|
|
|
if (!IS_SET(uiJobFlag, uiChrJob))
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-27 14:36:04 +02:00
|
|
|
|
if (!chFind || Random::get(1, ++n) == 1)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
chFind = ch;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return chFind;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetMobItemRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
//PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
if ( LC_IsNewCIBN() )
|
|
|
|
|
{
|
|
|
|
|
if ( ch->IsOverTime( OT_3HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0)
|
|
|
|
|
return m_iMobItemRatePremium/2;
|
|
|
|
|
return m_iMobItemRate/2;
|
|
|
|
|
}
|
|
|
|
|
else if ( ch->IsOverTime( OT_5HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//END_PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_ITEM) > 0)
|
|
|
|
|
return m_iMobItemRatePremium;
|
|
|
|
|
return m_iMobItemRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetMobDamageRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
return m_iMobDamageRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetMobGoldAmountRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
if ( !ch )
|
|
|
|
|
return m_iMobGoldAmountRate;
|
|
|
|
|
|
|
|
|
|
//PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
if ( LC_IsNewCIBN() )
|
|
|
|
|
{
|
|
|
|
|
if ( ch->IsOverTime( OT_3HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0)
|
|
|
|
|
return m_iMobGoldAmountRatePremium/2;
|
|
|
|
|
return m_iMobGoldAmountRate/2;
|
|
|
|
|
}
|
|
|
|
|
else if ( ch->IsOverTime( OT_5HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//END_PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0)
|
|
|
|
|
return m_iMobGoldAmountRatePremium;
|
|
|
|
|
return m_iMobGoldAmountRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetMobGoldDropRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
if ( !ch )
|
|
|
|
|
return m_iMobGoldDropRate;
|
|
|
|
|
|
|
|
|
|
//PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
if ( LC_IsNewCIBN() )
|
|
|
|
|
{
|
|
|
|
|
if ( ch->IsOverTime( OT_3HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0)
|
|
|
|
|
return m_iMobGoldDropRatePremium/2;
|
|
|
|
|
return m_iMobGoldDropRate/2;
|
|
|
|
|
}
|
|
|
|
|
else if ( ch->IsOverTime( OT_5HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//END_PREVENT_TOXICATION_FOR_CHINA
|
|
|
|
|
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_GOLD) > 0)
|
|
|
|
|
return m_iMobGoldDropRatePremium;
|
|
|
|
|
return m_iMobGoldDropRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetMobExpRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
if ( !ch )
|
|
|
|
|
return m_iMobExpRate;
|
|
|
|
|
|
|
|
|
|
if ( LC_IsNewCIBN() )
|
|
|
|
|
{
|
|
|
|
|
if ( ch->IsOverTime( OT_3HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
|
|
|
|
|
return m_iMobExpRatePremium/2;
|
|
|
|
|
return m_iMobExpRate/2;
|
|
|
|
|
}
|
|
|
|
|
else if ( ch->IsOverTime( OT_5HOUR ) )
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
|
|
|
|
|
return m_iMobExpRatePremium;
|
|
|
|
|
return m_iMobExpRate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHARACTER_MANAGER::GetUserDamageRate(LPCHARACTER ch)
|
|
|
|
|
{
|
|
|
|
|
if (!ch)
|
|
|
|
|
return m_iUserDamageRate;
|
|
|
|
|
|
|
|
|
|
if (ch && ch->GetPremiumRemainSeconds(PREMIUM_EXP) > 0)
|
|
|
|
|
return m_iUserDamageRatePremium;
|
|
|
|
|
|
|
|
|
|
return m_iUserDamageRate;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-12 11:39:41 +02:00
|
|
|
|
void CHARACTER_MANAGER::SendScriptToMap(int lMapIndex, const std::string & s)
|
2022-03-05 12:44:06 +02:00
|
|
|
|
{
|
|
|
|
|
LPSECTREE_MAP pSecMap = SECTREE_MANAGER::instance().GetMap(lMapIndex);
|
|
|
|
|
|
|
|
|
|
if (NULL == pSecMap)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
struct packet_script p;
|
|
|
|
|
|
|
|
|
|
p.header = HEADER_GC_SCRIPT;
|
|
|
|
|
p.skin = 1;
|
|
|
|
|
p.src_size = s.size();
|
|
|
|
|
|
|
|
|
|
quest::FSendPacket f;
|
|
|
|
|
p.size = p.src_size + sizeof(struct packet_script);
|
|
|
|
|
f.buf.write(&p, sizeof(struct packet_script));
|
|
|
|
|
f.buf.write(&s[0], s.size());
|
|
|
|
|
|
|
|
|
|
pSecMap->for_each(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CHARACTER_MANAGER::BeginPendingDestroy()
|
|
|
|
|
{
|
|
|
|
|
// Begin <20><> <20>Ŀ<EFBFBD> Begin<69><6E> <20><> <20>ϴ<EFBFBD> <20><><EFBFBD>쿡 Flush <20><><EFBFBD><EFBFBD> <20>ʴ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
|
|
|
|
// <20>̹<EFBFBD> <20><><EFBFBD>۵Ǿ<DBB5><C7BE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> false <20><><EFBFBD><EFBFBD> ó<><C3B3>
|
|
|
|
|
if (m_bUsePendingDestroy)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
m_bUsePendingDestroy = true;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CHARACTER_MANAGER::FlushPendingDestroy()
|
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
m_bUsePendingDestroy = false; // <20>÷<EFBFBD><C3B7><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>ؾ<EFBFBD> <20><><EFBFBD><EFBFBD> Destroy ó<><C3B3><EFBFBD><EFBFBD> <20><>
|
|
|
|
|
|
|
|
|
|
if (!m_set_pkChrPendingDestroy.empty())
|
|
|
|
|
{
|
|
|
|
|
sys_log(0, "FlushPendingDestroy size %d", m_set_pkChrPendingDestroy.size());
|
|
|
|
|
|
|
|
|
|
CHARACTER_SET::iterator it = m_set_pkChrPendingDestroy.begin(),
|
|
|
|
|
end = m_set_pkChrPendingDestroy.end();
|
|
|
|
|
for ( ; it != end; ++it) {
|
|
|
|
|
M2_DESTROY_CHARACTER(*it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_set_pkChrPendingDestroy.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CharacterVectorInteractor::CharacterVectorInteractor(const CHARACTER_SET & r)
|
|
|
|
|
{
|
|
|
|
|
using namespace std;
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
using namespace __gnu_cxx;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
reserve(r.size());
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
transform(r.begin(), r.end(), back_inserter(*this), identity<CHARACTER_SET::value_type>());
|
|
|
|
|
#else
|
|
|
|
|
insert(end(), r.begin(), r.end());
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (CHARACTER_MANAGER::instance().BeginPendingDestroy())
|
|
|
|
|
m_bMyBegin = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CharacterVectorInteractor::~CharacterVectorInteractor()
|
|
|
|
|
{
|
|
|
|
|
if (m_bMyBegin)
|
|
|
|
|
CHARACTER_MANAGER::instance().FlushPendingDestroy();
|
|
|
|
|
}
|
|
|
|
|
|