Cleaned up remaining TODO for Achievements (#4446)

* Converts labels to camelCase.
* Converts labels to plural where needed.
* Converts Target MobID to use the Sprite name.
* Converts Rewards ItemID to use the Aegis name.
* Dependents now checks for duplicate IDs.
* Dependents now properly supports import methods.
* Added YAMLUpgrade tool to convert previous YAML databases to new format.
* Removes the unique index value for Dependent Achievement ID.
* Adds support for the AG_CHATTING achievement types triggered by script (achievementupdate script command).
* AG_CHATTING map type achievements are still disabled as behavior is unknown.
* AG_HEAR and AG_SEE are now dropped and those achievements have been converted to AG_CHATTING.
* Converts the group constants to how Aegis names them.
Co-authored-by: Lemongrass3110 <lemongrass@kstp.at>
This commit is contained in:
Aleos 2021-02-17 18:01:11 -05:00 committed by GitHub
parent 0af4a9b40c
commit 5aa9f75343
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 6537 additions and 5880 deletions

2
.gitignore vendored
View File

@ -43,6 +43,7 @@ Thumbs.db
/mapcache
/nbproject
/yaml2sql
/yamlupgrade
# /3rdparty/libconfig/
/3rdparty/libconfig/Makefile
@ -122,6 +123,7 @@ Thumbs.db
/runserver.bat
/serv.bat
/yaml2sql.bat
/yamlupgrade.bat
# dlls
/libmysql.dll

View File

@ -16,70 +16,34 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Renewal Achievement Database
# Achievement Database
###########################################################################
#
# Achievement Settings
#
###########################################################################
# ID - Unique achievement ID.
###########################################################################
# Group - Achievement group type. Each achievement type calls a specific
# objective check.
# Valid groups:
# AG_ADD_FRIEND
# AG_ADVENTURE
# AG_BABY
# AG_BATTLE
# AG_CHATTING
# AG_CHATTING_COUNT
# AG_CHATTING_CREATE
# AG_CHATTING_DYING
# AG_EAT
# AG_GET_ITEM
# AG_GET_ZENY
# AG_GOAL_ACHIEVE
# AG_GOAL_LEVEL
# AG_GOAL_STATUS
# AG_HEAR
# AG_JOB_CHANGE
# AG_MARRY
# AG_PARTY
# AG_ENCHANT_FAIL
# AG_ENCHANT_SUCCESS
# AG_SEE
# AG_SPEND_ZENY
# AG_TAMING
###########################################################################
# Name - Achievement name. Used when sending rewards through RODEX.
###########################################################################
# Target - A list of monster ID and count values that the achievement
# requires. The target count can also be used for achievements that keep
# a counter while not being related to monster kills.
# Capped at MAX_ACHIEVEMENT_OBJECTIVES.
###########################################################################
# Condition - A conditional statement that must be met for the achievement
# to be considered complete.
###########################################################################
# Map - A map name that is used for the AG_CHATTING type which increments
# the counter based on the player's map.
###########################################################################
# Dependent: - A list of achievement IDs that need to be completed before
# this achievement is considered complete.
###########################################################################
# Reward - A list of rewards that are given on completion. All fields are
# optional.
# ItemID: Item ID
# Amount: Amount of Item ID (default 1)
# Script: Bonus Script
# TitleID: Title ID
###########################################################################
# Score - Achievement points that are given on completion.
# - Id Achievement ID.
# Group Achievement group type. (Defaut: None)
# Name Achievement name.
# Targets: List of targets the achievement requires. (Default: null)
# - Id Index value used for import methods.
# Mob Target mob. (Default: 0)
# Count Target count. (Default: 1)
# Condition Conditional statement that must be met for the achievement to be considered complete. (Default: null)
# Map Map name that is used for the AG_CHATTING type. (Default: -1)
# Dependents: List of achievements that need to be completed before this achievement is considered complete. (Default: null)
# - Id: <bool> Achievement ID pre-requisite.
# Rewards: List of rewards that are given on completion. (Default: null)
# Item Item name.
# Amount Amount of item. (Default: 1)
# Script Bonus Script. (Default: null)
# TitleId Title ID. (Default: 0)
# Score Achievement points that are given on completion. (Default: 0)
###########################################################################
Header:
Type: ACHIEVEMENT_DB
Version: 1
Version: 2
Footer:
Imports:

View File

@ -22,9 +22,8 @@
# Achievement Level Settings
#
###########################################################################
# Level - Achievement Level
###########################################################################
# Points - Required total scoring points to reach this level.
# - Level Achievement Level.
# Points Required total scoring points to reach this level.
###########################################################################
Header:

View File

@ -16,67 +16,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Custom Achievement Database
# Achievement Database
###########################################################################
#
# Achievement Settings
#
###########################################################################
# ID - Unique achievement ID.
###########################################################################
# Group - Achievement group type. Each achievement type calls a specific
# objective check.
# Valid groups:
# AG_ADD_FRIEND
# AG_ADVENTURE
# AG_BABY
# AG_BATTLE
# AG_CHATTING
# AG_CHATTING_COUNT
# AG_CHATTING_CREATE
# AG_CHATTING_DYING
# AG_EAT
# AG_GET_ITEM
# AG_GET_ZENY
# AG_GOAL_ACHIEVE
# AG_GOAL_LEVEL
# AG_GOAL_STATUS
# AG_HEAR
# AG_JOB_CHANGE
# AG_MARRY
# AG_PARTY
# AG_ENCHANT_FAIL
# AG_ENCHANT_SUCCESS
# AG_SEE
# AG_SPEND_ZENY
# AG_TAMING
###########################################################################
# Name - Achievement name. Used when sending rewards through RODEX.
###########################################################################
# Target - A list of monster ID and count values that the achievement
# requires. The target count can also be used for achievements that keep
# a counter while not being related to monster kills.
# Capped at MAX_ACHIEVEMENT_OBJECTIVES.
###########################################################################
# Condition - A conditional statement that must be met for the achievement
# to be considered complete.
###########################################################################
# Map - A map name that is used for the AG_CHATTING type which increments
# the counter based on the player's map.
###########################################################################
# Dependent: - A list of achievement IDs that need to be completed before
# this achievement is considered complete.
###########################################################################
# Reward - A list of rewards that are given on completion. All fields are
# optional.
# ItemID: Item ID
# Amount: Amount of Item ID (default 1)
# Script: Bonus Script
# TitleID: Title ID
###########################################################################
# Score - Achievement points that are given on completion.
# - Id Achievement ID.
# Group Achievement group type. (Defaut: None)
# Name Achievement name.
# Targets: List of targets the achievement requires. (Default: null)
# - Id Index value used for import methods.
# Mob Target mob. (Default: 0)
# Count Target count. (Default: 1)
# Condition Conditional statement that must be met for the achievement to be considered complete. (Default: null)
# Map Map name that is used for the AG_CHATTING type. (Default: -1)
# Dependents: List of achievements that need to be completed before this achievement is considered complete. (Default: null)
# - Id: <bool> Achievement ID pre-requisite.
# Rewards: List of rewards that are given on completion. (Default: null)
# Item Item name.
# Amount Amount of item. (Default: 1)
# Script Bonus Script. (Default: null)
# TitleId Title ID. (Default: 0)
# Score Achievement points that are given on completion. (Default: 0)
###########################################################################
Header:
Type: ACHIEVEMENT_DB
Version: 1
Version: 2

View File

@ -16,15 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Import Achievement Level Database
# Achievement Level Database
###########################################################################
#
# Achievement Level Settings
#
###########################################################################
# Level - Achievement Level
###########################################################################
# Points - Required total scoring points to reach this level.
# - Level Achievement Level.
# Points Required total scoring points to reach this level.
###########################################################################
Header:

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Pre-Renewal Achievement Level Database
# Achievement Level Database
###########################################################################
#
# Achievement Level Settings
#
###########################################################################
# Level - Achievement Level
###########################################################################
# Points - Required total scoring points to reach this level.
# - Level Achievement Level.
# Points Required total scoring points to reach this level.
###########################################################################
Header:

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###########################################################################
# Renewal Achievement Level Database
# Achievement Level Database
###########################################################################
#
# Achievement Level Settings
#
###########################################################################
# Level - Achievement Level
###########################################################################
# Points - Required total scoring points to reach this level.
# - Level Achievement Level.
# Points Required total scoring points to reach this level.
###########################################################################
Header:

View File

@ -18,20 +18,18 @@ We want to add our own custom achievement that can be given to a player via an N
#### /db/import/achievement_db.yml
Achievements:
- ID: 280000
Group: "AG_GOAL_ACHIEVE"
Name: "Emperio"
Reward:
TitleID: 1035
Score: 50
- ID: 280001
Group: "AG_GOAL_ACHIEVE"
Name: "Staff"
Reward:
TitleID: 1036
Score: 50
- Id: 280000
Group: None
Name: Emperio
Reward:
TitleId: 1035
Score: 50
- Id: 280001
Group: None
Name: Staff
Reward:
TitleId: 1036
Score: 50
### Instances

View File

@ -3,59 +3,60 @@
//===== By: ==================================================
//= rAthena Dev Team
//===== Last Updated: ========================================
//= 20190226
//= 20200220
//===== Description: =========================================
//= Explanation of the achievements_db.yml file and structure.
//============================================================
---------------------------------------
ID: Unique achievement ID.
Id: Unique achievement ID.
---------------------------------------
Group: Achievement group type. Each achievement type calls a specific objective check.
Valid groups:
AG_ADD_FRIEND - Triggered when a player adds a friend.
AG_ADVENTURE - Does not trigger automatically. These are triggered by the achievementcomplete script command.
AG_BABY - Triggered when a player becomes a baby job.
AG_BATTLE - Triggered when a player kills a monster.
AG_CHATTING - Unknown.
AG_CHATTING_COUNT - Triggered when a player has a chatroom open and others join.
AG_CHATTING_CREATE - Triggered when a player creates a chatroom.
AG_CHATTING_DYING - Triggered when a player creates a chatroom and dies with it open.
AG_EAT - Unknown.
AG_GET_ITEM - Triggered when a player gets an item that has a specific sell value.
AG_GET_ZENY - Triggered when a player gets a specific amount of zeny at once.
AG_GOAL_ACHIEVE - Triggered when a player's achievement rank levels up.
AG_GOAL_LEVEL - Triggered when a player's base level or job level changes.
AG_GOAL_STATUS - Triggered when a player's base stats changes.
AG_HEAR - Unknown.
AG_JOB_CHANGE - Triggered when a player's job changes.
AG_MARRY - Triggered when two players get married.
AG_PARTY - Triggered when a player creates a party.
AG_ENCHANT_FAIL - Triggered when a player fails to refine an equipment.
AG_ENCHANT_SUCCESS - Triggered when a player successfully refines an equipment.
AG_SEE - Unknown.
AG_SPEND_ZENY - Triggered when a player spends any amount of zeny on vendors.
AG_TAMING - Triggered when a player tames a monster.
None - Can be used for custom achievements that are given through a script with no trigger events.
Add_Friend - Triggered when a player adds a friend.
Adventure - Does not trigger automatically. These are triggered by the achievementcomplete script command.
Baby - Triggered when a player becomes a baby job.
Battle - Triggered when a player kills a monster.
Chatting - Aegis uses this when talking to a NPC. These are triggered by the achievementupdate script command.
Chatting_Count - Triggered when a player has a chatroom open and others join.
Chatting_Create - Triggered when a player creates a chatroom.
Chatting_Dying - Triggered when a player creates a chatroom and dies with it open.
Eat - Unknown.
Get_Item - Triggered when a player gets an item that has a specific sell value.
Get_Zeny - Triggered when a player gets a specific amount of zeny at once.
Goal_Achieve - Triggered when a player's achievement rank levels up.
Goal_Level - Triggered when a player's base level or job level changes.
Goal_Status - Triggered when a player's base stats changes.
Job_Change - Triggered when a player's job changes.
Marry - Triggered when two players get married.
Party - Triggered when a player creates a party.
Enchant_Fail - Triggered when a player fails to refine an equipment.
Enchant_Success - Triggered when a player successfully refines an equipment.
Spend_Zeny - Triggered when a player spends any amount of zeny on vendors.
Taming - Triggered when a player tames a monster.
---------------------------------------
Name: Achievement name. Not read into source but used for quick look ups.
Name: Achievement name. Used when sending rewards through RODEX.
---------------------------------------
Target: A list of monster ID and count values that the achievement requires.
Targets: A list of monster names and count values that the achievement requires.
The target count can also be used for achievements that keep a counter while not being related to monster kills.
Capped at MAX_ACHIEVEMENT_OBJECTIVES.
Example:
// Player must kill 5 Scorpions and 10 Poring.
Target:
- MobID: 1001
Targets:
- Id: 0
Mob: SCORPION
Count: 5
- MobID: 1002
- Id: 1
Mob: PORING
Count: 10
Example 2:
@ -63,7 +64,7 @@ Example 2:
// and not checked for a total (UI_Type = 1).
// IE: In the achievement_list.lub file, UI_Type 0 is displayed as non-incremental while 1 shows a progress bar of completion for the achievement.
Condition: " ARG0 >= 100 "
Target:
Targets:
- Id: 0 // Array index value
Count: 100
@ -84,19 +85,30 @@ Example 2:
---------------------------------------
Map: A map name that is used for the AG_CHATTING type which increments the counter based on the player's map.
Map: A map name that is used for the Chatting group which increments the counter based on the player's map.
NOTICE: This option is currently disabled until the official behavior is confirmed.
---------------------------------------
Dependent: A list of achievement IDs that need to be completed before this achievement is considered complete.
Dependents: A list of achievement IDs that need to be completed before this achievement is considered complete.
Example:
// Player must complete achievements 10001 and 10002 first.
Dependents:
10001: true
10002: true
// Used with the import, dependent achievements can be disabled. The player now only requires completion of achievement 10001.
Dependents:
10002: false
---------------------------------------
Reward: A list of rewards that are given on completion. All fields are optional.
ItemID: Item ID
Amount: Amount of Item ID (default 1)
Rewards: A list of rewards that are given on completion. All fields are optional.
Item: Item Name
Amount: Amount of Item (Default: 1)
Script: Bonus Script
TitleID: Title ID
TitleId: Title ID
---------------------------------------

View File

@ -0,0 +1,25 @@
###########################################################################
# Achievement Database
###########################################################################
#
# Achievement Settings
#
###########################################################################
# - Id Achievement ID.
# Group Achievement group type. (Defaut: None)
# Name Achievement name.
# Targets: List of targets the achievement requires. (Default: null)
# - Id Index value used for import methods.
# Mob Target mob. (Default: 0)
# Count Target count. (Default: 1)
# Condition Conditional statement that must be met for the achievement to be considered complete. (Default: null)
# Map Map name that is used for the AG_CHATTING type. (Default: -1)
# Dependents: List of achievements that need to be completed before this achievement is considered complete. (Default: null)
# - Id: <bool> Achievement ID pre-requisite.
# Rewards: List of rewards that are given on completion. (Default: null)
# Item Item name.
# Amount Amount of item. (Default: 1)
# Script Bonus Script. (Default: null)
# TitleId Title ID. (Default: 0)
# Score Achievement points that are given on completion. (Default: 0)
###########################################################################

View File

@ -83,6 +83,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yaml2sql", "src\tool\yaml2s
{352B45B3-FE88-4431-9D89-48CF811446DB} = {352B45B3-FE88-4431-9D89-48CF811446DB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yamlupgrade", "src\tool\yamlupgrade.vcxproj", "{9115C6D1-520A-4540-B4FE-95F3C923FE2C}"
ProjectSection(ProjectDependencies) = postProject
{61D6A599-6BED-4154-A9FC-40553BD972E0} = {61D6A599-6BED-4154-A9FC-40553BD972E0}
{352B45B3-FE88-4431-9D89-48CF811446DB} = {352B45B3-FE88-4431-9D89-48CF811446DB}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -171,6 +177,14 @@ Global
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|Win32.Build.0 = Release|Win32
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|x64.ActiveCfg = Release|x64
{CDBBB260-B245-44EC-80FB-3F9421885E40}.Release|x64.Build.0 = Release|x64
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|Win32.ActiveCfg = Debug|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|Win32.Build.0 = Debug|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|x64.ActiveCfg = Debug|x64
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Debug|x64.Build.0 = Debug|x64
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Release|Win32.ActiveCfg = Release|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Release|Win32.Build.0 = Release|Win32
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Release|x64.ActiveCfg = Release|x64
{9115C6D1-520A-4540-B4FE-95F3C923FE2C}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -187,6 +201,7 @@ Global
{61D6A599-6BED-4154-A9FC-40553BD972E0} = {6ABA1767-6242-4CA0-BA22-A30972DC8918}
{5A9059F2-4933-49A2-BEE6-CC67F66FA070} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
{CDBBB260-B245-44EC-80FB-3F9421885E40} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
{9115C6D1-520A-4540-B4FE-95F3C923FE2C} = {9F328FE9-129D-4C0C-820B-BE4AA5996652}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {026DA20F-820C-40AA-983E-0E231EA90AD5}

View File

@ -50,8 +50,7 @@ const std::string AchievementDatabase::getDefaultLocation(){
uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
uint32 achievement_id;
// TODO: doesnt match camel case
if( !this->asUInt32( node, "ID", achievement_id ) ){
if( !this->asUInt32( node, "Id", achievement_id ) ){
return 0;
}
@ -59,11 +58,7 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
bool exists = achievement != nullptr;
if( !exists ){
if( !this->nodeExists( node, "Group" ) ){
return 0;
}
if( !this->nodeExists( node, "Name" ) ){
if( !this->nodesExist( node, { "Name" } ) ){
return 0;
}
@ -78,14 +73,18 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
return 0;
}
std::string group_name_constant = "AG_" + group_name;
int64 constant;
if( !script_get_constant( group_name.c_str(), &constant ) ){
this->invalidWarning( node, "achievement_read_db_sub: Invalid group %s for achievement %d, skipping.\n", group_name.c_str(), achievement_id );
if( !script_get_constant( group_name_constant.c_str(), &constant ) ){
this->invalidWarning( node, "Invalid Group %s for achievement %d, skipping.\n", group_name.c_str(), achievement_id );
return 0;
}
achievement->group = (e_achievement_group)constant;
} else {
if (!exists)
achievement->group = AG_NONE;
}
if( this->nodeExists( node, "Name" ) ){
@ -98,15 +97,10 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
achievement->name = name;
}
if( this->nodeExists( node, "Target" ) ){
const YAML::Node& targets = node["Target"];
if( this->nodeExists( node, "Targets" ) ){
const YAML::Node& targets = node["Targets"];
for( const YAML::Node& targetNode : targets ){
if( achievement->targets.size() >= MAX_ACHIEVEMENT_OBJECTIVES ){
this->invalidWarning( targetNode, "Node \"Target\" list exceeds the maximum of %d, skipping.\n", MAX_ACHIEVEMENT_OBJECTIVES );
return 0;
}
uint16 targetId;
if( !this->asUInt16( targetNode, "Id", targetId ) ){
@ -114,16 +108,16 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
}
if( targetId >= MAX_ACHIEVEMENT_OBJECTIVES ){
this->invalidWarning( targetNode["Id"], "Node \"Id\" is out of valid range [0,%d], skipping.\n", MAX_ACHIEVEMENT_OBJECTIVES );
return 0;
this->invalidWarning( targetNode["Id"], "Target Id is out of valid range [0,%d], skipping.\n", MAX_ACHIEVEMENT_OBJECTIVES );
continue;
}
std::shared_ptr<achievement_target> target = rathena::util::map_find( achievement->targets, targetId );
bool targetExists = target != nullptr;
if( !targetExists ){
if( !this->nodeExists( targetNode, "Count" ) && !this->nodeExists( targetNode, "MobID" ) ){
this->invalidWarning( targetNode, "Node \"Target\" has no data specified, skipping.\n" );
if( !this->nodeExists( targetNode, "Count" ) && !this->nodeExists( targetNode, "Mob" ) ){
this->invalidWarning( targetNode, "Target has no data specified, skipping.\n" );
return 0;
}
@ -137,30 +131,43 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
return 0;
}
if( count == 0 ){
if( targetExists ){
achievement->targets.erase( targetId );
continue;
}else{
this->invalidWarning( targetNode["Count"], "Target count has to be > 0, skipping.\n" );
return 0;
}
}
target->count = count;
}else{
if( !targetExists ){
target->count = 0;
target->count = 1;
}
}
if( this->nodeExists( targetNode, "MobID" ) ){
if( this->nodeExists( targetNode, "Mob" ) ){
if( achievement->group != AG_BATTLE && achievement->group != AG_TAMING ){
this->invalidWarning( targets, "Node \"MobID\" is only supported for targets in group AG_BATTLE or AG_TAMING, skipping.\n" );
this->invalidWarning( targets, "Target Mob is only supported for targets in group AG_BATTLE or AG_TAMING, skipping.\n" );
continue;
}
std::string mob_name;
if( !this->asString( targetNode, "Mob", mob_name ) ){
return 0;
}
uint32 mob_id;
std::shared_ptr<s_mob_db> mob = mobdb_search_aegisname( mob_name.c_str() );
// TODO: not camel case
if( !this->asUInt32( targetNode, "MobID", mob_id ) ){
if (mob == nullptr) {
this->invalidWarning(targetNode["Mob"], "Target Mob %s does not exist, skipping.\n", mob_name.c_str());
return 0;
}
if( mob_db.find( mob_id ) == nullptr ){
this->invalidWarning( targetNode["MobID"], "Unknown monster ID %d, skipping.\n", mob_id );
return 0;
}
uint32 mob_id = mob->vd.class_;
if( !this->mobexists( mob_id ) ){
this->achievement_mobs.push_back( mob_id );
@ -195,12 +202,13 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
achievement->condition = parse_script( condition.c_str(), this->getCurrentFile().c_str(), node["Condition"].Mark().line + 1, SCRIPT_IGNORE_EXTERNAL_BRACKETS );
}else{
achievement->condition = nullptr;
if (!exists)
achievement->condition = nullptr;
}
if( this->nodeExists( node, "Map" ) ){
if( achievement->group != AG_CHAT ){
this->invalidWarning( node, "Node \"Map\" can only be used with the group AG_CHATTING, skipping.\n" );
if( achievement->group != AG_CHATTING ){
this->invalidWarning( node, "Map can only be used with the group AG_CHATTING, skipping.\n" );
return 0;
}
@ -213,7 +221,7 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
achievement->mapindex = map_mapname2mapid( mapname.c_str() );
if( achievement->mapindex == -1 ){
this->invalidWarning( node["Map"], "Unknown map name '%s'.\n", mapname.c_str() );
this->invalidWarning( node["Map"], "Map %s does not exist, skipping.\n", mapname.c_str() );
return 0;
}
}else{
@ -222,43 +230,51 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
}
}
if( this->nodeExists( node, "Dependent" ) ){
for( const YAML::Node& subNode : node["Dependent"] ){
uint32 dependent_achievement_id;
if( this->nodeExists( node, "Dependents" ) ){
const YAML::Node &dependentNode = node["Dependents"];
if( !this->asUInt32( subNode, "Id", dependent_achievement_id ) ){
for( const auto it : dependentNode ){
uint32 dependent_achievement_id = it.first.as<uint32>();
bool active;
if (!this->asBool(dependentNode, std::to_string(dependent_achievement_id), active))
return 0;
}
// TODO: import logic for clearing => continue
// TODO: change to set to prevent multiple entries with the same id?
achievement->dependent_ids.push_back( dependent_achievement_id );
if (active) {
if (std::find(achievement->dependent_ids.begin(), achievement->dependent_ids.end(), dependent_achievement_id) != achievement->dependent_ids.end()) {
this->invalidWarning(dependentNode, "Dependent achievement %d is already part of the list, skipping.\n", dependent_achievement_id);
continue;
}
if (achievement->dependent_ids.size() >= MAX_ACHIEVEMENT_DEPENDENTS) {
this->invalidWarning(dependentNode, "Maximum amount (%d) of dependent achievements reached, skipping.\n", MAX_ACHIEVEMENT_DEPENDENTS);
break;
}
achievement->dependent_ids.push_back(dependent_achievement_id);
} else
util::vector_erase_if_exists(achievement->dependent_ids, dependent_achievement_id);
}
}
// TODO: not plural
if( this->nodeExists( node, "Reward" ) ){
const YAML::Node& rewardNode = node["Reward"];
if( this->nodeExists( node, "Rewards" ) ){
const YAML::Node& rewardNode = node["Rewards"];
// TODO: not camel case
if( this->nodeExists( rewardNode, "ItemID" ) ){
t_itemid itemId;
if( this->nodeExists( rewardNode, "Item" ) ){
std::string item_name;
if( !this->asUInt32( rewardNode, "ItemID", itemId ) ){
if( !this->asString( rewardNode, "Item", item_name ) ){
return 0;
}
if( !itemdb_exists( itemId ) ){
this->invalidWarning( rewardNode["ItemID"], "Unknown item with ID %u.\n", itemId );
struct item_data *item = itemdb_search_aegisname(item_name.c_str());
if (item == nullptr) {
this->invalidWarning(rewardNode["Item"], "Reward Item %s does not exist, skipping.\n", item_name.c_str());
return 0;
}
achievement->rewards.nameid = itemId;
if( achievement->rewards.amount == 0 ){
// Default the amount to 1
achievement->rewards.amount = 1;
}
achievement->rewards.nameid = item->nameid;
}
if( this->nodeExists( rewardNode, "Amount" ) ){
@ -269,6 +285,9 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
}
achievement->rewards.amount = amount;
} else {
if (!exists)
achievement->rewards.amount = 1;
}
if( this->nodeExists( rewardNode, "Script" ) ){
@ -285,18 +304,26 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
achievement->rewards.script = parse_script( script.c_str(), this->getCurrentFile().c_str(), achievement_id, SCRIPT_IGNORE_EXTERNAL_BRACKETS );
}else{
achievement->rewards.script = nullptr;
if (!exists)
achievement->rewards.script = nullptr;
}
// TODO: not camel case
if( this->nodeExists( rewardNode, "TitleID" ) ){
if( this->nodeExists( rewardNode, "TitleId" ) ){
uint32 title;
if( !this->asUInt32( rewardNode, "TitleID", title ) ){
if( !this->asUInt32( rewardNode, "TitleId", title ) ){
return 0;
}
if (title < TITLE_BASE || title > TITLE_MAX) {
this->invalidWarning(rewardNode["TitleId"], "Reward Title ID %u does not exist (%hu~%hu), skipping.\n", title, TITLE_BASE, TITLE_MAX);
return 0;
}
achievement->rewards.title_id = title;
} else {
if (!exists)
achievement->rewards.title_id = 0;
}
}
@ -308,6 +335,9 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
}
achievement->score = score;
} else {
if (!exists)
achievement->score = 0;
}
if( !exists ){
@ -317,6 +347,25 @@ uint64 AchievementDatabase::parseBodyNode(const YAML::Node &node){
return 1;
}
void AchievementDatabase::loadingFinished(){
for (const auto &achit : achievement_db) {
const std::shared_ptr<s_achievement_db> ach = achit.second;
for (auto dep = ach->dependent_ids.begin(); dep != ach->dependent_ids.end(); dep++) {
if (!achievement_db.exists(*dep)) {
ShowWarning("achievement_read_db: An invalid Dependent ID %d was given for Achievement %d. Removing from list.\n", *dep, ach->achievement_id);
dep = ach->dependent_ids.erase(dep);
if (dep == ach->dependent_ids.end()) {
break;
}
}
}
ach->dependent_ids.shrink_to_fit();
}
}
AchievementDatabase achievement_db;
/**
@ -507,8 +556,8 @@ bool achievement_check_dependent(struct map_session_data *sd, int achievement_id
// Check if the achievement has a dependent
// If so, then do a check on all dependents to see if they're complete
for (int i = 0; i < adb->dependent_ids.size(); i++) {
if (!achievement_done(sd, adb->dependent_ids[i]))
for (const auto &depit : adb->dependent_ids) {
if (!achievement_done(sd, depit))
return false; // One of the dependent is not complete!
}
@ -850,6 +899,19 @@ bool achievement_check_condition( struct script_code* condition, struct map_sess
return value != 0;
}
/**
* Check to see if an achievement's target count is complete
* @param ad: Achievement data
* @param current_count: Current target data
* @return True if all target values meet the requirements or false otherwise
*/
static bool achievement_target_complete(std::shared_ptr<s_achievement_db> ad, std::array<int, MAX_ACHIEVEMENT_OBJECTIVES> current_count) {
return std::find_if(ad->targets.begin(), ad->targets.end(),
[current_count](const std::pair<uint16, std::shared_ptr<achievement_target>> &target) -> bool {
return current_count[target.first] < target.second->count;
}) == ad->targets.end();
}
/**
* Update achievement objectives.
* @param sd: Player to update
@ -892,9 +954,9 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
switch (group) {
case AG_ADD_FRIEND:
case AG_BABY:
case AG_CHAT_COUNT:
case AG_CHAT_CREATE:
case AG_CHAT_DYING:
case AG_CHATTING_COUNT:
case AG_CHATTING_CREATE:
case AG_CHATTING_DYING:
case AG_GET_ITEM:
case AG_GET_ZENY:
case AG_GOAL_LEVEL:
@ -902,8 +964,8 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
case AG_JOB_CHANGE:
case AG_MARRY:
case AG_PARTY:
case AG_REFINE_FAIL:
case AG_REFINE_SUCCESS:
case AG_ENCHANT_FAIL:
case AG_ENCHANT_SUCCESS:
if (!ad->condition)
return false;
@ -914,15 +976,9 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
complete = true;
break;
case AG_SPEND_ZENY:
//case AG_CHAT: // No information on trigger events
if (ad->targets.empty() || !ad->condition)
return false;
//if (group == AG_CHAT) {
// if (ad->mapindex > -1 && sd->bl.m != ad->mapindex)
// return false;
//}
for (const auto &it : ad->targets) {
if (current_count[it.first] < it.second->count)
current_count[it.first] += update_count[it.first];
@ -933,11 +989,7 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
changed = true;
if (std::find_if(ad->targets.begin(), ad->targets.end(),
[current_count](const std::pair<uint16, std::shared_ptr<achievement_target>> &target) -> bool {
return current_count[target.first] < target.second->count;
}
) == ad->targets.end())
if (achievement_target_complete(ad, current_count))
complete = true;
break;
case AG_BATTLE:
@ -955,13 +1007,38 @@ static bool achievement_update_objectives(struct map_session_data *sd, std::shar
if (!changed)
return false;
if (std::find_if(ad->targets.begin(), ad->targets.end(),
[current_count](const std::pair<uint16, std::shared_ptr<achievement_target>> &target) -> bool {
return current_count[target.first] < target.second->count;
}
) == ad->targets.end())
if (achievement_target_complete(ad, current_count))
complete = true;
break;
case AG_GOAL_ACHIEVE:
if (!achievement_check_condition(ad->condition, sd)) // Parameters weren't met
return false;
changed = true;
complete = true;
break;
/*
case AG_CHATTING:
if (ad->targets.empty())
return false;
if (ad->mapindex > -1 && sd->bl.m != ad->mapindex)
return false;
for (const auto &it : ad->targets) {
if (current_count[it.first] < it.second->count) {
current_count[it.first]++;
changed = true;
}
}
if (!changed)
return false;
if (achievement_target_complete(ad, current_count))
complete = true;
break;
*/
}
if( isNew ){
@ -1022,15 +1099,8 @@ void achievement_update_objective(struct map_session_data *sd, enum e_achievemen
}
va_end(ap);
switch(group) {
case AG_CHAT: //! TODO: Not sure how this works officially
// These have no objective use.
break;
default:
for (auto &ach : achievement_db)
achievement_update_objectives(sd, ach.second, group, count);
break;
}
for (auto &ach : achievement_db)
achievement_update_objectives(sd, ach.second, group, count);
// Remove variables that might have been set
for (int i = 0; i < arg_count; i++){
@ -1069,34 +1139,11 @@ int achievement_update_objective_sub(block_list *bl, va_list ap)
return 1;
}
/**
* Loads achievements from the achievement db.
*/
void achievement_read_db(void)
{
achievement_db.load();
for (auto &achit : achievement_db) {
const auto ach = achit.second;
for (int i = 0; i < ach->dependent_ids.size(); i++) {
if (!achievement_db.exists(ach->dependent_ids[i])) {
ShowWarning("achievement_read_db: An invalid Dependent ID %d was given for Achievement %d. Removing from list.\n", ach->dependent_ids[i], ach->achievement_id);
ach->dependent_ids.erase(ach->dependent_ids.begin() + i);
}
}
}
achievement_level_db.load();
}
/**
* Reloads the achievement database
*/
void achievement_db_reload(void)
{
if (!battle_config.feature_achievement)
return;
do_final_achievement();
do_init_achievement();
}
@ -1108,7 +1155,8 @@ void do_init_achievement(void)
{
if (!battle_config.feature_achievement)
return;
achievement_read_db();
achievement_db.load();
achievement_level_db.load();
}
/**
@ -1129,7 +1177,7 @@ s_achievement_db::s_achievement_db()
, targets()
, dependent_ids()
, condition(nullptr)
, mapindex(0)
, mapindex(-1)
, rewards()
, score(0)
, has_dependent(0)

View File

@ -24,23 +24,21 @@ enum e_achievement_group {
AG_ADVENTURE,
AG_BABY,
AG_BATTLE,
AG_CHAT,
AG_CHAT_COUNT,
AG_CHAT_CREATE,
AG_CHAT_DYING,
AG_CHATTING,
AG_CHATTING_COUNT,
AG_CHATTING_CREATE,
AG_CHATTING_DYING,
AG_EAT,
AG_GET_ITEM,
AG_GET_ZENY,
AG_GOAL_ACHIEVE,
AG_GOAL_LEVEL,
AG_GOAL_STATUS,
AG_HEAR,
AG_JOB_CHANGE,
AG_MARRY,
AG_PARTY,
AG_REFINE_FAIL,
AG_REFINE_SUCCESS,
AG_SEE,
AG_ENCHANT_FAIL,
AG_ENCHANT_SUCCESS,
AG_SPEND_ZENY,
AG_TAMING,
AG_MAX
@ -65,6 +63,12 @@ enum e_achievement_info {
ACHIEVEINFO_MAX,
};
enum e_title_table : uint16 {
TITLE_NONE = 0,
TITLE_BASE = 1000,
TITLE_MAX = 1046,
};
struct achievement_target {
int mob;
int count;
@ -95,17 +99,17 @@ struct s_achievement_db {
class AchievementDatabase : public TypesafeYamlDatabase<uint32, s_achievement_db>{
private:
// Avoids checking achievements on every mob killed
std::vector<uint32> achievement_mobs;
std::vector<uint32> achievement_mobs; // Avoids checking achievements on every mob killed
public:
AchievementDatabase() : TypesafeYamlDatabase( "ACHIEVEMENT_DB", 1 ){
AchievementDatabase() : TypesafeYamlDatabase( "ACHIEVEMENT_DB", 2 ){
}
void clear();
const std::string getDefaultLocation();
uint64 parseBodyNode( const YAML::Node& node );
void loadingFinished();
// Additional
bool mobexists(uint32 mob_id);

View File

@ -2353,7 +2353,7 @@ ACMD_FUNC(refine)
clif_additem(sd, i, 1, 0);
pc_equipitem(sd, i, current_position);
clif_misceffect(&sd->bl, 3);
achievement_update_objective(sd, AG_REFINE_SUCCESS, 2, sd->inventory_data[i]->wlv, sd->inventory.u.items_inventory[i].refine);
achievement_update_objective(sd, AG_ENCHANT_SUCCESS, 2, sd->inventory_data[i]->wlv, sd->inventory.u.items_inventory[i].refine);
count++;
}
}

View File

@ -109,9 +109,9 @@ int chat_createpcchat(struct map_session_data* sd, const char* title, const char
clif_dispchat(cd,0);
if (status_isdead(&sd->bl))
achievement_update_objective(sd, AG_CHAT_DYING, 1, 1);
achievement_update_objective(sd, AG_CHATTING_DYING, 1, 1);
else
achievement_update_objective(sd, AG_CHAT_CREATE, 1, 1);
achievement_update_objective(sd, AG_CHATTING_CREATE, 1, 1);
} else
clif_createchat(sd,1);
@ -173,7 +173,7 @@ int chat_joinchat(struct map_session_data* sd, int chatid, const char* pass)
clif_dispchat(cd, 0); //Reported number of changes to the people around
if (cd->owner->type == BL_PC)
achievement_update_objective(map_id2sd(cd->owner->id), AG_CHAT_COUNT, 1, cd->users);
achievement_update_objective(map_id2sd(cd->owner->id), AG_CHATTING_COUNT, 1, cd->users);
chat_triggerevent(cd); //Event

View File

@ -10337,6 +10337,8 @@ static bool clif_process_message(struct map_session_data* sd, bool whisperFormat
if (battle_config.hom_idle_no_share && sd->hd && battle_config.idletime_hom_option&IDLE_CHAT)
sd->idletime_hom = last_tick;
//achievement_update_objective(sd, AG_CHATTING, 1, 1); // !TODO: Confirm how this achievement is triggered
return true;
}

View File

@ -9257,7 +9257,7 @@ BUILDIN_FUNC(successrefitem) {
clif_additem(sd,i,1,0);
pc_equipitem(sd,i,ep);
clif_misceffect(&sd->bl,3);
achievement_update_objective(sd, AG_REFINE_SUCCESS, 2, sd->inventory_data[i]->wlv, sd->inventory.u.items_inventory[i].refine);
achievement_update_objective(sd, AG_ENCHANT_SUCCESS, 2, sd->inventory_data[i]->wlv, sd->inventory.u.items_inventory[i].refine);
if (sd->inventory.u.items_inventory[i].refine == battle_config.blacksmith_fame_refine_threshold &&
sd->inventory.u.items_inventory[i].card[0] == CARD0_FORGE &&
sd->status.char_id == (int)MakeDWord(sd->inventory.u.items_inventory[i].card[2],sd->inventory.u.items_inventory[i].card[3]))
@ -9307,7 +9307,7 @@ BUILDIN_FUNC(failedrefitem) {
clif_refine(sd->fd,1,i,sd->inventory.u.items_inventory[i].refine); //notify client of failure
pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
clif_misceffect(&sd->bl,2); // display failure effect
achievement_update_objective(sd, AG_REFINE_FAIL, 1, 1);
achievement_update_objective(sd, AG_ENCHANT_FAIL, 1, 1);
script_pushint(st, 1);
return SCRIPT_CMD_SUCCESS;
}
@ -9356,7 +9356,7 @@ BUILDIN_FUNC(downrefitem) {
clif_additem(sd,i,1,0);
pc_equipitem(sd,i,ep);
clif_misceffect(&sd->bl,2);
achievement_update_objective(sd, AG_REFINE_FAIL, 1, sd->inventory.u.items_inventory[i].refine);
achievement_update_objective(sd, AG_ENCHANT_FAIL, 1, sd->inventory.u.items_inventory[i].refine);
script_pushint(st, sd->inventory.u.items_inventory[i].refine);
return SCRIPT_CMD_SUCCESS;
}

View File

@ -5211,29 +5211,27 @@
export_constant(USW_ALL);
/* achievement groups */
export_constant2("AG_ADD_FRIEND", AG_ADD_FRIEND);
export_constant2("AG_ADVENTURE", AG_ADVENTURE);
export_constant2("AG_BABY", AG_BABY);
export_constant2("AG_BATTLE", AG_BATTLE);
export_constant2("AG_CHATTING", AG_CHAT);
export_constant2("AG_CHATTING_COUNT", AG_CHAT_COUNT);
export_constant2("AG_CHATTING_CREATE", AG_CHAT_CREATE);
export_constant2("AG_CHATTING_DYING", AG_CHAT_DYING);
export_constant2("AG_EAT", AG_EAT);
export_constant2("AG_GET_ITEM", AG_GET_ITEM);
export_constant2("AG_GET_ZENY", AG_GET_ZENY);
export_constant2("AG_GOAL_ACHIEVE", AG_GOAL_ACHIEVE);
export_constant2("AG_GOAL_LEVEL", AG_GOAL_LEVEL);
export_constant2("AG_GOAL_STATUS", AG_GOAL_STATUS);
export_constant2("AG_HEAR", AG_HEAR);
export_constant2("AG_JOB_CHANGE", AG_JOB_CHANGE);
export_constant2("AG_MARRY", AG_MARRY);
export_constant2("AG_PARTY", AG_PARTY);
export_constant2("AG_ENCHANT_FAIL", AG_REFINE_FAIL);
export_constant2("AG_ENCHANT_SUCCESS", AG_REFINE_SUCCESS);
export_constant2("AG_SEE", AG_SEE);
export_constant2("AG_SPEND_ZENY", AG_SPEND_ZENY);
export_constant2("AG_TAMING", AG_TAMING);
export_constant(AG_ADD_FRIEND);
export_constant(AG_ADVENTURE);
export_constant(AG_BABY);
export_constant(AG_BATTLE);
export_constant(AG_CHATTING);
export_constant(AG_CHATTING_COUNT);
export_constant(AG_CHATTING_CREATE);
export_constant(AG_CHATTING_DYING);
export_constant(AG_EAT);
export_constant(AG_GET_ITEM);
export_constant(AG_GET_ZENY);
export_constant(AG_GOAL_ACHIEVE);
export_constant(AG_GOAL_LEVEL);
export_constant(AG_GOAL_STATUS);
export_constant(AG_JOB_CHANGE);
export_constant(AG_MARRY);
export_constant(AG_PARTY);
export_constant(AG_ENCHANT_FAIL);
export_constant(AG_ENCHANT_SUCCESS);
export_constant(AG_SPEND_ZENY);
export_constant(AG_TAMING);
/* achievement info */
export_constant(ACHIEVEINFO_COUNT1);

View File

@ -17687,7 +17687,7 @@ void skill_weaponrefine(struct map_session_data *sd, int idx)
clif_upgrademessage(sd, 0, item->nameid);
clif_inventorylist(sd);
clif_refine(sd->fd,0,idx,item->refine);
achievement_update_objective(sd, AG_REFINE_SUCCESS, 2, ditem->wlv, item->refine);
achievement_update_objective(sd, AG_ENCHANT_SUCCESS, 2, ditem->wlv, item->refine);
if (ep)
pc_equipitem(sd,idx,ep);
clif_misceffect(&sd->bl,3);
@ -17713,7 +17713,7 @@ void skill_weaponrefine(struct map_session_data *sd, int idx)
pc_unequipitem(sd,idx,3);
clif_upgrademessage(sd, 1, item->nameid);
clif_refine(sd->fd,1,idx,item->refine);
achievement_update_objective(sd, AG_REFINE_FAIL, 1, 1);
achievement_update_objective(sd, AG_ENCHANT_FAIL, 1, 1);
pc_delitem(sd,idx,1,0,2, LOG_TYPE_OTHER);
clif_misceffect(&sd->bl,2);
clif_emotion(&sd->bl, ET_HUK);

View File

@ -18,12 +18,14 @@ CSV2YAML_OBJ = obj_all/csv2yaml.o
YAML2SQL_OBJ = obj_all/yaml2sql.o
YAMLUPGRADE_OBJ = obj_all/yamlupgrade.o
@SET_MAKE@
#####################################################################
.PHONY : all mapcache csv2yaml yaml2sql clean help
.PHONY : all mapcache csv2yaml yaml2sql yamlupgrade clean help
all: mapcache csv2yaml yaml2sql
all: mapcache csv2yaml yaml2sql yamlupgrade
mapcache: obj_all $(MAPCACHE_OBJ) $(COMMON_DIR_OBJ)
@echo " LD $@"
@ -37,18 +39,23 @@ yaml2sql: obj_all $(YAML2SQL_OBJ) $(COMMON_DIR_OBJ) $(YAML_CPP_AR)
@echo " LD $@"
@@CXX@ @LDFLAGS@ -o ../../yaml2sql@EXEEXT@ $(YAML2SQL_OBJ) $(COMMON_DIR_OBJ) $(YAML_CPP_AR) @LIBS@
yamlupgrade: obj_all $(YAMLUPGRADE_OBJ) $(COMMON_DIR_OBJ) $(YAML_CPP_AR)
@echo " LD $@"
@@CXX@ @LDFLAGS@ -o ../../yamlupgrade@EXEEXT@ $(YAMLUPGRADE_OBJ) $(COMMON_DIR_OBJ) ../common/obj/database.o $(YAML_CPP_AR) @LIBS@
clean:
@echo " CLEAN tool"
@rm -rf obj_all/*.o ../../mapcache@EXEEXT@ ../../csv2yaml@EXEEXT@ ../../yaml2sql@EXEEXT@
@rm -rf obj_all/*.o ../../mapcache@EXEEXT@ ../../csv2yaml@EXEEXT@ ../../yaml2sql@EXEEXT@ ../../yamlupgrade@EXEEXT@
help:
@echo "possible targets are 'mapcache' 'csv2yaml' 'yaml2sql' 'all' 'clean' 'help'"
@echo "'mapcache' - mapcache generator"
@echo "'csv2yaml' - converts TXT databases to YAML"
@echo "'yaml2sql' - converts YAML databases to SQL"
@echo "'all' - builds all above targets"
@echo "'clean' - cleans builds and objects"
@echo "'help' - outputs this message"
@echo "possible targets are 'mapcache' 'csv2yaml' 'yaml2sql' 'yamlupgrade' 'all' 'clean' 'help'"
@echo "'mapcache' - mapcache generator"
@echo "'csv2yaml' - converts TXT databases to YAML"
@echo "'yaml2sql' - converts YAML databases to SQL"
@echo "'yamlupgrade' - upgrades YAML databases to latest version"
@echo "'all' - builds all above targets"
@echo "'clean' - cleans builds and objects"
@echo "'help' - outputs this message"
#####################################################################

File diff suppressed because it is too large Load Diff

384
src/tool/csv2yaml.hpp Normal file
View File

@ -0,0 +1,384 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#ifndef CSV2YAML_HPP
#define CSV2YAML_HPP
#include "yaml.hpp"
// Required constant and structure definitions
#define MAX_GUILD_SKILL_REQUIRE 5
#define MAX_SKILL_ITEM_REQUIRE 10
#define MAX_SKILL_STATUS_REQUIRE 3
#define MAX_SKILL_EQUIP_REQUIRE 10
#define MAX_QUEST_DROPS 3
#define MAX_MAP_PER_INSTANCE 255
// Database to memory maps
struct s_skill_unit_csv : s_skill_db {
std::string target_str;
int unit_flag_csv;
};
std::unordered_map<uint16, s_skill_require> skill_require;
std::unordered_map<uint16, s_skill_db> skill_cast;
std::unordered_map<uint16, s_skill_db> skill_castnodex;
std::unordered_map<uint16, s_skill_unit_csv> skill_unit;
std::unordered_map<uint16, s_skill_copyable> skill_copyable;
std::unordered_map<uint16, s_skill_db> skill_nearnpc;
static unsigned int level_penalty[3][CLASS_MAX][MAX_LEVEL * 2 + 1];
struct s_item_flag_csv2yaml {
bool buyingstore, dead_branch, group, guid, broadcast, bindOnEquip, delay_consume;
e_item_drop_effect dropEffect;
};
struct s_item_delay_csv2yaml {
uint32 delay;
std::string sc;
};
struct s_item_stack_csv2yaml {
uint16 amount;
bool inventory, cart, storage, guild_storage;
};
struct s_item_nouse_csv2yaml {
uint16 override;
bool sitting;
};
struct s_item_trade_csv2yaml {
uint16 override;
bool drop, trade, trade_partner, sell, cart, storage, guild_storage, mail, auction;
};
std::unordered_map<t_itemid, t_itemid> item_avail;
std::unordered_map<t_itemid, bool> item_buyingstore;
std::unordered_map<t_itemid, s_item_flag_csv2yaml> item_flag;
std::unordered_map<t_itemid, s_item_delay_csv2yaml> item_delay;
std::unordered_map<t_itemid, s_item_stack_csv2yaml> item_stack;
std::unordered_map<t_itemid, s_item_nouse_csv2yaml> item_nouse;
std::unordered_map<t_itemid, s_item_trade_csv2yaml> item_trade;
struct s_random_opt_group_csv : s_random_opt_group {
std::vector<uint16> rate;
};
std::unordered_map<uint16, std::string> rand_opt_db;
std::unordered_map<uint16, s_random_opt_group_csv> rand_opt_group;
static std::map<std::string, int> um_mapid2jobname {
{ "Novice", JOB_NOVICE }, // Novice and Super Novice share the same value
{ "SuperNovice", JOB_NOVICE },
{ "Swordman", JOB_SWORDMAN },
{ "Mage", JOB_MAGE },
{ "Archer", JOB_ARCHER },
{ "Acolyte", JOB_ACOLYTE },
{ "Merchant", JOB_MERCHANT },
{ "Thief", JOB_THIEF },
{ "Knight", JOB_KNIGHT },
{ "Priest", JOB_PRIEST },
{ "Wizard", JOB_WIZARD },
{ "Blacksmith", JOB_BLACKSMITH },
{ "Hunter", JOB_HUNTER },
{ "Assassin", JOB_ASSASSIN },
{ "Crusader", JOB_CRUSADER },
{ "Monk", JOB_MONK },
{ "Sage", JOB_SAGE },
{ "Rogue", JOB_ROGUE },
{ "Alchemist", JOB_ALCHEMIST },
{ "BardDancer", JOB_BARD }, // Bard and Dancer share the same value
{ "BardDancer", JOB_DANCER },
{ "Gunslinger", JOB_GUNSLINGER },
{ "Ninja", JOB_NINJA },
{ "Taekwon", 21 },
{ "StarGladiator", 22 },
{ "SoulLinker", 23 },
// { "Gangsi", 26 },
// { "DeathKnight", 27 },
// { "DarkCollector", 28 },
#ifdef RENEWAL
{ "KagerouOboro", 29 }, // Kagerou and Oboro share the same value
{ "Rebellion", 30 },
{ "Summoner", 31 },
#endif
};
static std::unordered_map<std::string, equip_pos> um_equipnames {
{ "Head_Low", EQP_HEAD_LOW },
{ "Head_Mid", EQP_HEAD_MID },
{ "Head_Top", EQP_HEAD_TOP },
{ "Right_Hand", EQP_HAND_R },
{ "Left_Hand", EQP_HAND_L },
{ "Armor", EQP_ARMOR },
{ "Shoes", EQP_SHOES },
{ "Garment", EQP_GARMENT },
{ "Right_Accessory", EQP_ACC_R },
{ "Left_Accessory", EQP_ACC_L },
{ "Costume_Head_Top", EQP_COSTUME_HEAD_TOP },
{ "Costume_Head_Mid", EQP_COSTUME_HEAD_MID },
{ "Costume_Head_Low", EQP_COSTUME_HEAD_LOW },
{ "Costume_Garment", EQP_COSTUME_GARMENT },
{ "Ammo", EQP_AMMO },
{ "Shadow_Armor", EQP_SHADOW_ARMOR },
{ "Shadow_Weapon", EQP_SHADOW_WEAPON },
{ "Shadow_Shield", EQP_SHADOW_SHIELD },
{ "Shadow_Shoes", EQP_SHADOW_SHOES },
{ "Shadow_Right_Accessory", EQP_SHADOW_ACC_R },
{ "Shadow_Left_Accessory", EQP_SHADOW_ACC_L },
};
// Initialize Random Option constants
void init_random_option_constants() {
#define export_constant2(a, b) script_set_constant_(a, b, a, false, false)
export_constant2("RDMOPT_VAR_MAXHPAMOUNT", 1);
export_constant2("RDMOPT_VAR_MAXSPAMOUNT", 2);
export_constant2("RDMOPT_VAR_STRAMOUNT", 3);
export_constant2("RDMOPT_VAR_AGIAMOUNT", 4);
export_constant2("RDMOPT_VAR_VITAMOUNT", 5);
export_constant2("RDMOPT_VAR_INTAMOUNT", 6);
export_constant2("RDMOPT_VAR_DEXAMOUNT", 7);
export_constant2("RDMOPT_VAR_LUKAMOUNT", 8);
export_constant2("RDMOPT_VAR_MAXHPPERCENT", 9);
export_constant2("RDMOPT_VAR_MAXSPPERCENT", 10);
export_constant2("RDMOPT_VAR_HPACCELERATION", 11);
export_constant2("RDMOPT_VAR_SPACCELERATION", 12);
export_constant2("RDMOPT_VAR_ATKPERCENT", 13);
export_constant2("RDMOPT_VAR_MAGICATKPERCENT", 14);
export_constant2("RDMOPT_VAR_PLUSASPD", 15);
export_constant2("RDMOPT_VAR_PLUSASPDPERCENT", 16);
export_constant2("RDMOPT_VAR_ATTPOWER", 17);
export_constant2("RDMOPT_VAR_HITSUCCESSVALUE", 18);
export_constant2("RDMOPT_VAR_ATTMPOWER", 19);
export_constant2("RDMOPT_VAR_ITEMDEFPOWER", 20);
export_constant2("RDMOPT_VAR_MDEFPOWER", 21);
export_constant2("RDMOPT_VAR_AVOIDSUCCESSVALUE", 22);
export_constant2("RDMOPT_VAR_PLUSAVOIDSUCCESSVALUE", 23);
export_constant2("RDMOPT_VAR_CRITICALSUCCESSVALUE", 24);
export_constant2("RDMOPT_ATTR_TOLERACE_NOTHING", 25);
export_constant2("RDMOPT_ATTR_TOLERACE_WATER", 26);
export_constant2("RDMOPT_ATTR_TOLERACE_GROUND", 27);
export_constant2("RDMOPT_ATTR_TOLERACE_FIRE", 28);
export_constant2("RDMOPT_ATTR_TOLERACE_WIND", 29);
export_constant2("RDMOPT_ATTR_TOLERACE_POISON", 30);
export_constant2("RDMOPT_ATTR_TOLERACE_SAINT", 31);
export_constant2("RDMOPT_ATTR_TOLERACE_DARKNESS", 32);
export_constant2("RDMOPT_ATTR_TOLERACE_TELEKINESIS", 33);
export_constant2("RDMOPT_ATTR_TOLERACE_UNDEAD", 34);
export_constant2("RDMOPT_ATTR_TOLERACE_ALLBUTNOTHING", 35);
export_constant2("RDMOPT_DAMAGE_PROPERTY_NOTHING_USER", 36);
export_constant2("RDMOPT_DAMAGE_PROPERTY_NOTHING_TARGET", 37);
export_constant2("RDMOPT_DAMAGE_PROPERTY_WATER_USER", 38);
export_constant2("RDMOPT_DAMAGE_PROPERTY_WATER_TARGET", 39);
export_constant2("RDMOPT_DAMAGE_PROPERTY_GROUND_USER", 40);
export_constant2("RDMOPT_DAMAGE_PROPERTY_GROUND_TARGET", 41);
export_constant2("RDMOPT_DAMAGE_PROPERTY_FIRE_USER", 42);
export_constant2("RDMOPT_DAMAGE_PROPERTY_FIRE_TARGET", 43);
export_constant2("RDMOPT_DAMAGE_PROPERTY_WIND_USER", 44);
export_constant2("RDMOPT_DAMAGE_PROPERTY_WIND_TARGET", 45);
export_constant2("RDMOPT_DAMAGE_PROPERTY_POISON_USER", 46);
export_constant2("RDMOPT_DAMAGE_PROPERTY_POISON_TARGET", 47);
export_constant2("RDMOPT_DAMAGE_PROPERTY_SAINT_USER", 48);
export_constant2("RDMOPT_DAMAGE_PROPERTY_SAINT_TARGET", 49);
export_constant2("RDMOPT_DAMAGE_PROPERTY_DARKNESS_USER", 50);
export_constant2("RDMOPT_DAMAGE_PROPERTY_DARKNESS_TARGET", 51);
export_constant2("RDMOPT_DAMAGE_PROPERTY_TELEKINESIS_USER", 52);
export_constant2("RDMOPT_DAMAGE_PROPERTY_TELEKINESIS_TARGET", 53);
export_constant2("RDMOPT_DAMAGE_PROPERTY_UNDEAD_USER", 54);
export_constant2("RDMOPT_DAMAGE_PROPERTY_UNDEAD_TARGET", 55);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_NOTHING_USER", 56);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_NOTHING_TARGET", 57);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_WATER_USER", 58);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_WATER_TARGET", 59);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_GROUND_USER", 60);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_GROUND_TARGET", 61);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_FIRE_USER", 62);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_FIRE_TARGET", 63);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_WIND_USER", 64);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_WIND_TARGET", 65);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_POISON_USER", 66);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_POISON_TARGET", 67);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_SAINT_USER", 68);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_SAINT_TARGET", 69);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_DARKNESS_USER", 70);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_DARKNESS_TARGET", 71);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_TELEKINESIS_USER", 72);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_TELEKINESIS_TARGET", 73);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_UNDEAD_USER", 74);
export_constant2("RDMOPT_MDAMAGE_PROPERTY_UNDEAD_TARGET", 75);
export_constant2("RDMOPT_BODY_ATTR_NOTHING", 76);
export_constant2("RDMOPT_BODY_ATTR_WATER", 77);
export_constant2("RDMOPT_BODY_ATTR_GROUND", 78);
export_constant2("RDMOPT_BODY_ATTR_FIRE", 79);
export_constant2("RDMOPT_BODY_ATTR_WIND", 80);
export_constant2("RDMOPT_BODY_ATTR_POISON", 81);
export_constant2("RDMOPT_BODY_ATTR_SAINT", 82);
export_constant2("RDMOPT_BODY_ATTR_DARKNESS", 83);
export_constant2("RDMOPT_BODY_ATTR_TELEKINESIS", 84);
export_constant2("RDMOPT_BODY_ATTR_UNDEAD", 85);
export_constant2("RDMOPT_RACE_TOLERACE_NOTHING", 87);
export_constant2("RDMOPT_RACE_TOLERACE_UNDEAD", 88);
export_constant2("RDMOPT_RACE_TOLERACE_ANIMAL", 89);
export_constant2("RDMOPT_RACE_TOLERACE_PLANT", 90);
export_constant2("RDMOPT_RACE_TOLERACE_INSECT", 91);
export_constant2("RDMOPT_RACE_TOLERACE_FISHS", 92);
export_constant2("RDMOPT_RACE_TOLERACE_DEVIL", 93);
export_constant2("RDMOPT_RACE_TOLERACE_HUMAN", 94);
export_constant2("RDMOPT_RACE_TOLERACE_ANGEL", 95);
export_constant2("RDMOPT_RACE_TOLERACE_DRAGON", 96);
export_constant2("RDMOPT_RACE_DAMAGE_NOTHING", 97);
export_constant2("RDMOPT_RACE_DAMAGE_UNDEAD", 98);
export_constant2("RDMOPT_RACE_DAMAGE_ANIMAL", 99);
export_constant2("RDMOPT_RACE_DAMAGE_PLANT", 100);
export_constant2("RDMOPT_RACE_DAMAGE_INSECT", 101);
export_constant2("RDMOPT_RACE_DAMAGE_FISHS", 102);
export_constant2("RDMOPT_RACE_DAMAGE_DEVIL", 103);
export_constant2("RDMOPT_RACE_DAMAGE_HUMAN", 104);
export_constant2("RDMOPT_RACE_DAMAGE_ANGEL", 105);
export_constant2("RDMOPT_RACE_DAMAGE_DRAGON", 106);
export_constant2("RDMOPT_RACE_MDAMAGE_NOTHING", 107);
export_constant2("RDMOPT_RACE_MDAMAGE_UNDEAD", 108);
export_constant2("RDMOPT_RACE_MDAMAGE_ANIMAL", 109);
export_constant2("RDMOPT_RACE_MDAMAGE_PLANT", 110);
export_constant2("RDMOPT_RACE_MDAMAGE_INSECT", 111);
export_constant2("RDMOPT_RACE_MDAMAGE_FISHS", 112);
export_constant2("RDMOPT_RACE_MDAMAGE_DEVIL", 113);
export_constant2("RDMOPT_RACE_MDAMAGE_HUMAN", 114);
export_constant2("RDMOPT_RACE_MDAMAGE_ANGEL", 115);
export_constant2("RDMOPT_RACE_MDAMAGE_DRAGON", 116);
export_constant2("RDMOPT_RACE_CRI_PERCENT_NOTHING", 117);
export_constant2("RDMOPT_RACE_CRI_PERCENT_UNDEAD", 118);
export_constant2("RDMOPT_RACE_CRI_PERCENT_ANIMAL", 119);
export_constant2("RDMOPT_RACE_CRI_PERCENT_PLANT", 120);
export_constant2("RDMOPT_RACE_CRI_PERCENT_INSECT", 121);
export_constant2("RDMOPT_RACE_CRI_PERCENT_FISHS", 122);
export_constant2("RDMOPT_RACE_CRI_PERCENT_DEVIL", 123);
export_constant2("RDMOPT_RACE_CRI_PERCENT_HUMAN", 124);
export_constant2("RDMOPT_RACE_CRI_PERCENT_ANGEL", 125);
export_constant2("RDMOPT_RACE_CRI_PERCENT_DRAGON", 126);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_NOTHING", 127);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_UNDEAD", 128);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_ANIMAL", 129);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_PLANT", 130);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_INSECT", 131);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_FISHS", 132);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_DEVIL", 133);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_HUMAN", 134);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_ANGEL", 135);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_DRAGON", 136);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_NOTHING", 137);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_UNDEAD", 138);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_ANIMAL", 139);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_PLANT", 140);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_INSECT", 141);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_FISHS", 142);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_DEVIL", 143);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_HUMAN", 144);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_ANGEL", 145);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_DRAGON", 146);
export_constant2("RDMOPT_CLASS_DAMAGE_NORMAL_TARGET", 147);
export_constant2("RDMOPT_CLASS_DAMAGE_BOSS_TARGET", 148);
export_constant2("RDMOPT_CLASS_DAMAGE_NORMAL_USER", 149);
export_constant2("RDMOPT_CLASS_DAMAGE_BOSS_USER", 150);
export_constant2("RDMOPT_CLASS_MDAMAGE_NORMAL", 151);
export_constant2("RDMOPT_CLASS_MDAMAGE_BOSS", 152);
export_constant2("RDMOPT_CLASS_IGNORE_DEF_PERCENT_NORMAL", 153);
export_constant2("RDMOPT_CLASS_IGNORE_DEF_PERCENT_BOSS", 154);
export_constant2("RDMOPT_CLASS_IGNORE_MDEF_PERCENT_NORMAL", 155);
export_constant2("RDMOPT_CLASS_IGNORE_MDEF_PERCENT_BOSS", 156);
export_constant2("RDMOPT_DAMAGE_SIZE_SMALL_TARGET", 157);
export_constant2("RDMOPT_DAMAGE_SIZE_MIDIUM_TARGET", 158);
export_constant2("RDMOPT_DAMAGE_SIZE_LARGE_TARGET", 159);
export_constant2("RDMOPT_DAMAGE_SIZE_SMALL_USER", 160);
export_constant2("RDMOPT_DAMAGE_SIZE_MIDIUM_USER", 161);
export_constant2("RDMOPT_DAMAGE_SIZE_LARGE_USER", 162);
export_constant2("RDMOPT_DAMAGE_SIZE_PERFECT", 163);
export_constant2("RDMOPT_DAMAGE_CRI_TARGET", 164);
export_constant2("RDMOPT_DAMAGE_CRI_USER", 165);
export_constant2("RDMOPT_RANGE_ATTACK_DAMAGE_TARGET", 166);
export_constant2("RDMOPT_RANGE_ATTACK_DAMAGE_USER", 167);
export_constant2("RDMOPT_HEAL_VALUE", 168);
export_constant2("RDMOPT_HEAL_MODIFY_PERCENT", 169);
export_constant2("RDMOPT_DEC_SPELL_CAST_TIME", 170);
export_constant2("RDMOPT_DEC_SPELL_DELAY_TIME", 171);
export_constant2("RDMOPT_DEC_SP_CONSUMPTION", 172);
export_constant2("RDMOPT_WEAPON_ATTR_NOTHING", 175);
export_constant2("RDMOPT_WEAPON_ATTR_WATER", 176);
export_constant2("RDMOPT_WEAPON_ATTR_GROUND", 177);
export_constant2("RDMOPT_WEAPON_ATTR_FIRE", 178);
export_constant2("RDMOPT_WEAPON_ATTR_WIND", 179);
export_constant2("RDMOPT_WEAPON_ATTR_POISON", 180);
export_constant2("RDMOPT_WEAPON_ATTR_SAINT", 181);
export_constant2("RDMOPT_WEAPON_ATTR_DARKNESS", 182);
export_constant2("RDMOPT_WEAPON_ATTR_TELEKINESIS", 183);
export_constant2("RDMOPT_WEAPON_ATTR_UNDEAD", 184);
export_constant2("RDMOPT_WEAPON_INDESTRUCTIBLE", 185);
export_constant2("RDMOPT_BODY_INDESTRUCTIBLE", 186);
export_constant2("RDMOPT_MDAMAGE_SIZE_SMALL_TARGET", 187);
export_constant2("RDMOPT_MDAMAGE_SIZE_MIDIUM_TARGET", 188);
export_constant2("RDMOPT_MDAMAGE_SIZE_LARGE_TARGET", 189);
export_constant2("RDMOPT_MDAMAGE_SIZE_SMALL_USER", 190);
export_constant2("RDMOPT_MDAMAGE_SIZE_MIDIUM_USER", 191);
export_constant2("RDMOPT_MDAMAGE_SIZE_LARGE_USER", 192);
export_constant2("RDMOPT_ATTR_TOLERACE_ALL", 193);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_NOTHING", 194);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_UNDEAD", 195);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_ANIMAL", 196);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_PLANT", 197);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_INSECT", 198);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_FISHS", 199);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_DEVIL", 200);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_HUMAN", 201);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_ANGEL", 202);
export_constant2("RDMOPT_RACE_WEAPON_TOLERACE_DRAGON", 203);
export_constant2("RDMOPT_RACE_TOLERACE_PLAYER_HUMAN", 206);
export_constant2("RDMOPT_RACE_TOLERACE_PLAYER_DORAM", 207);
export_constant2("RDMOPT_RACE_DAMAGE_PLAYER_HUMAN", 208);
export_constant2("RDMOPT_RACE_DAMAGE_PLAYER_DORAM", 209);
export_constant2("RDMOPT_RACE_MDAMAGE_PLAYER_HUMAN", 210);
export_constant2("RDMOPT_RACE_MDAMAGE_PLAYER_DORAM", 211);
export_constant2("RDMOPT_RACE_CRI_PERCENT_PLAYER_HUMAN", 212);
export_constant2("RDMOPT_RACE_CRI_PERCENT_PLAYER_DORAM", 213);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_PLAYER_HUMAN", 214);
export_constant2("RDMOPT_RACE_IGNORE_DEF_PERCENT_PLAYER_DORAM", 215);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_PLAYER_HUMAN", 216);
export_constant2("RDMOPT_RACE_IGNORE_MDEF_PERCENT_PLAYER_DORAM", 217);
export_constant2("RDMOPT_MELEE_ATTACK_DAMAGE_TARGET", 219);
export_constant2("RDMOPT_MELEE_ATTACK_DAMAGE_USER", 220);
#undef export_constant2
}
static bool guild_read_guildskill_tree_db( char* split[], int columns, int current );
static bool pet_read_db( const char* file );
static bool skill_parse_row_magicmushroomdb(char *split[], int column, int current);
static bool skill_parse_row_abradb(char* split[], int columns, int current);
static bool skill_parse_row_spellbookdb(char* split[], int columns, int current);
static bool mob_readdb_mobavail(char *str[], int columns, int current);
static bool skill_parse_row_requiredb(char* split[], int columns, int current);
static bool skill_parse_row_castdb(char* split[], int columns, int current);
static bool skill_parse_row_castnodexdb(char* split[], int columns, int current);
static bool skill_parse_row_unitdb(char* split[], int columns, int current);
static bool skill_parse_row_copyabledb(char* split[], int columns, int current);
static bool skill_parse_row_nonearnpcrangedb(char* split[], int columns, int current);
static bool skill_parse_row_skilldb(char* split[], int columns, int current);
static bool quest_read_db(char *split[], int columns, int current);
static bool instance_readdb_sub(char* str[], int columns, int current);
static bool itemdb_read_itemavail(char *str[], int columns, int current);
static bool itemdb_read_buyingstore(char* fields[], int columns, int current);
static bool itemdb_read_flag(char* fields[], int columns, int current);
static bool itemdb_read_itemdelay(char* str[], int columns, int current);
static bool itemdb_read_stack(char* fields[], int columns, int current);
static bool itemdb_read_nouse(char* fields[], int columns, int current);
static bool itemdb_read_itemtrade(char* fields[], int columns, int current);
static bool itemdb_read_db(const char *file);
static bool itemdb_read_randomopt(const char* file);
static bool itemdb_read_randomopt_group(char *str[], int columns, int current);
static bool itemdb_randomopt_group_yaml(void);
static bool pc_readdb_levelpenalty(char* fields[], int columns, int current);
static bool pc_levelpenalty_yaml();
#endif /* CSV2YAML_HPP */

View File

@ -158,6 +158,10 @@
<AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common-minicore.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="csv2yaml.hpp" />
<ClInclude Include="yaml.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\common\database.cpp" />
<ClCompile Include="csv2yaml.cpp" />

View File

@ -10,6 +10,14 @@
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="csv2yaml.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="yaml.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="csv2yaml.cpp">
<Filter>Source Files</Filter>

657
src/tool/yaml.hpp Normal file
View File

@ -0,0 +1,657 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#ifndef YAML_HPP
#define YAML_HPP
#include <fstream>
#include <functional>
#include <iostream>
#include <locale>
#include <map>
#include <unordered_map>
#include <vector>
#ifdef WIN32
#include <conio.h>
#else
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#endif
#include <yaml-cpp/yaml.h>
#include "../common/cbasetypes.hpp"
#include "../common/core.hpp"
#include "../common/malloc.hpp"
#include "../common/mmo.hpp"
#include "../common/nullpo.hpp"
#include "../common/showmsg.hpp"
#include "../common/strlib.hpp"
#include "../common/utilities.hpp"
#include "../common/utils.hpp"
#ifdef WIN32
#include "../common/winapi.hpp"
#endif
// Only for constants - do not use functions of it or linking will fail
#include "../map/achievement.hpp"
#include "../map/battle.hpp"
#include "../map/battleground.hpp"
#include "../map/channel.hpp"
#include "../map/chat.hpp"
#include "../map/date.hpp"
#include "../map/instance.hpp"
#include "../map/mercenary.hpp"
#include "../map/mob.hpp"
#include "../map/npc.hpp"
#include "../map/pc.hpp"
#include "../map/pet.hpp"
#include "../map/quest.hpp"
#include "../map/script.hpp"
#include "../map/skill.hpp"
#include "../map/storage.hpp"
using namespace rathena;
#ifndef WIN32
int getch(void) {
struct termios oldattr, newattr;
int ch;
tcgetattr(STDIN_FILENO, &oldattr);
newattr = oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
return ch;
}
#endif
YAML::Node inNode;
YAML::Emitter body;
// Constants for conversion
std::unordered_map<t_itemid, std::string> aegis_itemnames;
std::unordered_map<uint32, t_itemid> aegis_itemviewid;
std::unordered_map<uint16, std::string> aegis_mobnames;
std::unordered_map<uint16, std::string> aegis_skillnames;
std::unordered_map<const char *, int64> constants;
// Implement the function instead of including the original version by linking
void script_set_constant_(const char *name, int64 value, const char *constant_name, bool isparameter, bool deprecated) {
constants[name] = value;
}
const char *constant_lookup(int32 value, const char *prefix) {
nullpo_retr(nullptr, prefix);
for (auto const &pair : constants) {
// Same prefix group and same value
if (strncasecmp(pair.first, prefix, strlen(prefix)) == 0 && pair.second == value) {
return pair.first;
}
}
return nullptr;
}
int64 constant_lookup_int(const char *constant) {
nullpo_retr(-100, constant);
for (auto const &pair : constants) {
if (strlen(pair.first) == strlen(constant) && strncasecmp(pair.first, constant, strlen(constant)) == 0) {
return pair.second;
}
}
return -100;
}
/**
* Determines if a file exists.
* @param path: File to check for
* @return True if file exists or false otherwise
*/
bool fileExists(const std::string &path) {
std::ifstream in;
in.open(path);
if (in.is_open()) {
in.close();
return true;
} else {
return false;
}
}
/**
* Prompt for confirmation.
* @param fmt: Message to print
* @param va_arg: Any arguments needed for message
* @return True on yes or false otherwise
*/
bool askConfirmation(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
_vShowMessage(MSG_NONE, fmt, ap);
va_end(ap);
char c = getch();
if (c == 'Y' || c == 'y') {
return true;
} else {
return false;
}
}
/**
* Returns a YAML header version
* @param node: YAML node
* @return Version number
*/
uint32 getHeaderVersion(YAML::Node &node) {
return node["Header"]["Version"].as<uint32>();
}
/**
* Copy a file if it exists.
* @param file: File stream
* @param name: File name
* @param newLine: Append new line at end of copy
*/
void copyFileIfExists(std::ofstream &file, const std::string &name, bool newLine) {
std::string path = "doc/yaml/db/" + name + ".yml";
if (fileExists(path)) {
std::ifstream source(path, std::ios::binary);
std::istreambuf_iterator<char> begin_source(source);
std::istreambuf_iterator<char> end_source;
std::ostreambuf_iterator<char> begin_dest(file);
copy(begin_source, end_source, begin_dest);
source.close();
if (newLine) {
file << "\n";
}
}
}
/**
* Prepares header for output.
* @param file: File stream
* @param type: Database type
* @param version: Database version
* @param name: File name
*/
void prepareHeader(std::ofstream &file, const std::string &type, uint32 version, const std::string &name) {
copyFileIfExists(file, "license", false);
copyFileIfExists(file, name, true);
YAML::Emitter header(file);
header << YAML::BeginMap;
header << YAML::Key << "Header";
header << YAML::BeginMap;
header << YAML::Key << "Type" << YAML::Value << type;
header << YAML::Key << "Version" << YAML::Value << version;
header << YAML::EndMap;
header << YAML::EndMap;
file << "\n";
file << "\n";
}
/**
* Prepares footer for output.
* @param file: File stream
*/
void prepareFooter(std::ostream &file) {
if (!inNode["Footer"].IsDefined())
return;
if (inNode["Body"].IsDefined()) {
file << "\n";
file << "\n";
}
YAML::Emitter footer(file);
footer << YAML::BeginMap;
footer << YAML::Key << "Footer";
footer << YAML::BeginMap;
footer << YAML::Key << "Imports";
footer << YAML::BeginSeq;
for (const YAML::Node &import : inNode["Footer"]["Imports"]) {
footer << YAML::BeginMap;
footer << YAML::Key << "Path" << YAML::Value << import["Path"];
if (import["Mode"].IsDefined())
footer << YAML::Key << "Mode" << YAML::Value << import["Mode"];
footer << YAML::EndMap;
}
footer << YAML::EndSeq;
footer << YAML::EndMap;
footer << YAML::EndMap;
}
/**
* Prepares body for output.
*/
void prepareBody(void) {
body << YAML::BeginMap;
body << YAML::Key << "Body";
body << YAML::BeginSeq;
}
/**
* Finalizes body's output.
*/
void finalizeBody(void) {
body << YAML::EndSeq;
body << YAML::EndMap;
}
/**
* Split the string with ':' as separator and put each value for a skilllv
* @param str: String to split
* @param val: Array of MAX_SKILL_LEVEL to put value into
* @return 0:error, x:number of value assign (should be MAX_SKILL_LEVEL)
*/
int skill_split_atoi(char *str, int *val) {
int i;
for (i = 0; i < MAX_SKILL_LEVEL; i++) {
if (!str)
break;
val[i] = atoi(str);
str = strchr(str, ':');
if (str)
*str++ = 0;
}
if (i == 0) // No data found.
return 0;
if (i == 1) // Single value, have the whole range have the same value.
return 1;
return i;
}
/**
* Split string to int by constant value (const.txt) or atoi()
* @param *str: String input
* @param *val: Temporary storage
* @param *delim: Delimiter (for multiple value support)
* @param min_value: Minimum value. If the splitted value is less or equal than this, will be skipped
* @param max: Maximum number that can be allocated
* @return count: Number of success
*/
uint8 skill_split_atoi2(char *str, int64 *val, const char *delim, int min_value, uint16 max) {
uint8 i = 0;
char *p = strtok(str, delim);
while (p != NULL) {
int64 n = min_value;
trim(p);
if (ISDIGIT(p[0])) // If using numeric
n = atoi(p);
else {
n = constant_lookup_int(p);
p = strtok(NULL, delim);
}
if (n > min_value) {
val[i] = n;
i++;
if (i >= max)
break;
}
p = strtok(NULL, delim);
}
return i;
}
/**
* Split string to int
* @param str: String input
* @param val1: Temporary storage to first value
* @param val2: Temporary storage to second value
*/
static void itemdb_re_split_atoi(char* str, int* val1, int* val2) {
int i, val[2];
for (i = 0; i < 2; i++) {
if (!str)
break;
val[i] = atoi(str);
str = strchr(str, ':');
if (str)
*str++ = 0;
}
if (i == 0) {
*val1 = *val2 = 0;
return; // no data found
}
if (i == 1) { // Single Value
*val1 = val[0];
*val2 = 0;
return;
}
// We assume we have 2 values.
*val1 = val[0];
*val2 = val[1];
return;
}
/**
* Determine if array contains level specific data
* @param arr: Array to check
* @return True if level specific or false for same for all levels
*/
static bool isMultiLevel(int arr[]) {
uint8 count = 0;
for (uint8 i = 0; i < MAX_SKILL_LEVEL; i++) {
if (arr[i] != 0)
count++;
}
return (count < 2 ? false : true);
}
/**
* Converts a string to upper case characters based on specific cases.
* @param name: String to convert
* @return Converted string
*/
std::string name2Upper(std::string name) {
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
name[0] = toupper(name[0]);
for (size_t i = 0; i < name.size(); i++) {
if (name[i - 1] == '_' || (name[i - 2] == '1' && name[i - 1] == 'h') || (name[i - 2] == '2' && name[i - 1] == 'h'))
name[i] = toupper(name[i]);
}
return name;
}
// Constant loading functions
static bool parse_item_constants_txt(const char *path) {
uint32 lines = 0, count = 0;
char line[1024];
FILE *fp;
fp = fopen(path, "r");
if (fp == NULL) {
ShowWarning("itemdb_readdb: File not found \"%s\", skipping.\n", path);
return false;
}
// process rows one by one
while (fgets(line, sizeof(line), fp))
{
char *str[32], *p;
int i;
lines++;
if (line[0] == '/' && line[1] == '/')
continue;
memset(str, 0, sizeof(str));
p = strstr(line, "//");
if (p != nullptr) {
*p = '\0';
}
p = line;
while (ISSPACE(*p))
++p;
if (*p == '\0')
continue;// empty line
for (i = 0; i < 19; ++i)
{
str[i] = p;
p = strchr(p, ',');
if (p == NULL)
break;// comma not found
*p = '\0';
++p;
}
t_itemid item_id = strtoul(str[0], nullptr, 10);
if (p == NULL)
{
ShowError("itemdb_readdb: Insufficient columns in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
// Script
if (*p != '{')
{
ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
str[19] = p + 1;
p = strstr(p + 1, "},");
if (p == NULL)
{
ShowError("itemdb_readdb: Invalid format (Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
*p = '\0';
p += 2;
// OnEquip_Script
if (*p != '{')
{
ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
str[20] = p + 1;
p = strstr(p + 1, "},");
if (p == NULL)
{
ShowError("itemdb_readdb: Invalid format (OnEquip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
*p = '\0';
p += 2;
// OnUnequip_Script (last column)
if (*p != '{')
{
ShowError("itemdb_readdb: Invalid format (OnUnequip_Script column) in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
str[21] = p;
p = &str[21][strlen(str[21]) - 2];
if (*p != '}') {
/* lets count to ensure it's not something silly e.g. a extra space at line ending */
int lcurly = 0, rcurly = 0;
for (size_t v = 0; v < strlen(str[21]); v++) {
if (str[21][v] == '{')
lcurly++;
else if (str[21][v] == '}') {
rcurly++;
p = &str[21][v];
}
}
if (lcurly != rcurly) {
ShowError("itemdb_readdb: Mismatching curly braces in line %d of \"%s\" (item with id %u), skipping.\n", lines, path, item_id);
continue;
}
}
str[21] = str[21] + 1; //skip the first left curly
*p = '\0'; //null the last right curly
uint32 view_id = strtoul(str[18], nullptr, 10);
char *name = trim(str[1]);
aegis_itemnames[item_id] = std::string(name);
if (atoi(str[14]) & (EQP_HELM | EQP_COSTUME_HELM) && util::umap_find(aegis_itemviewid, view_id) == nullptr)
aegis_itemviewid[view_id] = item_id;
count++;
}
fclose(fp);
ShowStatus("Done reading '" CL_WHITE "%u" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", count, path);
return true;
}
const std::string ItemDatabase::getDefaultLocation() {
return std::string(db_path) + "/item_db.yml";
}
uint64 ItemDatabase::parseBodyNode(const YAML::Node& node) {
t_itemid nameid;
if (!this->asUInt32(node, "Id", nameid))
return 0;
if (this->nodeExists(node, "AegisName")) {
std::string name;
if (!this->asString(node, "AegisName", name))
return 0;
aegis_itemnames[nameid] = name;
}
if (this->nodeExists(node, "View")) {
uint32 look;
if (!this->asUInt32(node, "View", look))
return 0;
if (look > 0) {
if (this->nodeExists(node, "Locations")) {
const YAML::Node& locationNode = node["Locations"];
static std::vector<std::string> locations = {
"Head_Low",
"Head_Mid",
"Head_Top",
"Costume_Head_Low",
"Costume_Head_Mid",
"Costume_Head_Top"
};
for (std::string& location : locations) {
if (this->nodeExists(locationNode, location)) {
bool active;
if (!this->asBool(locationNode, location, active))
return 0;
aegis_itemviewid[look] = nameid;
break;
}
}
}
}
}
return 1;
}
void ItemDatabase::loadingFinished() {
}
ItemDatabase item_db;
static bool parse_mob_constants_txt(char *split[], int columns, int current) {
uint16 mob_id = atoi(split[0]);
char *name = trim(split[1]);
aegis_mobnames[mob_id] = std::string(name);
return true;
}
static bool parse_skill_constants_txt(char *split[], int columns, int current) {
uint16 skill_id = atoi(split[0]);
char *name = trim(split[16]);
aegis_skillnames[skill_id] = std::string(name);
return true;
}
const std::string SkillDatabase::getDefaultLocation() {
return std::string(db_path) + "/skill_db.yml";
}
uint64 SkillDatabase::parseBodyNode(const YAML::Node &node) {
t_itemid nameid;
if (!this->asUInt32(node, "Id", nameid))
return 0;
if (this->nodeExists(node, "Name")) {
std::string name;
if (!this->asString(node, "Name", name))
return 0;
aegis_skillnames[nameid] = name;
}
return 1;
}
void SkillDatabase::clear() {
TypesafeCachedYamlDatabase::clear();
}
SkillDatabase skill_db;
const std::string MobDatabase::getDefaultLocation(){
return std::string( db_path ) + "/mob_db.yml";
}
uint64 MobDatabase::parseBodyNode(const YAML::Node& node) {
uint16 mob_id;
if (!this->asUInt16(node, "Id", mob_id))
return 0;
if (this->nodeExists(node, "AegisName")) {
std::string name;
if (!this->asString(node, "AegisName", name))
return 0;
aegis_mobnames[mob_id] = name;
}
return 1;
}
void MobDatabase::loadingFinished() {};
MobDatabase mob_db;
#endif /* YAML_HPP */

221
src/tool/yamlupgrade.cpp Normal file
View File

@ -0,0 +1,221 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#include "yamlupgrade.hpp"
static bool upgrade_achievement_db(std::string file, const uint32 source_version);
template<typename Func>
bool process(const std::string &type, uint32 version, const std::vector<std::string> &paths, const std::string &name, Func lambda) {
for (const std::string &path : paths) {
const std::string name_ext = name + ".yml";
const std::string from = path + "/" + name_ext;
const std::string to = path + "/" + name + "-upgrade.yml";
inNode.reset();
if (fileExists(from)) {
inNode = YAML::LoadFile(from);
uint32 source_version = getHeaderVersion(inNode);
if (source_version >= version) {
continue;
}
if (!askConfirmation("Found the file \"%s\", which requires an upgrade.\nDo you want to convert it now? (Y/N)\n", from.c_str())) {
continue;
}
if (fileExists(to)) {
if (!askConfirmation("The file \"%s\" already exists.\nDo you want to replace it? (Y/N)\n", to.c_str())) {
continue;
}
}
ShowNotice("Upgrade process has begun.\n");
std::ofstream out;
body.~Emitter();
new (&body) YAML::Emitter();
out.open(to);
if (!out.is_open()) {
ShowError("Can not open file \"%s\" for writing.\n", to.c_str());
return false;
}
prepareHeader(out, type, version, name);
if (inNode["Body"].IsDefined()) {
prepareBody();
if (!lambda(path, name_ext, source_version)) {
out.close();
return false;
}
finalizeBody();
out << body.c_str();
}
prepareFooter(out);
// Make sure there is an empty line at the end of the file for git
out << "\n";
out.close();
}
}
return true;
}
int do_init(int argc, char** argv) {
const std::string path_db = std::string(db_path);
const std::string path_db_mode = path_db + "/" + DBPATH;
const std::string path_db_import = path_db + "/" + DBIMPORT;
// Loads required conversion constants
if (fileExists(item_db.getDefaultLocation())) {
item_db.load();
} else {
parse_item_constants_txt((path_db_mode + "item_db.txt").c_str());
parse_item_constants_txt((path_db_import + "item_db.txt").c_str());
}
if (fileExists(mob_db.getDefaultLocation())) {
mob_db.load();
} else {
sv_readdb(path_db_mode.c_str(), "mob_db.txt", ',', 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, -1, &parse_mob_constants_txt, false);
sv_readdb(path_db_import.c_str(), "mob_db.txt", ',', 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, 31 + 2 * MAX_MVP_DROP + 2 * MAX_MOB_DROP, -1, &parse_mob_constants_txt, false);
}
if (fileExists(skill_db.getDefaultLocation())) {
skill_db.load();
} else {
sv_readdb(path_db_mode.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants_txt, false);
sv_readdb(path_db_import.c_str(), "skill_db.txt", ',', 18, 18, -1, parse_skill_constants_txt, false);
}
// Load constants
#define export_constant_npc(a) export_constant(a)
#include "../map/script_constants.hpp"
std::vector<std::string> root_paths = {
path_db,
path_db_mode,
path_db_import
};
if (!process("ACHIEVEMENT_DB", 2, root_paths, "achievement_db", [](const std::string &path, const std::string &name_ext, uint32 source_version) -> bool {
return upgrade_achievement_db(path + name_ext, source_version);
})) {
return 0;
}
return 0;
}
void do_final(void) {
}
// Implementation of the upgrade functions
static bool upgrade_achievement_db(std::string file, const uint32 source_version) {
size_t entries = 0;
for (const auto &input : inNode["Body"]) {
body << YAML::BeginMap;
body << YAML::Key << "Id" << YAML::Value << input["ID"];
std::string constant = input["Group"].as<std::string>();
constant.erase(0, 3); // Remove "AG_"
if (constant.compare("Chat") == 0) // Chat -> Chatting
constant.insert(4, "ting");
else if (constant.compare("Hear") == 0 || constant.compare("See") == 0)
constant = "Chatting"; // Aegis treats these as general "Talk to NPC" achievements.
else if (constant.compare("Refine") == 0) { // Refine -> Enchant
constant.erase(0, 6);
constant = "Enchant" + constant;
}
body << YAML::Key << "Group" << YAML::Value << name2Upper(constant);
body << YAML::Key << "Name" << YAML::Value << input["Name"];
if (input["Target"].IsDefined()) {
body << YAML::Key << "Targets";
body << YAML::BeginSeq;
for (const auto &it : input["Target"]) {
body << YAML::BeginMap;
body << YAML::Key << "Id" << YAML::Value << it["Id"];
if (it["MobID"].IsDefined()) {
uint16 mob_id = it["MobID"].as<uint16>();
std::string *mob_name = util::umap_find(aegis_mobnames, mob_id);
if (mob_name == nullptr) {
ShowWarning("mob_avail reading: Invalid mob-class %hu, Mob not read.\n", mob_id);
return false;
}
body << YAML::Key << "Mob" << YAML::Value << *mob_name;
}
if (it["Count"].IsDefined() && it["Count"].as<int32>() > 1)
body << YAML::Key << "Count" << YAML::Value << it["Count"];
body << YAML::EndMap;
}
body << YAML::EndSeq;
}
if (input["Condition"].IsDefined())
body << YAML::Key << "Condition" << YAML::Value << input["Condition"];
if (input["Map"].IsDefined())
body << YAML::Key << "Map" << YAML::Value << input["Map"];
if (input["Dependent"].IsDefined()) {
body << YAML::Key << "Dependents";
body << YAML::BeginMap;
for (const auto &it : input["Dependent"]) {
body << YAML::Key << it["Id"] << YAML::Value << true;
}
body << YAML::EndMap;
}
/**
* Example usage for adding label at specific version.
if (source_version < ?) {
body << YAML::Key << "CustomLabel" << YAML::Value << "Unique";
}
*/
if (input["Reward"].IsDefined()) {
body << YAML::Key << "Rewards";
body << YAML::BeginMap;
if (input["Reward"]["ItemID"].IsDefined()) {
t_itemid item_id = input["Reward"]["ItemID"].as<t_itemid>();
std::string *item_name = util::umap_find(aegis_itemnames, item_id);
if (item_name == nullptr) {
ShowError("Reward item name for item ID %u is not known.\n", item_id);
return false;
}
body << YAML::Key << "Item" << YAML::Value << *item_name;
}
if (input["Reward"]["Amount"].IsDefined() && input["Reward"]["Amount"].as<uint16>() > 1)
body << YAML::Key << "Amount" << YAML::Value << input["Reward"]["Amount"];
if (input["Reward"]["Script"].IsDefined())
body << YAML::Key << "Script" << YAML::Value << input["Reward"]["Script"];
if (input["Reward"]["TitleID"].IsDefined())
body << YAML::Key << "TitleId" << YAML::Value << input["Reward"]["TitleID"];
body << YAML::EndMap;
}
body << YAML::Key << "Score" << YAML::Value << input["Score"];
body << YAML::EndMap;
entries++;
}
ShowStatus("Done converting/upgrading '" CL_WHITE "%d" CL_RESET "' achievements in '" CL_WHITE "%s" CL_RESET "'.\n", entries, file.c_str());
return true;
}

9
src/tool/yamlupgrade.hpp Normal file
View File

@ -0,0 +1,9 @@
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder
#ifndef YAMLUPGRADE_HPP
#define YAMLUPGRADE_HPP
#include "yaml.hpp"
#endif /* YAMLUPGRADE_HPP */

View File

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9115C6D1-520A-4540-B4FE-95F3C923FE2C}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>yamlupgrade</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)</OutDir>
<IntDir>$(SolutionDir).vs\build\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\yaml-cpp\include\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common-minicore.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;_DEBUG;_CONSOLE;_LIB;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\yaml-cpp\include\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common-minicore.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\yaml-cpp\include\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common-minicore.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>$(DefineConstants);WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_WINSOCK_DEPRECATED_NO_WARNINGS;LIBCONFIG_STATIC;YY_USE_CONST;MINICORE;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>$(SolutionDir)3rdparty\yaml-cpp\include\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;$(SolutionDir).vs\build\common-minicore.lib;$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.lib;$(SolutionDir).vs\build\yaml-cpp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="yaml.hpp" />
<ClInclude Include="yamlupgrade.hpp" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\common\database.cpp" />
<ClCompile Include="yamlupgrade.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\database.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Target Name="AfterClean">
<Delete Files="$(SolutionDir)zlib.dll" ContinueOnError="true" />
<Delete Files="$(SolutionDir)serv.bat" ContinueOnError="true" />
<Delete Files="$(SolutionDir)yamlupgrade.bat" ContinueOnError="true" />
</Target>
<Target Name="AfterBuild">
<Copy SourceFiles="$(SolutionDir)3rdparty\zlib\lib\$(Platform)\zlib.dll" DestinationFolder="$(SolutionDir)" ContinueOnError="true" Condition="!Exists('$(SolutionDir)zlib.dll')" />
<Copy SourceFiles="$(SolutionDir)tools\serv.bat" DestinationFolder="$(SolutionDir)" ContinueOnError="true" Condition="!Exists('$(SolutionDir)serv.bat')" />
<Copy SourceFiles="$(SolutionDir)tools\yamlupgrade.bat" DestinationFolder="$(SolutionDir)" ContinueOnError="true" Condition="!Exists('$(SolutionDir)yamlupgrade.bat')" />
</Target>
</Project>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{de48fb82-cea1-4318-bd96-9aab02b5b59d}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{82d5c708-a60f-4ae1-8b0d-0aafaa8da273}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="yaml.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="yamlupgrade.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="yamlupgrade.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\database.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\database.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

2
tools/yamlupgrade.bat Normal file
View File

@ -0,0 +1,2 @@
@ECHO OFF
CALL serv.bat yamlupgrade.exe YAML UPGRADE