CN1602 is the local client. KR is the official tree with unpacked Data, System, and Sound. Packed .wpk files stay off git.
1101 lines
44 KiB
C++
1101 lines
44 KiB
C++
#include "stdafx.h"
|
|
#include "./NPCInfoParser.h"
|
|
|
|
#include <Struct.h>
|
|
#include "./MapInfoParser.h"
|
|
#include "./SkillInfoParser.h"
|
|
|
|
// ~09/12/23
|
|
// 09/12/23, waverix, code-arrangement
|
|
// 10.06.06|waverix|????????????? BASE_NPCINFO ?????????
|
|
// ??????????????????????????? O (1) ???????.
|
|
|
|
//==================================================================================================
|
|
|
|
NPCInfoParser* NPCInfoParser::npc_info_parser_static_ = NULL;
|
|
|
|
NPCInfoParser* NPCInfoParser::CreateInstance()
|
|
{
|
|
// (NOTE) ???????????
|
|
npc_info_parser_static_ = new NPCInfoParser;
|
|
return npc_info_parser_static_;
|
|
}
|
|
|
|
void NPCInfoParser::DestroyInstance()
|
|
{
|
|
SAFE_DELETE(npc_info_parser_static_);
|
|
}
|
|
|
|
//==================================================================================================
|
|
// NOTE: f110520.2L
|
|
namespace util {
|
|
;
|
|
static NpcElementWeakness CalculateNpcElementWeakness(const BASE_NPCINFO* npc_info);
|
|
|
|
};
|
|
//==================================================================================================
|
|
|
|
NPCInfoParser::NPCInfoParser()
|
|
: loaded_(false),
|
|
extra_npc_table_(NULL),
|
|
extra_npc_info_array_(NULL)
|
|
{
|
|
ZeroMemory(&npc_table_, sizeof(npc_table_));
|
|
}
|
|
|
|
NPCInfoParser::~NPCInfoParser()
|
|
{
|
|
Unload();
|
|
UnLoadExtra();
|
|
}
|
|
|
|
void NPCInfoParser::Release()
|
|
{
|
|
Unload();
|
|
UnLoadExtra();
|
|
}
|
|
|
|
void NPCInfoParser::Init(DWORD unused_arg1, DWORD unused_arg2)
|
|
{
|
|
__UNUSED((unused_arg1, unused_arg2));
|
|
}
|
|
|
|
void NPCInfoParser::Init(DWORD unused_arg1, char* pack_file_name, DWORD unused_arg2)
|
|
{
|
|
Init(unused_arg1, unused_arg2);
|
|
SetPackFileName(pack_file_name);
|
|
}
|
|
|
|
void NPCInfoParser::Unload()
|
|
{
|
|
SetFirst();
|
|
for (const BASE_NPCINFO* npc_info = GetNext(); npc_info; npc_info = GetNext())
|
|
{
|
|
#ifdef _NA_007620_20140915_STATE_RESISTS
|
|
if (npc_info->state_resistances_) {
|
|
delete npc_info->state_resistances_;
|
|
}
|
|
#endif //_NA_007620_20140915_STATE_RESISTS
|
|
}
|
|
|
|
if (npc_table_.seq_total_array_)
|
|
{
|
|
::free((void*)npc_table_.seq_total_indexes_);
|
|
::free((void*)npc_table_.seq_total_array_);
|
|
ZeroMemory(&npc_table_, sizeof(npc_table_));
|
|
}
|
|
}
|
|
|
|
void NPCInfoParser::UnLoadExtra()
|
|
{
|
|
if (extra_npc_table_ == NULL) {
|
|
return;
|
|
}
|
|
extra_npc_table_->RemoveAll();
|
|
SAFE_DELETE(extra_npc_table_);
|
|
::free(extra_npc_info_array_);
|
|
extra_npc_info_array_ = NULL;
|
|
}
|
|
|
|
EXTRA_NPCINFO_SET* NPCInfoParser::GetExtraInfoSetFromFieldID(MAPCODE map_code,FIELDID field_id)
|
|
{
|
|
extra_npc_info_set_.Clear();
|
|
extra_npc_table_->SetFirst();
|
|
while (EXTRA_NPCINFO* extra_info = extra_npc_table_->GetNext())
|
|
{
|
|
if (extra_info->m_MapCode == map_code && extra_info->m_FieldID == field_id) {
|
|
extra_npc_info_set_.Insert(extra_info);
|
|
}
|
|
}
|
|
return &extra_npc_info_set_;
|
|
}
|
|
|
|
bool NPCInfoParser::_BuildReference(int added_number_of_nodes, bool is_extra_loading)
|
|
{
|
|
if (added_number_of_nodes <= 0) {
|
|
return false;
|
|
}
|
|
//
|
|
if (is_extra_loading == false)
|
|
{ //????NPC???
|
|
Unload();
|
|
const size_t indexes_chunk_size = sizeof(npc_table_.seq_total_indexes_[0]) *
|
|
added_number_of_nodes;
|
|
void* ptr = ::malloc(indexes_chunk_size);
|
|
ZeroMemory(ptr, indexes_chunk_size);
|
|
npc_table_.seq_total_indexes_ = reinterpret_cast<uint16_t*>(ptr);
|
|
//
|
|
const size_t npc_info_chunk_size = sizeof(npc_table_.seq_total_array_[0]) *
|
|
added_number_of_nodes;
|
|
ptr = ::malloc(npc_info_chunk_size);
|
|
ZeroMemory(ptr, npc_info_chunk_size);
|
|
npc_table_.seq_total_array_ = reinterpret_cast<BASE_NPCINFO*>(ptr);
|
|
this->SetFirst();
|
|
}
|
|
else
|
|
{ // ??????????
|
|
UnLoadExtra();
|
|
extra_npc_table_ = new util::SolarHashTable<EXTRA_NPCINFO*>;
|
|
extra_npc_table_->Initialize(50);
|
|
const size_t chunk_size = sizeof(extra_npc_info_array_[0]) * added_number_of_nodes;
|
|
extra_npc_info_array_ = reinterpret_cast<EXTRA_NPCINFO*>(::malloc(chunk_size));
|
|
ZeroMemory(extra_npc_info_array_, chunk_size);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL NPCInfoParser::_LoadExtra(BOOL reloaded)
|
|
{
|
|
const int max_rows = GetRowSize();
|
|
if (_BuildReference(max_rows, true) == false) {
|
|
return false;
|
|
}
|
|
//
|
|
static const char __NullUnitID[4] = { '0', '0', '0', '0', };
|
|
const WzID kNullUnitID = StrToWzID(__NullUnitID);
|
|
//
|
|
EXTRA_NPCINFO* extra_npcinfo_it = this->extra_npc_info_array_;
|
|
for (int row = 0; row < max_rows; ++row)
|
|
{
|
|
MAPCODE MapCode = GetDataWORD("MapCode", row);
|
|
FIELDID FieldID = GetDataInt("FieldID", row);
|
|
BYTE byNPCTYPE = GetDataBYTE("NpcType", row);
|
|
CODETYPE MonsterCode = GetDataDWORD("MonsterCode", row);
|
|
EXTRA_NPCINFO* extra_npc_info = extra_npc_table_->GetData(MonsterCode);
|
|
if (extra_npc_info == NULL)
|
|
{
|
|
extra_npc_info = extra_npcinfo_it++;
|
|
extra_npc_table_->Add(extra_npc_info, MonsterCode);
|
|
}
|
|
else
|
|
{
|
|
if (reloaded == false)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, "NPCExtraInfo duplicated ID (%ld)", MonsterCode);
|
|
}
|
|
}
|
|
//
|
|
extra_npc_info->m_MapCode = MapCode;
|
|
extra_npc_info->m_FieldID = FieldID;
|
|
extra_npc_info->m_eNPCTYPE = (eNPC_TYPE)byNPCTYPE;
|
|
extra_npc_info->m_NPCCODE = MonsterCode;
|
|
|
|
extra_npc_info->m_eRotateType = \
|
|
(EXTRA_NPCINFO::eROTATE_TYPE)GetDataInt("RotateType", row);
|
|
extra_npc_info->m_eDisplayType = \
|
|
(EXTRA_NPCINFO::eDISPLAY_TYPE)GetDataInt("DisplayType", row);
|
|
|
|
extra_npc_info->m_fNPCPos[0] = GetDataFloat("NPCPos_X", row);
|
|
extra_npc_info->m_fNPCPos[1] = GetDataFloat("NPCPos_Y", row);
|
|
extra_npc_info->m_fNPCPos[2] = GetDataFloat("NPCPos_Z", row);
|
|
|
|
extra_npc_info->m_fNPCDir[0] = GetDataFloat("NPCDir_X", row);
|
|
extra_npc_info->m_fNPCDir[1] = GetDataFloat("NPCDir_Y", row);
|
|
extra_npc_info->m_fNPCDir[2] = GetDataFloat("NPCDir_Z", row);
|
|
|
|
extra_npc_info->m_fNPCDir[0] = extra_npc_info->m_fNPCDir[0] - extra_npc_info->m_fNPCPos[0];
|
|
extra_npc_info->m_fNPCDir[1] = extra_npc_info->m_fNPCDir[1] - extra_npc_info->m_fNPCPos[1];
|
|
extra_npc_info->m_fNPCDir[2] = extra_npc_info->m_fNPCDir[2] - extra_npc_info->m_fNPCPos[2];
|
|
|
|
extra_npc_info->m_iMoveType = GetDataInt("MoveType", row);
|
|
|
|
extra_npc_info->m_fMovePos[0] = GetDataFloat("MovePos_X", row);
|
|
extra_npc_info->m_fMovePos[1] = GetDataFloat("MovePos_Y", row);
|
|
extra_npc_info->m_fMovePos[2] = GetDataFloat("MovePos_Z", row);
|
|
|
|
extra_npc_info->m_fRange = GetDataFloat("Range", row);
|
|
extra_npc_info->m_dwJobStrID = GetDataDWORD("JobStrID", row);
|
|
extra_npc_info->m_iNPC_IconID = GetDataInt("NPCIconID", row);
|
|
;{ //_NA_0_20100222_UNIT_TRIGGERS_REGEN
|
|
// CHANGES: f110118.5L, change WzID reading method
|
|
// to support excel converting format... 6L, difficult, make to easy
|
|
char unit_id_string[sizeof(WzID)];
|
|
ulong id_value = GetDataDWORD("UnitID", row);
|
|
_snprintf(unit_id_string, _countof(unit_id_string), "%04u", id_value);
|
|
WzID wz_id = StrToWzID(unit_id_string);
|
|
extra_npc_info->m_unitID = 0;
|
|
if (kNullUnitID != wz_id) {
|
|
extra_npc_info->m_unitID = wz_id;
|
|
}
|
|
};
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NPCInfoParser::_AddNpcInfo(CODETYPE npc_code, BASE_NPCINFO* npc_info)
|
|
{
|
|
if (npc_code == 0 || npc_code > USHRT_MAX || npc_info == NULL) {
|
|
__debugbreak();
|
|
return false;
|
|
}
|
|
--npc_code;
|
|
++npc_table_.number_of_total_npcs_;
|
|
*const_cast<uint16_t*>(npc_table_.seq_total_it_) = static_cast<uint16_t>(npc_code);
|
|
++npc_table_.seq_total_it_;
|
|
npc_table_.array_[npc_code] = npc_info;
|
|
return true;
|
|
}
|
|
|
|
BOOL NPCInfoParser::_Load(BOOL reloaded)
|
|
{
|
|
// (????) ????????????????????????????????????
|
|
static const char __wzid_nullstream[4] = { '0', '\0', '\0', '\0' };
|
|
const WzID WzID_NullCode = StrToWzID(__wzid_nullstream);
|
|
|
|
const int max_rows = GetRowSize();
|
|
if (_BuildReference(max_rows, false) == false) {
|
|
return false;
|
|
}
|
|
// (????) ????????????? SetFirst??GetNext ????
|
|
//uint16_t* npc_info_index_it = const_cast<uint16_t*>(npc_table_.seq_total_indexes_);
|
|
BASE_NPCINFO* npc_info_data_it = const_cast<BASE_NPCINFO*>(npc_table_.seq_total_array_);
|
|
for (int row = 0; row < max_rows; ++row)
|
|
{
|
|
CODETYPE MonsterCode = GetDataDWORD("MonsterCode", row);
|
|
|
|
BASE_NPCINFO* npc_info = this->GetNPCInfo(MonsterCode);
|
|
if (npc_info == NULL)
|
|
{
|
|
npc_info = npc_info_data_it++;
|
|
//*npc_info_index_it++ = static_cast<uint16_t>(MonsterCode);
|
|
this->_AddNpcInfo(MonsterCode, npc_info);
|
|
}
|
|
else
|
|
{
|
|
if (false == reloaded)
|
|
{
|
|
SUNLOG(eCRITICAL_LOG, "NPCInfo duplicated ID (%ld)", MonsterCode);
|
|
}
|
|
}
|
|
|
|
static CODETYPE testCode = MonsterCode;
|
|
|
|
npc_info->m_MonsterCode = static_cast<MONSTERCODE>(MonsterCode);
|
|
strncpy(npc_info->m_pszName, GetDataString("Name", row), _countof(npc_info->m_pszName));
|
|
npc_info->m_pszName[_countof(npc_info->m_pszName) - 1] = '\0';
|
|
#ifdef _DH_NPC_GROUP_NAME
|
|
npc_info->group_name = GetDataWORD("GName", row);
|
|
#endif//_DH_NPC_GROUP_NAME
|
|
npc_info->m_Level = GetDataWORD("Level", row);
|
|
npc_info->m_DisplayLevel = GetDataWORD("DisplayLevel", row);
|
|
npc_info->m_dwMaxHP = GetDataDWORD("MaxHP", row);
|
|
npc_info->m_dwMaxMP = GetDataDWORD("MaxMP", row);
|
|
npc_info->m_dwMaxSD = GetDataDWORD("MaxSD", row);
|
|
npc_info->m_NCode = GetDataDWORD("NCode", row);
|
|
npc_info->m_dwICode = GetDataDWORD("ICode", row);
|
|
npc_info->m_wNpcImageIndex = GetDataWORD("NPCImageIndex", row);
|
|
|
|
npc_info->m_byGrade = GetDataBYTE("Grade", row);
|
|
npc_info->m_fGradeExpRatio = GetDataFloat("GradeExpRatio", row); // 2006.3.24 ????
|
|
npc_info->m_wSize = GetDataWORD("Size", row);
|
|
npc_info->m_wAICode = GetDataWORD("AICode", row);
|
|
npc_info->m_byAttType = GetDataBYTE("AttType", row);
|
|
|
|
npc_info->m_byCriticalRatio = GetDataBYTE("CriticalRatio", row);
|
|
npc_info->m_dwMinAttackPower = GetDataDWORD("MinAttackPower", row);
|
|
npc_info->m_dwMaxAttackPower = GetDataDWORD("MaxAttackPower", row);
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
npc_info->skill_power = GetDataDWORD("SkillPower", row);
|
|
#endif
|
|
npc_info->m_dwPhyDef = GetDataDWORD("PhyDef", row);
|
|
#if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
npc_info->m_dwMagicDef = GetDataDWORD("MagicDef", row);
|
|
#endif
|
|
npc_info->m_wClass = GetDataWORD("Class", row);
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
npc_info->element_fire_attack = GetDataWORD("FireAttack", row);
|
|
npc_info->element_water_attack = GetDataWORD("WaterAttack", row);
|
|
npc_info->element_wind_attack = GetDataWORD("WindAttack", row);
|
|
npc_info->element_earth_attack = GetDataWORD("EarthAttack", row);
|
|
npc_info->element_dark_attack = GetDataWORD("DarkAttack", row);
|
|
// script ranges = [0, 2.2]
|
|
// reduction ratio against AttackPower
|
|
// Resist = 1.0, Conversion Resist = 0%
|
|
// AttackPower = 100, Damage = 100
|
|
// Resist = 2.0, Conversion Resist = 100%
|
|
// AttackPower = 100, Damage = 0
|
|
// Resist = 0.0, Conversion Resist = -100%
|
|
// AttackPower = 100, Damage = 200
|
|
npc_info->element_fire_resist = static_cast<int16_t>(
|
|
util::LL_Round(GetDataNumber("FireResist", row) * 100.0f - 100.0f));
|
|
npc_info->element_water_resist = static_cast<int16_t>(
|
|
util::LL_Round(GetDataNumber("WaterResist", row) * 100.0f - 100.0f));
|
|
npc_info->element_wind_resist = static_cast<int16_t>(
|
|
util::LL_Round(GetDataNumber("WindResist", row) * 100.0f - 100.0f));
|
|
npc_info->element_earth_resist = static_cast<int16_t>(
|
|
util::LL_Round(GetDataNumber("EarthResist", row) * 100.0f - 100.0f));
|
|
npc_info->element_dark_resist = static_cast<int16_t>(
|
|
util::LL_Round(GetDataNumber("DarkResist", row) * 100.0f - 100.0f));
|
|
// CHANGES: f110520.2L, npc ????????????????????????????
|
|
npc_info->element_weakness = util::CalculateNpcElementWeakness(npc_info);
|
|
//
|
|
npc_info->m_wPhyAttRate = static_cast<uint16_t>(
|
|
util::LL_Round(GetDataNumber("PhyAttRate", row) * 100.0f));
|
|
npc_info->m_wPhyAttAvoid = static_cast<uint16_t>(
|
|
util::LL_Round(GetDataNumber("PhyAttAvoid", row) * 100.0f));
|
|
#else
|
|
npc_info->element_fire_resist = GetDataWORD("FireResist", row);
|
|
npc_info->element_water_resist = GetDataWORD("WaterResist", row);
|
|
npc_info->element_wind_resist = GetDataWORD("WindResist", row);
|
|
npc_info->element_earth_resist = GetDataWORD("EarthResist", row);
|
|
npc_info->m_wPhyAttRate = GetDataWORD("PhyAttRate", row);
|
|
npc_info->m_wPhyAttAvoid = GetDataWORD("PhyAttAvoid", row);
|
|
#endif
|
|
npc_info->m_byMeleeType = GetDataBYTE("MeleeType", row);
|
|
npc_info->m_byArmorType = GetDataBYTE("ArmorType", row);
|
|
npc_info->m_fBodyRadius = GetDataFloat("BodyRadius", row); // 2006.4.12 ????
|
|
npc_info->m_fAttRange = GetDataFloat("AttRange", row);
|
|
npc_info->m_fViewRange = GetDataFloat("ViewRange", row);
|
|
npc_info->m_fMoveRange = GetDataFloat("MoveRange", row);
|
|
|
|
npc_info->m_fWalkSpeed = GetDataFloat("WalkSpeed", row);
|
|
npc_info->m_fRunSpeed = GetDataFloat("RunSpeed", row);
|
|
npc_info->m_wAttSpeed = GetDataWORD("AttSpeed", row);
|
|
npc_info->m_wAttSpeed2 = GetDataWORD("AttSpeed2", row);
|
|
npc_info->m_wProjectileCode = GetDataWORD("ProjectileCode", row);
|
|
npc_info->m_wProjectileCode2 = GetDataWORD("ProjectileCode2", row);
|
|
npc_info->m_byJumpRatio = GetDataBYTE("JumpRatio", row);
|
|
npc_info->m_bIsNotice = GetDataBYTE("IsNotice", row); //xiaomi ?????????????
|
|
|
|
//_YJ_ADD_DAMAGE_EFFECT_NPCINFO
|
|
TCHAR szDamageEffectID[4] = {0,};
|
|
strncpy(szDamageEffectID, GetDataString("AttackEffectCode", row), 4);
|
|
DWORD dwDamageEffect = StrToWzID(szDamageEffectID);
|
|
if (WzID_NullCode != dwDamageEffect) {
|
|
npc_info->m_wiDamageEffectID = dwDamageEffect;
|
|
}
|
|
else {
|
|
npc_info->m_wiDamageEffectID = INVALID_WZID;
|
|
}
|
|
//
|
|
TCHAR szCDamageEffectID[4] = {0,};
|
|
strncpy(szCDamageEffectID, GetDataString("AttackCriticalEffectCode", row), 4);
|
|
DWORD dwCDamageEffect = StrToWzID(szCDamageEffectID);
|
|
if (WzID_NullCode != dwCDamageEffect) {
|
|
npc_info->m_wiCDamageEffectID = dwCDamageEffect;
|
|
}
|
|
else {
|
|
npc_info->m_wiCDamageEffectID = INVALID_WZID;
|
|
}
|
|
|
|
TCHAR szSpawnEffectID[4] = {0,};
|
|
strncpy(szSpawnEffectID, GetDataString("SpawnEffectCode", row), 4);
|
|
DWORD dwEffect = StrToWzID(szSpawnEffectID);
|
|
if (WzID_NullCode != dwEffect) {
|
|
npc_info->m_dwSpawnEffectCode = dwEffect;
|
|
}
|
|
else {
|
|
npc_info->m_dwSpawnEffectCode = INVALID_WZID;
|
|
}
|
|
|
|
strncpy(npc_info->m_wzidSpawnAniCode.code, GetDataString("SpawnAniCode", row), 4);
|
|
npc_info->m_dwSpawnTime = GetDataDWORD("SpawnAniTime", row);
|
|
npc_info->m_dwDeadWaitingTime = GetDataDWORD("DeadWaitingTime", row);
|
|
npc_info->m_byAttitude = GetDataBYTE("Attitude", row);
|
|
npc_info->m_byMoveAttitude = GetDataBYTE("MoveAttitude", row);
|
|
TCHAR szLocationID[MAX_AREA_ID_LENGTH];
|
|
strncpy(szLocationID, GetDataString("MoveAreaID", row), MAX_AREA_ID_LENGTH);
|
|
npc_info->m_dwMoveAreaID = StrToWzID(szLocationID);
|
|
|
|
// ????????
|
|
TCHAR szTemp[MAX_FIELD_NAME_SIZE];
|
|
for (int i = 0; i < BASE_NPCINFO::MAX_SPECIAL_NUM; ++i)
|
|
{
|
|
_snprintf(szTemp, _countof(szTemp), "MonsterCondition%d\0", i+1);
|
|
npc_info->m_SpecialCondition[i].byCondition = GetDataBYTE(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "MonsterConditionParam%d\0", i+1);
|
|
npc_info->m_SpecialCondition[i].wConditionParam = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "ActionType%d\0", i+1);
|
|
npc_info->m_SpecialCondition[i].wActionType = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "ActionParam%d\0", i+1);
|
|
npc_info->m_SpecialCondition[i].wActionParam = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "ActionRate%d\0", i+1);
|
|
npc_info->m_SpecialCondition[i].byActionRate = GetDataBYTE(szTemp, row);
|
|
}
|
|
|
|
// ?????
|
|
for (int i = 0; i < BASE_NPCINFO::MAX_RESISTANCE_NUM; ++i)
|
|
{
|
|
_snprintf(szTemp, _countof(szTemp), "StateCode%d\0", i+1);
|
|
npc_info->m_ResistanceCondition[i].m_wStateCode = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "ConditionRatio%d\0", i+1);
|
|
npc_info->m_ResistanceCondition[i].m_byRatio = GetDataBYTE(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "ConditionEffectCode%d\0", i+1);
|
|
strncpy(npc_info->m_ResistanceCondition[i].m_wzidEffectCode.code,
|
|
GetDataString(szTemp, row), 4);
|
|
}
|
|
npc_info->m_byChangeTargetRatio = GetDataBYTE("ChangeTargetRatio", row);
|
|
|
|
#ifdef _NA_007667_20141001_WITCHBLADE_SKILL
|
|
//????SkillUpdateTime???????
|
|
npc_info->skill_update_condition = GetDataBYTE("SkillUpdateCondition", row);
|
|
#endif //_NA_007667_20141001_WITCHBLADE_SKILL
|
|
// ???G???????????????
|
|
npc_info->m_wSkillUpdateTime = GetDataWORD("SkillUpdateTime", row);
|
|
// ??????
|
|
npc_info->m_byReviveCondition = GetDataBYTE("ReviveCondition", row);
|
|
npc_info->m_wReviveConditionParam = GetDataWORD("ReviveConditionParam", row);
|
|
npc_info->m_byReviveRate = GetDataBYTE("ReviveRate", row);
|
|
npc_info->m_wReviveCode = GetDataWORD("ReviveCode", row);
|
|
npc_info->m_wReviveDelay = GetDataWORD("ReviveDelay", row);
|
|
// ???????
|
|
npc_info->m_byHealCondition = GetDataBYTE("HealCondition", row);
|
|
npc_info->m_wHealConditionParam = GetDataWORD("HealConditionParam", row);
|
|
npc_info->m_byHealRate = GetDataBYTE("HealRate", row);
|
|
npc_info->m_wHealCode = GetDataWORD("HealCode", row);
|
|
npc_info->m_wHealDelay = GetDataWORD("HealDelay", row);
|
|
|
|
// ???????
|
|
npc_info->m_bySummonCondition = GetDataBYTE("SummonCondition", row);
|
|
npc_info->m_wSummonConditionParam = GetDataWORD("SummonConditionParam", row);
|
|
npc_info->m_bySummonRate = GetDataBYTE("SummonRate", row);
|
|
npc_info->m_wSummonCode= GetDataWORD("SummonCode", row);
|
|
npc_info->m_wSummonDelay = GetDataWORD("SummonDelay", row);
|
|
|
|
// ????????
|
|
npc_info->m_bySkillUsePossible = GetDataBYTE("SkillUsePossible", row);
|
|
// (CHANGES) (f100709.2L)
|
|
BOOST_STATIC_ASSERT(BASE_NPCINFO::MAX_SKILL_NUM == _countof(npc_info->m_wSkillCode) &&
|
|
BASE_NPCINFO::MAX_SKILL_NUM == _countof(npc_info->m_bySkillRate) &&
|
|
BASE_NPCINFO::MAX_SKILL_NUM == _countof(npc_info->m_wSkillDelay));
|
|
static const char* kSkillCode_string[_countof(npc_info->m_wSkillCode)] = {
|
|
"SkillCode1", "SkillCode2", "SkillCode3", "SkillCode4", "SkillCode5",
|
|
"SkillCode6", "SkillCode7", "SkillCode8", "SkillCode9", "SkillCode10"
|
|
};
|
|
static const char* kSkillRate_string[_countof(npc_info->m_bySkillRate)] = {
|
|
"SkillRate1", "SkillRate2", "SkillRate3", "SkillRate4", "SkillRate5",
|
|
"SkillRate6", "SkillRate7", "SkillRate8", "SkillRate9", "SkillRate10"
|
|
};
|
|
static const char* kSkillDelay_string[_countof(npc_info->m_wSkillDelay)] = {
|
|
"SkillDelay1", "SkillDelay2", "SkillDelay3", "SkillDelay4", "SkillDelay5",
|
|
"SkillDelay6", "SkillDelay7", "SkillDelay8", "SkillDelay9", "SkillDelay10"
|
|
};
|
|
for (int i = 0; i < _countof(npc_info->m_wSkillCode); ++i)
|
|
{
|
|
npc_info->m_wSkillCode[i] = GetDataWORD(kSkillCode_string[i], row);
|
|
npc_info->m_bySkillRate[i] = GetDataBYTE(kSkillRate_string[i], row);
|
|
npc_info->m_wSkillDelay[i] = GetDataWORD(kSkillDelay_string[i], row);
|
|
}
|
|
//
|
|
npc_info->m_wHateSkill = GetDataWORD("HateSkill", row);
|
|
npc_info->m_wRevengeSkill = GetDataWORD("RevengeSkill", row);
|
|
npc_info->m_byRevengeRate = GetDataBYTE("RevengeRate", row);
|
|
|
|
DWORD dwDropOwnerShip = GetDataDWORD("DropOwnership", row);
|
|
npc_info->m_bRegion = (GetDataDWORD("Region", row) != false);
|
|
// (CHANGES) (f100604.3L) fix up a compatible error on the vc++ 1600
|
|
// pseudo : set = { (dwDropOwnerShip & (1 << n)) != 0 }
|
|
// unset = { (dwDropOwnerShip & (1 << n)) == 0 }
|
|
//std::bitset<BASE_NPCINFO::MAX_DROPRATIO_INDEX_NUM> bitOwnerShip(dwDropOwnerShip);
|
|
// (CHANGES) (f100709.2L)
|
|
const int kDropRatioIndexNum = _countof(npc_info->m_bOwnerShipForDropIndex);
|
|
BOOST_STATIC_ASSERT(_countof(npc_info->m_bOwnerShipForDropIndex) ==
|
|
_countof(npc_info->m_dwDropIndex) &&
|
|
_countof(npc_info->m_bOwnerShipForDropIndex) ==
|
|
_countof(npc_info->m_dwFieldDropIndex) &&
|
|
_countof(npc_info->m_bOwnerShipForDropIndex) ==
|
|
_countof(npc_info->m_dwBattleDropIndex));
|
|
static const char* kDropIndex_strings[kDropRatioIndexNum] = {
|
|
"DropIndex1", "DropIndex2", "DropIndex3", "DropIndex4", "DropIndex5",
|
|
"DropIndex6", "DropIndex7", "DropIndex8", "DropIndex9", "DropIndex10"
|
|
};
|
|
static const char* kFieldDropIndex_strings[kDropRatioIndexNum] = {
|
|
"FieldDropIndex1", "FieldDropIndex2", "FieldDropIndex3",
|
|
"FieldDropIndex4", "FieldDropIndex5", "FieldDropIndex6",
|
|
"FieldDropIndex7", "FieldDropIndex8", "FieldDropIndex9", "FieldDropIndex10"
|
|
};
|
|
static const char* kBattleDropIndex_strings[kDropRatioIndexNum] = {
|
|
"BattleDropIndex1", "BattleDropIndex2", "BattleDropIndex3",
|
|
"BattleDropIndex4", "BattleDropIndex5", "BattleDropIndex6",
|
|
"BattleDropIndex7", "BattleDropIndex8", "BattleDropIndex9", "BattleDropIndex10"
|
|
};
|
|
|
|
for (int i = 0; i < _countof(npc_info->m_dwDropIndex); ++i)
|
|
{ // (f100604.3L)
|
|
npc_info->m_bOwnerShipForDropIndex[i] = ((dwDropOwnerShip & (1 << i)) != 0);
|
|
npc_info->m_dwDropIndex[i] = GetDataDWORD(kDropIndex_strings[i], row);
|
|
//?????????????
|
|
npc_info->m_dwFieldDropIndex[i] = GetDataDWORD(kFieldDropIndex_strings[i], row);
|
|
//??????????
|
|
npc_info->m_dwBattleDropIndex[i] = GetDataDWORD(kBattleDropIndex_strings[i], row);
|
|
}
|
|
//
|
|
for (DWORD idx=0 ; idx < BASE_NPCINFO::MAX_QUEST_ITEM_DROP_NUM; ++idx)
|
|
{
|
|
_snprintf(szTemp, _countof(szTemp), "QuestCode%d\0", idx+1);
|
|
npc_info->m_QuestCode[idx] = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "QuestDropIndex%d\0", idx+1);
|
|
npc_info->m_dwQuestDropIndex[idx] = GetDataDWORD(szTemp, row);
|
|
}
|
|
// 'm_wACCode' ep2 AC??????small type
|
|
npc_info->m_wACCode = GetDataBYTE("ACCode", row);
|
|
npc_info->m_byACReferenceID = GetDataBYTE("ACReferenceID", row);
|
|
//
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
_snprintf(szTemp, _countof(szTemp), "OptionID%d\0", i+1);
|
|
npc_info->m_dwOptionID[i] = GetDataDWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "OptionParam%d\0", i+1);
|
|
npc_info->m_dwOptionParam[i] = GetDataDWORD(szTemp, row);
|
|
}
|
|
npc_info->m_wDest = GetDataWORD("DEST", row);
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
_snprintf(szTemp, _countof(szTemp), "ACT%d\0", i+1);
|
|
npc_info->m_wAct[i] = GetDataWORD(szTemp, row);
|
|
_snprintf(szTemp, _countof(szTemp), "P%d\0", i+1);
|
|
npc_info->m_wP[i] = GetDataWORD(szTemp, row);
|
|
}
|
|
testCode = MonsterCode;
|
|
|
|
#ifdef _NA_20100307_BETAKEY_SYSTEM
|
|
npc_info->m_BeytaKey = GetDataInt("BetaKeyCode", row);
|
|
#endif
|
|
#ifdef _NA_007620_20140915_STATE_RESISTS
|
|
npc_info->state_resistances_ = NULL;
|
|
CHAR state_resists[2048] = {0,};
|
|
const char* char_state_regists = GetDataString("StateRegists", row);
|
|
strncpy(state_resists, char_state_regists, sizeof(state_resists));
|
|
if (state_resists[0] != '0')
|
|
{
|
|
npc_info->state_resistances_ = new STLX_MAP<WORD, RESISTANCE_CONDITION>();
|
|
//char* state_resists = "1200,1100%80,1000";
|
|
std::istringstream iss(state_resists);
|
|
std::string token;
|
|
while(std::getline(iss, token, '.'))
|
|
{
|
|
//std::cout << token << std::endl;
|
|
std::istringstream iss2(token);
|
|
std::string token_state_id;
|
|
std::string token_ratio;
|
|
WORD state_regist_code = 0;
|
|
WORD state_regist_ratio = 100;
|
|
|
|
if (std::getline(iss2, token_state_id, '%'))
|
|
state_regist_code = atoi(token_state_id.c_str());
|
|
//std::cout << token_state_id << std::endl;
|
|
|
|
if (std::getline(iss2, token_ratio, ' '))
|
|
state_regist_ratio = atoi(token_ratio.c_str());
|
|
//std::cout << token_ratio << std::endl;
|
|
|
|
RESISTANCE_CONDITION resist_state;
|
|
resist_state.m_wStateCode = state_regist_code;
|
|
resist_state.m_byRatio = state_regist_ratio;
|
|
npc_info->state_resistances_->insert( \
|
|
STLX_MAP<WORD, RESISTANCE_CONDITION>::value_type( \
|
|
state_regist_code, resist_state));
|
|
}
|
|
}
|
|
#endif //_NA_007620_20140915_STATE_RESISTS
|
|
|
|
// ?? 12096 ???????????????????
|
|
// ???????????????????????????????????????????????.
|
|
// [5/19/2010 lst1024]
|
|
|
|
// ?????????.
|
|
CheckValidity(npc_info);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOL NPCInfoParser::LoadScript(eSCRIPT_CODE script_code, BOOL reloaded)
|
|
{
|
|
switch (script_code)
|
|
{
|
|
case SCRIPT_NPCINFO:
|
|
return _Load(reloaded);
|
|
case SCRIPT_NPCEXTRAINFO:
|
|
return _LoadExtra(reloaded);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
BOOL NPCInfoParser::CheckValidity(BASE_NPCINFO* const npc_info)
|
|
{
|
|
BOOL success = true;
|
|
//
|
|
if (npc_info->m_byGrade == eNPC_MERCHANT_NPC) {
|
|
return true;
|
|
}
|
|
const MONSTERCODE npc_code = npc_info->m_MonsterCode;
|
|
const SkillInfoParser* const skill_parser = SkillInfoParser::Instance();
|
|
const SkillScriptInfo* base_skill_info = NULL;
|
|
// NPC???????????????
|
|
;{
|
|
int ratioSum = 0;
|
|
int count = 0;
|
|
for (int i = 0; i < npc_info->MAX_SKILL_NUM; ++i)
|
|
{
|
|
const SLOTCODE skill_code = npc_info->m_wSkillCode[i];
|
|
if (skill_code == 0) {
|
|
continue;
|
|
}
|
|
base_skill_info = skill_parser->GetSkillInfo(skill_code);
|
|
if (base_skill_info == NULL)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
_T("|[%s]|Monster(%d) field [m_wSkillCode(%d)] skill table not found|"),
|
|
__FUNCTION__, npc_code, skill_code);
|
|
npc_info->m_wSkillCode[i] = 0;
|
|
success = false;
|
|
}
|
|
else if (npc_info->m_bySkillRate == 0)
|
|
{
|
|
SUNLOG(eDEV_LOG, _T("|[%s]|Msg=invalid skill config")
|
|
_T("|Npc=%d, %dth={SkillCode=%d, SkillRate=0}")
|
|
_T("|Expected=SkillRate must not be zero|"),
|
|
__FUNCTION__, npc_code, i+1, skill_code);
|
|
}
|
|
ratioSum += npc_info->m_bySkillRate[i];
|
|
++count;
|
|
}
|
|
|
|
if (count > 0 && ratioSum == 0)
|
|
{
|
|
SUNLOG(eDEV_LOG, _T("|[%s]|Monster(%d) field [m_bySkillRate] is 0|"),
|
|
__FUNCTION__, npc_code);
|
|
success = false;
|
|
}
|
|
};
|
|
//
|
|
if (npc_info->m_wReviveCode)
|
|
{
|
|
base_skill_info = skill_parser->GetSkillInfo(npc_info->m_wReviveCode);
|
|
if (base_skill_info == NULL)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
_T("|[%s]|Monster(%d) field [m_wReviveCode(%d)] not found in skill table|"),
|
|
__FUNCTION__, npc_code, npc_info->m_wReviveCode);
|
|
npc_info->m_wReviveCode = 0;
|
|
success = false;
|
|
}
|
|
};
|
|
//
|
|
if (npc_info->m_wHealCode)
|
|
{
|
|
base_skill_info = skill_parser->GetSkillInfo(npc_info->m_wHealCode);
|
|
if (base_skill_info == NULL)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
_T("|[%s]|Monster(%d) field [m_wHealCode(%d)] not found in skill table|"),
|
|
__FUNCTION__, npc_code, npc_info->m_wHealCode);
|
|
npc_info->m_wHealCode = 0;
|
|
success = false;
|
|
}
|
|
};
|
|
//
|
|
if (npc_info->m_wSummonCode)
|
|
{
|
|
base_skill_info = skill_parser->GetSkillInfo(npc_info->m_wSummonCode);
|
|
if (base_skill_info == NULL) {
|
|
SUNLOG(eDEV_LOG,
|
|
_T("|[%s]|Monster(%d) field [m_wSummonCode(%d)] not found in skill table|"),
|
|
__FUNCTION__, npc_code, npc_info->m_wSummonCode);
|
|
npc_info->m_wSummonCode = 0;
|
|
success = false;
|
|
}
|
|
};
|
|
//----------------------------------------------------------------------------------------------
|
|
// ??????????????????
|
|
if (npc_info->m_wSkillUpdateTime == 0)
|
|
{
|
|
bool need_skill_update_time = false;
|
|
for (int i = 0; i < npc_info->MAX_SKILL_NUM; ++i)
|
|
{
|
|
if (npc_info->m_wSkillCode[i])
|
|
{
|
|
need_skill_update_time = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (npc_info->m_wReviveCode || npc_info->m_wHealCode || npc_info->m_wSummonCode)
|
|
{
|
|
need_skill_update_time = true;
|
|
}
|
|
|
|
for (int i = 0; i < npc_info->MAX_SPECIAL_NUM; ++i)
|
|
{
|
|
if (npc_info->m_SpecialCondition[i].wActionType) {
|
|
need_skill_update_time = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < BASE_NPCINFO::MAX_RESISTANCE_NUM; ++i)
|
|
{
|
|
if (npc_info->m_ResistanceCondition[i].m_wStateCode) {
|
|
need_skill_update_time = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (need_skill_update_time)
|
|
{
|
|
//????????????????????????????????
|
|
SUNLOG(eDEV_LOG,
|
|
_T("|[%s]|Monster(%d) field [m_wSkillUpdateTime] is 0!|"),
|
|
__FUNCTION__, npc_code);
|
|
npc_info->m_wSkillUpdateTime = 2000;
|
|
success = false;
|
|
}
|
|
}
|
|
else if (npc_info->m_wSkillUpdateTime < 1000)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"|[%s]|Monster(%d) field [m_wSkillUpdateTime(%d)] too small!|"
|
|
"|Minimum value is 1000|",
|
|
__FUNCTION__, npc_code, npc_info->m_wSkillUpdateTime);
|
|
npc_info->m_wSkillUpdateTime = 1000;
|
|
success = false;
|
|
}
|
|
|
|
// HP??MP???
|
|
if (!npc_info->m_dwMaxHP || !npc_info->m_dwMaxMP)
|
|
{
|
|
SUNLOG(eDEV_LOG, _T("|[%s]|Monster(%d) field [MaxHP] or [MaxMP] is 0!|"),
|
|
__FUNCTION__, npc_code);
|
|
npc_info->m_dwMaxHP = 100;
|
|
success = false;
|
|
}
|
|
|
|
// ???? [6/15/2010 lst1024]
|
|
// ??????????????????????????0??.
|
|
|
|
// EXP????????
|
|
// if (npc_info->m_fGradeExpRatio < 1)
|
|
// {
|
|
// SUNLOG(eDEV_LOG, "[NPCInfoParser] ???????[%d] m_fGradeExpRatio[%f] ???? 1.0!",
|
|
// npc_code, npc_info->m_fGradeExpRatio);
|
|
// npc_info->m_fGradeExpRatio = 1.0f;
|
|
// success = false;
|
|
// }
|
|
|
|
// CHANGES: f101223.2L, ??????????????????????? m_byAttitude???????? m_byAttType
|
|
// ????????
|
|
if (!npc_info->m_byAttitude)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byAttType] is 0!", npc_code);
|
|
npc_info->m_byAttitude = 1;
|
|
success = false;
|
|
}
|
|
|
|
if (npc_info->m_byMoveAttitude == eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK &&
|
|
npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byMoveAttitude] is 7, field [m_byAttType] cannot be 7", npc_code);
|
|
npc_info->m_byAttitude = ATTACK_ATTITUDE_NO_ATTACK;
|
|
success = false;
|
|
}
|
|
// UNDONE: check related codes
|
|
if (npc_info->m_byMeleeType == eMELEE_TYPE_NOT &&
|
|
npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byAttType] is not 7, field [m_byMeleeType] is 0", npc_code);
|
|
success = false;
|
|
}
|
|
|
|
if (npc_info->m_byMeleeType >= eMELEE_TYPE_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] invalid field [m_byMeleeType][%d]!",
|
|
npc_code, npc_info->m_byMeleeType);
|
|
npc_info->m_byMeleeType = eMELEE_TYPE_NOT;
|
|
success = false;
|
|
}
|
|
|
|
// ?????? ???
|
|
if (npc_info->m_byCriticalRatio > 100)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byCriticalRatio][%d] value too large, maximum 100!",
|
|
npc_code, npc_info->m_byCriticalRatio);
|
|
success = false;
|
|
}
|
|
|
|
// ???, ??? ??????
|
|
/* if (npc_info->m_dwMinAttackPower >= 100000 || npc_info->m_dwMaxAttackPower >= 100000)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_dwMinAttackPower][%d] field [m_dwMaxAttackPower[%d]] value too large "
|
|
"Maximum value 99999!",
|
|
npc_code, npc_info->m_dwMinAttackPower, npc_info->m_dwMaxAttackPower);
|
|
success = false;
|
|
}*/ //xiaomi?????????????????
|
|
|
|
if (npc_info->m_dwMinAttackPower > npc_info->m_dwMaxAttackPower)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_dwMinAttackPower][%d] value greater than "
|
|
"field [m_dwMaxAttackPower][%d]!",
|
|
npc_code, npc_info->m_dwMinAttackPower, npc_info->m_dwMaxAttackPower);
|
|
npc_info->m_dwMinAttackPower = npc_info->m_dwMaxAttackPower;
|
|
success = false;
|
|
}
|
|
|
|
// ?????????????
|
|
|
|
if (npc_info->m_wPhyAttRate == 0 || npc_info->m_wPhyAttAvoid == 0)
|
|
{
|
|
// ????????????????????????????????????????????.
|
|
if (npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK &&
|
|
npc_info->m_byMoveAttitude != eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_wPhyAttRate] or field [m_wPhyAttAvoid] is 0!", npc_code);
|
|
}
|
|
|
|
if (!npc_info->m_wPhyAttRate) npc_info->m_wPhyAttRate = 1;
|
|
if (!npc_info->m_wPhyAttAvoid) npc_info->m_wPhyAttAvoid = 1;
|
|
success = false;
|
|
}
|
|
#ifdef _NA001605_EP2_ATTRIBUTES_RENEWAL_
|
|
npc_info->m_byMeleeType = eMELEE_TYPE_SLASH;
|
|
npc_info->m_byArmorType = eARMOR_HARD;
|
|
#else //if !defined(_NA001605_EP2_ATTRIBUTES_RENEWAL_)
|
|
// ?????????????????
|
|
if (npc_info->m_byMeleeType >= eMELEE_TYPE_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] MonsterCode[%d] Invalid m_byMeleeType[%d]!",
|
|
npc_code, npc_info->m_byMeleeType);
|
|
npc_info->m_byMeleeType = eMELEE_TYPE_SLASH;
|
|
success = false;
|
|
}
|
|
if (npc_info->m_byArmorType >= eARMOR_TYPE_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] MonsterCode[%d] Invalid m_byArmorType[%d]!",
|
|
npc_code, npc_info->m_byArmorType);
|
|
npc_info->m_byArmorType = eARMOR_HARD;
|
|
success = false;
|
|
}
|
|
#endif
|
|
// ????????
|
|
if (npc_info->m_fBodyRadius > 3.0f)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_fBodyRadius][%f] value too large, maximum 3.0!",
|
|
npc_code, npc_info->m_fBodyRadius);
|
|
success = false;
|
|
}
|
|
|
|
|
|
// ????????
|
|
if (npc_info->m_fAttRange == 0 || npc_info->m_fAttRange > 250.f)
|
|
{
|
|
if (npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK &&
|
|
npc_info->m_byMoveAttitude != eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_fAttRange][%f] invalid value, valid range 0~250.0!",
|
|
npc_code, npc_info->m_fAttRange);
|
|
}
|
|
|
|
if (npc_info->m_fAttRange)
|
|
npc_info->m_fAttRange = 250.f;
|
|
else
|
|
npc_info->m_fAttRange = 1.f;
|
|
|
|
success = false;
|
|
}
|
|
|
|
// ???
|
|
if (npc_info->m_fViewRange == 0 || npc_info->m_fViewRange > 250.f)
|
|
{
|
|
if (npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK &&
|
|
npc_info->m_byMoveAttitude != eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_fViewRange][%f] invalid value, valid range 0~250.0!",
|
|
npc_code, npc_info->m_fViewRange);
|
|
}
|
|
|
|
if (npc_info->m_fViewRange)
|
|
npc_info->m_fViewRange = 250.f;
|
|
else
|
|
npc_info->m_fViewRange = 1.f;
|
|
|
|
success = false;
|
|
}
|
|
|
|
// ???
|
|
if (npc_info->m_byMoveAttitude == eMOVE_ATTITUDE_ETERNAL_STOP ||
|
|
npc_info->m_byMoveAttitude == eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK)
|
|
{
|
|
if (npc_info->m_fWalkSpeed != 0 || npc_info->m_fRunSpeed != 0)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [MoveAttitude][%d] is a fixed attribute. "
|
|
"m_fWalkSpeed, m_fRunSpeed cannot have values other than 0",
|
|
npc_code, npc_info->m_byMoveAttitude);
|
|
|
|
npc_info->m_fWalkSpeed = 0;
|
|
npc_info->m_fRunSpeed = 0;
|
|
success = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (npc_info->m_fWalkSpeed < 0.2f || npc_info->m_fWalkSpeed > 20.f)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_fWalkSpeed][%f] invalid value, valid range 0.2~20.0!",
|
|
npc_code, npc_info->m_fWalkSpeed);
|
|
if (npc_info->m_fWalkSpeed > 20.f) {
|
|
npc_info->m_fWalkSpeed = 20.f;
|
|
}
|
|
else {
|
|
npc_info->m_fWalkSpeed = 0.2f;
|
|
}
|
|
success = false;
|
|
}
|
|
|
|
if (npc_info->m_fRunSpeed < 0.2f || npc_info->m_fRunSpeed > 20.f)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_fRunSpeed][%f] invalid value, valid range 0.2~20.0!",
|
|
npc_code, npc_info->m_fRunSpeed);
|
|
if (npc_info->m_fRunSpeed > 20.f) {
|
|
npc_info->m_fRunSpeed = 20.f;
|
|
}
|
|
else {
|
|
npc_info->m_fRunSpeed = 0.2f;
|
|
}
|
|
success = false;
|
|
}
|
|
|
|
if (npc_info->m_fWalkSpeed > npc_info->m_fRunSpeed)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_fWalkSpeed][%f] value greater than "
|
|
"field [m_fRunSpeed][%f]!",
|
|
npc_code, npc_info->m_fWalkSpeed, npc_info->m_fRunSpeed);
|
|
npc_info->m_fWalkSpeed = npc_info->m_fRunSpeed;
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (npc_info->m_wAttSpeed == 0 || npc_info->m_wAttSpeed2 == 0)
|
|
{
|
|
if (npc_info->m_byAttitude != ATTACK_ATTITUDE_NO_ATTACK &&
|
|
npc_info->m_byMoveAttitude != eMOVE_ATTITUDE_ETERNAL_STOP_NO_ATTACK)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_wAttSpeed] or [m_wAttSpeed2] is 0!", npc_code);
|
|
}
|
|
|
|
if (!npc_info->m_wAttSpeed)
|
|
npc_info->m_wAttSpeed = 1000;
|
|
|
|
if (!npc_info->m_wAttSpeed2)
|
|
npc_info->m_wAttSpeed2 = 1000;
|
|
|
|
success = false;
|
|
}
|
|
|
|
// ????????
|
|
if (npc_info->m_byAttitude >= ATTACK_ATTITUDE_TYPE_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byAttitude][%d] invalid value, must be <= [%d]!",
|
|
npc_code, npc_info->m_byAttitude,ATTACK_ATTITUDE_TYPE_MAX);
|
|
npc_info->m_byAttitude = ATTACK_ATTITUDE_NEARES_FIRST;
|
|
success = false;
|
|
}
|
|
|
|
// ???????
|
|
if (npc_info->m_byMoveAttitude >= eMOVE_ATTITUDE_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] Monster code[%d] field [m_byMoveAttitude][%d], must be <= [%d]!",
|
|
npc_code, npc_info->m_byMoveAttitude,eMOVE_ATTITUDE_MAX);
|
|
npc_info->m_byMoveAttitude = eMOVE_ATTITUDE_WANDER;
|
|
success = false;
|
|
}
|
|
|
|
//?????????????
|
|
//??????:m_dwmoveareaid??????regen???????. [6/15/2010 lst1024]
|
|
// if (npc_info->m_byMoveAttitude == eMOVE_ATTITUDE_PATROL ||
|
|
// npc_info->m_byMoveAttitude == eMOVE_ATTITUDE_SEARCH_AREA)
|
|
// {
|
|
// if (npc_info->m_dwMoveAreaID == 48)
|
|
// {
|
|
// SUNLOG(eDEV_LOG, "[NPCInfoParser] ???????[%d] ???[m_dwMoveAreaID]??0!", npc_code);
|
|
// success = false;
|
|
// }
|
|
// }
|
|
|
|
int exist_trans = -1;
|
|
int exist_selfdest = -1;
|
|
|
|
// ???????
|
|
for (int i = 0; i < BASE_NPCINFO::MAX_SPECIAL_NUM; ++i)
|
|
{
|
|
if (npc_info->m_SpecialCondition[i].wActionType >= eNPC_SPECIAL_ACTION_MAX)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_SpecialCondition].[wActionType][%d] invalid, must be <= [%d]!",
|
|
npc_code, npc_info->m_SpecialCondition[i].wActionType,eNPC_SPECIAL_ACTION_MAX);
|
|
npc_info->m_SpecialCondition[i].wActionType = 0;
|
|
success = false;
|
|
}
|
|
|
|
if (npc_info->m_SpecialCondition[i].byCondition &&
|
|
npc_info->m_SpecialCondition[i].byCondition != 5)
|
|
{
|
|
if (npc_info->m_SpecialCondition[i].wConditionParam == 0 ||
|
|
npc_info->m_SpecialCondition[i].wConditionParam > 100)
|
|
{
|
|
SUNLOG(eDEV_LOG,
|
|
"[NPCInfoParser] Monster code[%d] field [m_SpecialCondition].[wConditionParam][%d] invalid, 0 < range < 100!",
|
|
npc_code, npc_info->m_SpecialCondition[i].wConditionParam);
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
if (npc_info->m_SpecialCondition[i].wActionType == eNPC_SPECIAL_ACTION_TRANSFORMATION) {
|
|
exist_trans = i;
|
|
}
|
|
if (npc_info->m_SpecialCondition[i].wActionType == eNPC_SPECIAL_ACTION_SELP_DESTRUCTION) {
|
|
exist_selfdest = i;
|
|
}
|
|
}
|
|
|
|
// _NA_0_20100514_NPC_DEAD_STATE_TIME
|
|
// ????????????????????????.
|
|
if (exist_trans != -1 &&
|
|
exist_selfdest != -1)
|
|
{
|
|
SUNLOG(eDEV_LOG, "[NPCInfoParser] ???????[%d] ???[SpecialCondition]"
|
|
" TRANSFORMATION[%d] && DESTRUCTION[%d]"
|
|
" These actions cannot exist at the same time",
|
|
npc_code, exist_trans, exist_selfdest);
|
|
success = false;
|
|
}
|
|
return success;
|
|
}
|
|
|
|
|
|
static NpcElementWeakness util::CalculateNpcElementWeakness(const BASE_NPCINFO* npc_info)
|
|
{
|
|
// NOTE: ?????????????????????????????????
|
|
#define COMPOSED_RESIST_RELATION(__resist__, __weakness__) \
|
|
ulong((((__resist__) + 10000) << 16) | (__weakness__))
|
|
NpcElementWeakness weakness = { weakness.eElementWeakness_Default };
|
|
ulong resist_list[weakness.eElementWeakness_Counts] = { 0, };
|
|
//
|
|
resist_list[weakness.eElementWeakness_Fire] = \
|
|
COMPOSED_RESIST_RELATION(npc_info->element_fire_resist, weakness.eElementWeakness_Dark);
|
|
resist_list[weakness.eElementWeakness_Water] = \
|
|
COMPOSED_RESIST_RELATION(npc_info->element_water_resist, weakness.eElementWeakness_Fire);
|
|
resist_list[weakness.eElementWeakness_Wind] = \
|
|
COMPOSED_RESIST_RELATION(npc_info->element_wind_resist, weakness.eElementWeakness_Earth);
|
|
resist_list[weakness.eElementWeakness_Earth] = \
|
|
COMPOSED_RESIST_RELATION(npc_info->element_earth_resist, weakness.eElementWeakness_Water);
|
|
resist_list[weakness.eElementWeakness_Dark] = \
|
|
COMPOSED_RESIST_RELATION(npc_info->element_dark_resist, weakness.eElementWeakness_Wind);
|
|
//
|
|
std::sort(resist_list, &resist_list[weakness.eElementWeakness_Counts]);
|
|
//
|
|
if ((resist_list[0] & 0xFFFF0000) != (resist_list[1] & 0xFFFF0000)) {
|
|
weakness.value = uint8(resist_list[0] & 0x000000FF);
|
|
};
|
|
return weakness;
|
|
#undef COMPOSED_RESIST_RELATION
|
|
}
|
|
|