forked from metin2/server
564 lines
14 KiB
C++
564 lines
14 KiB
C++
#include "stdafx.h"
|
||
#include <common/stl.h>
|
||
#include "constants.h"
|
||
#include "motion.h"
|
||
#include "text_file_loader.h"
|
||
#include "mob_manager.h"
|
||
#include "char.h"
|
||
|
||
// POLYMORPH_BUG_FIX
|
||
static float MSA_GetNormalAttackDuration(const char* msaPath)
|
||
{
|
||
float duration = 99.0f;
|
||
FILE * fp = fopen(msaPath, "rt");
|
||
if (!fp)
|
||
return duration;
|
||
|
||
char line[1024];
|
||
while (fgets(line, sizeof(line), fp))
|
||
{
|
||
char key[1024];
|
||
char val[1024];
|
||
|
||
sscanf(line, "%s %s", key, val);
|
||
if (strcmp(key, "MotionDuration") == 0)
|
||
{
|
||
duration = atof(val);
|
||
break;
|
||
}
|
||
}
|
||
fclose(fp);
|
||
|
||
return duration;
|
||
}
|
||
|
||
static float MOB_GetNormalAttackDuration(TMobTable* mobTable)
|
||
{
|
||
float minDuration = 99.0f;
|
||
|
||
const char * folder = mobTable->szFolder;
|
||
|
||
char motlistPath[1024];
|
||
snprintf(motlistPath, sizeof(motlistPath), "data/monster/%s/motlist.txt", folder);
|
||
|
||
FILE * fp = fopen(motlistPath, "rt");
|
||
if (!fp)
|
||
return minDuration;
|
||
|
||
char line[1024];
|
||
while (fgets(line, sizeof(line), fp))
|
||
{
|
||
char mode[1024];
|
||
char type[1024];
|
||
char msaName[1024];
|
||
int percent;
|
||
|
||
sscanf(line, "%s %s %s %d", mode, type, msaName, &percent);
|
||
if (strcmp(mode, "GENERAL") == 0 && strncmp(type, "NORMAL_ATTACK", 13) == 0)
|
||
{
|
||
char msaPath[4096];
|
||
snprintf(msaPath, sizeof(msaPath), "data/monster/%s/%s", folder, msaName);
|
||
float curDuration = MSA_GetNormalAttackDuration(msaPath);
|
||
if (curDuration < minDuration)
|
||
minDuration = curDuration;
|
||
}
|
||
}
|
||
fclose(fp);
|
||
|
||
return minDuration;
|
||
}
|
||
// END_OF_POLYMORPH_BUG_FIX
|
||
|
||
static const char* GetMotionFileName(TMobTable* mobTable, EPublicMotion motion)
|
||
{
|
||
char buf[1024];
|
||
const char * folder = mobTable->szFolder;
|
||
snprintf(buf, sizeof(buf), "data/monster/%s/motlist.txt", folder);
|
||
|
||
FILE * fp = fopen(buf, "rt");
|
||
char * v[4];
|
||
|
||
if (fp != NULL)
|
||
{
|
||
const char* field = NULL;
|
||
|
||
switch (motion)
|
||
{
|
||
case MOTION_WALK : field = "WALK"; break;
|
||
case MOTION_RUN : field = "RUN"; break;
|
||
case MOTION_NORMAL_ATTACK : field = "NORMAL_ATTACK"; break;
|
||
case MOTION_SPECIAL_1 : field = "SPECIAL"; break;
|
||
case MOTION_SPECIAL_2 : field = "SPECIAL1"; break;
|
||
case MOTION_SPECIAL_3 : field = "SPECIAL2"; break;
|
||
case MOTION_SPECIAL_4 : field = "SPECIAL3"; break;
|
||
case MOTION_SPECIAL_5 : field = "SPECIAL4"; break;
|
||
|
||
default:
|
||
fclose(fp);
|
||
sys_err("Motion: no process for this motion(%d) vnum(%d)", motion, mobTable->dwVnum);
|
||
return NULL;
|
||
}
|
||
|
||
while (fgets(buf, 1024, fp))
|
||
{
|
||
v[0] = strtok(buf, " \t\r\n");
|
||
v[1] = strtok(NULL, " \t\r\n");
|
||
v[2] = strtok(NULL, " \t\r\n");
|
||
v[3] = strtok(NULL, " \t\r\n");
|
||
|
||
if (NULL != v[0] && NULL != v[1] && NULL != v[2] && NULL != v[3] && !strcasecmp(v[1], field))
|
||
{
|
||
fclose(fp);
|
||
|
||
static std::string str;
|
||
str = "data/monster/";
|
||
str += folder;
|
||
str += "/";
|
||
str += v[2];
|
||
|
||
return str.c_str();
|
||
}
|
||
}
|
||
|
||
fclose(fp);
|
||
}
|
||
else
|
||
{
|
||
sys_err("Motion: %s have not motlist.txt vnum(%d) folder(%s)", folder, mobTable->dwVnum, mobTable->szFolder);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
static void LoadMotion(CMotionSet* pMotionSet, TMobTable* mob_table, EPublicMotion motion)
|
||
{
|
||
const char* cpFileName = GetMotionFileName(mob_table, motion);
|
||
|
||
if (cpFileName == NULL)
|
||
{
|
||
return;
|
||
}
|
||
|
||
CMotion* pMotion = M2_NEW CMotion;
|
||
|
||
if (pMotion->LoadFromFile(cpFileName) == true)
|
||
{
|
||
if (motion == MOTION_RUN)
|
||
if (0.0f == pMotion->GetAccumVector().y)
|
||
sys_err("cannot find accumulation data in file '%s'", cpFileName);
|
||
|
||
pMotionSet->Insert(MAKE_MOTION_KEY(MOTION_MODE_GENERAL, motion), pMotion);
|
||
}
|
||
else
|
||
{
|
||
M2_DELETE(pMotion);
|
||
sys_err("Motion: Load failed vnum(%d) motion(%d) file(%s)", mob_table->dwVnum, motion, cpFileName);
|
||
}
|
||
}
|
||
|
||
static void LoadSkillMotion(CMotionSet* pMotionSet, CMob* pMob, EPublicMotion motion)
|
||
{
|
||
int idx = 0;
|
||
|
||
switch (motion)
|
||
{
|
||
case MOTION_SPECIAL_1 : idx = 0; break;
|
||
case MOTION_SPECIAL_2 : idx = 1; break;
|
||
case MOTION_SPECIAL_3 : idx = 2; break;
|
||
case MOTION_SPECIAL_4 : idx = 3; break;
|
||
case MOTION_SPECIAL_5 : idx = 4; break;
|
||
|
||
default :
|
||
return;
|
||
}
|
||
|
||
TMobTable* mob_table = &pMob->m_table;
|
||
|
||
if (mob_table->Skills[idx].dwVnum == 0) return;
|
||
|
||
const char* cpFileName = GetMotionFileName(mob_table, motion);
|
||
if (cpFileName == NULL) return;
|
||
|
||
CMotion* pMotion = M2_NEW CMotion;
|
||
|
||
if (pMotion->LoadMobSkillFromFile(cpFileName, pMob, idx) == true)
|
||
{
|
||
pMotionSet->Insert(MAKE_MOTION_KEY(MOTION_MODE_GENERAL, motion), pMotion);
|
||
}
|
||
else
|
||
{
|
||
if (mob_table->Skills[idx].dwVnum != 0)
|
||
{
|
||
sys_err("Motion: Skill exist but no motion data for index %d mob %u skill %u",
|
||
idx, mob_table->dwVnum, mob_table->Skills[idx].dwVnum);
|
||
}
|
||
M2_DELETE(pMotion);
|
||
}
|
||
}
|
||
|
||
CMotionManager::CMotionManager()
|
||
{
|
||
}
|
||
|
||
CMotionManager::~CMotionManager()
|
||
{
|
||
iterator it = m_map_pkMotionSet.begin();
|
||
for ( ; it != m_map_pkMotionSet.end(); ++it) {
|
||
M2_DELETE(it->second);
|
||
}
|
||
}
|
||
|
||
const CMotionSet * CMotionManager::GetMotionSet(DWORD dwVnum)
|
||
{
|
||
iterator it = m_map_pkMotionSet.find(dwVnum);
|
||
|
||
if (m_map_pkMotionSet.end() == it)
|
||
return NULL;
|
||
|
||
return it->second;
|
||
}
|
||
|
||
const CMotion * CMotionManager::GetMotion(DWORD dwVnum, DWORD dwKey)
|
||
{
|
||
const CMotionSet * pkMotionSet = GetMotionSet(dwVnum);
|
||
|
||
if (!pkMotionSet)
|
||
return NULL;
|
||
|
||
return pkMotionSet->GetMotion(dwKey);
|
||
}
|
||
|
||
float CMotionManager::GetMotionDuration(DWORD dwVnum, DWORD dwKey)
|
||
{
|
||
const CMotion * pkMotion = GetMotion(dwVnum, dwKey);
|
||
return pkMotion ? pkMotion->GetDuration() : 0.0f;
|
||
}
|
||
|
||
// POLYMORPH_BUG_FIX
|
||
float CMotionManager::GetNormalAttackDuration(DWORD dwVnum)
|
||
{
|
||
std::map<DWORD, float>::iterator f = m_map_normalAttackDuration.find(dwVnum);
|
||
if (m_map_normalAttackDuration.end() == f)
|
||
return 0.0f;
|
||
else
|
||
return f->second;
|
||
}
|
||
// END_OF_POLYMORPH_BUG_FIX
|
||
|
||
enum EMotionEventType
|
||
{
|
||
MOTION_EVENT_TYPE_NONE, // 0
|
||
MOTION_EVENT_TYPE_EFFECT, // 1
|
||
MOTION_EVENT_TYPE_SCREEN_WAVING, // 2
|
||
MOTION_EVENT_TYPE_SCREEN_FLASHING, // 3
|
||
MOTION_EVENT_TYPE_SPECIAL_ATTACKING, // 4
|
||
MOTION_EVENT_TYPE_SOUND, // 5
|
||
MOTION_EVENT_TYPE_FLY, // 6
|
||
MOTION_EVENT_TYPE_CHARACTER_SHOW, // 7
|
||
MOTION_EVENT_TYPE_CHARACTER_HIDE, // 8
|
||
MOTION_EVENT_TYPE_WARP, // 9
|
||
MOTION_EVENT_TYPE_EFFECT_TO_TARGET, // 10
|
||
MOTION_EVENT_TYPE_MAX_NUM, // 11
|
||
};
|
||
|
||
bool CMotionManager::Build()
|
||
{
|
||
const char * c_apszFolderName[MAIN_RACE_MAX_NUM] =
|
||
{
|
||
"data/pc/warrior",
|
||
"data/pc/assassin",
|
||
"data/pc/sura",
|
||
"data/pc/shaman",
|
||
"data/pc2/warrior",
|
||
"data/pc2/assassin",
|
||
"data/pc2/sura",
|
||
"data/pc2/shaman"
|
||
};
|
||
|
||
for (int i = 0; i < MAIN_RACE_MAX_NUM; ++i)
|
||
{
|
||
CMotionSet * pkMotionSet = M2_NEW CMotionSet;
|
||
m_map_pkMotionSet.insert(TContainer::value_type(i, pkMotionSet));
|
||
|
||
char sz[256];
|
||
|
||
snprintf(sz, sizeof(sz), "%s/general/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_GENERAL, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/general/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_GENERAL, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/twohand_sword/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_TWOHAND_SWORD, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/twohand_sword/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_TWOHAND_SWORD, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/onehand_sword/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_ONEHAND_SWORD, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/onehand_sword/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_ONEHAND_SWORD, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/dualhand_sword/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_DUALHAND_SWORD, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/dualhand_sword/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_DUALHAND_SWORD, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/bow/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_BOW, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/bow/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_BOW, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/bell/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_BELL, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/bell/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_BELL, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/fan/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_FAN, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/fan/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_FAN, MOTION_WALK);
|
||
|
||
snprintf(sz, sizeof(sz), "%s/horse/run.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_HORSE, MOTION_RUN);
|
||
snprintf(sz, sizeof(sz), "%s/horse/walk.msa", c_apszFolderName[i]);
|
||
pkMotionSet->Load(sz, MOTION_MODE_HORSE, MOTION_WALK);
|
||
}
|
||
|
||
CMobManager::iterator it = CMobManager::instance().begin();
|
||
|
||
while (it != CMobManager::instance().end())
|
||
{
|
||
CMob * pkMob = (it++)->second;
|
||
TMobTable * t = &pkMob->m_table;
|
||
|
||
if ('\0' != t->szFolder[0])
|
||
{
|
||
CMotionSet * pkMotionSet = M2_NEW CMotionSet;
|
||
m_map_pkMotionSet.insert(TContainer::value_type(t->dwVnum, pkMotionSet));
|
||
|
||
LoadMotion(pkMotionSet, t, MOTION_WALK);
|
||
LoadMotion(pkMotionSet, t, MOTION_RUN);
|
||
LoadMotion(pkMotionSet, t, MOTION_NORMAL_ATTACK);
|
||
|
||
LoadSkillMotion(pkMotionSet, pkMob, MOTION_SPECIAL_1);
|
||
LoadSkillMotion(pkMotionSet, pkMob, MOTION_SPECIAL_2);
|
||
LoadSkillMotion(pkMotionSet, pkMob, MOTION_SPECIAL_3);
|
||
LoadSkillMotion(pkMotionSet, pkMob, MOTION_SPECIAL_4);
|
||
LoadSkillMotion(pkMotionSet, pkMob, MOTION_SPECIAL_5);
|
||
|
||
// POLYMORPH_BUG_FIX
|
||
float normalAttackDuration = MOB_GetNormalAttackDuration(t);
|
||
sys_log(0, "mob_normal_attack_duration:%d:%s:%.2f", t->dwVnum, t->szFolder, normalAttackDuration);
|
||
m_map_normalAttackDuration.insert(std::map<DWORD, float>::value_type(t->dwVnum, normalAttackDuration));
|
||
// END_OF_POLYMORPH_BUG_FIX
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
CMotionSet::CMotionSet()
|
||
{
|
||
}
|
||
|
||
CMotionSet::~CMotionSet()
|
||
{
|
||
iterator it = m_map_pkMotion.begin();
|
||
for ( ; it != m_map_pkMotion.end(); ++it) {
|
||
M2_DELETE(it->second);
|
||
}
|
||
}
|
||
|
||
const CMotion * CMotionSet::GetMotion(DWORD dwKey) const
|
||
{
|
||
const_iterator it = m_map_pkMotion.find(dwKey);
|
||
|
||
if (it == m_map_pkMotion.end())
|
||
return NULL;
|
||
|
||
return it->second;
|
||
}
|
||
|
||
void CMotionSet::Insert(DWORD dwKey, CMotion * pkMotion)
|
||
{
|
||
m_map_pkMotion.insert(TContainer::value_type(dwKey, pkMotion));
|
||
}
|
||
|
||
bool CMotionSet::Load(const char * szFileName, int mode, int motion)
|
||
{
|
||
CMotion * pkMotion = M2_NEW CMotion;
|
||
|
||
if (!pkMotion->LoadFromFile(szFileName))
|
||
{
|
||
M2_DELETE(pkMotion);
|
||
return false;
|
||
}
|
||
|
||
Insert(MAKE_MOTION_KEY(mode, motion), pkMotion);
|
||
|
||
return true;
|
||
}
|
||
|
||
CMotion::CMotion() : m_isEmpty(true), m_fDuration(0.0f), m_isAccumulation(false)
|
||
{
|
||
m_vec3Accumulation.x = 0.0f;
|
||
m_vec3Accumulation.y = 0.0f;
|
||
m_vec3Accumulation.z = 0.0f;
|
||
}
|
||
|
||
CMotion::~CMotion()
|
||
{
|
||
}
|
||
|
||
bool CMotion::LoadMobSkillFromFile(const char * c_pszFileName, CMob* pMob, int iSkillIndex)
|
||
{
|
||
CTextFileLoader rkTextFileLoader;
|
||
if (!rkTextFileLoader.Load(c_pszFileName))
|
||
return false;
|
||
|
||
//if (rkTextFileLoader.IsEmpty())
|
||
//return false;
|
||
|
||
rkTextFileLoader.SetTop();
|
||
|
||
if (!rkTextFileLoader.GetTokenFloat("motionduration", &m_fDuration))
|
||
{
|
||
sys_err("Motion: no motion duration %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
//if (rkTextFileLoader.GetTokenPosition("accumulation", &m_vec3Accumulation))
|
||
//m_isAccumulation = true;
|
||
|
||
std::string strNodeName;
|
||
for (DWORD i = 0; i < rkTextFileLoader.GetChildNodeCount(); ++i)
|
||
{
|
||
//CTextFileLoader::CGotoChild GotoChild(rkTextFileLoader, i);
|
||
rkTextFileLoader.SetChildNode(i);
|
||
|
||
rkTextFileLoader.GetCurrentNodeName(&strNodeName);
|
||
|
||
if (0 == strNodeName.compare("motioneventdata"))
|
||
{
|
||
DWORD dwMotionEventDataCount;
|
||
|
||
if (!rkTextFileLoader.GetTokenDoubleWord("motioneventdatacount", &dwMotionEventDataCount))
|
||
continue;
|
||
|
||
for (DWORD j = 0; j < dwMotionEventDataCount; ++j)
|
||
{
|
||
if (!rkTextFileLoader.SetChildNode("event", j))
|
||
{
|
||
sys_err("Motion: no event data %d %s", j, c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
int iType;
|
||
if (!rkTextFileLoader.GetTokenInteger("motioneventtype", &iType))
|
||
{
|
||
sys_err("Motion: no motioneventtype data %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
//float fRadius;
|
||
D3DVECTOR v3Position;
|
||
|
||
switch (iType)
|
||
{
|
||
case MOTION_EVENT_TYPE_FLY:
|
||
case MOTION_EVENT_TYPE_EFFECT:
|
||
case MOTION_EVENT_TYPE_SCREEN_WAVING:
|
||
case MOTION_EVENT_TYPE_SOUND:
|
||
case MOTION_EVENT_TYPE_CHARACTER_SHOW:
|
||
case MOTION_EVENT_TYPE_CHARACTER_HIDE:
|
||
case MOTION_EVENT_TYPE_WARP:
|
||
case MOTION_EVENT_TYPE_EFFECT_TO_TARGET:
|
||
rkTextFileLoader.SetParentNode();
|
||
continue;
|
||
|
||
case MOTION_EVENT_TYPE_SPECIAL_ATTACKING:
|
||
// <20><> <20><><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD> <20>ϳ<EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>
|
||
if (!rkTextFileLoader.SetChildNode("spheredata", 0))
|
||
{
|
||
sys_err("Motion: no sphere data %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
//if (!rTextFileLoader.GetTokenFloat("radius", &fRadius))
|
||
//return false;
|
||
if (!rkTextFileLoader.GetTokenPosition("position", &v3Position))
|
||
{
|
||
sys_err("Motion: no position data %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
rkTextFileLoader.SetParentNode();
|
||
break;
|
||
default:
|
||
assert(!" CRaceMotionData::LoadMotionData - Strange Event Type");
|
||
return false;
|
||
break;
|
||
}
|
||
|
||
float fStartingTime;
|
||
|
||
if (!rkTextFileLoader.GetTokenFloat("startingtime", &fStartingTime))
|
||
{
|
||
sys_err("Motion: no startingtime data %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
pMob->AddSkillSplash(iSkillIndex, 100 + DWORD(fStartingTime * 1000), -(DWORD)(v3Position.y));
|
||
|
||
rkTextFileLoader.SetParentNode();
|
||
}
|
||
}
|
||
rkTextFileLoader.SetParentNode();
|
||
}
|
||
|
||
pMob->m_mobSkillInfo[iSkillIndex].dwSkillVnum = pMob->m_table.Skills[iSkillIndex].dwVnum;
|
||
pMob->m_mobSkillInfo[iSkillIndex].bSkillLevel = pMob->m_table.Skills[iSkillIndex].bLevel;
|
||
m_isEmpty = false;
|
||
return true;
|
||
}
|
||
|
||
bool CMotion::LoadFromFile(const char * c_pszFileName)
|
||
{
|
||
CTextFileLoader loader;
|
||
|
||
if (!loader.Load(c_pszFileName))
|
||
{
|
||
sys_log(0, "Motion: LoadFromFile fail: %s", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
if (!loader.GetTokenFloat("motionduration", &m_fDuration))
|
||
{
|
||
sys_err("Motion: %s does not have a duration", c_pszFileName);
|
||
return false;
|
||
}
|
||
|
||
if (loader.GetTokenPosition("accumulation", &m_vec3Accumulation))
|
||
m_isAccumulation = true;
|
||
|
||
sys_log(1, "%-48s %.3f %f", strchr(c_pszFileName, '/') + 1, GetDuration(), GetAccumVector().y);
|
||
|
||
m_isEmpty = false;
|
||
return true;
|
||
}
|
||
|
||
float CMotion::GetDuration() const
|
||
{
|
||
return m_fDuration;
|
||
}
|
||
|
||
const D3DVECTOR & CMotion::GetAccumVector() const
|
||
{
|
||
return m_vec3Accumulation;
|
||
}
|
||
|
||
bool CMotion::IsEmpty()
|
||
{
|
||
return m_isEmpty;
|
||
}
|
||
|