#include "stdafx.h" #include "./NPCInfoParser.h" #include #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(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(ptr); this->SetFirst(); } else { // ?????????? UnLoadExtra(); extra_npc_table_ = new util::SolarHashTable; 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(::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(npc_table_.seq_total_it_) = static_cast(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(npc_table_.seq_total_indexes_); BASE_NPCINFO* npc_info_data_it = const_cast(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(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); 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( util::LL_Round(GetDataNumber("FireResist", row) * 100.0f - 100.0f)); npc_info->element_water_resist = static_cast( util::LL_Round(GetDataNumber("WaterResist", row) * 100.0f - 100.0f)); npc_info->element_wind_resist = static_cast( util::LL_Round(GetDataNumber("WindResist", row) * 100.0f - 100.0f)); npc_info->element_earth_resist = static_cast( util::LL_Round(GetDataNumber("EarthResist", row) * 100.0f - 100.0f)); npc_info->element_dark_resist = static_cast( 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( util::LL_Round(GetDataNumber("PhyAttRate", row) * 100.0f)); npc_info->m_wPhyAttAvoid = static_cast( 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 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(); //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::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 }