server/game/src/motion.cpp

564 lines
14 KiB
C++
Raw Normal View History

2022-03-05 12:44:06 +02:00
#include "stdafx.h"
2022-03-05 19:59:39 +02:00
#include <common/stl.h>
2022-03-05 12:44:06 +02:00
#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];
2022-03-05 12:44:06 +02:00
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;
}