Files
Sun1602/ProgramCommon/SkillSystem/PerkInfoParser.cpp
T
2022-10-26 12:25:11 +08:00

396 lines
15 KiB
C++

#include "stdafx.h"
#include "./PerkInfoParser.h"
#include "Attribute.h"
#include "ItemAttrCalculator.h"
//==================================================================================================
const PerkInfo* PerkGroup::GetPerkInfoByExp(EXPTYPE exp) const
{
if (item_code_ != 0) { // cash perk (has only 1 level)
return perk_info_array_[0];
}
const PerkInfo* const * perk_info_it = perk_info_array_;
const PerkInfo* const * perk_info_end = perk_info_array_ + number_of_perk_infos_;
for ( ; perk_info_it != perk_info_end; ++perk_info_it)
{
const PerkInfo* perk_info = *perk_info_it;
assert(perk_info);
if (exp >= perk_info->exp_value_ &&
exp < perk_info->next_level_exp_)
{
return perk_info;
}
}
assert(!"unexpected exp value or corrupted perk group data");
return NULL;
}
//==================================================================================================
PerkInfoParser* PerkInfoParser::perk_info_parser_static_ = NULL;
PerkInfoParser* PerkInfoParser::CreateInstance()
{
// (NOTE) thread-unsafe singleton
perk_info_parser_static_ = new PerkInfoParser;
return perk_info_parser_static_;
}
void PerkInfoParser::DestroyInstance()
{
SAFE_DELETE(perk_info_parser_static_);
}
//==================================================================================================
PerkInfoParser::PerkInfoParser()
: perk_info_array_(NULL)
{
}
PerkInfoParser::~PerkInfoParser()
{
}
//==================================================================================================
// IParser interface
void PerkInfoParser::Release()
{
Unload();
}
// IParser interface
BOOL PerkInfoParser::LoadScript(eSCRIPT_CODE script_code, BOOL reloaded)
{
__UNUSED(reloaded);
switch (script_code)
{
case SCRIPT_PERKINFO:
return _Load();
}
return false;
}
//==================================================================================================
void PerkInfoParser::Init(char* pack_file_name)
{
SetPackFileName(pack_file_name);
}
void PerkInfoParser::Unload()
{
if (perk_info_array_)
{
::free(perk_info_array_);
perk_info_array_ = NULL;
perk_table_.clear();
}
group_table_.clear();
}
//==================================================================================================
bool PerkInfoParser::_BuildReference(int max_number_of_perk_infos)
{
if (max_number_of_perk_infos < 0 || max_number_of_perk_infos >= USHRT_MAX) {
return false;
}
//
if (perk_info_array_) {
::free(perk_info_array_);
}
;{ // perk info
const size_t kMaxArraySize = sizeof(*perk_info_array_) * max_number_of_perk_infos;
void* ptr = ::malloc(kMaxArraySize);
ZeroMemory(ptr, kMaxArraySize);
perk_info_array_ = reinterpret_cast<PerkInfo*>(ptr);
};
perk_table_.clear();
// perk group
group_table_.clear();
return true;
}
namespace nsSkill {
;
struct NameTable {
struct NameNode {
char name[MAX_FIELD_NAME_SIZE];
char type_name[MAX_FIELD_NAME_SIZE];
char value_name[MAX_FIELD_NAME_SIZE];
};
NameTable(const char* prefix, const int number_of_array)
: number_of_array_(number_of_array)
{
ZeroMemory(name_table, sizeof(name_table));
for (int i = 0; i < number_of_array; ++i)
{
_snprintf(name_table[i].name, _countof(name_table[i].name),
"%s%d", prefix, i + 1);
name_table[i].name[_countof(name_table[i].name) - 1] = '\0';
//
_snprintf(name_table[i].type_name, _countof(name_table[i].type_name),
"%sType%d", prefix, i + 1);
name_table[i].type_name[_countof(name_table[i].type_name) - 1] = '\0';
//
_snprintf(name_table[i].value_name, _countof(name_table[i].value_name),
"%sValue%d", prefix, i + 1);
name_table[i].value_name[_countof(name_table[i].value_name) - 1] = '\0';
}
}
//
const int number_of_array_;
NameNode name_table[\
max(PerkInfo::kNumberOfBenefitOptions, PerkInfo::kNumberOfPenaltyOptions)];
//
__DISABLE_COPY(NameTable);
};
}; //end of namespace
bool PerkInfoParser::_Load()
{
//------------------------------------------------------------------------------
// build index table
using nsSkill::NameTable;
const NameTable effect_name("Option", PerkInfo::kNumberOfBenefitOptions),
penalty_2x("DoublePenalty", PerkInfo::kNumberOfPenaltyOptions),
penalty_3x("TriplePenalty", PerkInfo::kNumberOfPenaltyOptions),
penalty_4x("QuadruplePenalty", PerkInfo::kNumberOfPenaltyOptions),
* name_table_ref[4] = { &effect_name, &penalty_2x, &penalty_3x, &penalty_4x };
//------------------------------------------------------------------------------
PerkInfo perk_info;
PerkInfo::OptionNode* value_table_bind[4] = {
perk_info.benefit_option_, perk_info.penalty_2x_option_,
perk_info.penalty_3x_option_, perk_info.penalty_4x_option_
};
SORTED_GROUP sorted_group;
//------------------------------------------------------------------------------
const int max_rows = this->GetRowSize();
const bool perk_records_index_out_of_range = _BuildReference(max_rows);
if (FlowControl::FCAssert(perk_records_index_out_of_range) == false) {
return false;
};
//
PerkInfo* it_for_array = perk_info_array_;
for (int row = 0; row < max_rows; ++row)
{
ZeroMemory(&perk_info, sizeof(perk_info));
const PerkID& perk_id = perk_info.perk_id_;
;{ // make uid
ulong group = GetDataDWORD("Group", row);
ulong level = GetDataDWORD("Level", row);
FASSERT(group < USHRT_MAX &&
(level != 0 && level <= PerkGroup::kMaxNumberOfPerkInfos));
perk_info.perk_id_.group_ = static_cast<SLOTCODE>(group);
perk_info.perk_id_.level_ = static_cast<uint8_t>(level);
};
;{
PerkTable::const_iterator found = perk_table_.find(perk_id.uid_);
if (found != perk_table_.end()) {
SUNLOG(eCRITICAL_LOG, _T("|%s|ÐÅÏ¢=duplicated perk group(%d) & level(%d)|"),
perk_info.perk_id_.group_, perk_info.perk_id_.level_);
__debugbreak();
return false;
}
};
perk_info.item_code_ = GetDataWORD("ItemCode", row);
perk_info.name_code_ = GetDataDWORD("NameCode", row);
perk_info.icon_code_ = GetDataDWORD("IconCode", row);
perk_info.option_description_code_ = GetDataDWORD("OptionDescriptionCode", row);
perk_info.penalty_description_code_ = GetDataDWORD("PenaltyDescriptionCode", row);
perk_info.exp_value_ = GetDataInt64("ExpValue", row);
// read benefit & penalty options
for (int ref_index = 0; ref_index < _countof(name_table_ref); ++ref_index)
{
const NameTable* name_table = name_table_ref[ref_index];
const int number_of_indexes = name_table->number_of_array_;
for (int index = 0; index < number_of_indexes; ++index)
{
PerkInfo::OptionNode* data = &value_table_bind[ref_index][index];
const NameTable::NameNode& name_node = name_table->name_table[index];
data->item_option_ = GetDataBYTE(name_node.name, row);
if (data->item_option_ == 0) {
continue;
}
data->value_type_ = GetDataBYTE(name_node.type_name, row);
data->value_ = GetDataInt(name_node.value_name, row);
// (f100812.2L) pre-calculated
#ifdef _NA000000_VALUE_TYPE_SIMPLIFICATION
const ulong kAcceptValueTypeBits = (1 << VALUE_TYPE_VALUE) |
(1 << VALUE_TYPE_PERCENT_PER_CUR) |
(1 << VALUE_TYPE_PERCENT_PER_MAX);
#else
const ulong kAcceptValueTypeBits = (1 << VALUE_TYPE_VALUE) |
(1 << VALUE_TYPE_PERCENT_PER_CUR) |
(1 << VALUE_TYPE_PERCENT_PER_MAX) |
(1 << VALUE_TYPE_RATIO_VALUE);
#endif
if (data->item_option_ >= _countof(g_ITEM_OPTION) ||
(kAcceptValueTypeBits & (1 << data->value_type_)) == 0)
{
SUNLOG(eCRITICAL_LOG,
_T("|[%s]|violate a rule of an option value, change to an empty section|")
_T("Group=%d|Level=%d|Name=%s|Option=%d|ValueType=%d|Value=%d|"),
__FUNCTION__, perk_id.group_, perk_id.level_,
name_node.name, data->item_option_, data->value_type_, data->value_);
ZeroMemory(data, sizeof(*data));
continue;
}
data->option_ = static_cast<uint16_t>(g_ITEM_OPTION[data->item_option_]);
uint8_t attr_kind = eATTR_KIND_ITEM;
ItemAttrCalculator::ModifyAttrKindAndCalculatedValue(\
data->value_type_, &attr_kind, &data->value_);
// change to skill attribute
attr_kind = attr_kind == eATTR_KIND_ITEM ? eATTR_KIND_SKILL
: attr_kind == eATTR_KIND_ITEM_RATIO ? eATTR_KIND_SKILL_RATIO
: attr_kind;
// skill value = 100.0, item value = 100
data->attr_kind_ = attr_kind;
//
}
}
//
;{
PerkInfo* perk_info_data = it_for_array++;
*perk_info_data = perk_info;
perk_table_.insert(std::make_pair(perk_id.uid_, perk_info_data));
sorted_group.insert(std::make_pair(perk_id.group_, 0)); // true, false...
if (perk_info_data->item_code_ != 0)
{
cash_perk_table_.insert(std::make_pair(perk_info_data->item_code_, perk_info_data));
}
};
} //end 'for'
_BuildGroupInfo(sorted_group);
sorted_group.clear();
//
return true;
}
bool PerkInfoParser::_BuildGroupInfo(SORTED_GROUP& sorted_group)
{
;{ // renumbering
int order = 0;
FOREACH_CONTAINER(SORTED_GROUP::value_type& node, sorted_group, SORTED_GROUP) {
node.second = order++;
}
};
//
FOREACH_CONTAINER(const PerkTable::value_type& perk_node, perk_table_, PerkTable)
{
const PerkInfo* perk_info = perk_node.second;
const PerkID& perk_id = perk_info->perk_id_;
GroupTable::iterator it = group_table_.find(perk_id.group_);
const bool exist = (it != group_table_.end());
PerkGroup* const perk_group = exist
? const_cast<PerkGroup*>(&it->second)
: &group_table_[perk_id.group_];
if (exist == false) {
ZeroMemory(perk_group, sizeof(*perk_group));
}
;{
perk_group->group_code_ = perk_id.group_;
assert(perk_group->number_of_perk_infos_ <= perk_group->kMaxNumberOfPerkInfos);
perk_group->perk_info_array_[perk_id.level_ - 1] = perk_info;
++perk_group->number_of_perk_infos_;
};
if (exist == false)
{
int order = sorted_group.find(perk_id.group_)->second;
perk_group->order_ = static_cast<uint8_t>(order);
perk_group->item_code_ = perk_info->item_code_;
if (perk_group->item_code_ && perk_id.level_ != 1)
{
SUNLOG(eCRITICAL_LOG,
_T("|[%s]|ÐÅÏ¢=the perk of (Group=%d, Level=%d) is violated a perk rule. ")
_T("a cash perk using an item should be have '1' level|"),
__FUNCTION__, perk_id.group_, perk_id.level_);
__debugbreak();
}
//group_table_.insert(std::make_pair(perk_id.group_, perk_group));
}
else
{
// WARNING, f100823.5L, each of perk item types should be have one group code.
bool fail_cash_type_rule = false;
const TCHAR* error_msg = NULL;
if (perk_group->item_code_)
{
fail_cash_type_rule = true;
error_msg = _T("each of a cash perk types, using an item, ")
_T("should be have one group code.");
}
else
{
if (perk_info->item_code_ != 0) {
error_msg = _T("you need check the field of 'ItemCode'");
fail_cash_type_rule = true;
}
};
if (fail_cash_type_rule)
{
SUNLOG(eCRITICAL_LOG,
_T("|[%s]|ÐÅÏ¢=%s|Group=%d, Level=%d, a ItemCode of group=%d, ")
_T("the field of 'ItemCode=%d' is against a cash perk rule|"),
__FUNCTION__, error_msg, perk_id.group_, perk_id.level_,
perk_group->item_code_, perk_info->item_code_);
__debugbreak();
return false;
};
}
};
//
FOREACH_CONTAINER(GroupTable::value_type& node, group_table_, GroupTable)
{ // numbering
PerkGroup* const group = &node.second;
const int upperbound = group->number_of_perk_infos_;
const uint8_t order = static_cast<uint8_t>(group->order_);
if (upperbound)
{
PerkInfo* perk_info_edit = \
const_cast<PerkInfo*>(group->perk_info_array_[upperbound - 1]);
perk_info_edit->order_ = order;
perk_info_edit->max_level_ = true;
perk_info_edit->next_level_exp_ = 0x7FFFFFFFFFFFFFFFLL;
}
PerkInfo* prev_perk_info = NULL;
// signed comparison, ranges=[-1, x]
for (int i = 0; i < (upperbound - 1); ++i)
{
PerkInfo* perk_info_edit = const_cast<PerkInfo*>(group->perk_info_array_[i]);
perk_info_edit->order_ = order;
perk_info_edit->next_level_exp_ = group->perk_info_array_[i + 1]->exp_value_;
//
perk_info_edit->prev_perk_ = prev_perk_info;
if (prev_perk_info) {
prev_perk_info->next_perk_ = perk_info_edit;
}
prev_perk_info = perk_info_edit;
}
if (upperbound)
{
PerkInfo* perk_info_edit = \
const_cast<PerkInfo*>(group->perk_info_array_[upperbound - 1]);
//
perk_info_edit->prev_perk_ = prev_perk_info;
if (prev_perk_info) {
prev_perk_info->next_perk_ = perk_info_edit;
}
prev_perk_info = perk_info_edit;
}
};
return true;
}
//==================================================================================================