diff options
59 files changed, 17097 insertions, 91 deletions
diff --git a/Hercules.xcodeproj/project.pbxproj b/Hercules.xcodeproj/project.pbxproj index bb3149d3a..44ff7b53f 100644 --- a/Hercules.xcodeproj/project.pbxproj +++ b/Hercules.xcodeproj/project.pbxproj @@ -171,6 +171,8 @@ A5F7946C191CA34E002293AB /* sysinfo.c in Sources */ = {isa = PBXBuildFile; fileRef = A5F79469191CA34E002293AB /* sysinfo.c */; }; A5F7946D191CA34E002293AB /* sysinfo.c in Sources */ = {isa = PBXBuildFile; fileRef = A5F79469191CA34E002293AB /* sysinfo.c */; }; A5F7946E191CA34E002293AB /* sysinfo.c in Sources */ = {isa = PBXBuildFile; fileRef = A5F79469191CA34E002293AB /* sysinfo.c */; }; + D0647BA51E793E7300D0843D /* int_achievement.c in Sources */ = {isa = PBXBuildFile; fileRef = D0647BA31E793E7300D0843D /* int_achievement.c */; }; + D0647BA81E793E8200D0843D /* achievement.c in Sources */ = {isa = PBXBuildFile; fileRef = D0647BA61E793E8200D0843D /* achievement.c */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -444,6 +446,10 @@ A5F7946A191CA34E002293AB /* sysinfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sysinfo.h; path = src/common/sysinfo.h; sourceTree = "<group>"; }; A5F7946B191CA34E002293AB /* sysinfo.inc */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; fileEncoding = 4; name = sysinfo.inc; path = src/common/sysinfo.inc; sourceTree = "<group>"; }; A5F79476191CA3F4002293AB /* sysinfogen.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sysinfogen.sh; sourceTree = "<group>"; }; + D0647BA31E793E7300D0843D /* int_achievement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = int_achievement.c; path = src/char/int_achievement.c; sourceTree = SOURCE_ROOT; }; + D0647BA41E793E7300D0843D /* int_achievement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = int_achievement.h; path = src/char/int_achievement.h; sourceTree = SOURCE_ROOT; }; + D0647BA61E793E8200D0843D /* achievement.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = achievement.c; path = src/map/achievement.c; sourceTree = SOURCE_ROOT; }; + D0647BA71E793E8200D0843D /* achievement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = achievement.h; path = src/map/achievement.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -561,6 +567,8 @@ A5467AD81A16FD08008AFAA6 /* geoip.h */, A5B894AB1A03CDFA005AD22E /* HPMchar.c */, A5B894AC1A03CDFA005AD22E /* HPMchar.h */, + D0647BA31E793E7300D0843D /* int_achievement.c */, + D0647BA41E793E7300D0843D /* int_achievement.h */, A56CC745185657D9009EB79C /* int_auction.c */, A56CC746185657D9009EB79C /* int_auction.h */, A530267D202D792F0060E394 /* int_clan.c */, @@ -600,6 +608,8 @@ A56CC67D18564357009EB79C /* map-server */ = { isa = PBXGroup; children = ( + D0647BA61E793E8200D0843D /* achievement.c */, + D0647BA71E793E8200D0843D /* achievement.h */, A56CC76A18565812009EB79C /* atcommand.c */, A56CC76B18565812009EB79C /* atcommand.h */, A56CC76C18565812009EB79C /* battle.c */, @@ -1191,6 +1201,7 @@ A56CC6E5185643BB009EB79C /* memmgr.c in Sources */, A56CC6E2185643BB009EB79C /* HPM.c in Sources */, A56CC765185657D9009EB79C /* int_pet.c in Sources */, + D0647BA51E793E7300D0843D /* int_achievement.c in Sources */, A56CC769185657D9009EB79C /* pincode.c in Sources */, A56CC6F1185643BB009EB79C /* mutex.c in Sources */, A56CC762185657D9009EB79C /* int_mail.c in Sources */, @@ -1288,6 +1299,7 @@ A56CC6D1185643BB009EB79C /* core.c in Sources */, A56CC7D118565812009EB79C /* npc.c in Sources */, A56CC6D4185643BB009EB79C /* db.c in Sources */, + D0647BA81E793E8200D0843D /* achievement.c in Sources */, A56CC704185643BB009EB79C /* showmsg.c in Sources */, A56CC6E0185643BB009EB79C /* grfio.c in Sources */, A56CC70D185643BB009EB79C /* strlib.c in Sources */, diff --git a/conf/common/inter-server.conf b/conf/common/inter-server.conf index 3310c9e5c..1e738c587 100644 --- a/conf/common/inter-server.conf +++ b/conf/common/inter-server.conf @@ -84,6 +84,7 @@ inter_configuration: { hotkey_db: "hotkey" scdata_db: "sc_data" cart_db: "cart_inventory" + achievement_db: "char_achievements" inventory_db: "inventory" charlog_db: "charlog" storage_db: "storage" diff --git a/db/achievement_rank_db.conf b/db/achievement_rank_db.conf new file mode 100644 index 000000000..e19194fad --- /dev/null +++ b/db/achievement_rank_db.conf @@ -0,0 +1,43 @@ +//================= Hercules Database ========================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License ==================================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2018 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see <http://www.gnu.org/licenses/>. +//============================================================================== +//= Achievement Rank Database +//============================================================================== + +achievement_rank_db: +{ + Rank1: 18 + Rank2: 31 + Rank3: 49 + Rank4: 73 + Rank5: 135 + Rank6: 104 + Rank7: 140 + Rank8: 214 + Rank9: 305 + Rank10: 257 + Rank11: 300 +} diff --git a/db/constants.conf b/db/constants.conf index b7bc24985..c3b1fdb57 100644 --- a/db/constants.conf +++ b/db/constants.conf @@ -3931,4 +3931,52 @@ constants_db: { HAT_EF_QSCARABA: 54 HAT_EF_FSTONE: 55 HAT_EF_MAGICCIRCLE: 56 + + comment__: "Achievement Types" + ACH_QUEST: 0 + ACH_KILL_PC_TOTAL: 1 + ACH_KILL_PC_JOB: 2 + ACH_KILL_PC_JOBTYPE: 3 + ACH_KILL_MOB_CLASS: 4 + ACH_DAMAGE_PC_MAX: 5 + ACH_DAMAGE_PC_TOTAL: 6 + ACH_DAMAGE_PC_REC_MAX: 7 + ACH_DAMAGE_PC_REC_TOTAL: 8 + ACH_DAMAGE_MOB_MAX: 9 + ACH_DAMAGE_MOB_TOTAL: 10 + ACH_DAMAGE_MOB_REC_MAX: 11 + ACH_DAMAGE_MOB_REC_TOTAL: 12 + ACH_JOB_CHANGE: 13 + ACH_STATUS: 14 + ACH_STATUS_BY_JOB: 15 + ACH_STATUS_BY_JOBTYPE: 16 + ACH_CHATROOM_CREATE_DEAD: 17 + ACH_CHATROOM_CREATE: 18 + ACH_CHATROOM_MEMBERS: 19 + ACH_FRIEND_ADD: 20 + ACH_PARTY_CREATE: 21 + ACH_PARTY_JOIN: 22 + ACH_MARRY: 23 + ACH_ADOPT_BABY: 24 + ACH_ADOPT_PARENT: 25 + ACH_ZENY_HOLD: 26 + ACH_ZENY_GET_ONCE: 27 + ACH_ZENY_GET_TOTAL: 28 + ACH_ZENY_SPEND_ONCE: 29 + ACH_ZENY_SPEND_TOTAL: 30 + ACH_EQUIP_REFINE_SUCCESS: 31 + ACH_EQUIP_REFINE_FAILURE: 32 + ACH_EQUIP_REFINE_SUCCESS_TOTAL: 33 + ACH_EQUIP_REFINE_FAILURE_TOTAL: 34 + ACH_EQUIP_REFINE_SUCCESS_WLV: 35 + ACH_EQUIP_REFINE_FAILURE_WLV: 36 + ACH_EQUIP_REFINE_SUCCESS_ID: 37 + ACH_EQUIP_REFINE_FAILURE_ID: 38 + ACH_ITEM_GET_COUNT: 39 + ACH_ITEM_GET_COUNT_ITEMTYPE: 40 + ACH_ITEM_GET_WORTH: 41 + ACH_ITEM_SELL_WORTH: 42 + ACH_PET_CREATE: 43 + ACH_ACHIEVE: 44 + ACH_ACHIEVEMENT_RANK: 45 } diff --git a/db/pre-re/achievement_db.conf b/db/pre-re/achievement_db.conf new file mode 100644 index 000000000..db63ed4a8 --- /dev/null +++ b/db/pre-re/achievement_db.conf @@ -0,0 +1,6079 @@ +//================= Hercules Database ===================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2018 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see <http://www.gnu.org/licenses/>. +//========================================================================= +//= Achievement Database +//========================================================================= + +achievement_db: ( +/***************************************************************** + * Entry Structure + ***************************************************************** +{ + Id: (int) Unique ID of the achievement representing it's client-side equivalent. + Name: (string) Name/Title of the Achievement. + Type: (string) Validation type for the achievement. + [Type] [Validation Description] (Trigger) + ACH_QUEST: Specific achievement objective update (Script). + ACH_KILL_PC_TOTAL: (Accumulative) Total kill count. (Player kill) + ACH_KILL_PC_JOB: Kill a player of the specified job. (Player Kill) + ACH_KILL_PC_JOBTYPE: Kill a player of the specified job type. (Player Kill) + ACH_KILL_MOB_CLASS: Kill a particular mob class. (Mob Kill) + ACH_DAMAGE_PC_MAX: Maximum damage caused on a player. (Player Damage) + ACH_DAMAGE_PC_TOTAL: (Accumulative) Damage on players. (Player Damage) + ACH_DAMAGE_PC_REC_MAX: Maximum damage received by a player. (Receive Player Damage) + ACH_DAMAGE_PC_REC_TOTAL: (Accumulative) Damage received by players. (Receive Player Damage) + ACH_DAMAGE_MOB_MAX: Maximum damage caused on a monster. (Monster Damage) + ACH_DAMAGE_MOB_TOTAL: (Accumulative) Damage caused on monsters. (Monster Damage) + ACH_DAMAGE_MOB_REC_MAX: Maximum damage received by a monster. (Receive Monster Damage) + ACH_DAMAGE_MOB_REC_TOTAL: (Accumulative) Damage received by monsters. (Receive Monster Damage) + ACH_JOB_CHANGE: Change to a specific job. (Job Change) + ACH_STATUS: Acquire a specific amount of a particular status type. (Stat Change) + ACH_STATUS_BY_JOB: Acquire a specific amount of a status type as a job class. (Stat Change) + ACH_STATUS_BY_JOBTYPE: Acquire a specific amount of a status type as a job type. (Stat Change) + ACH_CHATROOM_CREATE_DEAD: (Accumulative) Create a chatroom when dead. (Chatroom Creation) + ACH_CHATROOM_CREATE: (Accumulative) Create a chatroom. (Chatroom Creation) + ACH_CHATROOM_MEMBERS: Gather 'n' members in a chatroom. (Chatroom Join) + ACH_FRIEND_ADD: Add a specific number of friends. (Friend Addition) + ACH_PARTY_CREATE: (Accumulative) Create a specific number of parties. (Party Creation) + ACH_PARTY_JOIN: (Accumulative) Join a specific number of parties. (Party Join) + ACH_MARRY: (Accumulative) Marry a specified number of times. (Marriage) + ACH_ADOPT_BABY: (Accumulative) Get Adopted. (Adoption) + ACH_ADOPT_PARENT: (Accumulative) Adopt a Baby. (Adoption) + ACH_ZENY_HOLD: Hold a specific amount of zeny in your inventory. (Gain Zeny) + ACH_ZENY_GET_ONCE: Gain a specific amount of zeny in one transaction. (Gain Zeny) + ACH_ZENY_GET_TOTAL: (Accumulative) Gain a specific amount of zeny in total. (Gain Zeny) + ACH_ZENY_SPEND_ONCE: Spend a specific amount of zeny in one transaction. (Pay Zeny) + ACH_ZENY_SPEND_TOTAL: (Accumulative) Spend a specific amount of zeny in total. (Pay Zeny) + ACH_EQUIP_REFINE_SUCCESS: Refine an item to +N. (Successful Refine) + ACH_EQUIP_REFINE_FAILURE: Fail to refine an item of +N refine. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_TOTAL: (Accumulative) Refine an item successfully N times. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_TOTAL: (Accumulative) Fail to refine an item N times. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_WLV: Refine a Weapon of a particular Level to +N. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_WLV: Fail to refine a Weapon of a particular level from +N. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_ID: Refine a particular Item successfully to +N. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_ID: Fail to refine a particular item successfully from +N. (Failed Refine) + ACH_ITEM_GET_COUNT: Acquire N amount of an item of a particular ID. (Acquire Item) + ACH_ITEM_GET_COUNT_ITEMTYPE: Acquire N amount of items of a particular type mask. (Acquire Item) + ACH_ITEM_GET_WORTH: Acquire an item of buy value N. (Acquire Item) + ACH_ITEM_SELL_WORTH: Sell an item of sell value N. (NPC Sell Item) + ACH_PET_CREATE: Successfully tame a pet of a particular mob class. (Successful Pet Tame) + ACH_ACHIEVE: Achieve an Achievement. (Achievement Completion) + ACH_ACHIEVEMENT_RANK: Achieve an Achievement Rank. (Achievement Rank Increase) + Objectives: { [Mandatory Field] Objectives of an achievement. Up to 10 objectives per achievement. + To comply with the client's order of objectives, this list must be in order. + *1: { + Description: (string) [Mandatory Field] Description of a particular objective. + Criteria: { This is a field for achievements whose objectives must meet + certain criteria before evaluating the player's progress for it. + MobId: (mixed) MonsterId required for an objective. + For types such as ACH_KILL_MOB_CLASS and ACH_PET_CREATE. Can be either int or string constant. + JobId: (mixed) Array or Single entry of JobIds. + For types - ACH_KILL_PC_JOBTYPE, ACH_JOB_CHANGE or ACH_STATUS_BY_JOBTYPE. + Can be either a numeric or string constant. + ItemId: (mixed) ItemId required for an objective. + For Types such as ACH_ITEM_GET_COUNT. Can be either int or string constant. + StatusType: (mixed) Status Type required for an objective. + For Types such as ACH_STATUS, ACH_STATUS_BY_JOB, ACH_STATUS_BY_JOBTYPE. + Types - + "SP_STR" - Strength + "SP_AGI" - Agility + "SP_VIT" - Vitality + "SP_INT" - Intelligence + "SP_DEX" - Dexterity + "SP_LUK" - Luck + "SP_BASELEVEL" - Base Level + "SP_JOBLEVEL" - Job Level + Can be either int or string constant. + ItemType: (mixed) Item type that is required for this achievement. + For Types such as ACH_ITEM_GET_COUNT_ITEMTYPE. + Can be either int, string or list. + Refer "Item types" Constants from constants.conf + WeaponLevel: (int) Weapon Level that is required for this achievement. (Eg. 0, 1, 2, 3 or 4). + For Types such as ACH_EQUIP_REFINE_SUCCESS_WLV and ACH_EQUIP_REFINE_FAILURE_WLV. + Achieve: (int) AchievementID to be achieved. + For Type - ACH_ACHIEVE. + } + Goal: (int) Target amount to be met for the completion of the objective. Default is 1. + } + ... + *10: {...} + } + Rewards: { + Bonus: <""> (script) Script code bonus to be given as a reward for an achievement. + Items: { Item rewards per achievement. With a maximum defined in mmo.h as MAX_ACHEIVEMENT_ITEM_REWARDS. + Apple: 1 (int) Item ID (int or string constant) : Amount (int) + } + TitleId: (int) ID of the Title (from the Title System) awarded. + } + Points: (int) Points per achievement given on it's successful completion. +} + *****************************************************************/ +{ + Id: 110000 + Name: "At this time I live to eat" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Bring the food to Bigfoot at this hour" + } + } + Points: 10 +}, +{ + Id: 110001 + Name: "A fan of this polarity" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Lady of the house" + } + *2: { + Description: "Beautiful fine anak" + } + *3: { + Description: "Mistress in ambience" + } + *4: { + Description: "Beautiful wife and children" + } + *5: { + Description: "Down town Bachelor" + } + *6: { + Description: "Great act ability anak" + } + *7: { + Description: "Great attack power bachelor" + } + } + Points: 10 +}, +{ + Id: 120001 + Name: "North Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120002 + Name: "North Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120003 + Name: "North Prontera Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120004 + Name: "West Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding west prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120005 + Name: "West Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding west prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120006 + Name: "East Prontera Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120007 + Name: "South Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120008 + Name: "South Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120009 + Name: "South Prontera Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120010 + Name: "South Prontera Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120011 + Name: "East Geffen Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120012 + Name: "Southeast Geffen Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southeast geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120013 + Name: "Northwest Geffen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120014 + Name: "Northwest Geffen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120015 + Name: "Northwest Geffen Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120016 + Name: "South Geffen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120017 + Name: "South Geffen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120018 + Name: "Sograt Desert Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120019 + Name: "Sograt Desert Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120020 + Name: "Sograt Desert Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120021 + Name: "Sograt Desert Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120022 + Name: "Sograt Desert Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120023 + Name: "Sograt Desert Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120024 + Name: "Southwest Payon Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120025 + Name: "Southwest Payon Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120026 + Name: "Southwest Payon Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120027 + Name: "Southwest Payon Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120028 + Name: "East Payon Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120029 + Name: "East Payon Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120030 + Name: "East Payon Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120031 + Name: "East Payon Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120032 + Name: "North Mjolnir Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120033 + Name: "North Mjolnir Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120034 + Name: "North Mjolnir Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120035 + Name: "North Mjolnir Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120036 + Name: "North Mjolnir Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120037 + Name: "South Mjolnir Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120038 + Name: "South Mjolnir Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120039 + Name: "South Mjolnir Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120040 + Name: "South Mjolnir Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120041 + Name: "South Mjolnir Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120042 + Name: "South Mjolnir Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120043 + Name: "South Aldebaran Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south aldebaran field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120044 + Name: "Comodo Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120045 + Name: "Comodo Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120046 + Name: "Comodo Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120047 + Name: "Comodo Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120048 + Name: "Comodo Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120049 + Name: "Comodo Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120050 + Name: "Comodo Field Exploration(7)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120051 + Name: "Comodo Field Exploration(8)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120052 + Name: "Border Checkpoint Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding border checkpoint field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120053 + Name: "Border Checkpoint Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding border checkpoint field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120054 + Name: "Kiel Hyre Mansion Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kiel hyre mansion field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120055 + Name: "El Mes Plateau Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120056 + Name: "El Mes Plateau Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120057 + Name: "El Mes Plateau Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120058 + Name: "El Mes Gorge Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes gorge field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120059 + Name: "Kiel Hyre Academy Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kiel hyre academy field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120060 + Name: "Guard Camp Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding guard camp field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120061 + Name: "Yuno Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding yuno field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120062 + Name: "Front of Thanatos Tower Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding front of thanatos tower field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120063 + Name: "Hugel Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120064 + Name: "Hugel Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120065 + Name: "Hugel Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120066 + Name: "Abyss Lake Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abyss lake field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120067 + Name: "Einbroch Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120068 + Name: "Einbroch Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120069 + Name: "Einbroch Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120070 + Name: "Einbroch Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120071 + Name: "Einbroch Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120072 + Name: "Einbroch Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120073 + Name: "Einbroch Field Exploration(7)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120074 + Name: "Einbroch Field Exploration(8)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120075 + Name: "Lighthalzen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120076 + Name: "Lighthalzen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120077 + Name: "Lighthalzen Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120078 + Name: "Rachel Audhumbla Plains Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120079 + Name: "Rachel Plains Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120080 + Name: "Rachel Plains Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120081 + Name: "Rachel Plains Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120082 + Name: "Rachel Audhumbla Grassland Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla grassland field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120083 + Name: "Rachel Audhumbla Grassland Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla grassland field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120084 + Name: "Portus Luna Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding portus luna field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120085 + Name: "Veins Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120086 + Name: "Veins Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120087 + Name: "Veins Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120088 + Name: "Veins Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120089 + Name: "Veins Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120090 + Name: "Eclage Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding eclage field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120091 + Name: "North Bitfrost Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north bitfrost field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120092 + Name: "South Bitfrost Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south bitfrost field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120093 + Name: "Splendide Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120094 + Name: "Splendide Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120095 + Name: "Splendide Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120096 + Name: "Manuk Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120097 + Name: "Manuk Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120098 + Name: "Manuk Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120099 + Name: "Outskirts of Kamidal Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding outskirts of kamidal field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120100 + Name: "Outskirts of Kamidal Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding outskirts of kamidal field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120101 + Name: "Amatsu Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding amatsu field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120102 + Name: "Kunlun Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kunlun field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120103 + Name: "Gonryun Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding gonryun field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120104 + Name: "Ayothaya Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ayothaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120105 + Name: "Moscovia Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding moscovia field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120106 + Name: "Brasilis Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding brasilis field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120107 + Name: "Dewata Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding dewata field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120108 + Name: "Malaya Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding malaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120109 + Name: "Malaya Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding malaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120110 + Name: "Abbey Underground Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abbey underground dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120111 + Name: "Abyss Lake Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abyss lake dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120112 + Name: "Clock Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding clock tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120113 + Name: "Amatsu Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding amatsu dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120114 + Name: "Ant Hell Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ant hell dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120115 + Name: "Ayothaya Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ayothaya dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120116 + Name: "Comodo Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120117 + Name: "Brasilis Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding brasilis dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120118 + Name: "Clock Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding clock tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120119 + Name: "Istana Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding istana dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120120 + Name: "Scaraba Hole Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding scaraba hole dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120121 + Name: "Bitfrost Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding bitfrost dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120122 + Name: "Einbroch Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding einbroch dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120123 + Name: "Geffen Underground Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding geffen underground dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120124 + Name: "Glastheim Dungeon Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120125 + Name: "Glastheim Dungeon Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120126 + Name: "Glastheim Dungeon Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120127 + Name: "Glastheim Dungeon Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120128 + Name: "Kunlun Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Kunlun dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120129 + Name: "Rachel Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120130 + Name: "Sphinx Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sphinx dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120131 + Name: "Izlude Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding izlude dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120132 + Name: "Robot Factory Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding robot factory dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120133 + Name: "Bio Lab Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding bio lab dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120134 + Name: "Gonryun Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding gonryun dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120135 + Name: "Nogg Road Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding nogg road dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120136 + Name: "Coal Mine Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Coal mine dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120137 + Name: "Pyramid Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding pyramid dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120138 + Name: "Orc Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding orc dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120139 + Name: "Payon Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding payon dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120140 + Name: "Labyrinth Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding labyrinth dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120141 + Name: "Undersea Tunnel Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding undersea tunnel dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120142 + Name: "Thanatos Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding thanatos tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120143 + Name: "Thor Volcano Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding thor volcano dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120144 + Name: "Sunken Ship Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sunken ship dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120145 + Name: "Turtle Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding turtle dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120146 + Name: "Toy Factory Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding toy factory dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 127001 + Name: "Prontera Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127002 + Name: "Geffen Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127003 + Name: "Morocc Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127004 + Name: "Payon Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127005 + Name: "Yuno Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127006 + Name: "Lighthalzen Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127007 + Name: "Einbroch Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127008 + Name: "Rachel Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127009 + Name: "Veins Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 128008 + Name: "Bold Adventurer" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128009 + Name: "Baphomet Hatred" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128010 + Name: "Goat's Nemesis" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128047 + Name: "Endless Tower challenger" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128048 + Name: "Endless Tower Slayer" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128049 + Name: "Lord of the tower" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 129001 + Name: "Prontera Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "North Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120001 + } + } + *2: { + Description: "North Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120002 + } + } + *3: { + Description: "North Prontera Field Exploration(3) completed" + Criteria: { + Achieve: 120003 + } + } + *4: { + Description: "West Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120004 + } + } + *5: { + Description: "West Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120005 + } + } + *6: { + Description: "East Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120006 + } + } + *7: { + Description: "South Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120007 + } + } + *8: { + Description: "South Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120008 + } + } + *9: { + Description: "South Prontera Field Exploration(3) completed" + Criteria: { + Achieve: 120009 + } + } + *10: { + Description: "South Prontera Field Exploration(4) completed" + Criteria: { + Achieve: 120010 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129002 + Name: "Geffen Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "East Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120011 + } + } + *2: { + Description: "Southeast Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120012 + } + } + *3: { + Description: "Northwest Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120013 + } + } + *4: { + Description: "Northwest Geffen Field Exploration(2) completed" + Criteria: { + Achieve: 120014 + } + } + *5: { + Description: "Northwest Geffen Field Exploration(3) completed" + Criteria: { + Achieve: 120015 + } + } + *6: { + Description: "South Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120016 + } + } + *7: { + Description: "South Geffen Field Exploration(2) completed" + Criteria: { + Achieve: 120017 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129003 + Name: "Sograt Desert Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Sograt Desert Field Exploration(1) completed" + Criteria: { + Achieve: 120018 + } + } + *2: { + Description: "Sograt Desert Field Exploration(2) completed" + Criteria: { + Achieve: 120019 + } + } + *3: { + Description: "Sograt Desert Field Exploration(3) completed" + Criteria: { + Achieve: 120020 + } + } + *4: { + Description: "Sograt Desert Field Exploration(4) completed" + Criteria: { + Achieve: 120021 + } + } + *5: { + Description: "Sograt Desert Field Exploration(5) completed" + Criteria: { + Achieve: 120022 + } + } + *6: { + Description: "Sograt Desert Field Exploration(6) completed" + Criteria: { + Achieve: 120023 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129004 + Name: "Payon Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Southwest Payon Field Exploration(1) completed" + Criteria: { + Achieve: 120024 + } + } + *2: { + Description: "Southwest Payon Field Exploration(2) completed" + Criteria: { + Achieve: 120025 + } + } + *3: { + Description: "Southwest Payon Field Exploration(3) completed" + Criteria: { + Achieve: 120026 + } + } + *4: { + Description: "Southwest Payon Field Exploration(4) completed" + Criteria: { + Achieve: 120027 + } + } + *5: { + Description: "East Payon Field Exploration(1) completed" + Criteria: { + Achieve: 120028 + } + } + *6: { + Description: "East Payon Field Exploration(2) completed" + Criteria: { + Achieve: 120029 + } + } + *7: { + Description: "East Payon Field Exploration(3) completed" + Criteria: { + Achieve: 120030 + } + } + *8: { + Description: "East Payon Field Exploration(4) completed" + Criteria: { + Achieve: 120031 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129005 + Name: "North Mjolnir Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "North Mjolnir Field Exploration(1) completed" + Criteria: { + Achieve: 120032 + } + } + *2: { + Description: "North Mjolnir Field Exploration(2) completed" + Criteria: { + Achieve: 120033 + } + } + *3: { + Description: "North Mjolnir Field Exploration(3) completed" + Criteria: { + Achieve: 120034 + } + } + *4: { + Description: "North Mjolnir Field Exploration(4) completed" + Criteria: { + Achieve: 120035 + } + } + *5: { + Description: "North Mjolnir Field Exploration(5) completed" + Criteria: { + Achieve: 120036 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129006 + Name: "South Mjolnir Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "South Mjolnir Field Exploration(1) completed" + Criteria: { + Achieve: 120037 + } + } + *2: { + Description: "South Mjolnir Field Exploration(2) completed" + Criteria: { + Achieve: 120038 + } + } + *3: { + Description: "South Mjolnir Field Exploration(3) completed" + Criteria: { + Achieve: 120039 + } + } + *4: { + Description: "South Mjolnir Field Exploration(4) completed" + Criteria: { + Achieve: 120040 + } + } + *5: { + Description: "South Mjolnir Field Exploration(5) completed" + Criteria: { + Achieve: 120041 + } + } + *6: { + Description: "South Mjolnir Field Exploration(6) completed" + Criteria: { + Achieve: 120042 + } + } + *7: { + Description: "South Aldebaran Field Exploration(1) completed" + Criteria: { + Achieve: 120043 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129007 + Name: "Comodo Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Comodo Field Exploration(1) completed" + Criteria: { + Achieve: 120044 + } + } + *2: { + Description: "Comodo Field Exploration(2) completed" + Criteria: { + Achieve: 120045 + } + } + *3: { + Description: "Comodo Field Exploration(3) completed" + Criteria: { + Achieve: 120046 + } + } + *4: { + Description: "Comodo Field Exploration(4) completed" + Criteria: { + Achieve: 120047 + } + } + *5: { + Description: "Comodo Field Exploration(5) completed" + Criteria: { + Achieve: 120048 + } + } + *6: { + Description: "Comodo Field Exploration(6) completed" + Criteria: { + Achieve: 120049 + } + } + *7: { + Description: "Comodo Field Exploration(7) completed" + Criteria: { + Achieve: 120050 + } + } + *8: { + Description: "Comodo Field Exploration(8) completed" + Criteria: { + Achieve: 120051 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129008 + Name: "Rune Midgard Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Prontera Explorer" + Criteria: { + Achieve: 129001 + } + } + *2: { + Description: "Geffen Explorer" + Criteria: { + Achieve: 129002 + } + } + *3: { + Description: "Sograt Desert Explorer" + Criteria: { + Achieve: 129003 + } + } + *4: { + Description: "Payon Explorer" + Criteria: { + Achieve: 129004 + } + } + *5: { + Description: "North Mjolnir Explorer" + Criteria: { + Achieve: 129005 + } + } + *6: { + Description: "South Mjolnir Explorer" + Criteria: { + Achieve: 129006 + } + } + *7: { + Description: "Comodo Explorer" + Criteria: { + Achieve: 129007 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129009 + Name: "Yuno Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Border Checkpoint Field Exploration(1) completed" + Criteria: { + Achieve: 120052 + } + } + *2: { + Description: "Border Checkpoint Field Exploration(2) completed" + Criteria: { + Achieve: 120053 + } + } + *3: { + Description: "Kiel Hyre Mansion Field Exploration(1) completed" + Criteria: { + Achieve: 120054 + } + } + *4: { + Description: "El Mes Plateau Field Exploration(1) completed" + Criteria: { + Achieve: 120055 + } + } + *5: { + Description: "El Mes Plateau Field Exploration(2) completed" + Criteria: { + Achieve: 120056 + } + } + *6: { + Description: "El Mes Plateau Field Exploration(3) completed" + Criteria: { + Achieve: 120057 + } + } + *7: { + Description: "El Mes Gorge Field Exploration(1) completed" + Criteria: { + Achieve: 120058 + } + } + *8: { + Description: "Kiel Hyre Academy Field Exploration(1) completed" + Criteria: { + Achieve: 120059 + } + } + *9: { + Description: "Guard Camp Field Exploration(1) completed" + Criteria: { + Achieve: 120060 + } + } + *10: { + Description: "Yuno Field Exploration(1) completed" + Criteria: { + Achieve: 120061 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129010 + Name: "Hugel Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Front of Thanatos Tower Field Exploration(1) completed" + Criteria: { + Achieve: 120062 + } + } + *2: { + Description: "Hugel Field Exploration(1) completed" + Criteria: { + Achieve: 120063 + } + } + *3: { + Description: "Hugel Field Exploration(2) completed" + Criteria: { + Achieve: 120064 + } + } + *4: { + Description: "Hugel Field Exploration(3) completed" + Criteria: { + Achieve: 120065 + } + } + *5: { + Description: "Abyss Lake Field Exploration(1) completed" + Criteria: { + Achieve: 120066 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129011 + Name: "Einbroch Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Einbroch Field Exploration(1) completed" + Criteria: { + Achieve: 120067 + } + } + *2: { + Description: "Einbroch Field Exploration(2) completed" + Criteria: { + Achieve: 120068 + } + } + *3: { + Description: "Einbroch Field Exploration(3) completed" + Criteria: { + Achieve: 120069 + } + } + *4: { + Description: "Einbroch Field Exploration(4) completed" + Criteria: { + Achieve: 120070 + } + } + *5: { + Description: "Einbroch Field Exploration(5) completed" + Criteria: { + Achieve: 120071 + } + } + *6: { + Description: "Einbroch Field Exploration(6) completed" + Criteria: { + Achieve: 120072 + } + } + *7: { + Description: "Einbroch Field Exploration(7) completed" + Criteria: { + Achieve: 120073 + } + } + *8: { + Description: "Einbroch Field Exploration(8) completed" + Criteria: { + Achieve: 120074 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129012 + Name: "Lighthalzen Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Lighthalzen Field Exploration(1) completed" + Criteria: { + Achieve: 120075 + } + } + *2: { + Description: "Lighthalzen Field Exploration(2) completed" + Criteria: { + Achieve: 120076 + } + } + *3: { + Description: "Lighthalzen Field Exploration(3) completed" + Criteria: { + Achieve: 120077 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129013 + Name: "Schwarzwald Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Yuno Explorer" + Criteria: { + Achieve: 129009 + } + } + *2: { + Description: "Hugel Explorer" + Criteria: { + Achieve: 129010 + } + } + *3: { + Description: "Einbroch Explorer" + Criteria: { + Achieve: 129011 + } + } + *4: { + Description: "Lighthalzen Explorer" + Criteria: { + Achieve: 129012 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129014 + Name: "Rachel Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Rachel Audhumbla Plains Field Exploration(1) completed" + Criteria: { + Achieve: 120078 + } + } + *2: { + Description: "Rachel Plains Field Exploration(1) completed" + Criteria: { + Achieve: 120079 + } + } + *3: { + Description: "Rachel Plains Field Exploration(2) completed" + Criteria: { + Achieve: 120080 + } + } + *4: { + Description: "Rachel Plains Field Exploration(3) completed" + Criteria: { + Achieve: 120081 + } + } + *5: { + Description: "Rachel Audhumbla Grassland Field Exploration(1) completed" + Criteria: { + Achieve: 120082 + } + } + *6: { + Description: "Rachel Audhumbla Grassland Field Exploration(2) completed" + Criteria: { + Achieve: 120083 + } + } + *7: { + Description: "Portus Luna Field Exploration(1) completed" + Criteria: { + Achieve: 120084 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129015 + Name: "Veins Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Veins Field Exploration(1) completed" + Criteria: { + Achieve: 120085 + } + } + *2: { + Description: "Veins Field Exploration(2) completed" + Criteria: { + Achieve: 120086 + } + } + *3: { + Description: "Veins Field Exploration(3) completed" + Criteria: { + Achieve: 120087 + } + } + *4: { + Description: "Veins Field Exploration(4) completed" + Criteria: { + Achieve: 120088 + } + } + *5: { + Description: "Veins Field Exploration(5) completed" + Criteria: { + Achieve: 120089 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129016 + Name: "Arunafeltz Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Rachel Explorer" + Criteria: { + Achieve: 129014 + } + } + *2: { + Description: "Veins Explorer" + Criteria: { + Achieve: 129015 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129017 + Name: "Laphine Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Eclage Field Exploration(1) completed" + Criteria: { + Achieve: 120090 + } + } + *2: { + Description: "North Bitfrost Field Exploration(1) completed" + Criteria: { + Achieve: 120091 + } + } + *3: { + Description: "South Bitfrost Field Exploration(1) completed" + Criteria: { + Achieve: 120092 + } + } + *4: { + Description: "Splendide Field Exploration(1) completed" + Criteria: { + Achieve: 120093 + } + } + *5: { + Description: "Splendide Field Exploration(2) completed" + Criteria: { + Achieve: 120094 + } + } + *6: { + Description: "Splendide Field Exploration(3) completed" + Criteria: { + Achieve: 120095 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129018 + Name: "Manuk Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Manuk Field Exploration(1) completed" + Criteria: { + Achieve: 120096 + } + } + *2: { + Description: "Manuk Field Exploration(2) completed" + Criteria: { + Achieve: 120097 + } + } + *3: { + Description: "Manuk Field Exploration(3) completed" + Criteria: { + Achieve: 120098 + } + } + *4: { + Description: "Outskirts of Kamidal Field Exploration(1) completed" + Criteria: { + Achieve: 120099 + } + } + *5: { + Description: "Outskirts of Kamidal Field Exploration(2) completed" + Criteria: { + Achieve: 120100 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129019 + Name: "Eclage Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Laphine Explorer" + Criteria: { + Achieve: 129017 + } + } + *2: { + Description: "Manuk Explorer" + Criteria: { + Achieve: 129018 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129020 + Name: "Localizing fields explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Amatsu Field Exploration(1) completed" + Criteria: { + Achieve: 120101 + } + } + *2: { + Description: "Kunlun Field Exploration(1) completed" + Criteria: { + Achieve: 120102 + } + } + *3: { + Description: "Gonryun Field Exploration(1) completed" + Criteria: { + Achieve: 120103 + } + } + *4: { + Description: "Ayothaya Field Exploration(1) completed" + Criteria: { + Achieve: 120104 + } + } + *5: { + Description: "Moscovia Field Exploration(1) completed" + Criteria: { + Achieve: 120105 + } + } + *6: { + Description: "Brasilis Field Exploration(1) completed" + Criteria: { + Achieve: 120106 + } + } + *7: { + Description: "Dewata Field Exploration(1) completed" + Criteria: { + Achieve: 120107 + } + } + *8: { + Description: "Malaya Field Exploration(1) completed" + Criteria: { + Achieve: 120108 + } + } + *9: { + Description: "Malaya Field Exploration(2) completed" + Criteria: { + Achieve: 120109 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 130000 + Name: "Socialite debut" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Visit Heine Royal Family and start conversation" + } + *2: { + Description: "Visit Nerius Royal Family and start conversation" + } + *3: { + Description: "Visit Walter Royal Family and start conversation" + } + *4: { + Description: "Visit Wigner Royal Family and start conversation" + } + *5: { + Description: "Visit Richard Royal Family and start conversation" + } + *6: { + Description: "Visit Gaebolg Royal Family and start conversation" + } + *7: { + Description: "Visit Lugenburg Royal Family and start conversation" + } + } + Rewards: { + TitleId: 1034 + } + Points: 10 +}, +{ + Id: 170000 + Name: "Song chamber is not an accident" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Listen the secret song of five recomended court musician" + } + } + Points: 10 +}, +{ + Id: 190000 + Name: "Alliance workers of merchant city" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Novice Amethyst class" + Goal: 1 + } + *2: { + Description: "Expert Sardonyx class" + Goal: 10 + } + *3: { + Description: "Competence Person Aquamarine class" + Goal: 30 + } + *4: { + Description: "Sparkling Diamond class" + Goal: 100 + } + } + Points: 50 +}, +{ + Id: 200000 + Name: "Acquire the first aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 99" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 99 + } + } + Rewards: { + TitleId: 1000 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + White_Slim_Pot_Box2: 1 + } + } + Points: 50 +}, +{ + Id: 200001 + Name: "Acquire the second aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 150" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 150 + } + } + Rewards: { + TitleId: 1001 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Dark_Snake_Lord_Hat: 1 + } + } + Points: 60 +}, +/* +{ + Id: 200002 + Name: "Acquire the third aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 175" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 175 + } + } + Rewards: { + TitleId: 1002 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Advanced_Jao_King_Hat: 1 + } + } + Points: 70 +}, +*/ +{ + Id: 200003 + Name: "Master Job level!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Job level 50" + Criteria: { + StatusType: "SP_JOBLEVEL" + } + Goal: 50 + } + } + Rewards: { + TitleId: 1003 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Old_Violet_Box: 1 + } + } + Points: 30 +}, +/* +{ + Id: 200004 + Name: "Grandmaster Job level!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Job level 70" + Criteria: { + StatusType: "SP_JOBLEVEL" + } + Goal: 70 + } + } + Rewards: { + TitleId: 1004 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Old_Card_Album_: 1 + } + } + Points: 50 +}, +*/ +{ + Id: 200005 + Name: "Official Adventurer" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to first class" + Criteria: { + JobId: [ "Job_Swordman", "Job_Mage", "Job_Archer", "Job_Acolyte", "Job_Merchant", "Job_Thief" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 10 +}, +{ + Id: 200006 + Name: "First step of job change!" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "First step of job change" + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 20 +}, +{ + Id: 200007 + Name: "Veteran Adventurer! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 2-1 classes" + Criteria: { + JobId: [ "Job_Knight", "Job_Priest", "Job_Wizard", "Job_Blacksmith", "Job_Hunter", "Job_Assassin" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 25 +}, +{ + Id: 200008 + Name: "Veteran Adventurer! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 2-2 classes" + Criteria: { + JobId: [ "Job_Crusader", "Job_Sage", "Job_Bard", "Job_Dancer", "Job_Alchemist", "Job_Rogue" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 25 +}, +{ + Id: 200009 + Name: "Warrior (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent 2-1 classes" + Criteria: { + JobId: [ "Job_Lord_Knight", "Job_High_Wizard", "Job_Sniper", "Job_High_Priest", "Job_Whitesmith", "Job_Assassin_Cross" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 30 +}, +{ + Id: 200010 + Name: "Warrior (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent 2-2 classes" + Criteria: { + JobId: [ "Job_Paladin", "Job_Professor", "Job_Clown", "Job_Gypsy", "Job_Champion", "Job_Creator", "Job_Stalker" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 30 +}, +{ + Id: 200011 + Name: "Elite Adventurer! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-1 classes" + Criteria: { + JobId: [ "Job_Rune_Knight", "Job_Warlock", "Job_Ranger", "Job_Arch_Bishop", "Job_Mechanic", "Job_Guillotine_Cross" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 50 +}, +{ + Id: 200012 + Name: "Transcendentaler! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-1 classes after transcendent" + Criteria: { + JobId: [ "Job_Rune_Knight_T", "Job_Warlock_T", "Job_Ranger_T", "Job_Arch_Bishop_T", "Job_Mechanic_T", "Job_Guillotine_Cross_T" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 60 +}, +{ + Id: 200013 + Name: "Elite Adventurer! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-2 classes" + Criteria: { + JobId: [ "Job_Royal_Guard", "Job_Sorcerer", "Job_Minstrel", "Job_Wanderer", "Job_Sura", "Job_Genetic", "Job_Shadow_Chaser" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 50 +}, +{ + Id: 200014 + Name: "Transcendentaler! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-2 classes after transcendent" + Criteria: { + JobId: [ "Job_Royal_Guard_T", "Job_Sorcerer_T", "Job_Minstrel_T", "Job_Wanderer_T", "Job_Sura_T", "Job_Genetic_T", "Job_Shadow_Chaser_T" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 60 +}, +{ + Id: 200015 + Name: "The way of exceptional character" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to expanded classes" + Criteria: { + JobId: [ "Job_Taekwon", "Job_Gunslinger", "Job_Ninja", "Job_Summoner" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 10 +}, +{ + Id: 200016 + Name: "This is My way!" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Expanded 2nd classes" + Criteria: { + JobId: [ "Job_Star_Gladiator", "Job_Soul_Linker", "Job_Rebellion", "Job_Kagerou", "Job_Oboro" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 20 +}, +{ + Id: 200017 + Name: "Bearish Power!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base STR 90º" + Criteria: { + StatusType: "SP_STR" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200018 + Name: "Overflowing Magic!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base INT 90" + Criteria: { + StatusType: "SP_INT" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200019 + Name: "Healthy Body and Mental Health!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base VIT 90" + Criteria: { + StatusType: "SP_VIT" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200020 + Name: "Speed of Light" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base AGI 90" + Criteria: { + StatusType: "SP_AGI" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200021 + Name: "Hawk Eyes" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base DEX 90" + Criteria: { + StatusType: "SP_DEX" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200022 + Name: "Maximum Luck" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base LUK 90" + Criteria: { + StatusType: "SP_LUK" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200023 + Name: "Dragonlike Power!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base STR 125" + Criteria: { + StatusType: "SP_STR" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" sc_start SC_GIANTGROWTH, 180000, 1; "> + } + Points: 20 +}, +{ + Id: 200024 + Name: "Magic Insanity" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base INT 125" + Criteria: { + StatusType: "SP_INT" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_HASTEUP, AREA, playerattached()); sc_start2 SC_MAGIC_CANDY, 60000, 30, 70; "> + } + Points: 20 +}, +{ + Id: 200025 + Name: "Rock Alloy" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base VIT 125" + Criteria: { + StatusType: "SP_VIT" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_HEAL3, AREA, playerattached()); sc_start2 SC_S_LIFEPOTION, 600000, -5, 5; "> + } + Points: 20 +}, +{ + Id: 200026 + Name: "Speed of Light" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base AGI 125" + Criteria: { + StatusType: "SP_AGI" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_STEAL, AREA, playerattached()); sc_start SC_PLUSAVOIDVALUE, 60000, 20; "> + } + Points: 20 +}, +{ + Id: 200027 + Name: "Falcon's Eyes" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base DEX 125" + Criteria: { + StatusType: "SP_DEX" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_MAGICALATTHIT, AREA, playerattached()); sc_start SC_CRITICALPERCENT, 300000, 30; "> + } + Points: 20 +}, +{ + Id: 200028 + Name: "Lucky Fever" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base LUK 125" + Criteria: { + StatusType: "SP_LUK" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_GLORIA, AREA, playerattached()); sc_start SC_GLORIA, 15000, 0; "> + } + Points: 20 +}, +{ + Id: 200029 + Name: "Incarnation of Love and Hate" + Type: "ACH_STATUS_BY_JOB" + Objectives: { + *1: { + Description: "Achieve 99 base level as Novice" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: "Job_Novice" + } + Goal: 99 + } + } + Rewards: { + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 30 +}, +{ + Id: 200030 + Name: "I really love it!" + Type: "ACH_STATUS_BY_JOBTYPE" + Objectives: { + *1: { + Description: "Achieve 99 base level as 1st classes" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: ["Job_Swordman", "Job_Mage", "Job_Archer", "Job_Acolyte", "Job_Merchant", "Job_Thief"] + } + Goal: 99 + } + } + Rewards: { + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start(SC_BLESSING, 30000, 1); "> + Items: { + Bubble_Gum_Box_10: 1 + } + } + Points: 30 +}, +{ + Id: 200031 + Name: "Reborn in Valhalla!" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent Novice" + Criteria: { + JobId: "Job_Novice_High" + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Special_Gift_Box: 1 + } + } + Points: 10 +}, +/*{ + Id: 200032 + Name: "Start of Another Adventure" + Type: "ACH_STATUS_BY_JOB" + Objectives: { + *1: { + Description: "Achieve Base Level 100" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: "" // Rebirth? + } + Goal: 100 + } + } + Rewards: { + Items: { + ID23585: 1 (Non-Existent Item) + } + } + Points: 10 +},*/ +{ + Id: 220000 + Name: "Community begin" + Type: "ACH_CHATROOM_CREATE" + Objectives: { + *1: { + Description: "Create a chatroom" + } + } + Points: 10 +}, +{ + Id: 220001 + Name: "A mouth only moment" + Type: "ACH_CHATROOM_CREATE_DEAD" + Objectives: { + *1: { + Description: "Create a chatroom when KO'ed" + } + } + Points: 10 +}, +{ + Id: 220002 + Name: "Admiring the chatter" + Type: "ACH_CHATROOM_MEMBERS" + Objectives: { + *1: { + Description: "Fill the chatroom with 20 people" + } + } + Points: 10 +}, +{ + Id: 220003 + Name: "My friend's friend~" + Type: "ACH_FRIEND_ADD" + Objectives: { + *1: { + Description: "Add 1 person as a friend" + } + } + Points: 10 +}, +{ + Id: 220004 + Name: "A competition of popularity" + Type: "ACH_FRIEND_ADD" + Objectives: { + *1: { + Description: "Add 10 person as a friend" + } + } + Points: 10 +}, +{ + Id: 220005 + Name: "Let's Party~" + Type: "ACH_PARTY_CREATE" + Objectives: { + *1: { + Description: "Create a party" + } + } + Points: 10 +}, +{ + Id: 220006 + Name: "Married with who..?" + Type: "ACH_MARRY" + Objectives: { + *1: { + Description: "Succeed on marriage" + } + } + Rewards: { + TitleId: 1022 + } + Points: 20 +}, +{ + Id: 220007 + Name: "Can you grow?" + Type: "ACH_ADOPT_PARENT" + Objectives: { + *1: { + Description: "Adopted by parents" + } + } + Rewards: { + TitleId: 1032 + } + Points: 20 +}, +{ + Id: 220008 + Name: "Being a parent" + Type: "ACH_ADOPT_BABY" + Objectives: { + *1: { + Description: "Adopt a child" + } + } + Rewards: { + TitleId: 1033 + } + Points: 20 +}, +{ + Id: 220009 + Name: "Activating the market economy (1)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 10000 zeny on vending merchant" + Goal: 10000 + } + } + Points: 10 +}, +{ + Id: 220010 + Name: "Activating the market economy (2)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 100000 zeny on vending merchant" + Goal: 1000000 + } + } + Points: 15 +}, +{ + Id: 220011 + Name: "Activating the market economy (3)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 500000 zeny on vending merchant" + Goal: 5000000 + } + } + Points: 20 +}, +{ + Id: 220012 + Name: "Activating the market economy (4)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 1.000000 zeny on vending merchant" + Goal: 100000000 + } + } + Points: 30 +}, +{ + Id: 220013 + Name: "Activating the market economy (5)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 5.000000 zeny on vending merchant" + Goal: 500000000 + } + } + Points: 50 +}, +{ + Id: 220014 + Name: "I can't quit from refining! (1)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 1 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 1 + } + Goal: 7 + } + } + Points: 10 +}, +{ + Id: 220015 + Name: "I can't quit from refining! (2)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 1 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 1 + } + Goal: 12 + } + } + Points: 15 +}, +{ + Id: 220016 + Name: "I can't quit from refining! (3)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 2 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 2 + } + Goal: 7 + } + } + Points: 10 +}, +{ + Id: 220017 + Name: "I can't quit from refining! (4)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 2 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 2 + } + Goal: 12 + } + } + Points: 15 +}, +{ + Id: 220018 + Name: "I can't quit from refining! (5)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 3 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 3 + } + Goal: 7 + } + } + Points: 15 +}, +{ + Id: 220019 + Name: "I can't quit from refining! (6)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 3 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 3 + } + Goal: 12 + } + } + Points: 20 +}, +{ + Id: 220020 + Name: "I can't quit from refining! (7)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 4 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 4 + } + Goal: 7 + } + } + Points: 20 +}, +{ + Id: 220021 + Name: "I can't quit from refining! (8)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 4 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 4 + } + Goal: 12 + } + } + Points: 30 +}, +{ + Id: 220022 + Name: "Human's greed has no ending.." + Type: "ACH_EQUIP_REFINE_FAILURE_TOTAL" + Objectives: { + *1: { + Description: "Experience the fail of refining" + Criteria: { + ItemType: ["IT_WEAPON", "IT_ARMOR"] + } + } + } + Points: 10 +}, +{ + Id: 220023 + Name: "I found it! (1)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 100 zeny" + Goal: 100 + } + } + Points: 10 +}, +{ + Id: 220024 + Name: "I found it! (2)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 1000 zeny" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 220025 + Name: "I found it! (3)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 5000 zeny" + Goal: 5000 + } + } + Points: 15 +}, +{ + Id: 220026 + Name: "I found it! (4)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 10000 zeny" + Goal: 10000 + } + } + Points: 15 +}, +{ + Id: 220027 + Name: "I found it! (5)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 50000 zeny" + Goal: 50000 + } + } + Points: 20 +}, +{ + Id: 220028 + Name: "I found it! (6)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 100000 zeny" + Goal: 100000 + } + } + Points: 20 +}, +{ + Id: 220029 + Name: "I found it! (7)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 150000 zeny" + Goal: 150000 + } + } + Points: 30 +}, +{ + Id: 220030 + Name: "Rich King (1)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 10000 zeny in pocket" + Goal: 10000 + } + } + Points: 10 +}, +{ + Id: 220031 + Name: "Rich King (2)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 100000 zeny in pocket" + Goal: 100000 + } + } + Points: 15 +}, +{ + Id: 220032 + Name: "Rich King (3)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 1000000 zeny in pocket" + Goal: 1000000 + } + } + Points: 20 +}, +{ + Id: 220033 + Name: "Rich King (4)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 10000000 zeny in pocket" + Goal: 10000000 + } + } + Points: 25 +}, +{ + Id: 220034 + Name: "Rich King (5)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 100000000 zeny in pocket" + Goal: 100000000 + } + } + Points: 30 +}, +{ + Id: 220035 + Name: "Rich King (6)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 1000000000 zeny in pocket" + Goal: 1000000000 + } + } + Points: 40 +}, +{ + Id: 230101 + Name: "Poring - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poring" + Criteria: { + MobId: "PORING" + } + } + } + Points: 10 +}, +{ + Id: 230102 + Name: "Drops - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Drops" + Criteria: { + MobId: "DROPS" + } + } + } + Points: 10 +}, +{ + Id: 230103 + Name: "Poporing - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poporing" + Criteria: { + MobId: "POPORING" + } + } + } + Points: 10 +}, +/* +{ + Id: 230104 + Name: "Novice Poring - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Novice Poring" + Criteria: { + MobId: "LITTLE_PORING" + } + } + } + Points: 10 +}, +*/ +{ + Id: 230111 + Name: "Chonchon - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Chonchon" + Criteria: { + MobId: "CHONCHON" + } + } + } + Points: 10 +}, +{ + Id: 230112 + Name: "Steel Chonchon - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Steel Chonchon" + Criteria: { + MobId: "STEEL_CHONCHON" + } + } + } + Points: 10 +}, +{ + Id: 230113 + Name: "Hunter Fly - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Hunter Fly" + Criteria: { + MobId: "HUNTER_FLY" + } + } + } + Points: 10 +}, +{ + Id: 230114 + Name: "Rocker - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Rocker" + Criteria: { + MobId: "ROCKER" + } + } + } + Points: 10 +}, +{ + Id: 230115 + Name: "Spore - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Spore" + Criteria: { + MobId: "SPORE" + } + } + } + Points: 10 +}, +{ + Id: 230116 + Name: "Poison Spore - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poison Spore" + Criteria: { + MobId: "POISON_SPORE" + } + } + } + Points: 10 +}, +{ + Id: 230121 + Name: "Lunatic - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Lunatic" + Criteria: { + MobId: "LUNATIC" + } + } + } + Points: 10 +}, +{ + Id: 230122 + Name: "Picky - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Picky" + Criteria: { + MobId: "PICKY" + } + } + } + Points: 10 +}, +{ + Id: 230123 + Name: "Savage Bebe - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Savage Bebe" + Criteria: { + MobId: "SAVAGE_BABE" + } + } + } + Points: 10 +}, +/* +{ + Id: 230124 + Name: "Baby Desert Wolf - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Baby Desert Wolf" + Criteria: { + MobId: "M_DESERT_WOLF_B" + } + } + } + Points: 10 +}, +*/ +{ + Id: 230125 + Name: "Smokie - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Smokie" + Criteria: { + MobId: "SMOKIE" + } + } + } + Points: 10 +}, +{ + Id: 230126 + Name: "Yoyo - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Yoyo" + Criteria: { + MobId: "YOYO" + } + } + } + Points: 10 +}, +{ + Id: 230127 + Name: "Peco Peco - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Peco Peco" + Criteria: { + MobId: "PECOPECO" + } + } + } + Points: 10 +}, +{ + Id: 230128 + Name: "Petite - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Petite" + Criteria: { + MobId: "PETIT" + } + } + } + Points: 10 +}, +{ + Id: 230141 + Name: "Munak - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Munak" + Criteria: { + MobId: "MUNAK" + } + } + } + Points: 10 +}, +{ + Id: 230142 + Name: "Isis - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Isis" + Criteria: { + MobId: "ISIS" + } + } + } + Points: 10 +}, +{ + Id: 230143 + Name: "Sohee - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Sohee" + Criteria: { + MobId: "SOHEE" + } + } + } + Points: 10 +}, +{ + Id: 230144 + Name: "Zherlthsh - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Zherlthsh" + Criteria: { + MobId: "ZHERLTHSH" + } + } + } + Points: 10 +}, +{ + Id: 230145 + Name: "Alice - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Alice" + Criteria: { + MobId: "ALICE" + } + } + } + Points: 10 +}, +{ + Id: 230146 + Name: "Succubus - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Succubus" + Criteria: { + MobId: "SUCCUBUS" + } + } + } + Points: 10 +}, +{ + Id: 230147 + Name: "Loli Ruri - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Loli Ruri" + Criteria: { + MobId: "LOLI_RURI" + } + } + } + Points: 10 +}, +{ + Id: 230201 + Name: "Exploring Poring's life (1)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 10 Poring" + Criteria: { + MobId: "PORING" + } + Goal: 10 + } + *2: { + Description: "Hunt 10 Drops" + Criteria: { + MobId: "DROPS" + } + Goal: 10 + } + *3: { + Description: "Hunt 10 Poporing" + Criteria: { + MobId: "POPORING" + } + Goal: 10 + } + *4: { + Description: "Hunt 10 Marin" + Criteria: { + MobId: "MARIN" + } + Goal: 10 + } +/* Renewal Monster + *5: { + Description: "Hunt 10 Novice Poring" + Criteria: { + MobId: "LITTLE_PORING" + } + Goal: 10 + } +*/ + } + Points: 10 +}, + +{ + Id: 230202 + Name: "Exploring Poring's life (2)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 1 Mastering" + Criteria: { + MobId: "MASTERING" + } + Goal: 1 + } + *2: { + Description: "Hunt 1 Devilring" + Criteria: { + MobId: "DEVILING" + } + Goal: 1 + } + *3: { + Description: "Hunt 1 Angelring" + Criteria: { + MobId: "ANGELING" + } + Goal: 1 + } + *4: { + Description: "Hunt 1 Arch Angelring" + Criteria: { + MobId: "ARCHANGELING" + } + Goal: 1 + } + *5: { + Description: "Hunt 1 Ghostring" + Criteria: { + MobId: "GHOSTRING" + } + Goal: 1 + } + } + Points: 15 +}, +{ + Id: 230203 + Name: "Exploring Poring's life (3)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 5 Metalring" + Criteria: { + MobId: "METALING" + } + Goal: 5 + } + *2: { + Description: "Hunt 5 Heavy Metalring" + Criteria: { + MobId: "METALING" + } + Goal: 5 + } + *3: { + Description: "Hunt 5 Magmaring" + Criteria: { + MobId: "MAGMARING" + } + Goal: 5 + } + } + Points: 20 +}, +{ + Id: 240000 + Name: "Complete challenges after first introduction" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Complete challenges after first introduction" + } + } + Points: 10 +}, +{ + Id: 240001 + Name: "Achieve Level 1" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 1" + Goal: 1 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240002 + Name: "Achieve Level 2" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 2" + Goal: 2 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240003 + Name: "Achieve Level 3" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 3" + Goal: 3 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240004 + Name: "Achieve Level 4" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 4" + Goal: 4 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240005 + Name: "Achieve Level 5" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 5" + Goal: 5 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240006 + Name: "Achieve Level 6" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 6" + Goal: 6 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240007 + Name: "Achieve Level 7" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 7" + Goal: 7 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240008 + Name: "Achieve Level 8" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 8" + Goal: 8 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240009 + Name: "Achieve Level 9" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 9" + Goal: 9 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240010 + Name: "Achieve Level 10" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 10" + Goal: 10 + } + } + Rewards: { + TitleId: 1023 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240011 + Name: "Achieve Level 11" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 11" + Goal: 11 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240012 + Name: "Achieve Level 12" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 12" + Goal: 12 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240013 + Name: "Achieve Level 13" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 13" + Goal: 13 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240014 + Name: "Achieve Level 14" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 14" + Goal: 14 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240015 + Name: "Achieve Level 15" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 15" + Goal: 15 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240016 + Name: "Achieve Level 16" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 16" + Goal: 16 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240017 + Name: "Achieve Level 17" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 17" + Goal: 17 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240018 + Name: "Achieve Level 18" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 18" + Goal: 18 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240019 + Name: "Achieve Level 19" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 19" + Goal: 19 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240020 + Name: "Achieve Level 20" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 20" + Goal: 20 + } + } + Rewards: { + TitleId: 1024 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +/***************************************************************** + Postlude + Entries with criteria that must be read before itself. + *****************************************************************/ +{ + Id: 230100 + Name: "Poring is Love" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Poring - taming' challenge" + Criteria: { + Achieve: 230101 + } + } + *2: { + Description: "Complete 'Drops - taming' challenge" + Criteria: { + Achieve: 230102 + } + } + *3: { + Description: "Complete 'Poporing - taming' challenge" + Criteria: { + Achieve: 230103 + } + } +/* Renewal Monster + *4: { + Description: "Complete 'Novice Poring - taming' challenge" + Criteria: { + Achieve: 230104 + } + } +*/ + } + Rewards: { + TitleId: 1025 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230110 + Name: "Entomologist" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Chonchon - taming' challenge" + Criteria: { + Achieve: 230111 + } + } + *2: { + Description: "Complete 'Steel Chonchon - taming' challenge" + Criteria: { + Achieve: 230112 + } + } + *3: { + Description: "Complete 'Hunter Fly - taming' challenge" + Criteria: { + Achieve: 230113 + } + } + *4: { + Description: "Complete 'Rocker - taming' challenge" + Criteria: { + Achieve: 230114 + } + } + *5: { + Description: "Complete 'Spore - taming' challenge" + Criteria: { + Achieve: 230115 + } + } + *6: { + Description: "Complete 'Poison Spore - taming' challenge" + Criteria: { + Achieve: 230116 + } + } + } + Rewards: { + TitleId: 1026 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230120 + Name: "Animals are also our friend" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Lunatic - taming' challenge" + Criteria: { + Achieve: 230121 + } + } + *2: { + Description: "Complete 'Picky - taming' challenge" + Criteria: { + Achieve: 230122 + } + } + *3: { + Description: "Complete 'Savage Bebe - taming' challenge" + Criteria: { + Achieve: 230123 + } + } + *4: { + Description: "Complete 'Smokie - taming' challenge" + Criteria: { + Achieve: 230125 + } + } + *5: { + Description: "Complete 'Yoyo - taming' challenge" + Criteria: { + Achieve: 230126 + } + } + *6: { + Description: "Complete 'Peco Peco - taming' challenge" + Criteria: { + Achieve: 230127 + } + } + *7: { + Description: "Complete 'Petite - taming' challenge" + Criteria: { + Achieve: 230128 + } + } +/* Renewal Monster + *8: { + Description: "Complete 'Baby Desert Wolf - taming' challenge" + Criteria: { + Achieve: 230124 + } + } +*/ + } + Rewards: { + TitleId: 1027 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230140 + Name: "Monster Girls Unite!!" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Munak - taming' challenge" + Criteria: { + Achieve: 230141 + } + } + *2: { + Description: "Complete 'Isis - taming' challenge" + Criteria: { + Achieve: 230142 + } + } + *3: { + Description: "Complete 'Sohee - taming' challenge" + Criteria: { + Achieve: 230143 + } + } + *4: { + Description: "Complete 'Zherlthsh - taming' challenge" + Criteria: { + Achieve: 230144 + } + } + *5: { + Description: "Complete 'Alice - taming' challenge" + Criteria: { + Achieve: 230145 + } + } + *6: { + Description: "Complete 'Succubus - taming' challenge" + Criteria: { + Achieve: 230146 + } + } + *7: { + Description: "Complete 'Loli Ruri - taming' challenge" + Criteria: { + Achieve: 230147 + } + } + } + Rewards: { + TitleId: 1029 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230200 + Name: "Poring seeker" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Exploring Poring's life (1)' challenge" + Criteria: { + Achieve: 230201 + } + } + *2: { + Description: "Complete 'Exploring Poring's life (2)' challenge" + Criteria: { + Achieve: 230202 + } + } + *3: { + Description: "Complete 'Exploring Poring's life (3)' challenge" + Criteria: { + Achieve: 230203 + } + } + } + Points: 10 +}, +) diff --git a/db/pre-re/item_db.conf b/db/pre-re/item_db.conf index 55ddcbd3b..3f3b6d622 100644 --- a/db/pre-re/item_db.conf +++ b/db/pre-re/item_db.conf @@ -93846,6 +93846,24 @@ item_db: ( AegisName: "FRed_Wing_Hat_Box" }, */ +{ + Id: 16483 + AegisName: "Abrasive_Box_10" + Name: "Abrasive Box (10)" + Type: "IT_USABLE" + Buy: 20 + Weight: 10 + Script: <" getitem(E_Abrasive, 10); "> +}, +{ + Id: 16504 + AegisName: "Bubble_Gum_Box_10" + Name: "Bubble Gum Box(10)" + Type: "IT_CASH" + Buy: 10 + Weight: 10 + Script: <" getitem(Bubble_Gum, 10); "> +}, /* Id: 16543 AegisName: "Snowman_Hat_Box" @@ -94588,6 +94606,14 @@ item_db: ( "> }, { + Id: 22808 + AegisName: "Special_Gift_Box" + Name: "Special Gift Box" + Type: "IT_USABLE" + Buy: 10 + Weight: 100 +}, +{ Id: 22837 AegisName: "Integer_Time" Name: "Integer Time" @@ -94605,4 +94631,11 @@ item_db: ( } Script: <" TmpRouletteBronze += 1; "> }, +{ + Id: 22876 + AegisName: "Old_Money_Pocket" + Name: "Old Money Pocket" + Type: "IT_USABLE" + Script: <" Zeny += rand(500, 550); "> +}, ) diff --git a/db/re/achievement_db.conf b/db/re/achievement_db.conf new file mode 100644 index 000000000..e54a1d924 --- /dev/null +++ b/db/re/achievement_db.conf @@ -0,0 +1,6769 @@ +//================= Hercules Database ===================================== +//= _ _ _ +//= | | | | | | +//= | |_| | ___ _ __ ___ _ _| | ___ ___ +//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __| +//= | | | | __/ | | (__| |_| | | __/\__ \ +//= \_| |_/\___|_| \___|\__,_|_|\___||___/ +//================= License =============================================== +//= This file is part of Hercules. +//= http://herc.ws - http://github.com/HerculesWS/Hercules +//= +//= Copyright (C) 2018 Hercules Dev Team +//= +//= Hercules is free software: you can redistribute it and/or modify +//= it under the terms of the GNU General Public License as published by +//= the Free Software Foundation, either version 3 of the License, or +//= (at your option) any later version. +//= +//= This program is distributed in the hope that it will be useful, +//= but WITHOUT ANY WARRANTY; without even the implied warranty of +//= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//= GNU General Public License for more details. +//= +//= You should have received a copy of the GNU General Public License +//= along with this program. If not, see <http://www.gnu.org/licenses/>. +//========================================================================= +//= Achievement Database +//========================================================================= + +achievement_db: ( +/***************************************************************** + * Entry Structure + ***************************************************************** +{ + Id: (int) Unique ID of the achievement representing it's client-side equivalent. + Name: (string) Name/Title of the Achievement. + Type: (string) Validation type for the achievement. + [Type] [Validation Description] (Trigger) + ACH_QUEST: Specific achievement objective update (Script). + ACH_KILL_PC_TOTAL: (Accumulative) Total kill count. (Player kill) + ACH_KILL_PC_JOB: Kill a player of the specified job. (Player Kill) + ACH_KILL_PC_JOBTYPE: Kill a player of the specified job type. (Player Kill) + ACH_KILL_MOB_CLASS: Kill a particular mob class. (Mob Kill) + ACH_DAMAGE_PC_MAX: Maximum damage caused on a player. (Player Damage) + ACH_DAMAGE_PC_TOTAL: (Accumulative) Damage on players. (Player Damage) + ACH_DAMAGE_PC_REC_MAX: Maximum damage received by a player. (Receive Player Damage) + ACH_DAMAGE_PC_REC_TOTAL: (Accumulative) Damage received by players. (Receive Player Damage) + ACH_DAMAGE_MOB_MAX: Maximum damage caused on a monster. (Monster Damage) + ACH_DAMAGE_MOB_TOTAL: (Accumulative) Damage caused on monsters. (Monster Damage) + ACH_DAMAGE_MOB_REC_MAX: Maximum damage received by a monster. (Receive Monster Damage) + ACH_DAMAGE_MOB_REC_TOTAL: (Accumulative) Damage received by monsters. (Receive Monster Damage) + ACH_JOB_CHANGE: Change to a specific job. (Job Change) + ACH_STATUS: Acquire a specific amount of a particular status type. (Stat Change) + ACH_STATUS_BY_JOB: Acquire a specific amount of a status type as a job class. (Stat Change) + ACH_STATUS_BY_JOBTYPE: Acquire a specific amount of a status type as a job type. (Stat Change) + ACH_CHATROOM_CREATE_DEAD: (Accumulative) Create a chatroom when dead. (Chatroom Creation) + ACH_CHATROOM_CREATE: (Accumulative) Create a chatroom. (Chatroom Creation) + ACH_CHATROOM_MEMBERS: Gather 'n' members in a chatroom. (Chatroom Join) + ACH_FRIEND_ADD: Add a specific number of friends. (Friend Addition) + ACH_PARTY_CREATE: (Accumulative) Create a specific number of parties. (Party Creation) + ACH_PARTY_JOIN: (Accumulative) Join a specific number of parties. (Party Join) + ACH_MARRY: (Accumulative) Marry a specified number of times. (Marriage) + ACH_ADOPT_BABY: (Accumulative) Get Adopted. (Adoption) + ACH_ADOPT_PARENT: (Accumulative) Adopt a Baby. (Adoption) + ACH_ZENY_HOLD: Hold a specific amount of zeny in your inventory. (Gain Zeny) + ACH_ZENY_GET_ONCE: Gain a specific amount of zeny in one transaction. (Gain Zeny) + ACH_ZENY_GET_TOTAL: (Accumulative) Gain a specific amount of zeny in total. (Gain Zeny) + ACH_ZENY_SPEND_ONCE: Spend a specific amount of zeny in one transaction. (Pay Zeny) + ACH_ZENY_SPEND_TOTAL: (Accumulative) Spend a specific amount of zeny in total. (Pay Zeny) + ACH_EQUIP_REFINE_SUCCESS: Refine an item to +N. (Successful Refine) + ACH_EQUIP_REFINE_FAILURE: Fail to refine an item of +N refine. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_TOTAL: (Accumulative) Refine an item successfully N times. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_TOTAL: (Accumulative) Fail to refine an item N times. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_WLV: Refine a Weapon of a particular Level to +N. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_WLV: Fail to refine a Weapon of a particular level from +N. (Failed Refine) + ACH_EQUIP_REFINE_SUCCESS_ID: Refine a particular Item successfully to +N. (Success Refine) + ACH_EQUIP_REFINE_FAILURE_ID: Fail to refine a particular item successfully from +N. (Failed Refine) + ACH_ITEM_GET_COUNT: Acquire N amount of an item of a particular ID. (Acquire Item) + ACH_ITEM_GET_COUNT_ITEMTYPE: Acquire N amount of items of a particular type mask. (Acquire Item) + ACH_ITEM_GET_WORTH: Acquire an item of buy value N. (Acquire Item) + ACH_ITEM_SELL_WORTH: Sell an item of sell value N. (NPC Sell Item) + ACH_PET_CREATE: Successfully tame a pet of a particular mob class. (Successful Pet Tame) + ACH_ACHIEVE: Achieve an Achievement. (Achievement Completion) + ACH_ACHIEVEMENT_RANK: Achieve an Achievement Rank. (Achievement Rank Increase) + Objectives: { [Mandatory Field] Objectives of an achievement. Up to 10 objectives per achievement. + To comply with the client's order of objectives, this list must be in order. + *1: { + Description: (string) [Mandatory Field] Description of a particular objective. + Criteria: { This is a field for achievements whose objectives must meet + certain criteria before evaluating the player's progress for it. + MobId: (mixed) MonsterId required for an objective. + For types such as ACH_KILL_MOB_CLASS and ACH_PET_CREATE. Can be either int or string constant. + JobId: (mixed) Array or Single entry of JobIds. + For types - ACH_KILL_PC_JOBTYPE, ACH_JOB_CHANGE or ACH_STATUS_BY_JOBTYPE. + Can be either a numeric or string constant. + ItemId: (mixed) ItemId required for an objective. + For Types such as ACH_ITEM_GET_COUNT. Can be either int or string constant. + StatusType: (mixed) Status Type required for an objective. + For Types such as ACH_STATUS, ACH_STATUS_BY_JOB, ACH_STATUS_BY_JOBTYPE. + Types - + "SP_STR" - Strength + "SP_AGI" - Agility + "SP_VIT" - Vitality + "SP_INT" - Intelligence + "SP_DEX" - Dexterity + "SP_LUK" - Luck + "SP_BASELEVEL" - Base Level + "SP_JOBLEVEL" - Job Level + Can be either int or string constant. + ItemType: (mixed) Item type that is required for this achievement. + For Types such as ACH_ITEM_GET_COUNT_ITEMTYPE. + Can be either int, string or list. + Refer "Item types" Constants from constants.conf + WeaponLevel: (int) Weapon Level that is required for this achievement. (Eg. 0, 1, 2, 3 or 4). + For Types such as ACH_EQUIP_REFINE_SUCCESS_WLV and ACH_EQUIP_REFINE_FAILURE_WLV. + Achieve: (int) AchievementID to be achieved. + For Type - ACH_ACHIEVE. + } + Goal: (int) Target amount to be met for the completion of the objective. Default is 1. + } + ... + *10: {...} + } + Rewards: { + Bonus: <""> (script) Script code bonus to be given as a reward for an achievement. + Items: { Item rewards per achievement. With a maximum defined in mmo.h as MAX_ACHEIVEMENT_ITEM_REWARDS. + Apple: 1 (int) Item ID (int or string constant) : Amount (int) + } + TitleId: (int) ID of the Title (from the Title System) awarded. + } + Points: (int) Points per achievement given on it's successful completion. +} + *****************************************************************/ +{ + Id: 110000 + Name: "At this time I live to eat" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Bring the food to Bigfoot at this hour" + } + } + Points: 10 +}, +{ + Id: 110001 + Name: "A fan of this polarity" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Lady of the house" + } + *2: { + Description: "Beautiful fine anak" + } + *3: { + Description: "Mistress in ambience" + } + *4: { + Description: "Beautiful wife and children" + } + *5: { + Description: "Down town Bachelor" + } + *6: { + Description: "Great act ability anak" + } + *7: { + Description: "Great attack power bachelor" + } + } + Points: 10 +}, +{ + Id: 120001 + Name: "North Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120002 + Name: "North Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120003 + Name: "North Prontera Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120004 + Name: "West Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding west prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120005 + Name: "West Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding west prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120006 + Name: "East Prontera Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120007 + Name: "South Prontera Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120008 + Name: "South Prontera Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120009 + Name: "South Prontera Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120010 + Name: "South Prontera Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south prontera field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120011 + Name: "East Geffen Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120012 + Name: "Southeast Geffen Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southeast geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120013 + Name: "Northwest Geffen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120014 + Name: "Northwest Geffen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120015 + Name: "Northwest Geffen Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding northwest geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120016 + Name: "South Geffen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120017 + Name: "South Geffen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south geffen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120018 + Name: "Sograt Desert Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120019 + Name: "Sograt Desert Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120020 + Name: "Sograt Desert Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120021 + Name: "Sograt Desert Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120022 + Name: "Sograt Desert Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120023 + Name: "Sograt Desert Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sograt desert field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120024 + Name: "Southwest Payon Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120025 + Name: "Southwest Payon Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120026 + Name: "Southwest Payon Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120027 + Name: "Southwest Payon Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding southwest payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120028 + Name: "East Payon Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120029 + Name: "East Payon Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120030 + Name: "East Payon Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120031 + Name: "East Payon Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding east payon field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120032 + Name: "North Mjolnir Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120033 + Name: "North Mjolnir Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120034 + Name: "North Mjolnir Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120035 + Name: "North Mjolnir Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120036 + Name: "North Mjolnir Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120037 + Name: "South Mjolnir Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120038 + Name: "South Mjolnir Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120039 + Name: "South Mjolnir Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120040 + Name: "South Mjolnir Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120041 + Name: "South Mjolnir Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120042 + Name: "South Mjolnir Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south mjolnir field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120043 + Name: "South Aldebaran Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south aldebaran field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120044 + Name: "Comodo Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120045 + Name: "Comodo Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120046 + Name: "Comodo Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120047 + Name: "Comodo Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120048 + Name: "Comodo Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120049 + Name: "Comodo Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120050 + Name: "Comodo Field Exploration(7)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120051 + Name: "Comodo Field Exploration(8)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120052 + Name: "Border Checkpoint Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding border checkpoint field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120053 + Name: "Border Checkpoint Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding border checkpoint field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120054 + Name: "Kiel Hyre Mansion Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kiel hyre mansion field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120055 + Name: "El Mes Plateau Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120056 + Name: "El Mes Plateau Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120057 + Name: "El Mes Plateau Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes plateau field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120058 + Name: "El Mes Gorge Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding el mes gorge field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120059 + Name: "Kiel Hyre Academy Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kiel hyre academy field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120060 + Name: "Guard Camp Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding guard camp field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120061 + Name: "Yuno Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding yuno field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120062 + Name: "Front of Thanatos Tower Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding front of thanatos tower field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120063 + Name: "Hugel Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120064 + Name: "Hugel Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120065 + Name: "Hugel Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding hugel field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120066 + Name: "Abyss Lake Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abyss lake field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120067 + Name: "Einbroch Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120068 + Name: "Einbroch Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120069 + Name: "Einbroch Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120070 + Name: "Einbroch Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120071 + Name: "Einbroch Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120072 + Name: "Einbroch Field Exploration(6)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120073 + Name: "Einbroch Field Exploration(7)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120074 + Name: "Einbroch Field Exploration(8)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Einbroch field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120075 + Name: "Lighthalzen Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120076 + Name: "Lighthalzen Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120077 + Name: "Lighthalzen Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding lighthalzen field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120078 + Name: "Rachel Audhumbla Plains Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120079 + Name: "Rachel Plains Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120080 + Name: "Rachel Plains Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120081 + Name: "Rachel Plains Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel plains field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120082 + Name: "Rachel Audhumbla Grassland Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla grassland field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120083 + Name: "Rachel Audhumbla Grassland Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel audhumbla grassland field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120084 + Name: "Portus Luna Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding portus luna field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120085 + Name: "Veins Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120086 + Name: "Veins Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120087 + Name: "Veins Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120088 + Name: "Veins Field Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120089 + Name: "Veins Field Exploration(5)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding veins field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120090 + Name: "Eclage Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding eclage field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120091 + Name: "North Bitfrost Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding north bitfrost field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120092 + Name: "South Bitfrost Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding south bitfrost field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120093 + Name: "Splendide Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120094 + Name: "Splendide Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120095 + Name: "Splendide Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding splendide field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120096 + Name: "Manuk Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120097 + Name: "Manuk Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120098 + Name: "Manuk Field Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding manuk field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120099 + Name: "Outskirts of Kamidal Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding outskirts of kamidal field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120100 + Name: "Outskirts of Kamidal Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding outskirts of kamidal field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120101 + Name: "Amatsu Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding amatsu field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120102 + Name: "Kunlun Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding kunlun field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120103 + Name: "Gonryun Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding gonryun field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120104 + Name: "Ayothaya Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ayothaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120105 + Name: "Moscovia Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding moscovia field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120106 + Name: "Brasilis Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding brasilis field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120107 + Name: "Dewata Field Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding dewata field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120108 + Name: "Malaya Field Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding malaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120109 + Name: "Malaya Field Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding malaya field" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 10 +}, +{ + Id: 120110 + Name: "Abbey Underground Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abbey underground dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120111 + Name: "Abyss Lake Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding abyss lake dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120112 + Name: "Clock Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding clock tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120113 + Name: "Amatsu Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding amatsu dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120114 + Name: "Ant Hell Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ant hell dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120115 + Name: "Ayothaya Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding ayothaya dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120116 + Name: "Comodo Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding comodo dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120117 + Name: "Brasilis Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding brasilis dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120118 + Name: "Clock Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding clock tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120119 + Name: "Istana Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding istana dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120120 + Name: "Scaraba Hole Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding scaraba hole dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120121 + Name: "Bitfrost Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding bitfrost dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120122 + Name: "Einbroch Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding einbroch dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120123 + Name: "Geffen Underground Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding geffen underground dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120124 + Name: "Glastheim Dungeon Exploration(1)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120125 + Name: "Glastheim Dungeon Exploration(2)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120126 + Name: "Glastheim Dungeon Exploration(3)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120127 + Name: "Glastheim Dungeon Exploration(4)" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding glastheim dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120128 + Name: "Kunlun Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Kunlun dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120129 + Name: "Rachel Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding rachel dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120130 + Name: "Sphinx Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sphinx dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120131 + Name: "Izlude Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding izlude dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120132 + Name: "Robot Factory Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding robot factory dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120133 + Name: "Bio Lab Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding bio lab dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120134 + Name: "Gonryun Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding gonryun dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120135 + Name: "Nogg Road Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding nogg road dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120136 + Name: "Coal Mine Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding Coal mine dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120137 + Name: "Pyramid Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding pyramid dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120138 + Name: "Orc Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding orc dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120139 + Name: "Payon Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding payon dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120140 + Name: "Labyrinth Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding labyrinth dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120141 + Name: "Undersea Tunnel Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding undersea tunnel dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120142 + Name: "Thanatos Tower Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding thanatos tower dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120143 + Name: "Thor Volcano Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding thor volcano dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120144 + Name: "Sunken Ship Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding sunken ship dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120145 + Name: "Turtle Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding turtle dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 120146 + Name: "Toy Factory Dungeon Exploration" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Discover the treasure surrounding toy factory dungeon" + } + } + Rewards: { + Items: { + Old_Money_Pocket: 1 + } + } + Points: 20 +}, +{ + Id: 127001 + Name: "Prontera Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127002 + Name: "Geffen Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127003 + Name: "Morocc Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127004 + Name: "Payon Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127005 + Name: "Yuno Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127006 + Name: "Lighthalzen Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127007 + Name: "Einbroch Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127008 + Name: "Rachel Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 127009 + Name: "Veins Contribution" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Stranger" + Goal: 3000 + } + *2: { + Description: "Village Freshman" + Goal: 6000 + } + *3: { + Description: "Neighbor" + Goal: 12000 + } + *4: { + Description: "Celebrity" + Goal: 20000 + } + *5: { + Description: "City Dictator" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 128000 + Name: "Uninvited Guest" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Celine Kimmy" + Criteria: { + MobId: "XM_CELINE_KIMI" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128001 + Name: "Strange Guest" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Celine Kimmy" + Criteria: { + MobId: "XM_CELINE_KIMI" + } + Goal: 10 + } + } + Points: 10 +}, +{ + Id: 128002 + Name: "Get along with map..." + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 25 Celine Kimmy" + Criteria: { + MobId: "XM_CELINE_KIMI" + } + Goal: 25 + } + } + Points: 20 +}, +{ + Id: 128003 + Name: "Welcomed Guest" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Celine Kimmy" + Criteria: { + MobId: "XM_CELINE_KIMI" + } + Goal: 50 + } + } + Points: 30 +}, +{ + Id: 128004 + Name: "Kimmy's best friend" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 100 Celine Kimmy" + Criteria: { + MobId: "XM_CELINE_KIMI" + } + Goal: 100 + } + } + Points: 50 +}, +{ + Id: 128005 + Name: "Novice Angler" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Bakonawa" + Criteria: { + MobId: "BAKONAWA_1" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128006 + Name: "Juicy Hunter" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Bakonawa" + Criteria: { + MobId: "BAKONAWA_1" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128007 + Name: "Rhythm Master" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Bakonawa" + Criteria: { + MobId: "BAKONAWA_1" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128008 + Name: "Bold Adventurer" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128009 + Name: "Baphomet Hatred" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128010 + Name: "Goat's Nemesis" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Baphomet" + Criteria: { + MobId: "BAPHOMET" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128011 + Name: "Ordinary Tourist" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Grim Reaper Ankou" + Criteria: { + MobId: "GRIM_REAPER_ANKOU" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128012 + Name: "Backcountry Expert" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Grim Reaper Ankou" + Criteria: { + MobId: "GRIM_REAPER_ANKOU" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128013 + Name: "Able to eat more like this" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Grim Reaper Ankou" + Criteria: { + MobId: "GRIM_REAPER_ANKOU" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128014 + Name: "Digest hard meat" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Buwaya" + Criteria: { + MobId: "BUWAYA" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128015 + Name: "Master of Escape" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Buwaya" + Criteria: { + MobId: "BUWAYA" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128016 + Name: "Immortal Hunter" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Buwaya" + Criteria: { + MobId: "BUWAYA" + } + Goal: 50 + } + } + Points: 50 +}, +/*{ + Id: 128017 + Name: "Stood up and overcame despair" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 God of Despair Morocc" + Criteria: { + MobId: "MM_MOROCC_ADT" + } + Goal: 1 + } + } + Points: 10 +},*/ +/*{ + Id: 128018 + Name: "Ember of Hope" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 God of Despair Morocc" + Criteria: { + MobId: "MM_MOROCC_ADT" + } + Goal: 10 + } + } + Points: 10 +},*/ +/*{ + Id: 128019 + Name: "Pouring Aurora" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 25 God of Despair Morocc" + Criteria: { + MobId: "MM_MOROCC_ADT" + } + Goal: 25 + } + } + Points: 20 +},*/ +/*{ + Id: 128020 + Name: "Who is desperate? I am hopeless!" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 God of Despair Morocc" + Criteria: { + MobId: "MM_MOROCC_ADT" + } + Goal: 50 + } + } + Points: 30 +},*/ +/*{ + Id: 128021 + Name: "I know god will save the world" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 100 God of Despair Morocc" + Criteria: { + MobId: "MM_MOROCC_ADT" + } + Goal: 100 + } + } + Points: 50 +},*/ +/*{ + Id: 128022 + Name: "There was mercy in Morocc army" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Morocc necromancer" + Criteria: { + MobId: "EP14_MORS_BOSSB" + } + Goal: 1 + } + } + Points: 10 +},*/ +/*{ + Id: 128023 + Name: "There was fear in Morocc army" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate Morocc 10 necromancer" + Criteria: { + MobId: "EP14_MORS_BOSSB" + } + Goal: 10 + } + } + Points: 20 +},*/ +/*{ + Id: 128024 + Name: "Guard of weak army" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate Morocc 50 necromancer" + Criteria: { + MobId: "EP14_MORS_BOSSB" + } + Goal: 50 + } + } + Points: 50 +},*/ +{ + Id: 128025 + Name: "Audience with the queen" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Faceworm Queen" + Criteria: { + MobId: "FACEWORM_QUEEN" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128026 + Name: "Warm earth" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Earth Faceworm Queen" + Criteria: { + MobId: "FACEWORM_QUEEN_G" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128027 + Name: "Water is very good exactly" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Water Faceworm Queen" + Criteria: { + MobId: "FACEWORM_QUEEN_B" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128028 + Name: "Pleasant breeze" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Wind Faceworm Queen" + Criteria: { + MobId: "FACEWORM_QUEEN_Y" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128029 + Name: "Visitor of old castle" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Amdarais" + Criteria: { + MobId: "MG_AMDARAIS" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128030 + Name: "Lord of old castle" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Amdarais" + Criteria: { + MobId: "MG_AMDARAIS" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128031 + Name: "Conqueror of old castle" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Amdarais" + Criteria: { + MobId: "MG_AMDARAIS" + } + Goal: 50 + } + } + Points: 50 +}, +/*{ + Id: 128032 + Name: "Haggard sucker" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Amdarais (Superior)" + Criteria: { + MobId: "MG_AMDARAIS_H" + } + Goal: 1 + } + } + Points: 10 +},*/ +/*{ + Id: 128033 + Name: "Hope of the Knight" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Amdarais (Superior)" + Criteria: { + MobId: "MG_AMDARAIS_H" + } + Goal: 10 + } + } + Points: 20 +},*/ +/*{ + Id: 128034 + Name: "Guardian of the Dawn" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Amdarais (Superior)" + Criteria: { + MobId: "MG_AMDARAIS_H" + } + Goal: 50 + } + } + Points: 50 +},*/ +{ + Id: 128035 + Name: "Time Traveler" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Sarah Irene" + Criteria: { + MobId: "MM_SARAH" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128036 + Name: "Restore ancient relic" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Sarah Irene" + Criteria: { + MobId: "MM_SARAH" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128037 + Name: "Master of relic transport" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Sarah Irene" + Criteria: { + MobId: "MM_SARAH" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128038 + Name: "Show Jailbreak to the captain" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Ferlock" + Criteria: { + MobId: "E1_FELOCK" + } + Goal: 1 + } + } + Points: 10 +}, +/*{ + Id: 128039 + Name: "Show Jailbreak to the weak captain" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Ferlock" + Criteria: { + MobId: "E2_FELOCK" + } + Goal: 1 + } + } + Points: 10 +},*/ +{ + Id: 128040 + Name: "Riot on board" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Ferlock" + Criteria: { + MobId: "E1_FELOCK" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128041 + Name: "Turmoil on board" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Ferlock" + Criteria: { + MobId: "E1_FELOCK" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128042 + Name: "Rebellion on board" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Ferlock" + Criteria: { + MobId: "E1_FELOCK" + } + Goal: 50 + } + } + Points: 50 +}, +/*{ + Id: 128043 + Name: "Revolt of Riot" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Ferlock" + Criteria: { + MobId: "E2_FELOCK" + } + Goal: 50 + } + } + Points: 50 +},*/ +{ + Id: 128044 + Name: "Magic tournament champion" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Fenrir" + Criteria: { + MobId: "GEFFEN_FENRIR" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128045 + Name: "Gladiator of Coliseum" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Fenrir" + Criteria: { + MobId: "GEFFEN_FENRIR" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128046 + Name: "Slayer of Colosseum" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Fenrir" + Criteria: { + MobId: "GEFFEN_FENRIR" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128047 + Name: "Endless Tower challenger" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128048 + Name: "Endless Tower Slayer" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128049 + Name: "Lord of the tower" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Naght Sieger" + Criteria: { + MobId: "NAGHT_SIEGER" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 128050 + Name: "Novice Exorcist" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 1 Bangungot Doll" + Criteria: { + MobId: "BANGUNGOT_3" + } + Goal: 1 + } + } + Points: 10 +}, +{ + Id: 128051 + Name: "Experienced Exorcist" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 10 Bangungot Doll" + Criteria: { + MobId: "BANGUNGOT_3" + } + Goal: 10 + } + } + Points: 20 +}, +{ + Id: 128052 + Name: "Legendary Exorcist" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Eliminate 50 Bangungot Doll" + Criteria: { + MobId: "BANGUNGOT_3" + } + Goal: 50 + } + } + Points: 50 +}, +{ + Id: 129001 + Name: "Prontera Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "North Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120001 + } + } + *2: { + Description: "North Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120002 + } + } + *3: { + Description: "North Prontera Field Exploration(3) completed" + Criteria: { + Achieve: 120003 + } + } + *4: { + Description: "West Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120004 + } + } + *5: { + Description: "West Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120005 + } + } + *6: { + Description: "East Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120006 + } + } + *7: { + Description: "South Prontera Field Exploration(1) completed" + Criteria: { + Achieve: 120007 + } + } + *8: { + Description: "South Prontera Field Exploration(2) completed" + Criteria: { + Achieve: 120008 + } + } + *9: { + Description: "South Prontera Field Exploration(3) completed" + Criteria: { + Achieve: 120009 + } + } + *10: { + Description: "South Prontera Field Exploration(4) completed" + Criteria: { + Achieve: 120010 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129002 + Name: "Geffen Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "East Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120011 + } + } + *2: { + Description: "Southeast Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120012 + } + } + *3: { + Description: "Northwest Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120013 + } + } + *4: { + Description: "Northwest Geffen Field Exploration(2) completed" + Criteria: { + Achieve: 120014 + } + } + *5: { + Description: "Northwest Geffen Field Exploration(3) completed" + Criteria: { + Achieve: 120015 + } + } + *6: { + Description: "South Geffen Field Exploration(1) completed" + Criteria: { + Achieve: 120016 + } + } + *7: { + Description: "South Geffen Field Exploration(2) completed" + Criteria: { + Achieve: 120017 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129003 + Name: "Sograt Desert Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Sograt Desert Field Exploration(1) completed" + Criteria: { + Achieve: 120018 + } + } + *2: { + Description: "Sograt Desert Field Exploration(2) completed" + Criteria: { + Achieve: 120019 + } + } + *3: { + Description: "Sograt Desert Field Exploration(3) completed" + Criteria: { + Achieve: 120020 + } + } + *4: { + Description: "Sograt Desert Field Exploration(4) completed" + Criteria: { + Achieve: 120021 + } + } + *5: { + Description: "Sograt Desert Field Exploration(5) completed" + Criteria: { + Achieve: 120022 + } + } + *6: { + Description: "Sograt Desert Field Exploration(6) completed" + Criteria: { + Achieve: 120023 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129004 + Name: "Payon Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Southwest Payon Field Exploration(1) completed" + Criteria: { + Achieve: 120024 + } + } + *2: { + Description: "Southwest Payon Field Exploration(2) completed" + Criteria: { + Achieve: 120025 + } + } + *3: { + Description: "Southwest Payon Field Exploration(3) completed" + Criteria: { + Achieve: 120026 + } + } + *4: { + Description: "Southwest Payon Field Exploration(4) completed" + Criteria: { + Achieve: 120027 + } + } + *5: { + Description: "East Payon Field Exploration(1) completed" + Criteria: { + Achieve: 120028 + } + } + *6: { + Description: "East Payon Field Exploration(2) completed" + Criteria: { + Achieve: 120029 + } + } + *7: { + Description: "East Payon Field Exploration(3) completed" + Criteria: { + Achieve: 120030 + } + } + *8: { + Description: "East Payon Field Exploration(4) completed" + Criteria: { + Achieve: 120031 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129005 + Name: "North Mjolnir Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "North Mjolnir Field Exploration(1) completed" + Criteria: { + Achieve: 120032 + } + } + *2: { + Description: "North Mjolnir Field Exploration(2) completed" + Criteria: { + Achieve: 120033 + } + } + *3: { + Description: "North Mjolnir Field Exploration(3) completed" + Criteria: { + Achieve: 120034 + } + } + *4: { + Description: "North Mjolnir Field Exploration(4) completed" + Criteria: { + Achieve: 120035 + } + } + *5: { + Description: "North Mjolnir Field Exploration(5) completed" + Criteria: { + Achieve: 120036 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129006 + Name: "South Mjolnir Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "South Mjolnir Field Exploration(1) completed" + Criteria: { + Achieve: 120037 + } + } + *2: { + Description: "South Mjolnir Field Exploration(2) completed" + Criteria: { + Achieve: 120038 + } + } + *3: { + Description: "South Mjolnir Field Exploration(3) completed" + Criteria: { + Achieve: 120039 + } + } + *4: { + Description: "South Mjolnir Field Exploration(4) completed" + Criteria: { + Achieve: 120040 + } + } + *5: { + Description: "South Mjolnir Field Exploration(5) completed" + Criteria: { + Achieve: 120041 + } + } + *6: { + Description: "South Mjolnir Field Exploration(6) completed" + Criteria: { + Achieve: 120042 + } + } + *7: { + Description: "South Aldebaran Field Exploration(1) completed" + Criteria: { + Achieve: 120043 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129007 + Name: "Comodo Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Comodo Field Exploration(1) completed" + Criteria: { + Achieve: 120044 + } + } + *2: { + Description: "Comodo Field Exploration(2) completed" + Criteria: { + Achieve: 120045 + } + } + *3: { + Description: "Comodo Field Exploration(3) completed" + Criteria: { + Achieve: 120046 + } + } + *4: { + Description: "Comodo Field Exploration(4) completed" + Criteria: { + Achieve: 120047 + } + } + *5: { + Description: "Comodo Field Exploration(5) completed" + Criteria: { + Achieve: 120048 + } + } + *6: { + Description: "Comodo Field Exploration(6) completed" + Criteria: { + Achieve: 120049 + } + } + *7: { + Description: "Comodo Field Exploration(7) completed" + Criteria: { + Achieve: 120050 + } + } + *8: { + Description: "Comodo Field Exploration(8) completed" + Criteria: { + Achieve: 120051 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129008 + Name: "Rune Midgard Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Prontera Explorer" + Criteria: { + Achieve: 129001 + } + } + *2: { + Description: "Geffen Explorer" + Criteria: { + Achieve: 129002 + } + } + *3: { + Description: "Sograt Desert Explorer" + Criteria: { + Achieve: 129003 + } + } + *4: { + Description: "Payon Explorer" + Criteria: { + Achieve: 129004 + } + } + *5: { + Description: "North Mjolnir Explorer" + Criteria: { + Achieve: 129005 + } + } + *6: { + Description: "South Mjolnir Explorer" + Criteria: { + Achieve: 129006 + } + } + *7: { + Description: "Comodo Explorer" + Criteria: { + Achieve: 129007 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129009 + Name: "Yuno Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Border Checkpoint Field Exploration(1) completed" + Criteria: { + Achieve: 120052 + } + } + *2: { + Description: "Border Checkpoint Field Exploration(2) completed" + Criteria: { + Achieve: 120053 + } + } + *3: { + Description: "Kiel Hyre Mansion Field Exploration(1) completed" + Criteria: { + Achieve: 120054 + } + } + *4: { + Description: "El Mes Plateau Field Exploration(1) completed" + Criteria: { + Achieve: 120055 + } + } + *5: { + Description: "El Mes Plateau Field Exploration(2) completed" + Criteria: { + Achieve: 120056 + } + } + *6: { + Description: "El Mes Plateau Field Exploration(3) completed" + Criteria: { + Achieve: 120057 + } + } + *7: { + Description: "El Mes Gorge Field Exploration(1) completed" + Criteria: { + Achieve: 120058 + } + } + *8: { + Description: "Kiel Hyre Academy Field Exploration(1) completed" + Criteria: { + Achieve: 120059 + } + } + *9: { + Description: "Guard Camp Field Exploration(1) completed" + Criteria: { + Achieve: 120060 + } + } + *10: { + Description: "Yuno Field Exploration(1) completed" + Criteria: { + Achieve: 120061 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129010 + Name: "Hugel Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Front of Thanatos Tower Field Exploration(1) completed" + Criteria: { + Achieve: 120062 + } + } + *2: { + Description: "Hugel Field Exploration(1) completed" + Criteria: { + Achieve: 120063 + } + } + *3: { + Description: "Hugel Field Exploration(2) completed" + Criteria: { + Achieve: 120064 + } + } + *4: { + Description: "Hugel Field Exploration(3) completed" + Criteria: { + Achieve: 120065 + } + } + *5: { + Description: "Abyss Lake Field Exploration(1) completed" + Criteria: { + Achieve: 120066 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129011 + Name: "Einbroch Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Einbroch Field Exploration(1) completed" + Criteria: { + Achieve: 120067 + } + } + *2: { + Description: "Einbroch Field Exploration(2) completed" + Criteria: { + Achieve: 120068 + } + } + *3: { + Description: "Einbroch Field Exploration(3) completed" + Criteria: { + Achieve: 120069 + } + } + *4: { + Description: "Einbroch Field Exploration(4) completed" + Criteria: { + Achieve: 120070 + } + } + *5: { + Description: "Einbroch Field Exploration(5) completed" + Criteria: { + Achieve: 120071 + } + } + *6: { + Description: "Einbroch Field Exploration(6) completed" + Criteria: { + Achieve: 120072 + } + } + *7: { + Description: "Einbroch Field Exploration(7) completed" + Criteria: { + Achieve: 120073 + } + } + *8: { + Description: "Einbroch Field Exploration(8) completed" + Criteria: { + Achieve: 120074 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129012 + Name: "Lighthalzen Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Lighthalzen Field Exploration(1) completed" + Criteria: { + Achieve: 120075 + } + } + *2: { + Description: "Lighthalzen Field Exploration(2) completed" + Criteria: { + Achieve: 120076 + } + } + *3: { + Description: "Lighthalzen Field Exploration(3) completed" + Criteria: { + Achieve: 120077 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129013 + Name: "Schwarzwald Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Yuno Explorer" + Criteria: { + Achieve: 129009 + } + } + *2: { + Description: "Hugel Explorer" + Criteria: { + Achieve: 129010 + } + } + *3: { + Description: "Einbroch Explorer" + Criteria: { + Achieve: 129011 + } + } + *4: { + Description: "Lighthalzen Explorer" + Criteria: { + Achieve: 129012 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129014 + Name: "Rachel Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Rachel Audhumbla Plains Field Exploration(1) completed" + Criteria: { + Achieve: 120078 + } + } + *2: { + Description: "Rachel Plains Field Exploration(1) completed" + Criteria: { + Achieve: 120079 + } + } + *3: { + Description: "Rachel Plains Field Exploration(2) completed" + Criteria: { + Achieve: 120080 + } + } + *4: { + Description: "Rachel Plains Field Exploration(3) completed" + Criteria: { + Achieve: 120081 + } + } + *5: { + Description: "Rachel Audhumbla Grassland Field Exploration(1) completed" + Criteria: { + Achieve: 120082 + } + } + *6: { + Description: "Rachel Audhumbla Grassland Field Exploration(2) completed" + Criteria: { + Achieve: 120083 + } + } + *7: { + Description: "Portus Luna Field Exploration(1) completed" + Criteria: { + Achieve: 120084 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129015 + Name: "Veins Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Veins Field Exploration(1) completed" + Criteria: { + Achieve: 120085 + } + } + *2: { + Description: "Veins Field Exploration(2) completed" + Criteria: { + Achieve: 120086 + } + } + *3: { + Description: "Veins Field Exploration(3) completed" + Criteria: { + Achieve: 120087 + } + } + *4: { + Description: "Veins Field Exploration(4) completed" + Criteria: { + Achieve: 120088 + } + } + *5: { + Description: "Veins Field Exploration(5) completed" + Criteria: { + Achieve: 120089 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129016 + Name: "Arunafeltz Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Rachel Explorer" + Criteria: { + Achieve: 129014 + } + } + *2: { + Description: "Veins Explorer" + Criteria: { + Achieve: 129015 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129017 + Name: "Laphine Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Eclage Field Exploration(1) completed" + Criteria: { + Achieve: 120090 + } + } + *2: { + Description: "North Bitfrost Field Exploration(1) completed" + Criteria: { + Achieve: 120091 + } + } + *3: { + Description: "South Bitfrost Field Exploration(1) completed" + Criteria: { + Achieve: 120092 + } + } + *4: { + Description: "Splendide Field Exploration(1) completed" + Criteria: { + Achieve: 120093 + } + } + *5: { + Description: "Splendide Field Exploration(2) completed" + Criteria: { + Achieve: 120094 + } + } + *6: { + Description: "Splendide Field Exploration(3) completed" + Criteria: { + Achieve: 120095 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129018 + Name: "Manuk Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Manuk Field Exploration(1) completed" + Criteria: { + Achieve: 120096 + } + } + *2: { + Description: "Manuk Field Exploration(2) completed" + Criteria: { + Achieve: 120097 + } + } + *3: { + Description: "Manuk Field Exploration(3) completed" + Criteria: { + Achieve: 120098 + } + } + *4: { + Description: "Outskirts of Kamidal Field Exploration(1) completed" + Criteria: { + Achieve: 120099 + } + } + *5: { + Description: "Outskirts of Kamidal Field Exploration(2) completed" + Criteria: { + Achieve: 120100 + } + } + } + Rewards: { + Items: { + Gift_Box: 1 + } + } + Points: 20 +}, +{ + Id: 129019 + Name: "Eclage Explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Laphine Explorer" + Criteria: { + Achieve: 129017 + } + } + *2: { + Description: "Manuk Explorer" + Criteria: { + Achieve: 129018 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 129020 + Name: "Localizing fields explorer" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Amatsu Field Exploration(1) completed" + Criteria: { + Achieve: 120101 + } + } + *2: { + Description: "Kunlun Field Exploration(1) completed" + Criteria: { + Achieve: 120102 + } + } + *3: { + Description: "Gonryun Field Exploration(1) completed" + Criteria: { + Achieve: 120103 + } + } + *4: { + Description: "Ayothaya Field Exploration(1) completed" + Criteria: { + Achieve: 120104 + } + } + *5: { + Description: "Moscovia Field Exploration(1) completed" + Criteria: { + Achieve: 120105 + } + } + *6: { + Description: "Brasilis Field Exploration(1) completed" + Criteria: { + Achieve: 120106 + } + } + *7: { + Description: "Dewata Field Exploration(1) completed" + Criteria: { + Achieve: 120107 + } + } + *8: { + Description: "Malaya Field Exploration(1) completed" + Criteria: { + Achieve: 120108 + } + } + *9: { + Description: "Malaya Field Exploration(2) completed" + Criteria: { + Achieve: 120109 + } + } + } + Rewards: { + Items: { + Old_Violet_Box: 1 + } + } + Points: 50 +}, +{ + Id: 130000 + Name: "Socialite debut" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Visit Heine Royal Family and start conversation" + } + *2: { + Description: "Visit Nerius Royal Family and start conversation" + } + *3: { + Description: "Visit Walter Royal Family and start conversation" + } + *4: { + Description: "Visit Wigner Royal Family and start conversation" + } + *5: { + Description: "Visit Richard Royal Family and start conversation" + } + *6: { + Description: "Visit Gaebolg Royal Family and start conversation" + } + *7: { + Description: "Visit Lugenburg Royal Family and start conversation" + } + } + Rewards: { + TitleId: 1034 + } + Points: 10 +}, +{ + Id: 170000 + Name: "Song chamber is not an accident" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Listen the secret song of five recomended court musician" + } + } + Points: 10 +}, +{ + Id: 190000 + Name: "Alliance workers of merchant city" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Novice Amethyst class" + Goal: 1 + } + *2: { + Description: "Expert Sardonyx class" + Goal: 10 + } + *3: { + Description: "Competence Person Aquamarine class" + Goal: 30 + } + *4: { + Description: "Sparkling Diamond class" + Goal: 100 + } + } + Points: 50 +}, +{ + Id: 200000 + Name: "Acquire the first aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 99" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 99 + } + } + Rewards: { + TitleId: 1000 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + White_Slim_Pot_Box2: 1 + } + } + Points: 50 +}, +{ + Id: 200001 + Name: "Acquire the second aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 150" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 150 + } + } + Rewards: { + TitleId: 1001 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Dark_Snake_Lord_Hat: 1 + } + } + Points: 60 +}, +{ + Id: 200002 + Name: "Acquire the third aura!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Base level 175" + Criteria: { + StatusType: "SP_BASELEVEL" + } + Goal: 175 + } + } + Rewards: { + TitleId: 1002 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Advanced_Jao_King_Hat: 1 + } + } + Points: 70 +}, +{ + Id: 200003 + Name: "Master Job level!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Job level 50" + Criteria: { + StatusType: "SP_JOBLEVEL" + } + Goal: 50 + } + } + Rewards: { + TitleId: 1003 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Old_Violet_Box: 1 + } + } + Points: 30 +}, +{ + Id: 200004 + Name: "Grandmaster Job level!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Job level 70" + Criteria: { + StatusType: "SP_JOBLEVEL" + } + Goal: 70 + } + } + Rewards: { + TitleId: 1004 + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Old_Card_Album_: 1 + } + } + Points: 50 +}, +{ + Id: 200005 + Name: "Official Adventurer" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to first class" + Criteria: { + JobId: [ "Job_Swordman", "Job_Mage", "Job_Archer", "Job_Acolyte", "Job_Merchant", "Job_Thief" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 10 +}, +{ + Id: 200006 + Name: "First step of job change!" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "First step of job change" + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 20 +}, +{ + Id: 200007 + Name: "Veteran Adventurer! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 2-1 classes" + Criteria: { + JobId: [ "Job_Knight", "Job_Priest", "Job_Wizard", "Job_Blacksmith", "Job_Hunter", "Job_Assassin" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 25 +}, +{ + Id: 200008 + Name: "Veteran Adventurer! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 2-2 classes" + Criteria: { + JobId: [ "Job_Crusader", "Job_Sage", "Job_Bard", "Job_Dancer", "Job_Alchemist", "Job_Rogue" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 25 +}, +{ + Id: 200009 + Name: "Warrior (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent 2-1 classes" + Criteria: { + JobId: [ "Job_Lord_Knight", "Job_High_Wizard", "Job_Sniper", "Job_High_Priest", "Job_Whitesmith", "Job_Assassin_Cross" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 30 +}, +{ + Id: 200010 + Name: "Warrior (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent 2-2 classes" + Criteria: { + JobId: [ "Job_Paladin", "Job_Professor", "Job_Clown", "Job_Gypsy", "Job_Champion", "Job_Creator", "Job_Stalker" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 30 +}, +{ + Id: 200011 + Name: "Elite Adventurer! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-1 classes" + Criteria: { + JobId: [ "Job_Rune_Knight", "Job_Warlock", "Job_Ranger", "Job_Arch_Bishop", "Job_Mechanic", "Job_Guillotine_Cross" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 50 +}, +{ + Id: 200012 + Name: "Transcendentaler! (1)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-1 classes after transcendent" + Criteria: { + JobId: [ "Job_Rune_Knight_T", "Job_Warlock_T", "Job_Ranger_T", "Job_Arch_Bishop_T", "Job_Mechanic_T", "Job_Guillotine_Cross_T" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 60 +}, +{ + Id: 200013 + Name: "Elite Adventurer! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-2 classes" + Criteria: { + JobId: [ "Job_Royal_Guard", "Job_Sorcerer", "Job_Minstrel", "Job_Wanderer", "Job_Sura", "Job_Genetic", "Job_Shadow_Chaser" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 50 +}, +{ + Id: 200014 + Name: "Transcendentaler! (2)" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to 3-2 classes after transcendent" + Criteria: { + JobId: [ "Job_Royal_Guard_T", "Job_Sorcerer_T", "Job_Minstrel_T", "Job_Wanderer_T", "Job_Sura_T", "Job_Genetic_T", "Job_Shadow_Chaser_T" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 60 +}, +{ + Id: 200015 + Name: "The way of exceptional character" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to expanded classes" + Criteria: { + JobId: [ "Job_Taekwon", "Job_Gunslinger", "Job_Ninja", "Job_Summoner" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 10 +}, +{ + Id: 200016 + Name: "This is My way!" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Expanded 2nd classes" + Criteria: { + JobId: [ "Job_Star_Gladiator", "Job_Soul_Linker", "Job_Rebellion", "Job_Kagerou", "Job_Oboro" ] + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 20 +}, +{ + Id: 200017 + Name: "Bearish Power!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base STR 90º" + Criteria: { + StatusType: "SP_STR" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200018 + Name: "Overflowing Magic!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base INT 90" + Criteria: { + StatusType: "SP_INT" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200019 + Name: "Healthy Body and Mental Health!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base VIT 90" + Criteria: { + StatusType: "SP_VIT" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200020 + Name: "Speed of Light" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base AGI 90" + Criteria: { + StatusType: "SP_AGI" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200021 + Name: "Hawk Eyes" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base DEX 90" + Criteria: { + StatusType: "SP_DEX" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200022 + Name: "Maximum Luck" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base LUK 90" + Criteria: { + StatusType: "SP_LUK" + } + Goal: 90 + } + } + Points: 10 +}, +{ + Id: 200023 + Name: "Dragonlike Power!" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base STR 125" + Criteria: { + StatusType: "SP_STR" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" sc_start SC_GIANTGROWTH, 180000, 1; "> + } + Points: 20 +}, +{ + Id: 200024 + Name: "Magic Insanity" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base INT 125" + Criteria: { + StatusType: "SP_INT" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_HASTEUP, AREA, playerattached()); sc_start2 SC_MAGIC_CANDY, 60000, 30, 70; "> + } + Points: 20 +}, +{ + Id: 200025 + Name: "Rock Alloy" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base VIT 125" + Criteria: { + StatusType: "SP_VIT" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_HEAL3, AREA, playerattached()); sc_start2 SC_S_LIFEPOTION, 600000, -5, 5; "> + } + Points: 20 +}, +{ + Id: 200026 + Name: "Speed of Light" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base AGI 125" + Criteria: { + StatusType: "SP_AGI" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_STEAL, AREA, playerattached()); sc_start SC_PLUSAVOIDVALUE, 60000, 20; "> + } + Points: 20 +}, +{ + Id: 200027 + Name: "Falcon's Eyes" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base DEX 125" + Criteria: { + StatusType: "SP_DEX" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_MAGICALATTHIT, AREA, playerattached()); sc_start SC_CRITICALPERCENT, 300000, 30; "> + } + Points: 20 +}, +{ + Id: 200028 + Name: "Lucky Fever" + Type: "ACH_STATUS" + Objectives: { + *1: { + Description: "Achieve base LUK 125" + Criteria: { + StatusType: "SP_LUK" + } + Goal: 125 + } + } + Rewards: { + Bonus: <" specialeffect(EF_GLORIA, AREA, playerattached()); sc_start SC_GLORIA, 15000, 0; "> + } + Points: 20 +}, +{ + Id: 200029 + Name: "Incarnation of Love and Hate" + Type: "ACH_STATUS_BY_JOB" + Objectives: { + *1: { + Description: "Achieve 99 base level as Novice" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: "Job_Novice" + } + Goal: 99 + } + } + Rewards: { + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start SC_BLESSING, 30000, 1; "> + Items: { + Abrasive_Box_10: 1 + } + } + Points: 30 +}, +{ + Id: 200030 + Name: "I really love it!" + Type: "ACH_STATUS_BY_JOBTYPE" + Objectives: { + *1: { + Description: "Achieve 99 base level as 1st classes" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: ["Job_Swordman", "Job_Mage", "Job_Archer", "Job_Acolyte", "Job_Merchant", "Job_Thief"] + } + Goal: 99 + } + } + Rewards: { + Bonus: <" specialeffect(EF_BLESSING, AREA, playerattached()); sc_start(SC_BLESSING, 30000, 1); "> + Items: { + Bubble_Gum_Box_10: 1 + } + } + Points: 30 +}, +{ + Id: 200031 + Name: "Reborn in Valhalla!" + Type: "ACH_JOB_CHANGE" + Objectives: { + *1: { + Description: "Job change to transcendent Novice" + Criteria: { + JobId: "Job_Novice_High" + } + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Special_Gift_Box: 1 + } + } + Points: 10 +}, +/*{ + Id: 200032 + Name: "Start of Another Adventure" + Type: "ACH_STATUS_BY_JOB" + Objectives: { + *1: { + Description: "Achieve Base Level 100" + Criteria: { + StatusType: "SP_BASELEVEL" + JobId: "" // Rebirth? + } + Goal: 100 + } + } + Rewards: { + Items: { + ID23585: 1 (Non-Existent Item) + } + } + Points: 10 +},*/ +{ + Id: 220000 + Name: "Community begin" + Type: "ACH_CHATROOM_CREATE" + Objectives: { + *1: { + Description: "Create a chatroom" + } + } + Points: 10 +}, +{ + Id: 220001 + Name: "A mouth only moment" + Type: "ACH_CHATROOM_CREATE_DEAD" + Objectives: { + *1: { + Description: "Create a chatroom when KO'ed" + } + } + Points: 10 +}, +{ + Id: 220002 + Name: "Admiring the chatter" + Type: "ACH_CHATROOM_MEMBERS" + Objectives: { + *1: { + Description: "Fill the chatroom with 20 people" + } + } + Points: 10 +}, +{ + Id: 220003 + Name: "My friend's friend~" + Type: "ACH_FRIEND_ADD" + Objectives: { + *1: { + Description: "Add 1 person as a friend" + } + } + Points: 10 +}, +{ + Id: 220004 + Name: "A competition of popularity" + Type: "ACH_FRIEND_ADD" + Objectives: { + *1: { + Description: "Add 10 person as a friend" + } + } + Points: 10 +}, +{ + Id: 220005 + Name: "Let's Party~" + Type: "ACH_PARTY_CREATE" + Objectives: { + *1: { + Description: "Create a party" + } + } + Points: 10 +}, +{ + Id: 220006 + Name: "Married with who..?" + Type: "ACH_MARRY" + Objectives: { + *1: { + Description: "Succeed on marriage" + } + } + Rewards: { + TitleId: 1022 + } + Points: 20 +}, +{ + Id: 220007 + Name: "Can you grow?" + Type: "ACH_ADOPT_PARENT" + Objectives: { + *1: { + Description: "Adopted by parents" + } + } + Rewards: { + TitleId: 1032 + } + Points: 20 +}, +{ + Id: 220008 + Name: "Being a parent" + Type: "ACH_ADOPT_BABY" + Objectives: { + *1: { + Description: "Adopt a child" + } + } + Rewards: { + TitleId: 1033 + } + Points: 20 +}, +{ + Id: 220009 + Name: "Activating the market economy (1)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 10000 zeny on vending merchant" + Goal: 10000 + } + } + Points: 10 +}, +{ + Id: 220010 + Name: "Activating the market economy (2)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 100000 zeny on vending merchant" + Goal: 1000000 + } + } + Points: 15 +}, +{ + Id: 220011 + Name: "Activating the market economy (3)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 500000 zeny on vending merchant" + Goal: 5000000 + } + } + Points: 20 +}, +{ + Id: 220012 + Name: "Activating the market economy (4)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 1.000000 zeny on vending merchant" + Goal: 100000000 + } + } + Points: 30 +}, +{ + Id: 220013 + Name: "Activating the market economy (5)" + Type: "ACH_ZENY_SPEND_TOTAL" + Objectives: { + *1: { + Description: "Spend 5.000000 zeny on vending merchant" + Goal: 500000000 + } + } + Points: 50 +}, +{ + Id: 220014 + Name: "I can't quit from refining! (1)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 1 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 1 + } + Goal: 7 + } + } + Points: 10 +}, +{ + Id: 220015 + Name: "I can't quit from refining! (2)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 1 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 1 + } + Goal: 12 + } + } + Points: 15 +}, +{ + Id: 220016 + Name: "I can't quit from refining! (3)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 2 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 2 + } + Goal: 7 + } + } + Points: 10 +}, +{ + Id: 220017 + Name: "I can't quit from refining! (4)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 2 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 2 + } + Goal: 12 + } + } + Points: 15 +}, +{ + Id: 220018 + Name: "I can't quit from refining! (5)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 3 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 3 + } + Goal: 7 + } + } + Points: 15 +}, +{ + Id: 220019 + Name: "I can't quit from refining! (6)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 3 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 3 + } + Goal: 12 + } + } + Points: 20 +}, +{ + Id: 220020 + Name: "I can't quit from refining! (7)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 4 weapon to +7" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 4 + } + Goal: 7 + } + } + Points: 20 +}, +{ + Id: 220021 + Name: "I can't quit from refining! (8)" + Type: "ACH_EQUIP_REFINE_SUCCESS_WLV" + Objectives: { + *1: { + Description: "Succeed on refining level 4 weapon to +12" + Criteria: { + ItemType: "IT_WEAPON" + WeaponLevel: 4 + } + Goal: 12 + } + } + Points: 30 +}, +{ + Id: 220022 + Name: "Human's greed has no ending.." + Type: "ACH_EQUIP_REFINE_FAILURE_TOTAL" + Objectives: { + *1: { + Description: "Experience the fail of refining" + Criteria: { + ItemType: ["IT_WEAPON", "IT_ARMOR"] + } + } + } + Points: 10 +}, +{ + Id: 220023 + Name: "I found it! (1)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 100 zeny" + Goal: 100 + } + } + Points: 10 +}, +{ + Id: 220024 + Name: "I found it! (2)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 1000 zeny" + Goal: 1000 + } + } + Points: 10 +}, +{ + Id: 220025 + Name: "I found it! (3)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 5000 zeny" + Goal: 5000 + } + } + Points: 15 +}, +{ + Id: 220026 + Name: "I found it! (4)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 10000 zeny" + Goal: 10000 + } + } + Points: 15 +}, +{ + Id: 220027 + Name: "I found it! (5)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 50000 zeny" + Goal: 50000 + } + } + Points: 20 +}, +{ + Id: 220028 + Name: "I found it! (6)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 100000 zeny" + Goal: 100000 + } + } + Points: 20 +}, +{ + Id: 220029 + Name: "I found it! (7)" + Type: "ACH_ITEM_GET_WORTH" + Objectives: { + *1: { + Description: "Obtain an item with worth of more than 150000 zeny" + Goal: 150000 + } + } + Points: 30 +}, +{ + Id: 220030 + Name: "Rich King (1)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 10000 zeny in pocket" + Goal: 10000 + } + } + Points: 10 +}, +{ + Id: 220031 + Name: "Rich King (2)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 100000 zeny in pocket" + Goal: 100000 + } + } + Points: 15 +}, +{ + Id: 220032 + Name: "Rich King (3)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 1000000 zeny in pocket" + Goal: 1000000 + } + } + Points: 20 +}, +{ + Id: 220033 + Name: "Rich King (4)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 10000000 zeny in pocket" + Goal: 10000000 + } + } + Points: 25 +}, +{ + Id: 220034 + Name: "Rich King (5)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 100000000 zeny in pocket" + Goal: 100000000 + } + } + Points: 30 +}, +{ + Id: 220035 + Name: "Rich King (6)" + Type: "ACH_ZENY_HOLD" + Objectives: { + *1: { + Description: "Hold more than 1000000000 zeny in pocket" + Goal: 1000000000 + } + } + Points: 40 +}, +{ + Id: 230101 + Name: "Poring - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poring" + Criteria: { + MobId: "PORING" + } + } + } + Points: 10 +}, +{ + Id: 230102 + Name: "Drops - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Drops" + Criteria: { + MobId: "DROPS" + } + } + } + Points: 10 +}, +{ + Id: 230103 + Name: "Poporing - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poporing" + Criteria: { + MobId: "POPORING" + } + } + } + Points: 10 +}, +{ + Id: 230104 + Name: "Novice Poring - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Novice Poring" + Criteria: { + MobId: "LITTLE_PORING" + } + } + } + Points: 10 +}, +{ + Id: 230111 + Name: "Chonchon - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Chonchon" + Criteria: { + MobId: "CHONCHON" + } + } + } + Points: 10 +}, +{ + Id: 230112 + Name: "Steel Chonchon - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Steel Chonchon" + Criteria: { + MobId: "STEEL_CHONCHON" + } + } + } + Points: 10 +}, +{ + Id: 230113 + Name: "Hunter Fly - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Hunter Fly" + Criteria: { + MobId: "HUNTER_FLY" + } + } + } + Points: 10 +}, +{ + Id: 230114 + Name: "Rocker - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Rocker" + Criteria: { + MobId: "ROCKER" + } + } + } + Points: 10 +}, +{ + Id: 230115 + Name: "Spore - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Spore" + Criteria: { + MobId: "SPORE" + } + } + } + Points: 10 +}, +{ + Id: 230116 + Name: "Poison Spore - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Poison Spore" + Criteria: { + MobId: "POISON_SPORE" + } + } + } + Points: 10 +}, +{ + Id: 230121 + Name: "Lunatic - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Lunatic" + Criteria: { + MobId: "LUNATIC" + } + } + } + Points: 10 +}, +{ + Id: 230122 + Name: "Picky - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Picky" + Criteria: { + MobId: "PICKY" + } + } + } + Points: 10 +}, +{ + Id: 230123 + Name: "Savage Bebe - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Savage Bebe" + Criteria: { + MobId: "SAVAGE_BABE" + } + } + } + Points: 10 +}, +{ + Id: 230124 + Name: "Baby Desert Wolf - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Baby Desert Wolf" + Criteria: { + MobId: "M_DESERT_WOLF_B" + } + } + } + Points: 10 +}, +{ + Id: 230125 + Name: "Smokie - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Smokie" + Criteria: { + MobId: "SMOKIE" + } + } + } + Points: 10 +}, +{ + Id: 230126 + Name: "Yoyo - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Yoyo" + Criteria: { + MobId: "YOYO" + } + } + } + Points: 10 +}, +{ + Id: 230127 + Name: "Peco Peco - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Peco Peco" + Criteria: { + MobId: "PECOPECO" + } + } + } + Points: 10 +}, +{ + Id: 230128 + Name: "Petite - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Petite" + Criteria: { + MobId: "PETIT" + } + } + } + Points: 10 +}, +{ + Id: 230141 + Name: "Munak - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Munak" + Criteria: { + MobId: "MUNAK" + } + } + } + Points: 10 +}, +{ + Id: 230142 + Name: "Isis - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Isis" + Criteria: { + MobId: "ISIS" + } + } + } + Points: 10 +}, +{ + Id: 230143 + Name: "Sohee - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Sohee" + Criteria: { + MobId: "SOHEE" + } + } + } + Points: 10 +}, +{ + Id: 230144 + Name: "Zherlthsh - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Zherlthsh" + Criteria: { + MobId: "ZHERLTHSH" + } + } + } + Points: 10 +}, +{ + Id: 230145 + Name: "Alice - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Alice" + Criteria: { + MobId: "ALICE" + } + } + } + Points: 10 +}, +{ + Id: 230146 + Name: "Succubus - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Succubus" + Criteria: { + MobId: "SUCCUBUS" + } + } + } + Points: 10 +}, +{ + Id: 230147 + Name: "Loli Ruri - taming" + Type: "ACH_PET_CREATE" + Objectives: { + *1: { + Description: "Succeed on Taming Loli Ruri" + Criteria: { + MobId: "LOLI_RURI" + } + } + } + Points: 10 +}, +{ + Id: 230201 + Name: "Exploring Poring's life (1)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 10 Poring" + Criteria: { + MobId: "PORING" + } + Goal: 10 + } + *2: { + Description: "Hunt 10 Novice Poring" + Criteria: { + MobId: "LITTLE_PORING" + } + Goal: 10 + } + *3: { + Description: "Hunt 10 Drops" + Criteria: { + MobId: "DROPS" + } + Goal: 10 + } + *4: { + Description: "Hunt 10 Poporing" + Criteria: { + MobId: "POPORING" + } + Goal: 10 + } + *5: { + Description: "Hunt 10 Marin" + Criteria: { + MobId: "MARIN" + } + Goal: 10 + } + } + Points: 10 +}, +{ + Id: 230202 + Name: "Exploring Poring's life (2)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 1 Mastering" + Criteria: { + MobId: "MASTERING" + } + Goal: 1 + } + *2: { + Description: "Hunt 1 Devilring" + Criteria: { + MobId: "DEVILING" + } + Goal: 1 + } + *3: { + Description: "Hunt 1 Angelring" + Criteria: { + MobId: "ANGELING" + } + Goal: 1 + } + *4: { + Description: "Hunt 1 Arch Angelring" + Criteria: { + MobId: "ARCHANGELING" + } + Goal: 1 + } + *5: { + Description: "Hunt 1 Ghostring" + Criteria: { + MobId: "GHOSTRING" + } + Goal: 1 + } + } + Points: 15 +}, +{ + Id: 230203 + Name: "Exploring Poring's life (3)" + Type: "ACH_KILL_MOB_CLASS" + Objectives: { + *1: { + Description: "Hunt 5 Metalring" + Criteria: { + MobId: "METALING" + } + Goal: 5 + } + *2: { + Description: "Hunt 5 Heavy Metalring" + Criteria: { + MobId: "METALING" + } + Goal: 5 + } + *3: { + Description: "Hunt 5 Magmaring" + Criteria: { + MobId: "MAGMARING" + } + Goal: 5 + } + } + Points: 20 +}, +{ + Id: 240000 + Name: "Complete challenges after first introduction" + Type: "ACH_QUEST" + Objectives: { + *1: { + Description: "Complete challenges after first introduction" + } + } + Points: 10 +}, +{ + Id: 240001 + Name: "Achieve Level 1" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 1" + Goal: 1 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240002 + Name: "Achieve Level 2" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 2" + Goal: 2 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240003 + Name: "Achieve Level 3" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 3" + Goal: 3 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240004 + Name: "Achieve Level 4" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 4" + Goal: 4 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240005 + Name: "Achieve Level 5" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 5" + Goal: 5 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240006 + Name: "Achieve Level 6" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 6" + Goal: 6 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240007 + Name: "Achieve Level 7" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 7" + Goal: 7 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240008 + Name: "Achieve Level 8" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 8" + Goal: 8 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240009 + Name: "Achieve Level 9" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 9" + Goal: 9 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240010 + Name: "Achieve Level 10" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 10" + Goal: 10 + } + } + Rewards: { + TitleId: 1023 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240011 + Name: "Achieve Level 11" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 11" + Goal: 11 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240012 + Name: "Achieve Level 12" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 12" + Goal: 12 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240013 + Name: "Achieve Level 13" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 13" + Goal: 13 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240014 + Name: "Achieve Level 14" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 14" + Goal: 14 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240015 + Name: "Achieve Level 15" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 15" + Goal: 15 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240016 + Name: "Achieve Level 16" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 16" + Goal: 16 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240017 + Name: "Achieve Level 17" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 17" + Goal: 17 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240018 + Name: "Achieve Level 18" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 18" + Goal: 18 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240019 + Name: "Achieve Level 19" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 19" + Goal: 19 + } + } + Rewards: { + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +{ + Id: 240020 + Name: "Achieve Level 20" + Type: "ACH_ACHIEVEMENT_RANK" + Objectives: { + *1: { + Description: "Achieving Rank 20" + Goal: 20 + } + } + Rewards: { + TitleId: 1024 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + Items: { + Gift_Box: 1 + } + } + Points: 10 +}, +/***************************************************************** + Postlude + Entries with criteria that must be read before itself. + *****************************************************************/ +{ + Id: 230100 + Name: "Poring is Love" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Poring - taming' challenge" + Criteria: { + Achieve: 230101 + } + } + *2: { + Description: "Complete 'Drops - taming' challenge" + Criteria: { + Achieve: 230102 + } + } + *3: { + Description: "Complete 'Poporing - taming' challenge" + Criteria: { + Achieve: 230103 + } + } + *4: { + Description: "Complete 'Novice Poring - taming' challenge" + Criteria: { + Achieve: 230104 + } + } + } + Rewards: { + TitleId: 1025 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230110 + Name: "Entomologist" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Chonchon - taming' challenge" + Criteria: { + Achieve: 230111 + } + } + *2: { + Description: "Complete 'Steel Chonchon - taming' challenge" + Criteria: { + Achieve: 230112 + } + } + *3: { + Description: "Complete 'Hunter Fly - taming' challenge" + Criteria: { + Achieve: 230113 + } + } + *4: { + Description: "Complete 'Rocker - taming' challenge" + Criteria: { + Achieve: 230114 + } + } + *5: { + Description: "Complete 'Spore - taming' challenge" + Criteria: { + Achieve: 230115 + } + } + *6: { + Description: "Complete 'Poison Spore - taming' challenge" + Criteria: { + Achieve: 230116 + } + } + } + Rewards: { + TitleId: 1026 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230120 + Name: "Animals are also our friend" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Lunatic - taming' challenge" + Criteria: { + Achieve: 230121 + } + } + *2: { + Description: "Complete 'Picky - taming' challenge" + Criteria: { + Achieve: 230122 + } + } + *3: { + Description: "Complete 'Savage Bebe - taming' challenge" + Criteria: { + Achieve: 230123 + } + } + *4: { + Description: "Complete 'Baby Desert Wolf - taming' challenge" + Criteria: { + Achieve: 230124 + } + } + *5: { + Description: "Complete 'Smokie - taming' challenge" + Criteria: { + Achieve: 230125 + } + } + *6: { + Description: "Complete 'Yoyo - taming' challenge" + Criteria: { + Achieve: 230126 + } + } + *7: { + Description: "Complete 'Peco Peco - taming' challenge" + Criteria: { + Achieve: 230127 + } + } + *8: { + Description: "Complete 'Petite - taming' challenge" + Criteria: { + Achieve: 230128 + } + } + } + Rewards: { + TitleId: 1027 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230140 + Name: "Monster Girls Unite!!" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Munak - taming' challenge" + Criteria: { + Achieve: 230141 + } + } + *2: { + Description: "Complete 'Isis - taming' challenge" + Criteria: { + Achieve: 230142 + } + } + *3: { + Description: "Complete 'Sohee - taming' challenge" + Criteria: { + Achieve: 230143 + } + } + *4: { + Description: "Complete 'Zherlthsh - taming' challenge" + Criteria: { + Achieve: 230144 + } + } + *5: { + Description: "Complete 'Alice - taming' challenge" + Criteria: { + Achieve: 230145 + } + } + *6: { + Description: "Complete 'Succubus - taming' challenge" + Criteria: { + Achieve: 230146 + } + } + *7: { + Description: "Complete 'Loli Ruri - taming' challenge" + Criteria: { + Achieve: 230147 + } + } + } + Rewards: { + TitleId: 1029 + Bonus: <" specialeffect(EF_INCAGILITY, AREA, playerattached()); sc_start SC_INC_AGI, 30000, 1; "> + } + Points: 50 +}, +{ + Id: 230200 + Name: "Poring seeker" + Type: "ACH_ACHIEVE" + Objectives: { + *1: { + Description: "Complete 'Exploring Poring's life (1)' challenge" + Criteria: { + Achieve: 230201 + } + } + *2: { + Description: "Complete 'Exploring Poring's life (2)' challenge" + Criteria: { + Achieve: 230202 + } + } + *3: { + Description: "Complete 'Exploring Poring's life (3)' challenge" + Criteria: { + Achieve: 230203 + } + } + } + Points: 10 +}, +) diff --git a/db/re/item_db.conf b/db/re/item_db.conf index 21c11f3cd..093fc4868 100644 --- a/db/re/item_db.conf +++ b/db/re/item_db.conf @@ -74285,6 +74285,13 @@ item_db: ( Name: "Poring Badge" }, { + Id: 6649 + AegisName: "Broken_Horn" + Name: "Broken Horn" + Buy: 10 + Weight: 10 +}, +{ Id: 6654 AegisName: "Needle_And_Thread" Name: "Needle And Thread" @@ -80092,6 +80099,12 @@ item_db: ( Weight: 10 }, { + Id: 7642 + AegisName: "Bloody_Coin" + Name: "Bloody Coin" + Buy: 10 +}, +{ Id: 7646 AegisName: "RO_Luk_Bookmark" Name: "RO Luk Bookmark" @@ -92020,6 +92033,9 @@ item_db: ( nomail: true noauction: true } + Script: <" + autobonus("{ bonus(bCriticalRate, 30); }", 100, 1000); + "> }, { Id: 12515 @@ -95509,6 +95525,13 @@ item_db: ( Script: <" itemskill ECL_SEQUOIADUST,1; "> }, { + Id: 12817 + AegisName: "Old_Card_Album_" + Name: "Old Card Album" + Type: "IT_USABLE" + EquipLv: 80 +}, +{ Id: 12818 AegisName: "High_Weapon_Box_" Name: "Advanced Weapons Box" @@ -100402,6 +100425,46 @@ item_db: ( Subtype: "W_DAGGER" }, { + Id: 13090 + AegisName: "FaceWormQueen_Leg" + Name: "Faceworm Queen Leg" + Type: "IT_WEAPON" + Buy: 20 + Weight: 500 + Atk: 180 + Matk: 120 + Range: 1 + Slots: 2 + Job: { + Novice: true + Swordsman: true + Magician: true + Archer: true + Merchant: true + Thief: true + Knight: true + Wizard: true + Blacksmith: true + Hunter: true + Assassin: true + Crusader: true + Sage: true + Rogue: true + Alchemist: true + Bard: true + Soul_Linker: true + Ninja: true + } + Loc: "EQP_WEAPON" + WeaponLv: 4 + EquipLv: 100 + Subtype: "W_DAGGER" + Script: <" + bonus(bInt, 3); + autobonus("{ bonus3(bAutoSpell, NPC_EARTHQUAKE, 1, 200); }", 8, 5000, BF_NORMAL, "{ specialeffect(EF_POTION_BERSERK, AREA, playerattached()); montransform(FACEWORM_QUEEN, 5000); }"); + "> +}, +{ Id: 13092 AegisName: "RWC_Memory_Knife" Name: "RWC Memory Knife" @@ -124774,6 +124837,22 @@ item_db: ( "> }, { + Id: 15121 + AegisName: "Robe_Of_Sarah" + Name: "Sarah Combat Robe" + Type: "IT_ARMOR" + Buy: 10 + Weight: 800 + Def: 35 + Slots: 1 + Loc: "EQP_ARMOR" + EquipLv: 145 + Script: <" + //TODO: Confirm the real rate and additional MAtk + autobonus("{ bonus(bMatk, 20); }", 100, (10 + ((getrefine()) ? getrefine() * 8 : 0)) * 1000, BF_MAGIC); + "> +}, +{ Id: 15123 AegisName: "Whikebain_Suit" Name: "Whikebain Suit" @@ -127368,6 +127447,16 @@ item_db: ( Script: <" packageitem(); "> }, { + Id: 16483 + AegisName: "Abrasive_Box_10" + Name: "Abrasive Box (10)" + Type: "IT_USABLE" + Buy: 20 + Weight: 10 + Script: <" getitem(E_Abrasive, 10); "> +}, + +{ Id: 16503 AegisName: "E_Insurance_Package" Name: "E Insurance Package" @@ -127376,6 +127465,15 @@ item_db: ( Script: <" getitem 12209,10; "> }, { + Id: 16504 + AegisName: "Bubble_Gum_Box_10" + Name: "Bubble Gum Box(10)" + Type: "IT_CASH" + Buy: 10 + Weight: 10 + Script: <" getitem(Bubble_Gum, 10); "> +}, +{ Id: 16542 AegisName: "Xmas_Bless" Name: "Xmas Bless" @@ -147121,6 +147219,29 @@ item_db: ( Weight: 300 }, { + Id: 22534 + AegisName: "Closedmind_Box" + Name: "Closed Mind Box" + Type: "IT_CASH" + Buy: 10 + Weight: 1000 + EquipLv: 1 + Script: <" + // getgroupitem(IG_Sealed_Mind_Box); + "> +}, +{ + Id: 22537 + AegisName: "PrizeOfHero" + Name: "Prize Of Hero" + Type: "IT_USABLE" + Weight: 100 + EquipLv: 1 + Script: <" + // getrandgroupitem(IG_PrizeOfHero, 1); + "> +}, +{ Id: 22540 AegisName: "Runstone_Lux" Name: "Lux Anima Rune" @@ -147365,6 +147486,14 @@ item_db: ( "> }, { + Id: 22808 + AegisName: "Special_Gift_Box" + Name: "Special Gift Box" + Type: "IT_USABLE" + Buy: 10 + Weight: 100 +}, +{ Id: 22837 AegisName: "Integer_Time" Name: "Integer Time" @@ -147391,6 +147520,13 @@ item_db: ( Weight: 10 Script: <" getrandgroupitem 22838,1; "> }, +{ + Id: 22876 + AegisName: "Old_Money_Pocket" + Name: "Old Money Pocket" + Type: "IT_USABLE" + Script: <" Zeny += rand(500, 550); "> +}, //== Shadow Equipments ===================================== { @@ -150327,6 +150463,37 @@ item_db: ( "> }, +//== New Cards + +{ + Id: 27164 + AegisName: "Faceworm_Queen_Card" + Name: "Faceworm Queen Card" + Type: "IT_CARD" + Buy: 20 + Weight: 10 + Loc: 64 + Script: <" + bonus(bMaxHPrate, -10); + bonus(bCritical, 15 + getrefine()); + bonus(bCritAtkRate, getrefine()); + "> +}, +{ + Id: 27182 + AegisName: "Captain_Felock_Card" + Name: "Captain Felock Card" + Type: "IT_CARD" + Buy: 20 + Weight: 10 + Loc: "EQP_WEAPON" + Script: <" + bonus(bAtk, 30); + bonus2(bSkillAtk, RL_AM_BLAST, getrefine() >= 10 ? 60 : 30); + bonus2(bSkillAtk, RL_HAMMER_OF_GOD, getrefine() >= 10 ? 60 : 30); + "> +}, + //== New Katars ============================================ { Id: 28000 diff --git a/db/re/mob_db.conf b/db/re/mob_db.conf index 188654cf8..e90b478e7 100644 --- a/db/re/mob_db.conf +++ b/db/re/mob_db.conf @@ -65595,13 +65595,215 @@ mob_db: ( //2526,E_BANDIT //2527,ME_ANOPHELES //2528,FACEWORM -//2529,FACEWORM_QUEEN +{ + Id: 2529 + SpriteName: "FACEWORM_QUEEN" + Name: "Faceworm Queen" + Lv: 155 + Hp: 50000000 + Sp: 1 + Exp: 200000 + JExp: 200000 + AttackRange: 2 + Attack: [4024, 1609] + Def: 100 + Mdef: 60 + Stats: { + Str: 200 + Agi: 100 + Vit: 200 + Int: 200 + Dex: 200 + Luk: 100 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Insect" + Element: ("Ele_Poison", 4) + Mode: { + CanMove: true + Aggressive: true + CastSensorIdle: true + CanAttack: true + CastSensorChase: true + ChangeChase: true + ChangeTargetMelee: true + ChangeTargetChase: true + } + MoveSpeed: 200 + AttackDelay: 768 + AttackMotion: 540 + DamageMotion: 480 + MvpExp: 90909 + MvpDrops: { + Old_Violet_Box: 2500 + Magic_Card_Album: 2500 + } + Drops: { + Yggdrasilberry: 1000 + FaceWormQueen_Leg: 100 + Broken_Horn: 5000 + Faceworm_Queen_Card: 1 + } +}, //2530,FACEWORM_DARK //2531,VENOM_BUG //2532,FACEWORM_QUEEN_R -//2533,FACEWORM_QUEEN_G -//2534,FACEWORM_QUEEN_B -//2535,FACEWORM_QUEEN_Y +{ + Id: 2533 + SpriteName: "FACEWORM_QUEEN_G" + Name: "Faceworm Queen (Green)" + Lv: 155 + Hp: 50000000 + Sp: 1 + Exp: 200000 + JExp: 200000 + AttackRange: 2 + Attack: [5000, 2000] + Def: 500 + Mdef: 60 + Stats: { + Str: 200 + Agi: 100 + Vit: 400 + Int: 200 + Dex: 200 + Luk: 100 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Insect" + Element: ("Ele_Earth", 1) + Mode: { + CanMove: true + Aggressive: true + CastSensorIdle: true + CanAttack: true + CastSensorChase: true + ChangeChase: true + ChangeTargetMelee: true + ChangeTargetChase: true + } + MoveSpeed: 200 + AttackDelay: 768 + AttackMotion: 540 + DamageMotion: 480 + MvpExp: 90909 + MvpDrops: { + Old_Violet_Box: 2500 + Magic_Card_Album: 2500 + } + Drops: { + Broken_Horn: 5000 + FaceWormQueen_Leg: 100 + Great_Nature: 5000 + } +}, +{ + Id: 2534 + SpriteName: "FACEWORM_QUEEN_B" + Name: "Faceworm Queen (Blue)" + Lv: 155 + Hp: 50000000 + Sp: 1 + Exp: 200000 + JExp: 200000 + AttackRange: 2 + Attack: [5000, 2000] + Def: 100 + Mdef: 400 + Stats: { + Str: 200 + Agi: 100 + Vit: 200 + Int: 400 + Dex: 200 + Luk: 100 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Insect" + Element: ("Ele_Water", 1) + Mode: { + CanMove: true + Aggressive: true + CastSensorIdle: true + CanAttack: true + CastSensorChase: true + ChangeChase: true + ChangeTargetMelee: true + ChangeTargetChase: true + } + MoveSpeed: 200 + AttackDelay: 768 + AttackMotion: 540 + DamageMotion: 480 + MvpExp: 90909 + MvpDrops: { + Old_Violet_Box: 2500 + Magic_Card_Album: 2500 + } + Drops: { + Broken_Horn: 5000 + FaceWormQueen_Leg: 100 + Mistic_Frozen: 5000 + } +}, +{ + Id: 2535 + SpriteName: "FACEWORM_QUEEN_Y" + Name: "Faceworm Queen (Yellow)" + Lv: 155 + Hp: 50000000 + Sp: 1 + Exp: 200000 + JExp: 200000 + AttackRange: 2 + Attack: [5000, 2000] + Def: 100 + Mdef: 60 + Stats: { + Str: 200 + Agi: 400 + Vit: 200 + Int: 200 + Dex: 200 + Luk: 100 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Insect" + Element: ("Ele_Wind", 1) + Mode: { + CanMove: true + Aggressive: true + CastSensorIdle: true + CanAttack: true + CastSensorChase: true + ChangeChase: true + ChangeTargetMelee: true + ChangeTargetChase: true + } + MoveSpeed: 200 + AttackDelay: 768 + AttackMotion: 540 + DamageMotion: 480 + MvpExp: 90909 + MvpDrops: { + Old_Violet_Box: 2500 + Magic_Card_Album: 2500 + } + Drops: { + Broken_Horn: 5000 + FaceWormQueen_Leg: 100 + Rough_Wind: 5000 + } +}, + //2536,HIDDEN_MOB3 //2537,HIDDEN_MOB4 //2538,E_KING_PORING @@ -83446,7 +83648,62 @@ mob_db: ( //2993,XM_HYLOZOIST //2994,XM_MARIONETTE //2995,XM_TEDDY_BEAR -//2996,XM_CELINE_KIMI +{ + Id: 2996 + SpriteName: "XM_CELINE_KIMI" + Name: "Celine Kimi" + Lv: 160 + Hp: 66666666 + Sp: 1 + Exp: 4444444 + JExp: 4033332 + AttackRange: 2 + Attack: [5636, 8303] + Def: 479 + Mdef: 444 + Stats: { + Str: 144 + Agi: 166 + Vit: 44 + Int: 444 + Dex: 166 + Luk: 166 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Undead" + Element: ("Ele_Ghost", 1) + Mode: { + CanMove: true + Aggressive: true + CastSensorIdle: true + CanAttack: true + CastSensorChase: true + ChangeChase: true + ChangeTargetMelee: true + ChangeTargetChase: true + } + MoveSpeed: 100 + AttackDelay: 768 + AttackMotion: 1056 + DamageMotion: 480 + MvpExp: 444444 + MvpDrops: { + Old_Card_Album: 10000 + Old_Violet_Box: 10000 + Closedmind_Box: 10000 + } + Drops: { + Closedmind_Box: 4000 + Butterfly_Hairpin: 4000 + Bloody_Coin: 4000 + C_Red_Bonnet: 100 + Old_Parasol: 100 + Flower: 10000 + } +}, + //2997,G_XM_CELINE_KIMI //2998,EP14_MORS_EVENT //2999,EP14_MORS_BOSSA @@ -83479,7 +83736,50 @@ mob_db: ( //3026,FIREPIT //3027,FULBUK //3028,SONIA -//3029,GRIM_REAPER_ANKOU +{ + Id: 3029 + SpriteName: "GRIM_REAPER_ANKOU" + Name: "Grim Reaper Ankou" + Lv: 159 + Hp: 50000000 + Sp: 1553 + Exp: 300000 + JExp: 330000 + AttackRange: 1 + Attack: [1500, 2500] + Def: 200 + Mdef: 70 + Stats: { + Str: 200 + Agi: 100 + Vit: 200 + Int: 200 + Dex: 220 + Luk: 100 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Undead" + Element: ("Ele_Undead", 4) + Mode: { + CanMove: true + Aggressive: true + CanAttack: true + } + MoveSpeed: 200 + AttackDelay: 900 + AttackMotion: 864 + DamageMotion: 480 + MvpExp: 0 + Drops: { + Yggdrasilberry: 500 + Old_Blue_Box: 200 + Branch_Of_Dead_Tree: 200 + PrizeOfHero: 10000 + Fruit_Of_Mastela: 200 + } +}, //3030,STANDING_SOUL //3031,MUTANT_NECROMANCER //3032,MUTANT_GHOUL @@ -83624,7 +83924,80 @@ mob_db: ( //3155,REPAIR_ROBOT //3156,EXPLORATION_ROVER //3166,M_E_DEVILING -// +{ + Id: 3181 + SpriteName: "E1_FELOCK" + Name: "Captain Ferlock" + Lv: 130 + Hp: 3000000 + Sp: 1 + Exp: 3088 + JExp: 333333 + AttackRange: 10 + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Large" + Race: "RC_Dragon" + Element: ("Ele_Dark", 2) + Mode: { + CanMove: true + CanAttack: true + } + MoveSpeed: 170 + AttackDelay: 1018 + AttackMotion: 1008 + DamageMotion: 300 + MvpExp: 0 + Drops: { + Felock_Armor: 100 + Felock_Cape: 100 + Felock_Boots: 100 + Vit_Dish07: 3000 + Str_Dish07: 3000 + Agi_Dish07: 3000 + Int_Dish07: 3000 + Dex_Dish07: 3000 + Captain_Felock_Card: 1 + } +}, +{ + Id: 3190 + SpriteName: "MM_SARAH" + Name: "Sarah" + Lv: 160 + Hp: 100000000 + Sp: 1 + AttackRange: 12 + Attack: [1090, 2755] + Def: 276 + Mdef: 255 + Stats: { + Str: 43 + Agi: 161 + Vit: 6 + Int: 188 + Dex: 225 + Luk: 136 + } + ViewRange: 10 + ChaseRange: 12 + Size: "Size_Medium" + Race: "RC_Human" + Element: ("Ele_Neutral", 1) + Mode: { + Aggressive: true + Boss: true + CanAttack: true + } + MoveSpeed: 2000 + AttackDelay: 500 + AttackMotion: 500 + Drops: { + Robe_Of_Sarah: 1000 + Sarah_Card: 1 + } +}, + /*{ Id: 3201 SpriteName: "JT_LUCKYCASE" diff --git a/sql-files/main.sql b/sql-files/main.sql index d1c6364c6..d29f05a2e 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -118,6 +118,28 @@ CREATE TABLE IF NOT EXISTS `autotrade_merchants` ( ) ENGINE=MyISAM; -- +-- Table structure for table `char_achievements` +-- + +CREATE TABLE `char_achievements` ( + `char_id` INT(11) UNSIGNED NOT NULL, + `ach_id` INT(11) UNSIGNED NOT NULL, + `completed_at` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `rewarded_at` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_0` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_1` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_2` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_3` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_4` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_5` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_6` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_7` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_8` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_9` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`, `ach_id`) +) ENGINE=MyISAM; + +-- -- Table structure for table `cart_inventory` -- @@ -221,6 +243,7 @@ CREATE TABLE IF NOT EXISTS `char` ( `hotkey_rowshift` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `attendance_count` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `attendance_timer` BIGINT(20) NULL DEFAULT '0', + `title_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`char_id`), UNIQUE KEY `name_key` (`name`), KEY `account_id` (`account_id`), @@ -894,6 +917,8 @@ INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1496588700); -- 2017-06-0 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1509835214); -- 2017-11-04--10-39.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1519671456); -- 2018-02-26--15-57.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1520654809); -- 2018-03-10--04-06.sql +INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1527964800); -- 2018-06-03--00-10.sql +INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1528026381); -- 2018-06-03--17-16.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1528180320); -- 2018-06-05--12-02.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1532403228); -- 2018-07-24--03-23.sql -- diff --git a/sql-files/upgrades/2018-06-03--00-10.sql b/sql-files/upgrades/2018-06-03--00-10.sql new file mode 100644 index 000000000..c7f6ac48d --- /dev/null +++ b/sql-files/upgrades/2018-06-03--00-10.sql @@ -0,0 +1,40 @@ +#1527964800 + +-- This file is part of Hercules. +-- http://herc.ws - http://github.com/HerculesWS/Hercules +-- +-- Copyright (C) 2018 Hercules Dev Team +-- Copyright (C) Smokexyz +-- +-- Hercules is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +CREATE TABLE `char_achievements` ( + `char_id` INT(11) UNSIGNED NOT NULL, + `ach_id` INT(11) UNSIGNED NOT NULL, + `completed_at` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `rewarded_at` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_0` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_1` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_2` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_3` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_4` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_5` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_6` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_7` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_8` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `obj_9` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`, `ach_id`) +) ENGINE=MyISAM; + +INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1527964800, 'No'); diff --git a/sql-files/upgrades/2018-06-03--17-16.sql b/sql-files/upgrades/2018-06-03--17-16.sql new file mode 100644 index 000000000..e14ca62ca --- /dev/null +++ b/sql-files/upgrades/2018-06-03--17-16.sql @@ -0,0 +1,24 @@ +#1528026381 + +-- This file is part of Hercules. +-- http://herc.ws - http://github.com/HerculesWS/Hercules +-- +-- Copyright (C) 2018 Hercules Dev Team +-- Copyright (C) Dastgir +-- +-- Hercules is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +ALTER TABLE `char` ADD `title_id` INT(11) UNSIGNED NOT NULL DEFAULT '0'; + +INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1528026381, 'No'); diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index c88ba25d4..705234d01 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -45,5 +45,7 @@ 2017-11-04--10-39.sql 2018-02-26--15-57.sql 2018-03-10--04-06.sql +2018-06-03--00-10.sql +2018-06-03--17-16.sql 2018-06-05--12-02.sql 2018-07-24--03-23.sql diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c index 9f075d909..db2c3702e 100644 --- a/src/char/HPMchar.c +++ b/src/char/HPMchar.c @@ -27,6 +27,7 @@ #include "char/char.h" #include "char/geoip.h" #include "char/inter.h" +#include "char/int_achievement.h" #include "char/int_auction.h" #include "char/int_clan.h" #include "char/int_elemental.h" @@ -42,6 +43,7 @@ #include "char/loginif.h" #include "char/mapif.h" #include "char/pincode.h" + #include "common/HPMi.h" #include "common/conf.h" #include "common/console.h" diff --git a/src/char/Makefile.in b/src/char/Makefile.in index 49fc8ec92..95c8df813 100644 --- a/src/char/Makefile.in +++ b/src/char/Makefile.in @@ -40,12 +40,12 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_auction.c int_clan.c int_elemental.c int_guild.c \ - int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ +CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_achievement.c int_auction.c int_clan.c int_elemental.c \ + int_guild.c int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ int_quest.c int_rodex.c int_storage.c pincode.c CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C))) -CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_auction.h int_clan.h int_elemental.h int_guild.h \ - int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ +CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_achievement.h int_auction.h int_clan.h int_elemental.h \ + int_guild.h int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ int_quest.h int_rodex.h int_storage.h pincode.h CHAR_PH = diff --git a/src/char/char.c b/src/char/char.c index 99198fa50..54f6ca7d1 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -37,6 +37,7 @@ #include "char/int_quest.h" #include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/inter.h" #include "char/loginif.h" #include "char/mapif.h" @@ -112,6 +113,7 @@ char acc_reg_num_db[32] = "acc_reg_num_db"; char acc_reg_str_db[32] = "acc_reg_str_db"; char char_reg_str_db[32] = "char_reg_str_db"; char char_reg_num_db[32] = "char_reg_num_db"; +char char_achievement_db[256] = "char_achievements"; static struct char_interface char_s; struct char_interface *chr; @@ -291,12 +293,18 @@ static void char_set_char_offline(int char_id, int account_id) } else { - struct mmo_charstatus* cp = (struct mmo_charstatus*) idb_get(chr->char_db_,char_id); + struct mmo_charstatus *cp = (struct mmo_charstatus*) idb_get(chr->char_db_, char_id); + /* Character Achievements */ + struct char_achievements *c_ach = (struct char_achievements *) idb_get(inter_achievement->char_achievements, char_id); inter_guild->CharOffline(char_id, cp?cp->guild_id:-1); - if (cp) + if (cp != NULL) idb_remove(chr->char_db_,char_id); + if (c_ach != NULL) { + VECTOR_CLEAR(*c_ach); + idb_remove(inter_achievement->char_achievements, char_id); + } if( SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `online`='0' WHERE `char_id`='%d' LIMIT 1", char_db, char_id) ) Sql_ShowDebug(inter->sql_handle); @@ -468,7 +476,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || (p->clan_id != cp->clan_id) || (p->last_login != cp->last_login) || (p->attendance_count != cp->attendance_count) || - (p->attendance_timer != cp->attendance_timer) + (p->attendance_timer != cp->attendance_timer) || (p->title_id != cp->title_id) ) { //Save status unsigned int opt = 0; @@ -486,7 +494,8 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'," - "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'" + "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'," + "`title_id`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -499,6 +508,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (unsigned long)p->delete_date, // FIXME: platform-dependent size p->look.robe,p->slotchange,opt,p->font,p->uniqueitem_counter, p->hotkey_rowshift,p->clan_id,p->last_login, p->attendance_count, p->attendance_timer, + p->title_id, p->account_id, p->char_id) ) { Sql_ShowDebug(inter->sql_handle); @@ -1069,7 +1079,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`slotchange`,`unban_time`,`sex`" + "`robe`,`slotchange`,`unban_time`,`sex`,`title_id`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, sizeof p.char_id, NULL, NULL) @@ -1112,6 +1122,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, sizeof p.slotchange, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_TIME, &unban_time, sizeof unban_time, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof sex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_INT, &p.title_id, sizeof p.title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -1176,7 +1187,8 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`," - "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`" + "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`," + "`title_id`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) @@ -1243,6 +1255,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa || SQL_ERROR == SQL->StmtBindColumn(stmt, 60, SQLDT_INT64, &p->last_login, sizeof p->last_login, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 61, SQLDT_SHORT, &p->attendance_count, sizeof p->attendance_count, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 62, SQLDT_INT64, &p->attendance_timer, sizeof p->attendance_timer, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 63, SQLDT_INT, &p->title_id, sizeof p->title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -5443,6 +5456,7 @@ static bool char_sql_config_read_pc(const char *filename, const struct config_t libconfig->setting_lookup_mutable_string(setting, "hotkey_db", hotkey_db, sizeof(hotkey_db)); libconfig->setting_lookup_mutable_string(setting, "scdata_db", scdata_db, sizeof(scdata_db)); libconfig->setting_lookup_mutable_string(setting, "inventory_db", inventory_db, sizeof(inventory_db)); + libconfig->setting_lookup_mutable_string(setting, "achievement_db", char_achievement_db, sizeof(char_achievement_db)); libconfig->setting_lookup_mutable_string(setting, "cart_db", cart_db, sizeof(cart_db)); libconfig->setting_lookup_mutable_string(setting, "charlog_db", charlog_db, sizeof(charlog_db)); libconfig->setting_lookup_mutable_string(setting, "storage_db", storage_db, sizeof(storage_db)); @@ -6319,6 +6333,7 @@ void char_load_defaults(void) inter_quest_defaults(); inter_storage_defaults(); inter_rodex_defaults(); + inter_achievement_defaults(); inter_defaults(); geoip_defaults(); } diff --git a/src/char/char.h b/src/char/char.h index 4d816583a..81cab1eaf 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -340,6 +340,7 @@ extern char acc_reg_num_db[32]; extern char acc_reg_str_db[32]; extern char char_reg_str_db[32]; extern char char_reg_num_db[32]; +extern char char_achievement_db[256]; extern int guild_exp_rate; diff --git a/src/char/int_achievement.c b/src/char/int_achievement.c new file mode 100644 index 000000000..14311ecf0 --- /dev/null +++ b/src/char/int_achievement.c @@ -0,0 +1,252 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#define HERCULES_CORE + +#include "int_achievement.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" + +#include "common/db.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" + +#include <stdio.h> +#include <stdlib.h> + +static struct inter_achievement_interface inter_achievement_s; +struct inter_achievement_interface *inter_achievement; + +/** + * Saves changed achievements for a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to loaded achievements. + * @param[in] p pointer to map-sent character achievements. + * @return number of achievements saved. + */ +static int inter_achievement_tosql(int char_id, struct char_achievements *cp, const struct char_achievements *p) +{ + StringBuf buf; + int i = 0, rows = 0; + + nullpo_ret(cp); + nullpo_ret(p); + Assert_ret(char_id > 0); + + StrBuf->Init(&buf); + StrBuf->Printf(&buf, "REPLACE INTO `%s` (`char_id`, `ach_id`, `completed_at`, `rewarded_at`", char_achievement_db); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->AppendStr(&buf, ") VALUES "); + + for (i = 0; i < VECTOR_LENGTH(*p); i++) { + int j = 0; + bool save = false; + struct achievement *pa = &VECTOR_INDEX(*p, i), *cpa = NULL; + + ARR_FIND(0, VECTOR_LENGTH(*cp), j, ((cpa = &VECTOR_INDEX(*cp, j)) && cpa->id == pa->id)); + + if (j == VECTOR_LENGTH(*cp)) + save = true; + else if (memcmp(cpa, pa, sizeof(struct achievement)) != 0) + save = true; + + if (save) { + StrBuf->Printf(&buf, "%s('%d', '%d', '%"PRId64"', '%"PRId64"'", rows ?", ":"", char_id, pa->id, (int64)pa->completed_at, (int64)pa->rewarded_at); + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) + StrBuf->Printf(&buf, ", '%d'", pa->objective[j]); + StrBuf->AppendStr(&buf, ")"); + rows++; + } + } + + if (rows > 0 && SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); // Destroy the buffer. + return 0; + } + // Destroy the buffer. + StrBuf->Destroy(&buf); + + if (rows) { + ShowInfo("achievements saved for char %d (total: %d, saved: %d)\n", char_id, VECTOR_LENGTH(*p), rows); + + /* Sync with inter-db acheivements. */ + VECTOR_CLEAR(*cp); + VECTOR_ENSURE(*cp, VECTOR_LENGTH(*p), 1); + VECTOR_PUSHARRAY(*cp, VECTOR_DATA(*p), VECTOR_LENGTH(*p)); + } + + return rows; +} + +/** + * Retrieves all achievements of a character. + * @param[in] char_id character identifier. + * @param[out] cp pointer to character achievements structure. + * @return true on success, false on failure. + */ +static bool inter_achievement_fromsql(int char_id, struct char_achievements *cp) +{ + StringBuf buf; + char *data; + int i = 0, num_rows = 0; + + nullpo_ret(cp); + + Assert_ret(char_id > 0); + + // char_achievements (`char_id`, `ach_id`, `completed_at`, `rewarded_at`, `obj_0`, `obj_2`, ...`obj_9`) + StrBuf->Init(&buf); + StrBuf->AppendStr(&buf, "SELECT `ach_id`, `completed_at`, `rewarded_at`"); + for (i = 0; i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + StrBuf->Printf(&buf, ", `obj_%d`", i); + StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id` = '%d' ORDER BY `ach_id`", char_achievement_db, char_id); + + if (SQL_ERROR == SQL->QueryStr(inter->sql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(inter->sql_handle); + StrBuf->Destroy(&buf); + return false; + } + + VECTOR_CLEAR(*cp); + + if ((num_rows = (int) SQL->NumRows(inter->sql_handle)) != 0) { + int j = 0; + + VECTOR_ENSURE(*cp, num_rows, 1); + + for (i = 0; i < num_rows && SQL_SUCCESS == SQL->NextRow(inter->sql_handle); i++) { + struct achievement t_ach = { 0 }; + SQL->GetData(inter->sql_handle, 0, &data, NULL); t_ach.id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); t_ach.completed_at = atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); t_ach.rewarded_at = atoi(data); + /* Objectives */ + for (j = 0; j < MAX_ACHIEVEMENT_OBJECTIVES; j++) { + SQL->GetData(inter->sql_handle, j + 3, &data, NULL); + t_ach.objective[j] = atoi(data); + } + /* Add Entry */ + VECTOR_PUSH(*cp, t_ach); + } + } + + SQL->FreeResult(inter->sql_handle); + + StrBuf->Destroy(&buf); + + if (num_rows > 0) + ShowInfo("achievements loaded for char %d (total: %d)\n", char_id, num_rows); + + return true; +} + +/** + * Handles checking of map server packets and calls appropriate functions. + * @param fd socket descriptor. + * @return 0 on failure, 1 on succes. + */ +static int inter_achievement_parse_frommap(int fd) +{ + RFIFOHEAD(fd); + + switch (RFIFOW(fd,0)) { + case 0x3012: + mapif->pLoadAchievements(fd); + break; + case 0x3013: + mapif->pSaveAchievements(fd); + break; + default: + return 0; + } + + return 1; +} + +/** + * Initialization function + */ +static int inter_achievement_sql_init(void) +{ + // Initialize the loaded db storage. + // used as a comparand against map-server achievement data before saving. + inter_achievement->char_achievements = idb_alloc(DB_OPT_RELEASE_DATA); + return 1; +} + +/** + * This function ensures idb's entry. + */ +static struct DBData inter_achievement_ensure_char_achievements(union DBKey key, va_list args) +{ + struct char_achievements *ca = NULL; + + CREATE(ca, struct char_achievements, 1); + VECTOR_INIT(*ca); + + return DB->ptr2data(ca); +} + +/** + * Cleaning function called through db_destroy() + */ +static int inter_achievement_char_achievements_clear(union DBKey key, struct DBData *data, va_list args) +{ + struct char_achievements *ca = DB->data2ptr(data); + + VECTOR_CLEAR(*ca); + + return 0; +} + +/** + * Finalization function. + */ +static void inter_achievement_sql_final(void) +{ + inter_achievement->char_achievements->destroy(inter_achievement->char_achievements, inter_achievement->char_achievements_clear); +} + +/** + * Inter-achievement interface. + */ +void inter_achievement_defaults(void) +{ + inter_achievement = &inter_achievement_s; + /* */ + inter_achievement->ensure_char_achievements = inter_achievement_ensure_char_achievements; + /* */ + inter_achievement->sql_init = inter_achievement_sql_init; + inter_achievement->sql_final = inter_achievement_sql_final; + /* */ + inter_achievement->tosql = inter_achievement_tosql; + inter_achievement->fromsql = inter_achievement_fromsql; + /* */ + inter_achievement->parse_frommap = inter_achievement_parse_frommap; + inter_achievement->char_achievements_clear = inter_achievement_char_achievements_clear; +} diff --git a/src/char/int_achievement.h b/src/char/int_achievement.h new file mode 100644 index 000000000..4a44a798d --- /dev/null +++ b/src/char/int_achievement.h @@ -0,0 +1,53 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef CHAR_INT_ACHIEVEMENT_H +#define CHAR_INT_ACHIEVEMENT_H + +#include "common/hercules.h" +#include "common/db.h" + +struct achievement; +struct char_achievements; + +/** + * inter_achievement Interface + */ +struct inter_achievement_interface { + struct DBMap *char_achievements; + /* */ + int (*sql_init) (void); + void (*sql_final) (void); + /* */ + int (*tosql) (int char_id, struct char_achievements *cp, const struct char_achievements *p); + bool (*fromsql) (int char_id, struct char_achievements *a); + /* */ + struct DBData(*ensure_char_achievements) (union DBKey key, va_list args); + int (*char_achievements_clear) (union DBKey key, struct DBData *data, va_list args); + /* */ + int (*parse_frommap) (int fd); +}; + +#ifdef HERCULES_CORE +void inter_achievement_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_achievement_interface *inter_achievement; +#endif /* CHAR_INT_ACHIEVEMENT_H */ diff --git a/src/char/inter.c b/src/char/inter.c index 7269009a7..418c9b0a1 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -36,6 +36,7 @@ #include "char/int_quest.h" #include "char/int_rodex.h" #include "char/int_storage.h" +#include "char/int_achievement.h" #include "char/mapif.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -70,7 +71,7 @@ int party_share_level = 10; // recv. packet list static int inter_recv_packet_length[] = { -1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000- - 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage [Smokexyz] + 6,-1, 6,-1, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010- Account Storage, Achievements [Smokexyz] -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 9, 0, 0, 10,10, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- Clan System(3044-3045) @@ -974,6 +975,7 @@ static int inter_init_sql(const char *file) inter_mail->sql_init(); inter_auction->sql_init(); inter_rodex->sql_init(); + inter_achievement->sql_init(); geoip->init(); inter->msg_config_read("conf/messages.conf", false); @@ -995,6 +997,7 @@ static void inter_final(void) inter_mail->sql_final(); inter_auction->sql_final(); inter_rodex->sql_final(); + inter_achievement->sql_final(); geoip->final(true); inter->do_final_msg(); @@ -1133,6 +1136,7 @@ static int inter_parse_frommap(int fd) || inter_quest->parse_frommap(fd) || inter_rodex->parse_frommap(fd) || inter_clan->parse_frommap(fd) + || inter_achievement->parse_frommap(fd) ) break; else diff --git a/src/char/mapif.c b/src/char/mapif.c index 30f8c1178..dc5735550 100644 --- a/src/char/mapif.c +++ b/src/char/mapif.c @@ -24,6 +24,7 @@ #include "mapif.h" #include "char/char.h" +#include "char/int_achievement.h" #include "char/int_auction.h" #include "char/int_clan.h" #include "char/int_guild.h" @@ -2346,6 +2347,120 @@ static int mapif_parse_ClanMemberCount(int fd, int clan_id, int kick_interval) return 0; } +// Achievement System +/** + * Parse achievement load request from the map server + * @param[in] fd socket descriptor. + */ +static void mapif_parse_load_achievements(int fd) +{ + int char_id = 0; + + /* Read received information from map-server. */ + RFIFOHEAD(fd); + char_id = RFIFOL(fd, 2); + + /* Load and send achievements to map */ + mapif->achievement_load(fd, char_id); +} + +/** + * Loads achievements and sends to the map server. + * @param[in] fd socket descriptor + * @param[in] char_id character Id. + */ +static void mapif_achievement_load(int fd, int char_id) +{ + struct char_achievements *cp = NULL; + + /* Ensure data exists */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + /* Load storage for char-server. */ + inter_achievement->fromsql(char_id, cp); + + /* Send Achievements to map server. */ + mapif->sAchievementsToMap(fd, char_id, cp); +} + +/** + * Sends achievement data of a character to the map server. + * @packet[out] 0x3810 <packet_id>.W <payload_size>.W <char_id>.L <char_achievements[]>.P + * @param[in] fd socket descriptor. + * @param[in] char_id Character ID. + * @param[in] cp Pointer to character's achievement data vector. + */ +static void mapif_send_achievements_to_map(int fd, int char_id, const struct char_achievements *cp) +{ + int i = 0; + int data_size = 0; + + nullpo_retv(cp); + + data_size = sizeof(struct achievement) * VECTOR_LENGTH(*cp); + +STATIC_ASSERT((sizeof(struct achievement) * MAX_ACHIEVEMENT_DB + 8 <= UINT16_MAX), + "The achievements data can potentially be larger than the maximum packet size. This may cause errors at run-time."); + + /* Send to the map server. */ + WFIFOHEAD(fd, (8 + data_size)); + WFIFOW(fd, 0) = 0x3810; + WFIFOW(fd, 2) = (8 + data_size); + WFIFOL(fd, 4) = char_id; + for (i = 0; i < VECTOR_LENGTH(*cp); i++) + memcpy(WFIFOP(fd, 8 + i * sizeof(struct achievement)), &VECTOR_INDEX(*cp, i), sizeof(struct achievement)); + WFIFOSET(fd, 8 + data_size); +} + +/** + * Handles achievement request and saves data from map server. + * @packet[in] 0x3013 <packet_size>.W <char_id>.L <char_achievement>.P + * @param[in] fd session socket descriptor. + */ +static void mapif_parse_save_achievements(int fd) +{ + int size = 0, char_id = 0, payload_count = 0, i = 0; + struct char_achievements p = { 0 }; + + RFIFOHEAD(fd); + size = RFIFOW(fd, 2); + char_id = RFIFOL(fd, 4); + + payload_count = (size - 8) / sizeof(struct achievement); + + VECTOR_INIT(p); + VECTOR_ENSURE(p, payload_count, 1); + + for (i = 0; i < payload_count; i++) { + struct achievement ach = { 0 }; + memcpy(&ach, RFIFOP(fd, 8 + i * sizeof(struct achievement)), sizeof(struct achievement)); + VECTOR_PUSH(p, ach); + } + + mapif->achievement_save(char_id, &p); + + VECTOR_CLEAR(p); +} + +/** + * Handles inter-server achievement db ensuring + * and saves current achievements to sql. + * @param[in] char_id character identifier. + * @param[out] p pointer to character achievements vector. + */ +static void mapif_achievement_save(int char_id, struct char_achievements *p) +{ + struct char_achievements *cp = NULL; + + nullpo_retv(p); + + /* Get loaded achievements. */ + cp = idb_ensure(inter_achievement->char_achievements, char_id, inter_achievement->ensure_char_achievements); + + if (VECTOR_LENGTH(*p)) /* Save current achievements. */ + inter_achievement->tosql(char_id, cp, p); +} + void mapif_defaults(void) { mapif = &mapif_s; @@ -2361,6 +2476,11 @@ void mapif_defaults(void) mapif->sendallwos = mapif_sendallwos; mapif->send = mapif_send; mapif->send_users_count = mapif_send_users_count; + mapif->pLoadAchievements = mapif_parse_load_achievements; + mapif->sAchievementsToMap = mapif_send_achievements_to_map; + mapif->pSaveAchievements = mapif_parse_save_achievements; + mapif->achievement_load = mapif_achievement_load; + mapif->achievement_save = mapif_achievement_save; mapif->auction_message = mapif_auction_message; mapif->auction_sendlist = mapif_auction_sendlist; mapif->parse_auction_requestlist = mapif_parse_auction_requestlist; diff --git a/src/char/mapif.h b/src/char/mapif.h index d67ce1c79..bfdefe4ea 100644 --- a/src/char/mapif.h +++ b/src/char/mapif.h @@ -40,6 +40,11 @@ struct mapif_interface { int (*sendallwos) (int sfd, unsigned char *buf, unsigned int len); int (*send) (int fd, unsigned char *buf, unsigned int len); void (*send_users_count) (int users); + void (*pLoadAchievements) (int fd); + void (*sAchievementsToMap) (int fd, int char_id, const struct char_achievements *p); + void (*pSaveAchievements) (int fd); + void (*achievement_load) (int fd, int char_id); + void (*achievement_save) (int char_id, struct char_achievements *p); void (*auction_message) (int char_id, unsigned char result); void (*auction_sendlist) (int fd, int char_id, short count, short pages, unsigned char *buf); void (*parse_auction_requestlist) (int fd); diff --git a/src/common/mmo.h b/src/common/mmo.h index 1b9562e9d..7e0d915eb 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -218,6 +218,16 @@ #define MAX_QUEST_OBJECTIVES 3 // Max quest objectives for a quest #endif +// Achievements [Smokexyz/Hercules] +#ifndef MAX_ACHIEVEMENT_DB +#define MAX_ACHIEVEMENT_DB 360 // Maximum number of achievements +#define MAX_ACHIEVEMENT_OBJECTIVES 10 // Maximum number of achievement objectives +STATIC_ASSERT(MAX_ACHIEVEMENT_OBJECTIVES <= 10, "This value is limited by the client and database layout and should only be increased if you know the consequences."); +#define MAX_ACHIEVEMENT_RANKS 20 // Achievement Ranks +STATIC_ASSERT(MAX_ACHIEVEMENT_RANKS <= 255, "This value is limited by the client and database layout and should only be increased if you know the consequences."); +#define MAX_ACHIEVEMENT_ITEM_REWARDS 10 // Achievement Rewards +#endif + // for produce #define MIN_ATTRIBUTE 0 #define MAX_ATTRIBUTE 4 @@ -616,6 +626,14 @@ struct hotkey { #endif }; +struct achievement { // Achievements [Smokexyz/Hercules] + int id; + int objective[MAX_ACHIEVEMENT_OBJECTIVES]; + time_t completed_at, rewarded_at; +}; + +VECTOR_STRUCT_DECL(char_achievements, struct achievement); + struct mmo_charstatus { int char_id; int account_id; @@ -687,6 +705,8 @@ struct mmo_charstatus { short attendance_count; unsigned char hotkey_rowshift; + + int32 title_id; // Achievement Title[Dastgir/Hercules] }; typedef enum mail_status { diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c index e4640d09d..091a53311 100644 --- a/src/map/HPMmap.c +++ b/src/map/HPMmap.c @@ -47,6 +47,7 @@ #include "common/sysinfo.h" #include "common/timer.h" #include "common/utils.h" +#include "map/achievement.h" #include "map/atcommand.h" #include "map/battle.h" #include "map/battleground.h" diff --git a/src/map/Makefile.in b/src/map/Makefile.in index 7c04a4f37..1bef380e1 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -40,14 +40,14 @@ MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h -MAP_C = atcommand.c battle.c battleground.c buyingstore.c channel.c chat.c \ +MAP_C = achievement.c atcommand.c battle.c battleground.c buyingstore.c channel.c chat.c \ chrif.c clan.c clif.c date.c duel.c elemental.c guild.c homunculus.c HPMmap.c \ instance.c intif.c irc-bot.c itemdb.c log.c mail.c map.c mapreg_sql.c \ mercenary.c mob.c npc.c npc_chat.c party.c path.c pc.c pc_groups.c \ pet.c quest.c rodex.c script.c searchstore.c skill.c status.c storage.c \ trade.c unit.c vending.c MAP_OBJ = $(addprefix obj_sql/, $(patsubst %c,%o,$(MAP_C))) -MAP_H = atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \ +MAP_H = achievement.h atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \ chrif.h clan.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \ instance.h intif.h irc-bot.h itemdb.h log.h mail.h map.h mapreg.h \ mercenary.h messages.h messages_main.h messages_re.h messages_zero.h \ diff --git a/src/map/achievement.c b/src/map/achievement.c new file mode 100644 index 000000000..0369b0fb5 --- /dev/null +++ b/src/map/achievement.c @@ -0,0 +1,1983 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) Smokexyz +* Copyright (C) Dastgir +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#define HERCULES_CORE + +#include "map/achievement.h" + +#include "map/itemdb.h" +#include "map/mob.h" +#include "map/party.h" +#include "map/pc.h" +#include "map/script.h" + +#include "common/conf.h" +#include "common/db.h" +#include "common/memmgr.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/strlib.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static struct achievement_interface achievement_s; +struct achievement_interface *achievement; + +/** + * Retrieve an achievement via it's ID. + * @param aid as the achievement ID. + * @return NULL or pointer to the achievement data. + */ +static const struct achievement_data *achievement_get(int aid) +{ + return (struct achievement_data *) idb_get(achievement->db, aid); +} + +/** + * Searches the provided achievement data for an achievement, + * optionally creates a new one if no key exists. + * @param[in] sd a pointer to map_session_data. + * @param[in] aid ID of the achievement provided as key. + * @param[in] create new key creation flag. + * @return pointer to the session's achievement data. + */ +static struct achievement *achievement_ensure(struct map_session_data *sd, const struct achievement_data *ad) +{ + struct achievement *s_ad = NULL; + int i = 0; + + nullpo_retr(NULL, sd); + nullpo_retr(NULL, ad); + + /* Lookup for achievement entry */ + ARR_FIND(0, VECTOR_LENGTH(sd->achievement), i, (s_ad = &VECTOR_INDEX(sd->achievement, i)) && s_ad->id == ad->id); + + if (i == VECTOR_LENGTH(sd->achievement)) { + struct achievement ta = { 0 }; + ta.id = ad->id; + + VECTOR_ENSURE(sd->achievement, 1, 1); + VECTOR_PUSH(sd->achievement, ta); + + s_ad = &VECTOR_LAST(sd->achievement); + } + + return s_ad; +} + +/** + * Calculates the achievement's totals via reference. + * @param[in] sd pointer to map_session_data + * @param[out] tota_points pointer to total points var + * @param[out] completed pointer to total var + * @param[out] rank pointer to completed var + * @param[out] curr_rank_points pointer to achievement rank var + */ +static void achievement_calculate_totals(const struct map_session_data *sd, int *total_points, int *completed, int *rank, int *curr_rank_points) +{ + const struct achievement *a = NULL; + const struct achievement_data *ad = NULL; + int tmp_curr_points = 0; + int tmp_total_points = 0; + int tmp_total_completed = 0; + int tmp_rank = 0; + int i = 0; + + nullpo_retv(sd); + + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + a = &VECTOR_INDEX(sd->achievement, i); + + if ((ad = achievement->get(a->id)) == NULL) + continue; + + if (a->completed_at != 0) { + tmp_total_points += ad->points; + tmp_total_completed++; + } + } + + if (tmp_total_points > 0) { + tmp_curr_points = tmp_total_points; + for (i = 0; i < MAX_ACHIEVEMENT_RANKS + && tmp_curr_points >= VECTOR_INDEX(achievement->rank_exp, i) + && i < VECTOR_LENGTH(achievement->rank_exp); i++) { + tmp_curr_points -= VECTOR_INDEX(achievement->rank_exp, i); + tmp_rank++; + } + } + + if (total_points != NULL) + *total_points = tmp_total_points; + + if (completed != NULL) + *completed = tmp_total_completed; + + if (rank != NULL) + *rank = tmp_rank; + + if (curr_rank_points != NULL) + *curr_rank_points = tmp_curr_points; +} + +/** + * Checks whether all objectives of the achievement are completed. + * @param[in] sd as the map_session_data pointer + * @param[in] ad as the achievement_data pointer + * @return true if complete, false if not. + */ +static bool achievement_check_complete(struct map_session_data *sd, const struct achievement_data *ad) +{ + int i; + struct achievement *ach = NULL; + + nullpo_retr(false, sd); + nullpo_retr(false, ad); + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return false; + for (i = 0; i < VECTOR_LENGTH(ad->objective); i++) + if (ach->objective[i] < VECTOR_INDEX(ad->objective, i).goal) + return false; + + return true; +} + +/** + * Compares the progress of an objective against it's goal. + * Increments the progress of the objective by the specified amount, towards the goal. + * @param sd [in] as a pointer to map_session_data + * @param ad [in] as a pointer to the achievement_data + * @param obj_idx [in] as the index of the objective. + * @param progress [in] as the progress of the objective to be added. + */ +static void achievement_progress_add(struct map_session_data *sd, const struct achievement_data *ad, unsigned int obj_idx, int progress) +{ + struct achievement *ach = NULL; + + nullpo_retv(sd); + nullpo_retv(ad); + + Assert_retv(progress != 0); + Assert_retv(obj_idx < VECTOR_LENGTH(ad->objective)); + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + if (ach->completed_at) + return; // ignore the call if the achievement is completed. + + // Check and increment the objective count. + if (!ach->objective[obj_idx] || ach->objective[obj_idx] < VECTOR_INDEX(ad->objective, obj_idx).goal) { + ach->objective[obj_idx] = min(progress + ach->objective[obj_idx], VECTOR_INDEX(ad->objective, obj_idx).goal); + + // Check if the Achievement is complete. + if (achievement->check_complete(sd, ad)) { + achievement->validate_achieve(sd, ad->id); + ach->completed_at = time(NULL); + } + + // update client. + clif->achievement_send_update(sd->fd, sd, ad); + } +} + +/** + * Compare an absolute progress value against the goal of an objective. + * Does not add/increase progress. + * @param sd [in] pointer to map-server session data. + * @param ad [in] pointer to achievement data. + * @param obj_idx [in] index of the objective in question. + * @param progress progress of the objective in question. + */ +static void achievement_progress_set(struct map_session_data *sd, const struct achievement_data *ad, unsigned int obj_idx, int progress) +{ + struct achievement *ach = NULL; + + nullpo_retv(sd); + nullpo_retv(ad); + + Assert_retv(progress != 0); + Assert_retv(obj_idx < VECTOR_LENGTH(ad->objective)); + + if (progress >= VECTOR_INDEX(ad->objective, obj_idx).goal) { + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + if (ach->completed_at) + return; + + ach->objective[obj_idx] = VECTOR_INDEX(ad->objective, obj_idx).goal; + + if (achievement->check_complete(sd, ad)) { + achievement->validate_achieve(sd, ad->id); + ach->completed_at = time(NULL); + } + + clif->achievement_send_update(sd->fd, sd, ad); + } +} + +/** +* Checks if the given criteria satisfies the achievement's objective. +* @param objective [in] pointer to the achievement's objectives data. +* @param criteria [in] pointer to the current session's criteria as a comparand. +* @return true if all criteria are satisfied, else false. +*/ +static bool achievement_check_criteria(const struct achievement_objective *objective, const struct achievement_objective *criteria) +{ + int i = 0, j = 0; + + nullpo_retr(false, objective); + nullpo_retr(false, criteria); + + /* Item Id */ + if (objective->unique_type == CRITERIA_UNIQUE_ITEM_ID && objective->unique.itemid != criteria->unique.itemid) + return false; + /* Weapon Level */ + else if (objective->unique_type == CRITERIA_UNIQUE_WEAPON_LV && objective->unique.weapon_lv != criteria->unique.weapon_lv) + return false; + /* Status Types */ + else if (objective->unique_type == CRITERIA_UNIQUE_STATUS_TYPE && objective->unique.status_type != criteria->unique.status_type) + return false; + /* Achievement Id */ + else if (objective->unique_type == CRITERIA_UNIQUE_ACHIEVE_ID && objective->unique.achieve_id != criteria->unique.achieve_id) + return false; + + /* Monster Id */ + if (objective->mobid > 0 && objective->mobid != criteria->mobid) + return false; + + /* Item Type */ + if (objective->item_type > 0 && (objective->item_type & (2 << criteria->item_type)) == 0) + return false; + + /* Job Ids */ + for (i = 0; i < VECTOR_LENGTH(objective->jobid); i++) { + ARR_FIND(0, VECTOR_LENGTH(criteria->jobid), j, VECTOR_INDEX(criteria->jobid, j) != VECTOR_INDEX(objective->jobid, i)); + if (j < VECTOR_LENGTH(criteria->jobid)) + return false; + } + + return true; +} + +/** + * Validates an Achievement Objective of similar types. + * @param[in] sd as a pointer to the map session data. + * @param[in] type as the type of the achievement. + * @param[in] criteria as the criteria of the objective (mob id, job id etc.. 0 for no criteria). + * @param[in] progress as the current progress of the objective. + * @return total number of updated achievements on success, 0 on failure. + */ +static int achievement_validate_type(struct map_session_data *sd, enum achievement_types type, const struct achievement_objective *criteria, bool additive) +{ + int i = 0, total = 0; + struct achievement *ach = NULL; + + nullpo_ret(sd); + nullpo_ret(criteria); + + Assert_ret(criteria->goal != 0); + + if (type == ACH_QUEST) { + ShowError("achievement_validate_type: ACH_QUEST is not handled by this function. (use achievement_validate())\n"); + return 0; + } else if (type >= ACH_TYPE_MAX) { + ShowError("achievement_validate_type: Invalid Achievement Type %d! (min: %d, max: %d)\n", (int)type, (int)ACH_QUEST, (int)ACH_TYPE_MAX - 1); + return 0; + } + + /* Loop through all achievements of the type, checking for possible matches. */ + for (i = 0; i < VECTOR_LENGTH(achievement->category[type]); i++) { + int j = 0; + bool updated = false; + const struct achievement_data *ad = NULL; + + if ((ad = achievement->get(VECTOR_INDEX(achievement->category[type], i))) == NULL) + continue; + + for (j = 0; j < VECTOR_LENGTH(ad->objective); j++) { + // Check if objective criteria matches. + if (achievement->check_criteria(&VECTOR_INDEX(ad->objective, j), criteria) == false) + continue; + // Ensure availability of the achievement. + if ((ach = achievement->ensure(sd, ad)) == NULL) + return false; + // Criteria passed, check if not completed and update progress. + if ((ach->completed_at == 0 && ach->objective[j] < VECTOR_INDEX(ad->objective, j).goal)) { + if (additive == true) + achievement->progress_add(sd, ad, j, criteria->goal); + else + achievement->progress_set(sd, ad, j, criteria->goal); + updated = true; + } + } + + if (updated == true) + total++; + } + + return total; +} + +/** + * Validates any achievement's specific objective index. + * @param[in] sd pointer to the session data. + * @param[in] aid ID of the achievement. + * @param[in] index index of the objective. + * @param[in] progress progress to be added towards the goal. + */ +static bool achievement_validate(struct map_session_data *sd, int aid, unsigned int obj_idx, int progress, bool additive) +{ + const struct achievement_data *ad = NULL; + struct achievement *ach = NULL; + + nullpo_retr(false, sd); + Assert_retr(false, progress > 0); + Assert_retr(false, obj_idx < MAX_ACHIEVEMENT_OBJECTIVES); + + if ((ad = achievement->get(aid)) == NULL) { + ShowError("achievement_validate: Invalid Achievement %d provided.", aid); + return false; + } + + // Ensure availability of the achievement. + if ((ach = achievement->ensure(sd, ad)) == NULL) + return false; + + // Check if not completed and update progress. + if ((!ach->completed_at && ach->objective[obj_idx] < VECTOR_INDEX(ad->objective, obj_idx).goal)) { + if (additive == true) + achievement->progress_add(sd, ad, obj_idx, progress); + else + achievement->progress_set(sd, ad, obj_idx, progress); + } + + return true; +} + +/** + * Validates monster kill type objectives. + * @type ACH_KILL_MOB_CLASS + * @param[in] sd pointer to session data. + * @param[in] mob_id (criteria) class of the monster checked for. + * @param[in] progress (goal) progress to be added. + * @see achievement_vaildate_type() + */ +static void achievement_validate_mob_kill(struct map_session_data *sd, int mob_id) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + Assert_retv(mob_id > 0 && mob->db(mob_id) != NULL); + + if (sd->achievements_received == false) + return; + + criteria.mobid = mob_id; + criteria.goal = 1; + + achievement->validate_type(sd, ACH_KILL_MOB_CLASS, &criteria, true); +} + +/** + * Validate monster damage type objectives. + * @types ACH_DAMAGE_MOB_REC_MAX + * ACH_DAMAGE_MOB_REC_TOTAL + * @param[in] sd pointer to session data. + * @param[in] damage amount of damage received/dealt. + * @param received received/dealt boolean switch. + */ +static void achievement_validate_mob_damage(struct map_session_data *sd, unsigned int damage, bool received) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + Assert_retv(damage > 0); + + if (sd->achievements_received == false) + return; + + criteria.goal = (int) damage; + + if (received) { + achievement->validate_type(sd, ACH_DAMAGE_MOB_REC_MAX, &criteria, false); + achievement->validate_type(sd, ACH_DAMAGE_MOB_REC_TOTAL, &criteria, true); + } else { + achievement->validate_type(sd, ACH_DAMAGE_MOB_MAX, &criteria, false); + achievement->validate_type(sd, ACH_DAMAGE_MOB_TOTAL, &criteria, true); + } + +} + +/** + * Validate player kill (PVP) achievements. + * @types ACH_KILL_PC_TOTAL + * ACH_KILL_PC_JOB + * ACH_KILL_PC_JOBTYPE + * @param[in] sd pointer to killed player's session data. + * @param[in] dstsd pointer to killer's session data. + */ +static void achievement_validate_pc_kill(struct map_session_data *sd, struct map_session_data *dstsd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + nullpo_retv(dstsd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + + /* */ + achievement->validate_type(sd, ACH_KILL_PC_TOTAL, &criteria, true); + + /* */ + VECTOR_INIT(criteria.jobid); + VECTOR_ENSURE(criteria.jobid, 1, 1); + VECTOR_PUSH(criteria.jobid, dstsd->status.class); + + /* Job class */ + achievement->validate_type(sd, ACH_KILL_PC_JOB, &criteria, true); + /* Job Type */ + achievement->validate_type(sd, ACH_KILL_PC_JOBTYPE, &criteria, true); + + VECTOR_CLEAR(criteria.jobid); +} + +/** + * Validate player kill (PVP) achievements. + * @types ACH_DAMAGE_PC_MAX + * ACH_DAMAGE_PC_TOTAL + * ACH_DAMAGE_PC_REC_MAX + * ACH_DAMAGE_PC_REC_TOTAL + * @param[in] sd pointer to source player's session data. + * @param[in] dstsd pointer to target player's session data. + * @param[in] damage amount of damage dealt / received. + */ +static void achievement_validate_pc_damage(struct map_session_data *sd, struct map_session_data *dstsd, unsigned int damage) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + if (damage == 0) + return; + + criteria.goal = (int) damage; + + /* */ + achievement->validate_type(sd, ACH_DAMAGE_PC_MAX, &criteria, false); + achievement->validate_type(sd, ACH_DAMAGE_PC_TOTAL, &criteria, true); + + /* */ + achievement->validate_type(dstsd, ACH_DAMAGE_PC_REC_MAX, &criteria, false); + achievement->validate_type(dstsd, ACH_DAMAGE_PC_REC_TOTAL, &criteria, true); +} + +/** + * Validates job change objectives. + * @type ACH_JOB_CHANGE + * @param[in] sd pointer to session data. + * @see achivement_validate_type() + */ +static void achievement_validate_jobchange(struct map_session_data *sd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + VECTOR_INIT(criteria.jobid); + VECTOR_ENSURE(criteria.jobid, 1, 1); + VECTOR_PUSH(criteria.jobid, sd->status.class); + + criteria.goal = 1; + + achievement->validate_type(sd, ACH_JOB_CHANGE, &criteria, false); + + VECTOR_CLEAR(criteria.jobid); +} + +/** + * Validates stat type objectives. + * @types ACH_STATUS + * ACH_STATUS_BY_JOB + * ACH_STATUS_BY_JOBTYPE + * @param[in] sd pointer to session data. + * @param[in] stat_type (criteria) status point type. (see status_point_types) + * @param[in] progress (goal) amount of absolute progress to check. + * @see achievement_validate_type() + */ +static void achievement_validate_stats(struct map_session_data *sd, enum status_point_types stat_type, int progress) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + Assert_retv(progress > 0); + + if (sd->achievements_received == false) + return; + + if (!achievement_valid_status_types(stat_type)) { + ShowError("achievement_validate_stats: Invalid status type %d given.\n", (int) stat_type); + return; + } + + criteria.unique.status_type = stat_type; + + criteria.goal = progress; + + achievement->validate_type(sd, ACH_STATUS, &criteria, false); + + VECTOR_INIT(criteria.jobid); + VECTOR_ENSURE(criteria.jobid, 1, 1); + VECTOR_PUSH(criteria.jobid, sd->status.class); + + /* Stat and Job class */ + achievement->validate_type(sd, ACH_STATUS_BY_JOB, &criteria, false); + + /* Stat and Job Type */ + achievement->validate_type(sd, ACH_STATUS_BY_JOBTYPE, &criteria, false); + + VECTOR_CLEAR(criteria.jobid); +} + +/** + * Validates chatroom creation type objectives. + * @types ACH_CHATROOM_CREATE + * ACH_CHATROOM_CREATE_DEAD + * @param[in] sd pointer to session data. + * @see achievement_validate_type() + */ +static void achievement_validate_chatroom_create(struct map_session_data *sd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + + if (pc_isdead(sd)) { + achievement->validate_type(sd, ACH_CHATROOM_CREATE_DEAD, &criteria, true); + return; + } + + achievement->validate_type(sd, ACH_CHATROOM_CREATE, &criteria, true); +} + +/** + * Validates chatroom member count type objectives. + * @type ACH_CHATROOM_MEMBERS + * @param[in] sd pointer to session data. + * @param[in] progress (goal) amount of progress to be added. + * @see achievement_validate_type() + */ +static void achievement_validate_chatroom_members(struct map_session_data *sd, int progress) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(progress > 0); + + criteria.goal = progress; + + achievement->validate_type(sd, ACH_CHATROOM_MEMBERS, &criteria, false); +} + +/** + * Validates friend add type objectives. + * @type ACH_FRIEND_ADD + * @param[in] sd pointer to session data. + * @see achievement_validate_type() + */ +static void achievement_validate_friend_add(struct map_session_data *sd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + + achievement->validate_type(sd, ACH_FRIEND_ADD, &criteria, true); +} + +/** + * Validates party creation type objectives. + * @type ACH_PARTY_CREATE + * @param[in] sd pointer to session data. + * @see achievement_validate_type() + */ +static void achievement_validate_party_create(struct map_session_data *sd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + achievement->validate_type(sd, ACH_PARTY_CREATE, &criteria, true); +} + +/** + * Validates marriage type objectives. + * @type ACH_MARRY + * @param[in] sd pointer to session data. + * @see achievement_validate_type() + */ +static void achievement_validate_marry(struct map_session_data *sd) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + + achievement->validate_type(sd, ACH_MARRY, &criteria, true); +} + +/** + * Validates adoption type objectives. + * @types ACH_ADOPT_PARENT + * ACH_ADOPT_BABY + * @param[in] sd pointer to session data. + * @param[in] parent (type) boolean value to indicate if parent (true) or baby (false). + * @see achievement_validate_type() + */ +static void achievement_validate_adopt(struct map_session_data *sd, bool parent) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + criteria.goal = 1; + + if (parent) + achievement->validate_type(sd, ACH_ADOPT_PARENT, &criteria, true); + else + achievement->validate_type(sd, ACH_ADOPT_BABY, &criteria, true); +} + +/** + * Validates zeny type objectives. + * @types ACH_ZENY_HOLD + * ACH_ZENY_GET_ONCE + * ACH_ZENY_GET_TOTAL + * ACH_ZENY_SPEND_ONCE + * ACH_ZENY_SPEND_TOTAL + * @param[in] sd pointer to session data. + * @param[in] amount (goal) amount of zeny earned or spent. + * @see achievement_validate_type() + */ +static void achievement_validate_zeny(struct map_session_data *sd, int amount) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(amount != 0); + + if (amount > 0) { + criteria.goal = sd->status.zeny; + achievement->validate_type(sd, ACH_ZENY_HOLD, &criteria, false); + criteria.goal = amount; + achievement->validate_type(sd, ACH_ZENY_GET_ONCE, &criteria, false); + achievement->validate_type(sd, ACH_ZENY_GET_TOTAL, &criteria, true); + } else { + criteria.goal = amount; + achievement->validate_type(sd, ACH_ZENY_SPEND_ONCE, &criteria, false); + achievement->validate_type(sd, ACH_ZENY_SPEND_TOTAL, &criteria, true); + } +} + +/** + * Validates equipment refinement type objectives. + * @types ACH_EQUIP_REFINE_SUCCESS + * ACH_EQUIP_REFINE_FAILURE + * ACH_EQUIP_REFINE_SUCCESS_TOTAL + * ACH_EQUIP_REFINE_FAILURE_TOTAL + * ACH_EQUIP_REFINE_SUCCESS_WLV + * ACH_EQUIP_REFINE_FAILURE_WLV + * ACH_EQUIP_REFINE_SUCCESS_ID + * ACH_EQUIP_REFINE_FAILURE_ID + * @param[in] sd pointer to session data. + * @param[in] idx Inventory index of the item. + * @param[in] success (type) boolean switch for failure / success. + * @see achievement_validate_type() + */ +static void achievement_validate_refine(struct map_session_data *sd, unsigned int idx, bool success) +{ + struct achievement_objective criteria = { 0 }; + struct item_data *id = NULL; + + nullpo_retv(sd); + Assert_retv(idx < MAX_INVENTORY); + + id = itemdb->exists(sd->status.inventory[idx].nameid); + + if (sd->achievements_received == false) + return; + + Assert_retv(idx < MAX_INVENTORY); + Assert_retv(id != NULL); + + criteria.goal = sd->status.inventory[idx].refine; + + /* Universal */ + achievement->validate_type(sd, + success ? ACH_EQUIP_REFINE_SUCCESS : ACH_EQUIP_REFINE_FAILURE, + &criteria, false); + + /* Total */ + criteria.goal = 1; + achievement->validate_type(sd, + success ? ACH_EQUIP_REFINE_SUCCESS_TOTAL : ACH_EQUIP_REFINE_FAILURE_TOTAL, + &criteria, true); + + /* By Weapon Level */ + if (id->type == IT_WEAPON) { + criteria.item_type = id->type; + criteria.unique.weapon_lv = id->wlv; + criteria.goal = sd->status.inventory[idx].refine; + achievement->validate_type(sd, + success ? ACH_EQUIP_REFINE_SUCCESS_WLV : ACH_EQUIP_REFINE_FAILURE_WLV, + &criteria, false); + criteria.item_type = 0; + criteria.unique.weapon_lv = 0; // cleanup + } + + /* By NameId */ + criteria.unique.itemid = id->nameid; + criteria.goal = sd->status.inventory[idx].refine; + achievement->validate_type(sd, + success ? ACH_EQUIP_REFINE_SUCCESS_ID : ACH_EQUIP_REFINE_FAILURE_ID, + &criteria, false); + criteria.unique.itemid = 0; // cleanup +} + +/** + * Validates item received type objectives. + * @types ACH_ITEM_GET_COUNT + * ACH_ITEM_GET_WORTH + * ACH_ITEM_GET_COUNT_ITEMTYPE + * @param[in] sd pointer to session data. + * @param[in] nameid (criteria) ID of the item. + * @param[in] amount (goal) amount of the item collected. + */ +static void achievement_validate_item_get(struct map_session_data *sd, int nameid, int amount) +{ + struct item_data *it = itemdb->exists(nameid); + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(amount > 0); + nullpo_retv(it); + + criteria.unique.itemid = it->nameid; + criteria.goal = amount; + achievement->validate_type(sd, ACH_ITEM_GET_COUNT, &criteria, false); + criteria.unique.itemid = 0; // cleanup + + /* Item Buy Value*/ + criteria.goal = max(it->value_buy, 1); + achievement->validate_type(sd, ACH_ITEM_GET_WORTH, &criteria, false); + + /* Item Type */ + criteria.item_type = it->type; + criteria.goal = 1; + achievement->validate_type(sd, ACH_ITEM_GET_COUNT_ITEMTYPE, &criteria, false); + criteria.item_type = 0; // cleanup +} + +/** + * Validates item sold type objectives. + * @type ACH_ITEM_SELL_WORTH + * @param[in] sd pointer to session data. + * @param[in] nameid Item Id in question. + * @param[in] amount amount of item in question. + */ +static void achievement_validate_item_sell(struct map_session_data *sd, int nameid, int amount) +{ + struct item_data *it = itemdb->exists(nameid); + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(amount > 0); + nullpo_retv(it); + + criteria.unique.itemid = it->nameid; + + criteria.goal = max(it->value_sell, 1); + + achievement->validate_type(sd, ACH_ITEM_SELL_WORTH, &criteria, false); +} + +/** + * Validates achievement type objectives. + * @type ACH_ACHIEVE + * @param[in] sd pointer to session data. + * @param[in] achid (criteria) achievement id. + */ +static void achievement_validate_achieve(struct map_session_data *sd, int achid) +{ + const struct achievement_data *ad = achievement->get(achid); + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + nullpo_retv(ad); + + if (sd->achievements_received == false) + return; + + Assert_retv(achid > 0); + + criteria.unique.achieve_id = ad->id; + + criteria.goal = 1; + + achievement->validate_type(sd, ACH_ACHIEVE, &criteria, false); +} + +/** + * Validates taming type objectives. + * @type ACH_PET_CREATE + * @param[in] sd pointer to session data. + * @param[in] class (criteria) class of the monster tamed. + */ +static void achievement_validate_taming(struct map_session_data *sd, int class) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(class > 0); + Assert_retv(mob->db(class) != mob->dummy); + + criteria.mobid = class; + criteria.goal = 1; + + achievement->validate_type(sd, ACH_PET_CREATE, &criteria, true); +} + +/** + * Validated achievement rank type objectives. + * @type ACH_ACHIEVEMENT_RANK + * @param[in] sd pointer to session data. + * @param[in] rank (goal) rank of earned. + */ +static void achievement_validate_achievement_rank(struct map_session_data *sd, int rank) +{ + struct achievement_objective criteria = { 0 }; + + nullpo_retv(sd); + + if (sd->achievements_received == false) + return; + + Assert_retv(rank >= 0 && rank <= VECTOR_LENGTH(achievement->rank_exp)); + + criteria.goal = 1; + + achievement->validate_type(sd, ACH_ACHIEVEMENT_RANK, &criteria, false); +} + +/** + * Verifies if an achievement type requires a criteria field. + * @param[in] type achievement type in question. + * @return true if required, false if not. + */ +static bool achievement_type_requires_criteria(enum achievement_types type) +{ + if (type == ACH_KILL_PC_JOB + || type == ACH_KILL_PC_JOBTYPE + || type == ACH_KILL_MOB_CLASS + || type == ACH_JOB_CHANGE + || type == ACH_STATUS + || type == ACH_STATUS_BY_JOB + || type == ACH_STATUS_BY_JOBTYPE + || type == ACH_EQUIP_REFINE_SUCCESS_WLV + || type == ACH_EQUIP_REFINE_FAILURE_WLV + || type == ACH_EQUIP_REFINE_SUCCESS_ID + || type == ACH_EQUIP_REFINE_FAILURE_ID + || type == ACH_ITEM_GET_COUNT + || type == ACH_PET_CREATE + || type == ACH_ACHIEVE) + return true; + + return false; +} + +/** + * Stores all the title ID that has been earned by player + * @param[in] sd pointer to session data. + */ +static void achievement_init_titles(struct map_session_data *sd) +{ + int i; + nullpo_retv(sd); + + VECTOR_INIT(sd->title_ids); + /* Browse through the session's achievement list and gather their values. */ + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + struct achievement *a = &VECTOR_INDEX(sd->achievement, i); + const struct achievement_data *ad = NULL; + + /* Sanity check for nonull pointers. */ + if (a == NULL || (ad = achievement->get(a->id)) == NULL) + continue; + + if (a->completed_at > 0 && a->rewarded_at > 0 && ad->rewards.title_id > 0) { + VECTOR_ENSURE(sd->title_ids, 1, 1); + VECTOR_PUSH(sd->title_ids, ad->rewards.title_id); + } + } +} + +/** + * Validates whether player has earned the title. + * @param[in] sd pointer to session data. + * @param[in] title_id Title ID + * @return true, if title has been earned, else false + */ +static bool achievement_check_title(struct map_session_data *sd, int title_id) { + int i; + + nullpo_retr(false, sd); + + if (title_id == 0) + return true; + + for (i = 0; i < VECTOR_LENGTH(sd->title_ids); i++) { + if (VECTOR_INDEX(sd->title_ids, i) == title_id) { + return true; + } + } + + return false; +} + +/** + * Achievement rewards are given to player + * @param sd session data + * @param ad achievement data + */ +static void achievement_get_rewards(struct map_session_data *sd, const struct achievement_data *ad) { + int i = 0; + struct achievement *ach = NULL; + + nullpo_retv(sd); + nullpo_retv(ad); + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + /* Buff */ + if (ad->rewards.bonus != NULL) + script->run(ad->rewards.bonus, 0, sd->bl.id, 0); + + /* Give Items */ + for (i = 0; i < VECTOR_LENGTH(ad->rewards.item); i++) { + struct item it = { 0 }; + int total = 0; + + it.nameid = VECTOR_INDEX(ad->rewards.item, i).id; + total = VECTOR_INDEX(ad->rewards.item, i).amount; + + it.identify = 1; + + //Check if it's stackable. + if (!itemdb->isstackable(it.nameid)) { + int j = 0; + for (j = 0; j < total; ++j) + pc->additem(sd, &it, (it.amount = 1), LOG_TYPE_SCRIPT); + } else { + pc->additem(sd, &it, (it.amount = total), LOG_TYPE_SCRIPT); + } + } + + ach->rewarded_at = time(NULL); + + if (ad->rewards.title_id > 0) { // Add Title + VECTOR_ENSURE(sd->title_ids, 1, 1); + VECTOR_PUSH(sd->title_ids, ad->rewards.title_id); + clif->achievement_send_list(sd->fd, sd); + } else { + clif->achievement_reward_ack(sd->fd, sd, ad); + clif->achievement_send_update(sd->fd, sd, ad); // send update. + } +} + +/** + * Parses the Achievement Ranks. + * @read db/achievement_rank_db.conf + */ +static void achievement_readdb_ranks(void) +{ + const char *filename = "db/achievement_rank_db.conf"; + struct config_t ar_conf = { 0 }; + struct config_setting_t *ardb = NULL, *conf = NULL; + int entry = 0; + + if (!libconfig->load_file(&ar_conf, filename)) + return; // report error. + + if (!(ardb = libconfig->setting_get_member(ar_conf.root, "achievement_rank_db"))) { + ShowError("achievement_readdb_ranks: Could not process contents of file '%s', skipping...\n", filename); + libconfig->destroy(&ar_conf); + return; + } + + while (entry < libconfig->setting_length(ardb) && entry < MAX_ACHIEVEMENT_RANKS) { + char rank[8]; + + if (!(conf = libconfig->setting_get_elem(ardb, entry))) { + ShowError("achievement_readdb_ranks: Could not read value for entry %d, skipping...\n", entry+1); + continue; + } + + entry++; // Rank counter; + + sprintf(rank, "Rank%d", entry); + + if (strcmp(rank, config_setting_name(conf)) == 0) { + int exp = 0; + + if ((exp = libconfig->setting_get_int(conf)) <= 0) { + ShowError("achievement_readdb_ranks: Invalid value provided for %s in '%s'.\n", rank, filename); + continue; + } + + VECTOR_ENSURE(achievement->rank_exp, 1, 1); + VECTOR_PUSH(achievement->rank_exp, exp); + } else { + ShowWarning("achievement_readdb_ranks: Ranks are not in order! Ignoring all ranks after Rank %d...\n", entry); + break; // break if elements are not in order or rank doesn't exist. + } + } + + if (libconfig->setting_length(ardb) > MAX_ACHIEVEMENT_RANKS) + ShowWarning("achievement_rankdb_ranks: Maximum number of achievement ranks exceeded. Skipping all after entry %d...\n", entry); + + libconfig->destroy(&ar_conf); + + if (!entry) { + ShowError("achievement_readdb_ranks: No ranks provided in '%s'!\n", filename); + return; + } + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", entry, filename); +} + +/** + * Validates Mob Id criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success + */ +static bool achievement_readdb_validate_criteria_mobid(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + const char *string = NULL; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + if (libconfig->setting_lookup_int(t, "MobId", &val)) { + if (mob->db_checkid(val) == 0) { + ShowError("achievement_readdb_validate_criteria_mobid: Non-existant monster with ID %id provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } + + obj->mobid = val; + } else if (libconfig->setting_lookup_string(t, "MobId", &string)) { + if (!script->get_constant(string, &val)) { + ShowError("achievement_readdb_validate_criteria_mobid: Non-existant constant %s provided (Achievement: %d, Objective: %d). Skipping...\n", string, entry_id, obj_idx); + return false; + } + + obj->mobid = val; + } else if (achievement_criteria_mobid(type)) { + ShowError("achievement_readdb_validate_criteria_mobid: Achievement type of ID %d requires MobId as objective criteria, setting not provided. Skipping...\n", entry_id); + return false; + } + + return true; +} + +/** + * Validates Job Id criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_jobid(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int job_id = 0; + const char *string = NULL; + struct config_setting_t *tt = NULL; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + // initialize the buffered objective jobid vector. + VECTOR_INIT(obj->jobid); + + if (libconfig->setting_lookup_int(t, "JobId", &job_id)) { + if (pc->jobid2mapid(job_id) == -1) { + ShowError("achievement_readdb_validate_criteria_jobid: Invalid JobId %d provided (Achievement: %d, Objective: %d). Skipping...\n", job_id, entry_id, obj_idx); + return false; + } + + VECTOR_ENSURE(obj->jobid, 1, 1); + VECTOR_PUSH(obj->jobid, job_id); + } else if (libconfig->setting_lookup_string(t, "JobId", &string)) { + if (script->get_constant(string, &job_id) == false) { + ShowError("achievement_readdb_validate_criteria_jobid: Invalid JobId %d provided (Achievement: %d, Objective: %d). Skipping...\n", job_id, entry_id, obj_idx); + return false; + } + + VECTOR_ENSURE(obj->jobid, 1, 1); + VECTOR_PUSH(obj->jobid, job_id); + } else if ((tt = libconfig->setting_get_member(t, "JobId")) && config_setting_is_array(tt)) { + int j = 0; + + while (j < libconfig->setting_length(tt)) { + if ((job_id = libconfig->setting_get_int_elem(tt, j)) == 0) { + if ((string = libconfig->setting_get_string_elem(tt, j)) != NULL && script->get_constant(string, &job_id) == false) { + ShowError("achievement_readdb_validate_criteria_jobid: Invalid JobId provided at index %d (Achievement: %d, Objective: %d). Skipping...\n", j, entry_id, obj_idx); + continue; + } + } + + /* Ensure size and allocation */ + VECTOR_ENSURE(obj->jobid, 1, 1); + /* push buffer */ + VECTOR_PUSH(obj->jobid, job_id); + j++; + } + } else if (achievement_criteria_jobid(type)) { + ShowError("achievement_readdb_validate_criteria_jobid: Achievement type of ID %d requires a JobId field in the objective criteria, setting not provided. Skipping...\n", entry_id); + return false; + } + + return true; + +} + +/** + * Validates Item Id criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_itemid(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + const char *string = NULL; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + + if (libconfig->setting_lookup_int(t, "ItemId", &val)) { + if (itemdb->exists(val) == NULL) { + ShowError("achievement_readdb_validate_criteria_itemid: Invalid ItemID %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_itemid: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.itemid = val; + obj->unique_type = CRITERIA_UNIQUE_ITEM_ID; + } else if (libconfig->setting_lookup_string(t, "ItemId", &string)) { + if (script->get_constant(string, &val) == false) { + ShowError("achievement_readdb_validate_criteria_itemid: Invalid ItemID %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_itemid: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.itemid = val; + obj->unique_type = CRITERIA_UNIQUE_ITEM_ID; + } else if (achievement_criteria_itemid(type)) { + ShowError("achievement_readdb_validate_criteria_itemid: Criteria requires a ItemId field (Achievement: %d, Objective: %d). Skipping...\n", entry_id, obj_idx); + return false; + } + + return true; +} + +/** + * Validates Status Type criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_statustype(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + const char *string = NULL; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + if (libconfig->setting_lookup_int(t, "StatusType", &val)) { + if (!achievement_valid_status_types(val)) { + ShowError("achievement_readdb_validate_criteria_statustype: Invalid StatusType %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_statustype: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.status_type = (enum status_point_types) val; + obj->unique_type = CRITERIA_UNIQUE_STATUS_TYPE; + } else if (libconfig->setting_lookup_string(t, "StatusType", &string)) { + if (strcmp(string, "SP_STR") == 0) val = SP_STR; + else if (strcmp(string, "SP_AGI") == 0) val = SP_AGI; + else if (strcmp(string, "SP_VIT") == 0) val = SP_VIT; + else if (strcmp(string, "SP_INT") == 0) val = SP_INT; + else if (strcmp(string, "SP_DEX") == 0) val = SP_DEX; + else if (strcmp(string, "SP_LUK") == 0) val = SP_LUK; + else if (strcmp(string, "SP_BASELEVEL") == 0) val = SP_BASELEVEL; + else if (strcmp(string, "SP_JOBLEVEL") == 0) val = SP_JOBLEVEL; + else val = SP_NONE; + + if (!achievement_valid_status_types(val)) { + ShowError("achievement_readdb_validate_criteria_statustype: Invalid StatusType %s provided (Achievement: %d, Objective: %d). Skipping...\n", string, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_statustype: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.status_type = (enum status_point_types) val; + obj->unique_type = CRITERIA_UNIQUE_STATUS_TYPE; + } else if (achievement_criteria_stattype(type)) { + ShowError("achievement_readdb_validate_criteria_statustype: Criteria requires a StatusType field (Achievement: %d, Objective: %d). Skipping...\n", entry_id, obj_idx); + return false; + } + + return true; +} + +/** + * Validates Item Type criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_itemtype(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + const char *string = NULL; + struct config_setting_t *tt = NULL; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + if (libconfig->setting_lookup_int(t, "ItemType", &val)) { + if (val < IT_HEALING || val > IT_MAX) { + ShowError("achievement_readdb_validate_criteria_itemtype: Invalid ItemType %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } + + if (val == IT_MAX) { + obj->item_type |= (2 << val) - 1; + } else { + obj->item_type |= (2 << val); + } + + } else if (libconfig->setting_lookup_string(t, "ItemType", &string)) { + if (!script->get_constant(string, &val) || val < IT_HEALING || val > IT_MAX) { + ShowError("achievement_readdb_validate_criteria_itemtype: Invalid ItemType %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } + + if (val == IT_MAX) { + obj->item_type |= (2 << val) - 1; + } else { + obj->item_type |= (2 << val); + } + } else if ((tt = libconfig->setting_get_member(t, "ItemType")) && config_setting_is_array(tt)) { + int j = 0; + uint32 it_type = 0; + + while (j < libconfig->setting_length(tt)) { + if ((val = libconfig->setting_get_int_elem(tt, j))) { + if (val < IT_HEALING || val >= IT_MAX) { + ShowError("achievement_readdb_validate_criteria_itemtype: Invalid ItemType %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + continue; + } + if (val == IT_MAX) { + obj->item_type |= (2 << val) - 1; + } else { + obj->item_type |= (2 << val); + } + } else if ((string = libconfig->setting_get_string_elem(tt, j))) { + if (!script->get_constant(string, &val)) { + ShowError("achievement_readdb_validate_criteria_itemtype: Invalid ItemType %s provided (Achievement: %d, Objective: %d). Skipping...\n", string, entry_id, obj_idx); + continue; + } + + if (val == IT_MAX) { + obj->item_type |= (2 << val) - 1; + } else { + obj->item_type |= (2 << val); + } + } + j++; + } + + obj->item_type = it_type; + } else if (achievement_criteria_itemtype(type)) { + ShowError("achievement_readdb_validate_criteria_itemtype: Criteria requires a ItemType field (Achievement: %d, Objective: %d). Skipping...\n", entry_id, obj_idx); + return false; + } + + return true; +} + +/** + * Validates Weapon Level criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_weaponlv(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + if (libconfig->setting_lookup_int(t, "WeaponLevel", &val)) { + if (val < 1 || val > 4) { + ShowError("achievement_readdb_validate_criteria_weaponlv: Invalid WeaponLevel %d provided (Achievement: %d, Objective: %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_weaponlv: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.weapon_lv = val; + obj->unique_type = CRITERIA_UNIQUE_WEAPON_LV; + } else if (achievement_criteria_weaponlv(type)) { + ShowError("achievement_readdb_validate_criteria_weaponlv: Criteria requires a WeaponType field. (Achievement: %d, Objective: %d). Skipping...\n", entry_id, obj_idx); + return false; + } + + return true; +} + +/** + * Validates achievement Id criteria of an objective while parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] obj pointer to the achievement objective entry being parsed. + * @param[in] type Type of the achievement being parsed. + * @param[in] entry_id Id of the entry being parsed. + * @param[in] obj_idx Index of the objective entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_criteria_achievement(const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx) +{ + int val = 0; + + nullpo_retr(false, t); + nullpo_retr(false, obj); + + if (libconfig->setting_lookup_int(t, "Achieve", &val)) { + if (achievement->get(val) == NULL) { + ShowError("achievement_readdb_validate_criteria_achievement: Invalid Achievement %d provided as objective (Achievement %d, Objective %d). Skipping...\n", val, entry_id, obj_idx); + return false; + } else if (obj->unique_type != CRITERIA_UNIQUE_NONE) { + ShowError("achievement_readdb_validate_criteria_achievement: Unique criteria has already been set to type %d. (Achievement: %d, Objective: %d). Skipping...\n", (int) obj->unique_type, entry_id, obj_idx); + return false; + } + + obj->unique.achieve_id = val; + obj->unique_type = CRITERIA_UNIQUE_ACHIEVE_ID; + } else if (type == ACH_ACHIEVE) { + ShowError("achievement_readdb_validate_criteria_achievement: Achievement type of ID %d requires an Achieve field in the objective criteria, setting not provided. Skipping...\n", entry_id); + return false; + } + + return true; +} + +/** + * Read a single objective entry of an achievement. + * @param[in] conf config pointer. + * @param[in] index Index of the objective being in the objective list being parsed. + * @param[out] entry pointer to the achievement db entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_objective_sub(const struct config_setting_t *conf, int index, struct achievement_data *entry) +{ + struct config_setting_t *tt = NULL; + char objnum[12]; + + nullpo_retr(false, conf); + nullpo_retr(false, entry); + + sprintf(objnum, "*%d", index); // Search Objective 1..MAX + if ((tt = libconfig->setting_get_member(conf, objnum)) && config_setting_is_group(tt)) { + struct achievement_objective obj = { 0 }; + struct config_setting_t *c = NULL; + + /* Description */ + if (libconfig->setting_lookup_mutable_string(tt, "Description", obj.description, OBJECTIVE_DESCRIPTION_LENGTH) == 0) { + ShowError("achievement_readdb_objective_sub: Objective %d has no description for Achievement %d, skipping...\n", index, entry->id); + return false; + } + + /* Criteria */ + if ((c = libconfig->setting_get_member(tt, "Criteria")) && config_setting_is_group(tt)) { + /* MobId */ + if (achievement->readdb_validate_criteria_mobid(c, &obj, entry->type, entry->id, index) == false) + return false; + + /* ItemId */ + if (achievement->readdb_validate_criteria_itemid(c, &obj, entry->type, entry->id, index) == false) + return false; + + /* StatusType */ + if (achievement->readdb_validate_criteria_statustype(c, &obj, entry->type, entry->id, index) == false) + return false; + + /* ItemType */ + if (achievement->readdb_validate_criteria_itemtype(c, &obj, entry->type, entry->id, index) == false) + return false; + + /* WeaponLevel */ + if (achievement->readdb_validate_criteria_weaponlv(c, &obj, entry->type, entry->id, index) == false) + return false; + + /* Achievement */ + if (achievement->readdb_validate_criteria_achievement(c, &obj, entry->type, entry->id, index) == false) + return false; + + /** + * Vectors are read last to avoid memory leaks if either of the above break, in cases where they are stacked with other criteria. + * Note to future editors - be sure to cleanup previous vectors before breaks. + */ + /* JobId */ + if (achievement->readdb_validate_criteria_jobid(c, &obj, entry->type, entry->id, index) == false) + return false; + } else if (achievement->type_requires_criteria(entry->type)) { + ShowError("achievement_readdb_objective_sub: No criteria field added (Achievement: %d, Objective: %d)! Skipping...\n", entry->id, index); + return false; + } + + /* Goal */ + if (libconfig->setting_lookup_int(tt, "Goal", &obj.goal) <= 0) + obj.goal = 1; // 1 count by default. + + /* Ensure size and allocation */ + VECTOR_ENSURE(entry->objective, 1, 1); + + /* Push buffer */ + VECTOR_PUSH(entry->objective, obj); + } else { // Break if not in order, to comply with the client's objective order. + ShowWarning("achievement_readdb_objective_sub: Objectives for Achievement %d are not in order (starting from *%d). Remaining objectives will be skipped.\n", entry->id, index); + return false; + } + + return true; +} + +/** + * Parses achievement objective entries of the current achievement db entry being parsed. + * @param[in] conf config pointer. + * @param[out] entry pointer to the achievement db entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_objectives(const struct config_setting_t *conf, struct achievement_data *entry) +{ + struct config_setting_t *t = NULL; + + nullpo_retr(false, conf); + nullpo_retr(false, entry); + + if ((t = libconfig->setting_get_member(conf, "Objectives")) && config_setting_is_group(t)) { + int i = 0; + // Initialize the buffer objective vector. + VECTOR_INIT(entry->objective); + + for (i = 0; i < libconfig->setting_length(t) && i < MAX_ACHIEVEMENT_OBJECTIVES; i++) + if (achievement_readdb_objective_sub(t, i + 1, entry) == false) + break; + + // Assess total objectives. + if (libconfig->setting_length(t) > MAX_ACHIEVEMENT_OBJECTIVES) + ShowWarning("achievement_readdb_objectives: Exceeded maximum number of objectives (%d) for Achievement %d. Remaining objectives will be skipped.\n", MAX_ACHIEVEMENT_OBJECTIVES, entry->id); + if (i == 0) { + ShowError("achievement_readdb_objectives: No Objectives provided for Achievement %d, skipping...\n", entry->id); + return false; + } + } else { + ShowError("achievement_readdb_objectives: No Objectives provided for Achievement %d, skipping...\n", entry->id); + return false; + } // end of "Objectives" + + return true; +} + +/** + * Validates a single reward item in the reward item list of an achievement. + * @param[in] t pointer to the config setting. + * @param[in] index Index of the item in the reward list. + * @param[out] entry pointer to the achievement entry being parsed. + * @return false on failure, true on success. + */ +static bool achievement_readdb_validate_reward_item_sub(const struct config_setting_t *t, int index, struct achievement_data *entry) +{ + struct config_setting_t *it = NULL; + struct achievement_reward_item item = { 0 }; + const char *name = NULL; + int amount = 0; + int val = 0; + + nullpo_retr(false, t); + nullpo_retr(false, entry); + + if ((it = libconfig->setting_get_elem(t, index)) == NULL) + return false; + + name = config_setting_name(it); + amount = libconfig->setting_get_int(it); + + if (name[0] == 'I' && name[1] == 'D' && itemdb->exists(atoi(name+2))) { + val = atoi(name); + } else if (!script->get_constant(name, &val)) { + ShowWarning("achievement_readdb_validate_reward_item_sub: Non existant Item %s provided as a reward in Achievement %d, skipping...\n", name, entry->id); + return false; + } + + if (amount <= 0) { + ShowWarning("achievement_readdb_validate_reward_item_sub: No amount provided for Item %s as a reward in Achievement %d, skipping...\n", name, entry->id); + return false; + } + + /* Ensure size and allocation */ + VECTOR_ENSURE(entry->rewards.item, 1, 1); + + item.id = val; + item.amount = amount; + + /* push buffer */ + VECTOR_PUSH(entry->rewards.item, item); + + return true; +} + +/** + * Validates the reward items when parsing an achievement db entry. + * @param[in] t pointer to the config setting. + * @param[out] entry pointer to the achievement entry being parsed. + */ +static void achievement_readdb_validate_reward_items(const struct config_setting_t *t, struct achievement_data *entry) +{ + struct config_setting_t *tt = NULL; + int i = 0; + + nullpo_retv(t); + nullpo_retv(entry); + + if ((tt = libconfig->setting_get_member(t, "Items")) && config_setting_is_group(t)) { + for (i = 0; i < libconfig->setting_length(tt) && i < MAX_ACHIEVEMENT_ITEM_REWARDS; i++) + if (achievement->readdb_validate_reward_item_sub(tt, i, entry) == false) + continue; + + if (libconfig->setting_length(tt) > MAX_ACHIEVEMENT_ITEM_REWARDS) + ShowError("achievement_readdb_validate_reward_items: Maximum amount of item rewards (%d) exceeded for Achievement %d. Remaining items will be skipped.\n", MAX_ACHIEVEMENT_ITEM_REWARDS, entry->id); + } +} + +/** + * Validates the reward Bonus script when parsing an achievement db entry. + * @param[in] t pointer to the config setting. + * @param[out] entry pointer to the achievement entry being parsed. + * @param[in] source pointer to the source file name. + */ +static void achievement_readdb_validate_reward_bonus(const struct config_setting_t *t, struct achievement_data *entry, const char *source) +{ + const char *string = NULL; + + nullpo_retv(source); + nullpo_retv(t); + nullpo_retv(entry); + + if (libconfig->setting_lookup_string(t, "Bonus", &string)) + entry->rewards.bonus = string ? script->parse(string, source, 0, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; +} + +/** + * Validates the reward Title Id when parsing an achievement entry. + * @param[in] t pointer to the config setting. + * @param[out] entry pointer to the entry. + */ +static void achievement_readdb_validate_reward_titleid(const struct config_setting_t *t, struct achievement_data *entry) +{ + nullpo_retv(t); + nullpo_retv(entry); + + libconfig->setting_lookup_int(t, "TitleId", &entry->rewards.title_id); +} + +/** + * Validates the reward Bonus script when parsing an achievement db entry. + * @param[in] conf pointer to the config setting. + * @param[out] entry pointer to the achievement entry being parsed. + * @param[in] source pointer to the source file name. + */ +static bool achievement_readdb_rewards(const struct config_setting_t *conf, struct achievement_data *entry, const char *source) +{ + struct config_setting_t *t = NULL; + + nullpo_retr(false, conf); + nullpo_retr(false, entry); + nullpo_retr(false, source); + + VECTOR_INIT(entry->rewards.item); + + if ((t = libconfig->setting_get_member(conf, "Rewards")) && config_setting_is_group(t)) { + /* Items */ + achievement->readdb_validate_reward_items(t, entry); + + /* Buff/Bonus Script Code */ + achievement->readdb_validate_reward_bonus(t, entry, source); + + /* Title Id */ + // @TODO Check Title ID against title DB! + achievement->readdb_validate_reward_titleid(t, entry); + + } + + return true; +} + +/** + * Validates the reward Bonus script when parsing an achievement db entry. + * @param[in] conf pointer to the config setting. + * @param[out] entry pointer to the achievement entry being parsed. + * @param[in] source pointer to the source file name. + */ +static void achievement_readdb_additional_fields(const struct config_setting_t *conf, struct achievement_data *entry, const char *source) +{ + // plugins do their own thing. +} + +/** + * Parses the Achievement DB. + * @read achievement_db.conf + */ +static void achievement_readb(void) +{ + const char *filename = "db/"DBPATH"achievement_db.conf"; + struct config_t ach_conf = { 0 }; + struct config_setting_t *achdb = NULL, *conf = NULL; + int entry = 0, count = 0; + VECTOR_DECL(int) duplicate; + + if (!libconfig->load_file(&ach_conf, filename)) + return; // report error. + + if (!(achdb = libconfig->setting_get_member(ach_conf.root, "achievement_db"))) { + ShowError("achievement_readdb: Could not process contents of file '%s', skipping...\n", filename); + libconfig->destroy(&ach_conf); + return; + } + + VECTOR_INIT(duplicate); + + while ((conf = libconfig->setting_get_elem(achdb, entry++))) { + const char *string = NULL; + int val = 0, i = 0; + struct achievement_data t_ad = { 0 }, *p_ad = NULL; + + /* Achievement ID */ + if (libconfig->setting_lookup_int(conf, "Id", &t_ad.id) == 0) { + ShowError("achievement_readdb: Id field for entry %d is not provided! Skipping...\n", entry); + continue; + } else if (t_ad.id <= 0 || t_ad.id > INT32_MAX) { + ShowError("achievement_readdb: Invalid Id %d for entry %d. Skipping...\n", t_ad.id, entry); + continue; + } + + ARR_FIND(0, VECTOR_LENGTH(duplicate), i, VECTOR_INDEX(duplicate, i) == t_ad.id); + if (i != VECTOR_LENGTH(duplicate)) { + ShowError("achievement_readdb: Duplicate Id %d for entry %d. Skipping...\n", t_ad.id, entry); + continue; + } + + /* Achievement Name */ + if (libconfig->setting_lookup_mutable_string(conf, "Name", t_ad.name, ACHIEVEMENT_NAME_LENGTH) == 0) { + ShowError("achievement_readdb: Name field not provided for Achievement %d!\n", t_ad.id); + continue; + } + + /* Achievement Type*/ + if (libconfig->setting_lookup_string(conf, "Type", &string) == 0) { + ShowError("achievement_readdb: Type field not provided for Achievement %d! Skipping...\n", t_ad.id); + continue; + } else if (!script->get_constant(string, &val)) { + ShowError("achievement_readdb: Invalid constant %s provided as type for Achievement %d! Skipping...\n", string, t_ad.id); + continue; + } + + t_ad.type = (enum achievement_types) val; + + /* Objectives */ + achievement->readdb_objectives(conf, &t_ad); + + /* Rewards */ + achievement->readdb_rewards(conf, &t_ad, filename); + + /* Achievement Points */ + libconfig->setting_lookup_int(conf, "Points", &t_ad.points); + + /* Additional Fields */ + achievement->readdb_additional_fields(conf, &t_ad, filename); + + /* Allocate memory for data. */ + CREATE(p_ad, struct achievement_data, 1); + *p_ad = t_ad; + + /* Place in the database. */ + idb_put(achievement->db, p_ad->id, p_ad); + + /* Put achievement key in categories. */ + VECTOR_ENSURE(achievement->category[p_ad->type], 1, 1); + VECTOR_PUSH(achievement->category[p_ad->type], p_ad->id); + + /* Qualify for duplicate Id checks */ + VECTOR_ENSURE(duplicate, 1, 1); + VECTOR_PUSH(duplicate, t_ad.id); + + count++; + } // end of achievement_db parsing. + + /* Destroy the buffer */ + libconfig->destroy(&ach_conf); + + VECTOR_CLEAR(duplicate); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); +} + +/** + * On server initiation. + * @param[in] minimal ignores alocating/reading databases. + */ +static void do_init_achievement(bool minimal) +{ + int i = 0; + + if (minimal) + return; + + /* DB Initialization */ + achievement->db = idb_alloc(DB_OPT_RELEASE_DATA); + + for (i = 0; i < ACH_TYPE_MAX; i++) + VECTOR_INIT(achievement->category[i]); + + VECTOR_INIT(achievement->rank_exp); + + /* Read LibConfig Files */ + achievement->readdb(); + achievement->readdb_ranks(); +} + +/** + * Cleaning function called through achievement->db->destroy() + */ +static int achievement_db_finalize(union DBKey key, struct DBData *data, va_list args) +{ + int i = 0; + struct achievement_data *ad = DB->data2ptr(data); + + for(i = 0; i < VECTOR_LENGTH(ad->objective); i++) + VECTOR_CLEAR(VECTOR_INDEX(ad->objective, i).jobid); + + VECTOR_CLEAR(ad->objective); + VECTOR_CLEAR(ad->rewards.item); + + // Free the script + if (ad->rewards.bonus != NULL) { + script->free_code(ad->rewards.bonus); + ad->rewards.bonus = NULL; + } + + return 0; +} + +/** + * On server finalizing. + */ +static void do_final_achievement(void) +{ + int i = 0; + + achievement->db->destroy(achievement->db, achievement->db_finalize); + + for (i = 0; i < ACH_TYPE_MAX; i++) + VECTOR_CLEAR(achievement->category[i]); + + VECTOR_CLEAR(achievement->rank_exp); +} + +/** + * Achievement Interface + */ +void achievement_defaults(void) +{ + achievement = &achievement_s; + /* */ + achievement->init = do_init_achievement; + achievement->final = do_final_achievement; + /* */ + achievement->db_finalize = achievement_db_finalize; + /* */ + achievement->readdb = achievement_readb; + /* */ + achievement->readdb_objectives_sub = achievement_readdb_objective_sub; + achievement->readdb_objectives = achievement_readdb_objectives; + /* */ + achievement->readdb_validate_criteria_mobid = achievement_readdb_validate_criteria_mobid; + achievement->readdb_validate_criteria_jobid = achievement_readdb_validate_criteria_jobid; + achievement->readdb_validate_criteria_itemid = achievement_readdb_validate_criteria_itemid; + achievement->readdb_validate_criteria_statustype = achievement_readdb_validate_criteria_statustype; + achievement->readdb_validate_criteria_itemtype = achievement_readdb_validate_criteria_itemtype; + achievement->readdb_validate_criteria_weaponlv = achievement_readdb_validate_criteria_weaponlv; + achievement->readdb_validate_criteria_achievement = achievement_readdb_validate_criteria_achievement; + /* */ + achievement->readdb_rewards = achievement_readdb_rewards; + achievement->readdb_validate_reward_items = achievement_readdb_validate_reward_items; + achievement->readdb_validate_reward_item_sub = achievement_readdb_validate_reward_item_sub; + achievement->readdb_validate_reward_bonus = achievement_readdb_validate_reward_bonus; + achievement->readdb_validate_reward_titleid = achievement_readdb_validate_reward_titleid; + /* */ + achievement->readdb_additional_fields = achievement_readdb_additional_fields; + /* */ + achievement->readdb_ranks = achievement_readdb_ranks; + /* */ + achievement->get = achievement_get; + achievement->ensure = achievement_ensure; + /* */ + achievement->calculate_totals = achievement_calculate_totals; + achievement->check_complete = achievement_check_complete; + achievement->progress_add = achievement_progress_add; + achievement->progress_set = achievement_progress_set; + achievement->check_criteria = achievement_check_criteria; + /* */ + achievement->validate = achievement_validate; + achievement->validate_type = achievement_validate_type; + /* */ + achievement->validate_mob_kill = achievement_validate_mob_kill; + achievement->validate_mob_damage = achievement_validate_mob_damage; + achievement->validate_pc_kill = achievement_validate_pc_kill; + achievement->validate_pc_damage = achievement_validate_pc_damage; + achievement->validate_jobchange = achievement_validate_jobchange; + achievement->validate_stats = achievement_validate_stats; + achievement->validate_chatroom_create = achievement_validate_chatroom_create; + achievement->validate_chatroom_members = achievement_validate_chatroom_members; + achievement->validate_friend_add = achievement_validate_friend_add; + achievement->validate_party_create = achievement_validate_party_create; + achievement->validate_marry = achievement_validate_marry; + achievement->validate_adopt = achievement_validate_adopt; + achievement->validate_zeny = achievement_validate_zeny; + achievement->validate_refine = achievement_validate_refine; + achievement->validate_item_get = achievement_validate_item_get; + achievement->validate_item_sell = achievement_validate_item_sell; + achievement->validate_achieve = achievement_validate_achieve; + achievement->validate_taming = achievement_validate_taming; + achievement->validate_achievement_rank = achievement_validate_achievement_rank; + /* */ + achievement->type_requires_criteria = achievement_type_requires_criteria; + /* */ + achievement->init_titles = achievement_init_titles; + achievement->check_title = achievement_check_title; + achievement->get_rewards = achievement_get_rewards; +} diff --git a/src/map/achievement.h b/src/map/achievement.h new file mode 100644 index 000000000..beba120a2 --- /dev/null +++ b/src/map/achievement.h @@ -0,0 +1,289 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2018 Hercules Dev Team +* Copyright (C) Smokexyz +* +* Hercules is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef MAP_ACHIEVEMENT_H +#define MAP_ACHIEVEMENT_H + +#include "common/hercules.h" +#include "common/db.h" +#include "map/map.h" // enum status_point_types + +#define ACHIEVEMENT_NAME_LENGTH 50 +#define OBJECTIVE_DESCRIPTION_LENGTH 100 + +struct achievement; +struct map_session_data; +struct char_achievements; + +/** + * Achievement Types + */ +enum achievement_types { + // Quest + ACH_QUEST, // achievement and objective specific + // PC Kills + ACH_KILL_PC_TOTAL, + ACH_KILL_PC_JOB, + ACH_KILL_PC_JOBTYPE, + // Mob Kills + ACH_KILL_MOB_CLASS, + // PC Damage + ACH_DAMAGE_PC_MAX, + ACH_DAMAGE_PC_TOTAL, + ACH_DAMAGE_PC_REC_MAX, + ACH_DAMAGE_PC_REC_TOTAL, + // Mob Damage + ACH_DAMAGE_MOB_MAX, + ACH_DAMAGE_MOB_TOTAL, + ACH_DAMAGE_MOB_REC_MAX, + ACH_DAMAGE_MOB_REC_TOTAL, + // Job + ACH_JOB_CHANGE, + // Status + ACH_STATUS, + ACH_STATUS_BY_JOB, + ACH_STATUS_BY_JOBTYPE, + // Chatroom + ACH_CHATROOM_CREATE_DEAD, + ACH_CHATROOM_CREATE, + ACH_CHATROOM_MEMBERS, + // Friend + ACH_FRIEND_ADD, + // Party + ACH_PARTY_CREATE, + ACH_PARTY_JOIN, + // Marriage + ACH_MARRY, + // Adoption + ACH_ADOPT_BABY, + ACH_ADOPT_PARENT, + // Zeny + ACH_ZENY_HOLD, + ACH_ZENY_GET_ONCE, + ACH_ZENY_GET_TOTAL, + ACH_ZENY_SPEND_ONCE, + ACH_ZENY_SPEND_TOTAL, + // Equipment + ACH_EQUIP_REFINE_SUCCESS, + ACH_EQUIP_REFINE_FAILURE, + ACH_EQUIP_REFINE_SUCCESS_TOTAL, + ACH_EQUIP_REFINE_FAILURE_TOTAL, + ACH_EQUIP_REFINE_SUCCESS_WLV, + ACH_EQUIP_REFINE_FAILURE_WLV, + ACH_EQUIP_REFINE_SUCCESS_ID, + ACH_EQUIP_REFINE_FAILURE_ID, + // Items + ACH_ITEM_GET_COUNT, + ACH_ITEM_GET_COUNT_ITEMTYPE, + ACH_ITEM_GET_WORTH, + ACH_ITEM_SELL_WORTH, + // Monsters + ACH_PET_CREATE, + // Achievement + ACH_ACHIEVE, + ACH_ACHIEVEMENT_RANK, + ACH_TYPE_MAX +}; + +enum unique_criteria_types { + CRITERIA_UNIQUE_NONE, + CRITERIA_UNIQUE_ACHIEVE_ID, + CRITERIA_UNIQUE_ITEM_ID, + CRITERIA_UNIQUE_STATUS_TYPE, + CRITERIA_UNIQUE_WEAPON_LV +}; + +/** + * Achievement Objective Structure + * + * @see achievement_type_requires_criteria() + * @see achievement_criteria_mobid() + * @see achievement_criteria_jobid() + * @see achievement_criteria_itemid() + * @see achievement_criteria_stattype() + * @see achievement_criteria_itemtype() + * @see achievement_criteria_weaponlv() + */ +struct achievement_objective { + int goal; + char description[OBJECTIVE_DESCRIPTION_LENGTH]; + /** + * Those criteria that do not make sense if stacked together. + * union identifier is set in unique_type. (@see unique_criteria_type) + */ + union { + int achieve_id; + int itemid; + enum status_point_types status_type; + int weapon_lv; + } unique; + enum unique_criteria_types unique_type; + /* */ + uint32 item_type; + int mobid; + VECTOR_DECL(int) jobid; +}; + +struct achievement_reward_item { + int id, amount; +}; + +struct achievement_rewards { + int title_id; + struct script_code *bonus; + VECTOR_DECL(struct achievement_reward_item) item; +}; + +/** + * Achievement Data Structure + */ +struct achievement_data { + int id; + char name[ACHIEVEMENT_NAME_LENGTH]; + enum achievement_types type; + int points; + VECTOR_DECL(struct achievement_objective) objective; + struct achievement_rewards rewards; +}; + +// Achievements types that use Mob ID as criteria. +#define achievement_criteria_mobid(t) ( \ + (t) == ACH_KILL_MOB_CLASS \ + || (t) == ACH_PET_CREATE ) + +// Achievements types that use JobID vector as criteria. +#define achievement_criteria_jobid(t) ( \ + (t) == ACH_KILL_PC_JOB \ + || (t) == ACH_KILL_PC_JOBTYPE \ + || (t) == ACH_JOB_CHANGE \ + || (t) == ACH_STATUS_BY_JOB \ + || (t) == ACH_STATUS_BY_JOBTYPE ) + +// Achievements types that use Item ID as criteria. +#define achievement_criteria_itemid(t) ( \ + (t) == ACH_ITEM_GET_COUNT \ + || (t) == ACH_EQUIP_REFINE_SUCCESS_ID \ + || (t) == ACH_EQUIP_REFINE_FAILURE_ID ) + +// Achievements types that use status type parameter as criteria. +#define achievement_criteria_stattype(t) ( \ + (t) == ACH_STATUS \ + || (t) == ACH_STATUS_BY_JOB \ + || (t) == ACH_STATUS_BY_JOBTYPE ) + +// Achievements types that use item type mask as criteria. +#define achievement_criteria_itemtype(t) ( \ + (t) == ACH_ITEM_GET_COUNT_ITEMTYPE ) + +// Achievements types that use weapon level as criteria. +#define achievement_criteria_weaponlv(t) ( \ + (t) == ACH_EQUIP_REFINE_SUCCESS_WLV \ + || (t) == ACH_EQUIP_REFINE_FAILURE_WLV ) + +// Valid status types for objective criteria. +#define achievement_valid_status_types(s) ( \ + (s) == SP_STR \ + || (s) == SP_AGI \ + || (s) == SP_VIT \ + || (s) == SP_INT \ + || (s) == SP_DEX \ + || (s) == SP_LUK \ + || (s) == SP_BASELEVEL || (s) == SP_JOBLEVEL ) + +struct achievement_interface { + struct DBMap *db; // int id -> struct achievement_data * + /* */ + VECTOR_DECL(int) rank_exp; // Achievement Rank Exp Requirements + VECTOR_DECL(int) category[ACH_TYPE_MAX]; /* A collection of Ids per type for faster processing. */ + /* */ + void (*init) (bool minimal); + void (*final) (void); + /* */ + int (*db_finalize) (union DBKey key, struct DBData *data, va_list args); + /* */ + void (*readdb)(void); + /* */ + bool (*readdb_objectives_sub) (const struct config_setting_t *conf, int index, struct achievement_data *entry); + bool (*readdb_objectives) (const struct config_setting_t *conf, struct achievement_data *entry); + /* */ + bool (*readdb_validate_criteria_mobid) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_jobid) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_itemid) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_statustype) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_itemtype) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_weaponlv) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + bool (*readdb_validate_criteria_achievement) (const struct config_setting_t *t, struct achievement_objective *obj, enum achievement_types type, int entry_id, int obj_idx); + /* */ + bool (*readdb_rewards) (const struct config_setting_t *conf, struct achievement_data *entry, const char *source); + void (*readdb_validate_reward_items) (const struct config_setting_t *t, struct achievement_data *entry); + bool (*readdb_validate_reward_item_sub) (const struct config_setting_t *t, int element, struct achievement_data *entry); + void (*readdb_validate_reward_bonus) (const struct config_setting_t *t, struct achievement_data *entry, const char *source); + void (*readdb_validate_reward_titleid) (const struct config_setting_t *t, struct achievement_data *entry); + /* */ + void (*readdb_additional_fields) (const struct config_setting_t *conf, struct achievement_data *entry, const char *source); + /* */ + void (*readdb_ranks) (void); + /* */ + const struct achievement_data *(*get) (int aid); + struct achievement *(*ensure) (struct map_session_data *sd, const struct achievement_data *ad); + /* */ + void (*calculate_totals) (const struct map_session_data *sd, int *points, int *completed, int *rank, int *curr_rank_points); + bool (*check_complete) (struct map_session_data *sd, const struct achievement_data *ad); + void (*progress_add) (struct map_session_data *sd, const struct achievement_data *ad, unsigned int obj_idx, int progress); + void (*progress_set) (struct map_session_data *sd, const struct achievement_data *ad, unsigned int obj_idx, int progress); + bool (*check_criteria) (const struct achievement_objective *objective, const struct achievement_objective *criteria); + /* */ + bool (*validate) (struct map_session_data *sd, int aid, unsigned int obj_idx, int progress, bool additive); + int (*validate_type) (struct map_session_data *sd, enum achievement_types type, const struct achievement_objective *criteria, bool additive); + /* */ + void (*validate_mob_kill) (struct map_session_data *sd, int mob_id); + void (*validate_mob_damage) (struct map_session_data *sd, unsigned int damage, bool received); + void (*validate_pc_kill) (struct map_session_data *sd, struct map_session_data *dstsd); + void (*validate_pc_damage) (struct map_session_data *sd, struct map_session_data *dstsd, unsigned int damage); + void (*validate_jobchange) (struct map_session_data *sd); + void (*validate_stats) (struct map_session_data *sd, enum status_point_types stat_type, int progress); + void (*validate_chatroom_create) (struct map_session_data *sd); + void (*validate_chatroom_members) (struct map_session_data *sd, int progress); + void (*validate_friend_add) (struct map_session_data *sd); + void (*validate_party_create) (struct map_session_data *sd); + void (*validate_marry) (struct map_session_data *sd); + void (*validate_adopt) (struct map_session_data *sd, bool parent); + void (*validate_zeny) (struct map_session_data *sd, int amount); + void (*validate_refine) (struct map_session_data *sd, unsigned int idx, bool success); + void (*validate_item_get) (struct map_session_data *sd, int nameid, int amount); + void (*validate_item_sell) (struct map_session_data *sd, int nameid, int amount); + void (*validate_achieve) (struct map_session_data *sd, int achid); + void (*validate_taming) (struct map_session_data *sd, int class); + void (*validate_achievement_rank) (struct map_session_data *sd, int rank); + /* */ + bool (*type_requires_criteria) (enum achievement_types type); + /* */ + void (*init_titles) (struct map_session_data *sd); + bool (*check_title) (struct map_session_data *sd, int title_id); + void (*get_rewards) (struct map_session_data *sd, const struct achievement_data *ad); +}; + +#ifdef HERCULES_CORE +void achievement_defaults(void); +#endif // HERCULES_CORE + +HPShared struct achievement_interface *achievement; + +#endif // MAP_ACHIEVEMENT_H diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 3ec709b57..52bf64e87 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -55,6 +55,7 @@ #include "map/storage.h" #include "map/trade.h" #include "map/unit.h" +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/core.h" @@ -1421,6 +1422,10 @@ ACMD(baselevelup) clif->updatestatus(sd, SP_BASEEXP); clif->updatestatus(sd, SP_NEXTBASEEXP); pc->baselevelchanged(sd); + + // achievements + achievement->validate_stats(sd, SP_BASELEVEL, sd->status.base_level); + if(sd->status.party_id) party->send_levelup(sd); @@ -2526,6 +2531,7 @@ ACMD(param) clif->updatestatus(sd, SP_USTR + i); status_calc_pc(sd, SCO_FORCE); clif->message(fd, msg_fd(fd,42)); // Stat changed. + achievement->validate_stats(sd, SP_STR + i, new_value); // Achievements [Smokexyz/Hercules] } else { if (value < 0) clif->message(fd, msg_fd(fd,41)); // Unable to decrease the number/value. diff --git a/src/map/chat.c b/src/map/chat.c index 9852131be..d9b642219 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -29,6 +29,7 @@ #include "map/npc.h" // npc_event_do() #include "map/pc.h" #include "map/skill.h" // ext_skill_unit_onplace() +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/memmgr.h" #include "common/mmo.h" @@ -126,6 +127,7 @@ static bool chat_createpcchat(struct map_session_data *sd, const char *title, co pc_stop_attack(sd); clif->createchat(sd,0); // 0 = success clif->dispchat(cd,0); + achievement->validate_chatroom_create(sd); // Achievements [Smokexyz/Hercules] return true; } clif->createchat(sd,1); // 1 = Room limit exceeded @@ -181,6 +183,9 @@ static bool chat_joinchat(struct map_session_data *sd, int chatid, const char *p cd->usersd[cd->users] = sd; cd->users++; + if (cd->owner->type == BL_PC) + achievement->validate_chatroom_members(BL_UCAST(BL_PC, cd->owner), cd->users); + pc_setchatid(sd,cd->bl.id); clif->joinchatok(sd, cd); //To the person who newly joined the list of all diff --git a/src/map/chrif.c b/src/map/chrif.c index cd24c5fea..609c856c6 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -336,6 +336,8 @@ static bool chrif_save(struct map_session_data *sd, int flag) elemental->save(sd->ed); if( sd->save_quest ) intif->quest_save(sd); + if (VECTOR_LENGTH(sd->achievement) > 0) + intif->achievements_save(sd); if (sd->storage.received == true && sd->storage.save == true) intif->send_account_storage(sd); diff --git a/src/map/clif.c b/src/map/clif.c index 82507a84a..80cd44aaf 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -56,13 +56,14 @@ #include "map/trade.h" #include "map/unit.h" #include "map/vending.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/ers.h" #include "common/grfio.h" #include "common/memmgr.h" -#include "common/mmo.h" // NEW_CARTS +#include "common/mmo.h" // NEW_CARTS, char_achievements #include "common/nullpo.h" #include "common/random.h" #include "common/showmsg.h" @@ -8816,17 +8817,18 @@ static void clif_refresh(struct map_session_data *sd) /// Updates the object's (bl) name on client. /// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME) /// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL) +/// 0A30 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B <title id>.L (ZC_ACK_REQNAMEALL2) static void clif_charnameack(int fd, struct block_list *bl) { - unsigned char buf[103]; - int cmd = 0x95; + struct packet_reqnameall_ack packet = { 0 }; + int len = sizeof(struct packet_reqnameall_ack); nullpo_retv(bl); - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = bl->id; + packet.packet_id = reqName; + packet.gid = bl->id; - switch( bl->type ) { + switch(bl->type) { case BL_PC: { const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); @@ -8834,17 +8836,28 @@ static void clif_charnameack(int fd, struct block_list *bl) const struct guild *g = NULL; int ps = -1; + if (ssd->fakename[0] != '\0' || ssd->status.guild_id > 0 || ssd->status.party_id > 0 || ssd->status.title_id > 0) { + packet.packet_id = reqNameAllType; + } + //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise != -1) - WBUFL(buf,2) = -bl->id; + if (ssd->fd == fd && ssd->disguise != -1) { + packet.gid = -bl->id; + } if (ssd->fakename[0] != '\0') { - WBUFW(buf, 0) = cmd = 0x195; - memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH); - WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0; + memcpy(packet.name, ssd->fakename, NAME_LENGTH); break; } - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + +#if PACKETVER >= 20150503 + // Title System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; + } +#endif + + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (ssd->status.party_id != 0) { p = party->search(ssd->status.party_id); @@ -8866,47 +8879,41 @@ static void clif_charnameack(int fd, struct block_list *bl) if (p == NULL && g == NULL) break; - WBUFW(buf, 0) = cmd = 0x195; - if (p != NULL) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) { + memcpy(packet.party_name, p->party.name, NAME_LENGTH); + } if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); - } else { //Assume no guild. - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } } break; //[blackhole89] case BL_HOM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); break; case BL_MER: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); break; case BL_PET: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); break; case BL_NPC: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); break; case BL_MOB: { const struct mob_data *md = BL_UCCAST(BL_MOB, bl); - memcpy(WBUFP(buf,6), md->name, NAME_LENGTH); + memcpy(packet.name, md->name, NAME_LENGTH); if (md->guardian_data && md->guardian_data->g) { - WBUFW(buf, 0) = cmd = 0x195; - WBUFB(buf,30) = 0; - memcpy(WBUFP(buf,54), md->guardian_data->g->name, NAME_LENGTH); - memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH); + packet.packet_id = reqNameAllType; + memcpy(packet.guild_name, md->guardian_data->g->name, NAME_LENGTH); + memcpy(packet.position_name, md->guardian_data->castle->castle_name, NAME_LENGTH); } else if (battle_config.show_mob_info) { char mobhp[50], *str_p = mobhp; - WBUFW(buf, 0) = cmd = 0x195; + packet.packet_id = reqNameAllType; if (battle_config.show_mob_info&4) str_p += sprintf(str_p, "Lv. %d | ", md->level); if (battle_config.show_mob_info&1) @@ -8917,34 +8924,39 @@ static void clif_charnameack(int fd, struct block_list *bl) //can parse it. [Skotlex] if (str_p != mobhp) { *(str_p-3) = '\0'; //Remove trailing space + pipe. - memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH); - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.party_name, mobhp, NAME_LENGTH); } } } break; case BL_CHAT: #if 0 //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex] - memcpy(WBUFP(buf,6), BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); break; #endif return; case BL_ELEM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); break; default: ShowError("clif_charnameack: bad type %u(%d)\n", bl->type, bl->id); return; } + if (packet.packet_id == reqName) { + len = sizeof(struct packet_reqname_ack); + } + // if no recipient specified just update nearby clients // if no recipient specified just update nearby clients if (fd == 0) { - clif->send(buf, packet_len(cmd), bl, AREA); + clif->send(&packet, len, bl, AREA); } else { - WFIFOHEAD(fd, packet_len(cmd)); - memcpy(WFIFOP(fd, 0), buf, packet_len(cmd)); - WFIFOSET(fd, packet_len(cmd)); + struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; + if (sd != NULL) { + clif->send(&packet, len, &sd->bl, SELF); + } else { + clif->send(&packet, len, bl, SELF); + } } } @@ -8952,54 +8964,52 @@ static void clif_charnameack(int fd, struct block_list *bl) //Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent. static void clif_charnameupdate(struct map_session_data *ssd) { - unsigned char buf[103]; - int cmd = 0x195, ps = -1; + int ps = -1; struct party_data *p = NULL; struct guild *g = NULL; + struct packet_reqnameall_ack packet = { 0 }; nullpo_retv(ssd); - if( ssd->fakename[0] ) + if (ssd->fakename[0]) return; //No need to update as the party/guild was not displayed anyway. - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = ssd->bl.id; + packet.packet_id = reqNameAllType; + packet.gid = ssd->bl.id; - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (!battle_config.display_party_name) { if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) p = party->search(ssd->status.party_id); - }else{ + } else { if (ssd->status.party_id > 0) p = party->search(ssd->status.party_id); } - if( ssd->status.guild_id > 0 && (g = ssd->guild) != NULL ) - { + if (ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) { int i; ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); if( i < g->max_member ) ps = g->member[i].position; } - if( p ) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) + memcpy(packet.party_name, p->party.name, NAME_LENGTH); - if( g && ps >= 0 && ps < MAX_GUILDPOSITION ) - { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); + if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } - else - { - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + +#if PACKETVER >= 20150503 + // Achievement System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; } +#endif // Update nearby clients - clif->send(buf, packet_len(cmd), &ssd->bl, AREA); + clif->send(&packet, sizeof(packet), &ssd->bl, AREA); } /// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP). @@ -14787,6 +14797,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) f_sd->status.friends[i].char_id = sd->status.char_id; memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH); clif->friendslist_reqack(f_sd, sd, 0); + achievement->validate_friend_add(f_sd); // Achievements [Smokexyz/Hercules] if (battle_config.friend_auto_add) { // Also add f_sd to sd's friendlist. @@ -14805,6 +14816,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) sd->status.friends[i].char_id = f_sd->status.char_id; memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH); clif->friendslist_reqack(sd, f_sd, 0); + achievement->validate_friend_add(sd); // Achievements [Smokexyz/Hercules] } } } @@ -20224,6 +20236,216 @@ static unsigned short clif_parse_cmd_optional(int fd, struct map_session_data *s return cmd; } +/** + * Send all of a session's achievement information to client. + * Called only once on login/char-loading. (PACKET_ZC_ALL_ACH_LIST) + * @packet [out] 0x0A23 <ID>.W <Length>.W <ach_count>.L <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *[]>.P + * @param fd socket descriptor + * @param sd pointer to map_session_data + */ +static void clif_achievement_send_list(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20141016 + int i = 0, count = 0, curr_exp_tmp = 0; + struct packet_achievement_list p = { 0 }; + + nullpo_retv(sd); + + /* Browse through the session's achievement list and gather their values. */ + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + int j = 0; + struct achievement *a = &VECTOR_INDEX(sd->achievement, i); + const struct achievement_data *ad = NULL; + + /* Sanity check for nonull pointers. */ + if (a == NULL || (ad = achievement->get(a->id)) == NULL) + continue; + + p.ach[count].ach_id = a->id; + + for (j = 0; j < VECTOR_LENGTH(ad->objective); j++) + p.ach[count].objective[j] = a->objective[j]; + + if (a->completed_at) { + p.ach[count].completed = 1; + p.ach[count].completed_at = (uint32) a->completed_at; + p.ach[count].reward = a->rewarded_at ? 1 : 0; + p.total_points += ad->points; + } + + count++; + } + + p.packet_id = achievementListType; + p.packet_len = sizeof(struct ach_list_info) * count + 22; + p.total_achievements = count; + /* Determine Achievement Rank and current exp */ + curr_exp_tmp = p.total_points; + for (i = 0; curr_exp_tmp && i < MAX_ACHIEVEMENT_RANKS && i < VECTOR_LENGTH(achievement->rank_exp); i++) { + if (curr_exp_tmp >= VECTOR_INDEX(achievement->rank_exp, i)) { + curr_exp_tmp -= VECTOR_INDEX(achievement->rank_exp, i); + p.rank++; + + // Validate achievement rank type achievements. + achievement->validate_achievement_rank(sd, p.rank); + } + } + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_exp_tmp; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + /* Send payload */ + clif->send(&p, p.packet_len, &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement information for a single achievement. + * Called on objective progress updates/completion. (PACKET_ZC_ACH_UPDATE) + * @packet [out] 0x0A24 <ID>.W <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *>.P + * @param fd socket descriptor + * @param sd pointer to struct map_session_data + * @param ad const pointer to struct achievement_data from the achievement db. + */ +static void clif_achievement_send_update(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER >= 20141016 + struct packet_achievement_update p = { 0 }; + struct achievement *a = NULL; + int i = 0, points = 0, rank = 0, curr_rank_points = 0; + + nullpo_retv(sd); + nullpo_retv(ad); + + /* Get Session Achievement Data */ + if ((a = achievement->ensure(sd, ad)) == NULL) + return; + + /* Get total points, current rank and current rank points from the session. */ + achievement->calculate_totals(sd, &points, NULL, &rank, &curr_rank_points); + + p.packet_id = achievementUpdateType; + + /* Determine Total Achievement Points */ + p.total_points = points; + + /* Determine Achievement Rank */ + p.rank = rank; + + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_rank_points; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + + p.ach.ach_id = ad->id; + p.ach.completed = (uint8) achievement->check_complete(sd, ad); + + for (i = 0; i < VECTOR_LENGTH(ad->objective); i++) + p.ach.objective[i] = a->objective[i]; + + p.ach.completed_at = (uint32) a->completed_at; + + p.ach.reward = a->rewarded_at ? 1 : 0; + + clif->send(&p, packet_len(achievementUpdateType), &sd->bl, SELF); + + /* Validate rank-related achievements */ + achievement->validate_achievement_rank(sd, rank); + +#endif // PACKETVER >= 20141016 +} + +/** + * Parses achievement reward packet from session. + * @packet [in] 0x0A25 <ach_id>.L + * @param fd socket descriptor. + * @param sd ptr to session data. + */ +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20141016 + int ach_id = RFIFOL(fd, 2); + const struct achievement_data *ad = NULL; + struct achievement *ach = NULL; + + if (ach_id <= 0 || (ad = achievement->get(ach_id)) == NULL) + return; + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + if (achievement->check_complete(sd, ad) && ach->completed_at && ach->rewarded_at == 0) { + achievement->get_rewards(sd, ad); + } +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet [out] 0x0A26 <packet_id>.W <received + */ +static void clif_achievement_reward_ack(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER >= 20141016 + struct packet_achievement_reward_ack p = { 0 }; + + nullpo_retv(sd); + nullpo_retv(ad); + + p.packet_id = achievementRewardAckType; + p.received = 1; + p.ach_id = ad->id; + + clif->send(&p, packet_len(achievementRewardAckType), &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet[in] 0x0A2E <packet_id>.W <title_id>.L + */ +static void clif_parse_change_title(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_change_title(int fd, struct map_session_data *sd) +{ + int title_id = RFIFOL(fd, 2); + + if (title_id == sd->status.title_id) { // Same Title + return; + } else if (title_id < 0) { + title_id = 0; + } + + clif->change_title_ack(fd, sd, title_id); +} + +/** + * [clif_change_title_ack description] + * @packet [out] 0x0A2F <packet_id>.W <Result>.B <title_id>.L + */ +static void clif_change_title_ack(int fd, struct map_session_data *sd, int title_id) +{ +#if PACKETVER >= 20141016 + unsigned char failed = 0; + + if (!achievement->check_title(sd, title_id)) { + clif->message(fd, "Title is not yet earned."); + failed = 1; + } + + sd->status.title_id = title_id; + + WFIFOHEAD(fd, packet_len(0xa2f)); + WFIFOW(fd, 0) = 0xa2f; + WFIFOB(fd, 2) = failed; + WFIFOL(fd, 3) = sd->status.title_id; + WFIFOSET(fd, packet_len(0xa2f)); + + // Update names + clif->charnameack(fd, &sd->bl); + clif->charnameack(0, &sd->bl); +#endif +} +// End of Achievement System + /*========================================== * RoDEX *------------------------------------------*/ @@ -22166,6 +22388,14 @@ void clif_defaults(void) clif->isdisguised = clif_isdisguised; clif->navigate_to = clif_navigate_to; clif->bl_type = clif_bl_type; + /* Achievement System */ + clif->achievement_send_list = clif_achievement_send_list; + clif->achievement_send_update = clif_achievement_send_update; + clif->pAchievementGetReward = clif_parse_achievement_get_reward; + clif->achievement_reward_ack = clif_achievement_reward_ack; + /* Title */ + clif->change_title_ack = clif_change_title_ack; + clif->pChangeTitle = clif_parse_change_title; /*------------------------ *- Parse Incoming Packet @@ -22413,6 +22643,7 @@ void clif_defaults(void) clif->pHotkeyRowShift = clif_parse_HotkeyRowShift; clif->dressroom_open = clif_dressroom_open; clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; + /* Achievements [Smokexyz/Hercules] */ clif->get_bl_name = clif_get_bl_name; /* RODEX */ clif->pRodexOpenWriteMail = clif_parse_rodex_open_write_mail; diff --git a/src/map/clif.h b/src/map/clif.h index 0077566b5..8cff8a21e 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -54,6 +54,7 @@ struct skill_cd; struct skill_unit; struct unit_data; struct view_data; +struct achievement_data; // map/achievement.h enum clif_messages; enum rodex_add_item; @@ -1197,6 +1198,13 @@ struct clif_interface { bool (*isdisguised) (struct block_list* bl); void (*navigate_to) (struct map_session_data *sd, const char* mapname, uint16 x, uint16 y, uint8 flag, bool hideWindow, uint16 mob_id); unsigned char (*bl_type) (struct block_list *bl); + /* Achievement System */ + void (*achievement_send_list) (int fd, struct map_session_data *sd); + void (*achievement_send_update) (int fd, struct map_session_data *sd, const struct achievement_data *ad); + void (*pAchievementGetReward) (int fd, struct map_session_data *sd); + void (*achievement_reward_ack) (int fd, struct map_session_data *sd, const struct achievement_data *ad); + void (*change_title_ack) (int fd, struct map_session_data *sd, int title_id); + void (*pChangeTitle) (int fd, struct map_session_data *sd); /*------------------------ *- Parse Incoming Packet *------------------------*/ diff --git a/src/map/intif.c b/src/map/intif.c index 393058a8a..5fafc0913 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -41,6 +41,8 @@ #include "map/quest.h" #include "map/rodex.h" #include "map/storage.h" +#include "map/achievement.h" + #include "common/memmgr.h" #include "common/nullpo.h" #include "common/showmsg.h" @@ -1730,6 +1732,99 @@ static void intif_parse_DeleteHomunculusOk(int fd) ShowError("Homunculus data delete failure\n"); } +/*************************************** + * ACHIEVEMENT SYSTEM FUNCTIONS + ***************************************/ +/** + * Sends a request to the inter-server to load + * and send a character's achievement data. + * @packet [out] 0x3012 <char_id>.L + * @param sd pointer to map_session_data. + */ +static void intif_achievements_request(struct map_session_data *sd) +{ + nullpo_retv(sd); + + if (intif->CheckForCharServer()) + return; + + WFIFOHEAD(inter_fd, 6); + WFIFOW(inter_fd, 0) = 0x3012; + WFIFOL(inter_fd, 2) = sd->status.char_id; + WFIFOSET(inter_fd, 6); +} + +/** + * Handles reception of achievement data for a character from the inter-server. + * @packet [in] 0x3810 <packet_len>.W <char_id>.L <char_achievements[]>.P + * @param fd socket descriptor. + */ +static void intif_parse_achievements_load(int fd) +{ + int size = RFIFOW(fd, 2); + int char_id = RFIFOL(fd, 4); + int payload_count = (size - 8) / sizeof(struct achievement); + struct map_session_data *sd = map->charid2sd(char_id); + int i = 0; + + if (sd == NULL) { + ShowError("intif_parse_achievements_load: Parse request for achievements received but character is offline!\n"); + return; + } + + if (VECTOR_LENGTH(sd->achievement) > 0) { + ShowError("intif_parse_achievements_load: Achievements already loaded! Possible multiple calls from the inter-server received.\n"); + return; + } + + VECTOR_ENSURE(sd->achievement, payload_count, 1); + + for (i = 0; i < payload_count; i++) { + struct achievement t_ach = { 0 }; + + memcpy(&t_ach, RFIFOP(fd, 8 + i * sizeof(struct achievement)), sizeof(struct achievement)); + + if (achievement->get(t_ach.id) == NULL) { + ShowError("intif_parse_achievements_load: Invalid Achievement %d received from character %d. Ignoring...\n", t_ach.id, char_id); + continue; + } + + VECTOR_PUSH(sd->achievement, t_ach); + } + + achievement->init_titles(sd); + clif->achievement_send_list(fd, sd); + sd->achievements_received = true; +} + +/** + * Send character's achievement data to the inter-server. + * @packet 0x3013 <packet_len>.W <char_id>.L <achievements[]>.P + * @param sd pointer to map session data. + */ +static void intif_achievements_save(struct map_session_data *sd) +{ + int packet_len = 0, payload_size = 0, i = 0; + + nullpo_retv(sd); + /* check for character server. */ + if (intif->CheckForCharServer()) + return; + /* Return if no data. */ + if (!(payload_size = VECTOR_LENGTH(sd->achievement) * sizeof(struct achievement))) + return; + + packet_len = payload_size + 8; + + WFIFOHEAD(inter_fd, packet_len); + WFIFOW(inter_fd, 0) = 0x3013; + WFIFOW(inter_fd, 2) = packet_len; + WFIFOL(inter_fd, 4) = sd->status.char_id; + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) + memcpy(WFIFOP(inter_fd, 8 + i * sizeof(struct achievement)), &VECTOR_INDEX(sd->achievement, i), sizeof(struct achievement)); + WFIFOSET(inter_fd, packet_len); +} + /************************************** * QUESTLOG SYSTEM FUNCTIONS * **************************************/ @@ -2799,6 +2894,7 @@ static int intif_parse(int fd) case 0x3806: intif->pChangeNameOk(fd); break; case 0x3807: intif->pMessageToFD(fd); break; case 0x3808: intif->pAccountStorageSaveAck(fd); break; + case 0x3810: intif->pAchievementsLoad(fd); break; case 0x3818: intif->pLoadGuildStorage(fd); break; case 0x3819: intif->pSaveGuildStorage(fd); break; case 0x3820: intif->pPartyCreated(fd); break; @@ -2896,7 +2992,7 @@ void intif_defaults(void) { const int packet_len_table [INTIF_PACKET_LEN_TABLE_SIZE] = { -1,-1,27,-1, -1,-1,37,-1, 7, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f - 0, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810 + -1, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810 Achievements [Smokexyz/Hercules] 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 10,-1,15, 0, 79,23, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 -1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 @@ -3001,6 +3097,9 @@ void intif_defaults(void) intif->CheckForCharServer = CheckForCharServer; /* */ intif->itembound_req = intif_itembound_req; + /* Achievement System */ + intif->achievements_request = intif_achievements_request; + intif->achievements_save = intif_achievements_save; /* parse functions */ intif->pWisMessage = intif_parse_WisMessage; intif->pWisEnd = intif_parse_WisEnd; @@ -3073,4 +3172,6 @@ void intif_defaults(void) intif->pRodexCheckName = intif_parse_RodexCheckName; /* Clan System */ intif->pRecvClanMemberAction = intif_parse_RecvClanMemberAction; + /* Achievement System */ + intif->pAchievementsLoad = intif_parse_achievements_load; } diff --git a/src/map/intif.h b/src/map/intif.h index c75b93201..1e98d11f8 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -145,6 +145,9 @@ struct intif_interface { void (*request_accinfo) (int u_fd, int aid, int group_lv, char* query); /* */ int (*CheckForCharServer) (void); + /* Achievement System [Smokexyz/Hercules] */ + void(*achievements_request) (struct map_session_data *sd); + void(*achievements_save) (struct map_session_data *sd); /* */ void (*pWisMessage) (int fd); void (*pWisEnd) (int fd); @@ -217,6 +220,8 @@ struct intif_interface { void(*pRodexCheckName) (int fd); /* Clan System */ void (*pRecvClanMemberAction) (int fd); + /* Achievements */ + void (*pAchievementsLoad) (int fd); }; #ifdef HERCULES_CORE diff --git a/src/map/map.c b/src/map/map.c index 0124a3035..ce8f4cdf5 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -59,6 +59,7 @@ #include "map/rodex.h" #include "map/trade.h" #include "map/unit.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -6134,6 +6135,7 @@ int do_final(void) map->list_final(); vending->final(); rodex->final(); + achievement->final(); HPM_map_do_final(); @@ -6338,6 +6340,7 @@ static void map_load_defaults(void) pet_defaults(); path_defaults(); quest_defaults(); + achievement_defaults(); npc_chat_defaults(); rodex_defaults(); } @@ -6653,6 +6656,7 @@ int do_init(int argc, char *argv[]) mercenary->init(minimal); elemental->init(minimal); quest->init(minimal); + achievement->init(minimal); npc->init(minimal); unit->init(minimal); bg->init(minimal); diff --git a/src/map/map.h b/src/map/map.h index 04525b4d5..207fef2ce 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -66,6 +66,16 @@ enum E_MAPSERVER_ST { #define block_free_max 1048576 #define BL_LIST_MAX 1048576 +// The following system marks a different job ID system used by the map server, +// which makes a lot more sense than the normal one. [Skotlex] +// These marks the "level" of the job. +#define JOBL_2_1 0x0100 +#define JOBL_2_2 0x0200 +#define JOBL_2 0x0300 // JOBL_2_1 | JOBL_2_2 +#define JOBL_UPPER 0x1000 +#define JOBL_BABY 0x2000 +#define JOBL_THIRD 0x4000 + // For filtering and quick checking. #define MAPID_BASEMASK 0x00ff #define MAPID_UPPERMASK 0x0fff @@ -537,6 +547,7 @@ struct flooritem_data { }; enum status_point_types { //we better clean up this enum and change it name [Hemagx] + SP_NONE = -1, SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7 SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15 SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23 diff --git a/src/map/mob.c b/src/map/mob.c index 0dbff9211..27039490c 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -44,6 +44,7 @@ #include "map/script.h" #include "map/skill.h" #include "map/status.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/conf.h" @@ -2188,6 +2189,10 @@ static void mob_damage(struct mob_data *md, struct block_list *src, int damage) if (src) mob->log_damage(md, src, damage); md->dmgtick = timer->gettick(); + + // Achievements [Smokexyz/Hercules] + if (src != NULL && src->type == BL_PC) + achievement->validate_mob_damage(BL_UCAST(BL_PC, src), damage, false); } if (battle_config.show_mob_info&3) @@ -2420,6 +2425,8 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) } if(zeny) // zeny from mobs [Valaris] pc->getzeny(tmpsd[i], zeny, LOG_TYPE_PICKDROP_MONSTER, NULL); + + achievement->validate_mob_kill(tmpsd[i], md->db->mob_id); // Achievements [Smokexyz/Hercules] } } diff --git a/src/map/npc.c b/src/map/npc.c index 383558eaf..3a00b38ea 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -40,6 +40,7 @@ #include "map/skill.h" #include "map/status.h" #include "map/unit.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/db.h" @@ -2272,6 +2273,9 @@ static int npc_selllist(struct map_session_data *sd, struct itemlist *item_list) } pc->delitem(sd, idx, entry->amount, 0, DELITEM_SOLD, LOG_TYPE_NPC); + + // Achievements [Smokexyz/Hercules] + achievement->validate_item_sell(sd, sd->status.inventory[idx].nameid, entry->amount); } if (z + sd->status.zeny > MAX_ZENY && nd->master_nd == NULL) diff --git a/src/map/packets.h b/src/map/packets.h index 8ef011d08..61bf625cf 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -3209,7 +3209,7 @@ packet(0x96e,-1,clif->ackmergeitems); packet(0x0a22,3); // ZC_RECV_ROULETTE_ITEM packet(0x0a23,-1); // ZC_ALL_ACH_LIST packet(0x0a24,35); // ZC_ACH_UPDATE - packet(0x0a25,6,clif->pDull/*,XXX*/); // CZ_REQ_ACH_REWARD + packet(0x0a25,6,clif->pAchievementGetReward, 2); // CZ_REQ_ACH_REWARD packet(0x0a26,7); // ZC_REQ_ACH_REWARD_ACK // changed packet sizes packet(0x0a18,14); // ZC_ACCEPT_ENTER3 @@ -3266,7 +3266,7 @@ packet(0x96e,-1,clif->ackmergeitems); // 2014-09-03aRagexeRE #if PACKETVER >= 20140903 // new packets - packet(0x0a2e,6,clif->pDull/*,XXX*/); // CZ_REQ_CHANGE_TITLE + packet(0x0a2e,6,clif->pChangeTitle); // CZ_REQ_CHANGE_TITLE packet(0x0a2f,7); // ZC_ACK_CHANGE_TITLE // changed packet sizes #endif diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index e8207d29c..5f6443ef3 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -302,6 +302,11 @@ enum packet_headers { rouletteinfoackType = 0xa1c, roulettgenerateackType = 0xa20, roulettercvitemackType = 0xa22, +#if PACKETVER >= 20141016 + achievementListType = 0xa23, + achievementUpdateType = 0xa24, + achievementRewardAckType = 0xa26, +#endif // PACKETVER >= 20141016 #if PACKETVER >= 20150513 // [4144] 0x09f8 handling in client from 2014-10-29aRagexe and 2014-03-26cRagexeRE questListType = 0x9f8, ///< ZC_ALL_QUEST_LIST3 #elif PACKETVER >= 20141022 @@ -401,6 +406,12 @@ enum packet_headers { #else hominfoType = 0x22e, #endif + reqName = 0x95, +#if PACKETVER >= 20150503 // Confirm this? + reqNameAllType = 0xA30, +#else + reqNameAllType = 0x195, +#endif }; #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute @@ -2589,6 +2600,61 @@ struct PACKET_ZC_SEARCH_STORE_INFO_ACK { struct PACKET_ZC_SEARCH_STORE_INFO_ACK_sub items[]; } __attribute__((packed)); +/* Achievement System */ +struct ach_list_info { + uint32 ach_id; + uint8 completed; + uint32 objective[MAX_ACHIEVEMENT_OBJECTIVES]; + uint32 completed_at; + uint8 reward; +} __attribute__((packed)); + +struct packet_achievement_list { + uint16 packet_id; + uint16 packet_len; + uint32 total_achievements; + uint32 total_points; + uint16 rank; + uint32 current_rank_points; + uint32 next_rank_points; + struct ach_list_info ach[MAX_ACHIEVEMENT_DB]; +} __attribute__((packed)); + +struct packet_achievement_update { + uint16 packet_id; + uint32 total_points; + uint16 rank; + uint32 current_rank_points; + uint32 next_rank_points; + struct ach_list_info ach; +} __attribute__((packed)); + +struct packet_achievement_reward_ack { + uint16 packet_id; + uint8 received; + uint32 ach_id; +} __attribute__((packed)); + +// Name Packet ZC_ACK_REQNAME +struct packet_reqname_ack { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; +} __attribute__((packed)); + +// ZC_ACK_REQNAMEALL / ZC_ACK_REQNAMEALL2 +struct packet_reqnameall_ack { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; + char party_name[NAME_LENGTH]; + char guild_name[NAME_LENGTH]; + char position_name[NAME_LENGTH]; +#if PACKETVER >= 20150503 // Confirm this? + int32 title_id; // Achievement Title +#endif +} __attribute__((packed)); + #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute #pragma pack(pop) #endif // not NetBSD < 6 / Solaris diff --git a/src/map/party.c b/src/map/party.c index 9024b1c19..e4fb18c23 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -36,6 +36,7 @@ #include "map/pc.h" #include "map/skill.h" #include "map/status.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/memmgr.h" @@ -189,6 +190,9 @@ static int party_create(struct map_session_data *sd, const char *name, int item, party->fill_member(&leader, sd, 1); intif->create_party(&leader,name,item,item2); + + achievement->validate_party_create(sd); //Achievements (Smokexyz) + return 0; } diff --git a/src/map/pc.c b/src/map/pc.c index d89abcca8..ff8f18489 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -55,6 +55,7 @@ #include "map/skill.h" #include "map/status.h" // struct status_data #include "map/storage.h" +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/core.h" // get_svn_revision() @@ -1053,6 +1054,11 @@ static bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data pc->skill(p1_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT); pc->skill(p2_sd, WE_CALLBABY, 1, SKILL_GRANT_PERMANENT); + // Achievements [Smokexyz/Hercules] + achievement->validate_adopt(p1_sd, true); // Parent 1 + achievement->validate_adopt(p2_sd, true); // Parent 2 + achievement->validate_adopt(b_sd, false); // Baby + return true; } @@ -1325,6 +1331,7 @@ static bool pc_authok(struct map_session_data *sd, int login_id2, time_t expirat sd->bg_queue.type = 0; VECTOR_INIT(sd->script_queues); + VECTOR_INIT(sd->achievement); // Achievements [Smokexyz/Hercules] VECTOR_INIT(sd->storage.item); // initialize storage item vector. VECTOR_INIT(sd->hatEffectId); @@ -1384,6 +1391,7 @@ static bool pc_authok(struct map_session_data *sd, int login_id2, time_t expirat " Group '"CL_WHITE"%d"CL_RESET"').\n", sd->status.name, sd->status.account_id, sd->status.char_id, CONVIP(ip), sd->group_id); + // Send friends list clif->friendslist_send(sd); @@ -1474,7 +1482,6 @@ static int pc_reg_received(struct map_session_data *sd) nullpo_ret(sd); sd->vars_ok = true; - sd->change_level_2nd = pc_readglobalreg(sd,script->add_str("jobchange_level")); sd->change_level_3rd = pc_readglobalreg(sd,script->add_str("jobchange_level_3rd")); sd->die_counter = pc_readglobalreg(sd,script->add_str("PC_DIE_COUNTER")); @@ -1593,6 +1600,9 @@ static int pc_reg_received(struct map_session_data *sd) if( npc->motd ) /* [Ind/Hercules] */ script->run(npc->motd->u.scr.script, 0, sd->bl.id, npc->fake_nd->bl.id); + // Achievements [Smokexyz/Hercules] + intif->achievements_request(sd); + return 1; } @@ -4500,6 +4510,8 @@ static int pc_payzeny(struct map_session_data *sd, int zeny, enum e_log_pick_typ sd->status.zeny -= zeny; clif->updatestatus(sd,SP_ZENY); + achievement->validate_zeny(sd, -zeny); // Achievements [Smokexyz/Hercules] + if(!tsd) tsd = sd; logs->zeny(sd, type, tsd, -zeny); if( zeny > 0 && sd->state.showzeny ) { @@ -4636,6 +4648,8 @@ static int pc_getzeny(struct map_session_data *sd, int zeny, enum e_log_pick_typ sd->status.zeny += zeny; clif->updatestatus(sd,SP_ZENY); + achievement->validate_zeny(sd, zeny); // Achievements [Smokexyz/Hercules] + if(!tsd) tsd = sd; logs->zeny(sd, type, tsd, zeny); if( zeny > 0 && sd->state.showzeny ) { @@ -4760,6 +4774,7 @@ static int pc_additem(struct map_session_data *sd, struct item *item_data, int a sd->status.inventory[i].amount = amount; sd->inventory_data[i] = data; clif->additem(sd,i,amount,0); + } if( ( !itemdb->isstackable2(data) || data->flag.force_serial || data->type == IT_CASH) && !item_data->unique_id ) @@ -4767,6 +4782,8 @@ static int pc_additem(struct map_session_data *sd, struct item *item_data, int a logs->pick_pc(sd, log_type, amount, &sd->status.inventory[i],sd->inventory_data[i]); + achievement->validate_item_get(sd, sd->status.inventory[i].nameid, sd->status.inventory[i].amount); // Achievements [Smokexyz/Hercules] + sd->weight += w; clif->updatestatus(sd,SP_WEIGHT); //Auto-equip @@ -6879,11 +6896,15 @@ static int pc_checkbaselevelup(struct map_session_data *sd) clif->misceffect(&sd->bl,0); npc->script_event(sd, NPCE_BASELVUP); //LORDALFA - LVLUPEVENT - if(sd->status.party_id) + if (sd->status.party_id) party->send_levelup(sd); pc->baselevelchanged(sd); + quest->questinfo_refresh(sd); + + achievement->validate_stats(sd, SP_BASELEVEL, sd->status.base_level); + return 1; } @@ -6946,7 +6967,11 @@ static int pc_checkjoblevelup(struct map_session_data *sd) clif->status_change(&sd->bl,SI_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. npc->script_event(sd, NPCE_JOBLVUP); + quest->questinfo_refresh(sd); + + achievement->validate_stats(sd, SP_BASELEVEL, sd->status.job_level); + return 1; } @@ -7243,6 +7268,8 @@ static int pc_setstat(struct map_session_data *sd, int type, int val) return -1; } + achievement->validate_stats(sd, type, val); // Achievements [Smokexyz/Hercules] + return val; } @@ -7974,6 +8001,14 @@ static void pc_damage(struct map_session_data *sd, struct block_list *src, unsig if (battle_config.prevent_logout_trigger & PLT_DAMAGE) sd->canlog_tick = timer->gettick(); + + // Achievements [Smokexyz/Hercules] + if (src != NULL) { + if (src->type == BL_PC) + achievement->validate_pc_damage(BL_UCAST(BL_PC, src), sd, hp); + else if (src->type == BL_MOB) + achievement->validate_mob_damage(sd, hp, true); + } } /*========================================== @@ -8124,6 +8159,8 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) pc->setparam(ssd, SP_KILLEDRID, sd->bl.id); npc->script_event(ssd, NPCE_KILLPC); + achievement->validate_pc_kill(ssd, sd); // Achievements [Smokexyz/Hercules] + if (battle_config.pk_mode&2) { ssd->status.manner -= 5; if(ssd->status.manner < 0) @@ -9042,6 +9079,8 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) } quest->questinfo_refresh(sd); + achievement->validate_jobchange(sd); // Achievements [Smokexyz/Hercules] + return 0; } @@ -10682,6 +10721,11 @@ static int pc_marriage(struct map_session_data *sd, struct map_session_data *dst return -1; sd->status.partner_id = dstsd->status.char_id; dstsd->status.partner_id = sd->status.char_id; + + // Achievements [Smokexyz/Hercules] + achievement->validate_marry(sd); + achievement->validate_marry(dstsd); + return 0; } diff --git a/src/map/pc.h b/src/map/pc.h index 0781fe801..17a9b8200 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -37,7 +37,7 @@ #include "common/db.h" #include "common/ers.h" // struct eri #include "common/hercules.h" -#include "common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus, NEW_CARTS +#include "common/mmo.h" // JOB_*, MAX_FAME_LIST, struct fame_list, struct mmo_charstatus, NEW_CARTS, struct s_achievement /** * Defines @@ -631,6 +631,12 @@ END_ZEROED_BLOCK; unsigned sitstand : 1; unsigned commands : 1; } block_action; + + /* Achievement System */ + struct char_achievements achievement; + bool achievements_received; + // Title + VECTOR_DECL(int) title_ids; }; #define EQP_WEAPON EQP_HAND_R diff --git a/src/map/pet.c b/src/map/pet.c index d1d8a5a33..cfb3e8253 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -23,6 +23,7 @@ #include "config/core.h" // DBPATH #include "pet.h" +#include "map/achievement.h" #include "map/atcommand.h" // msg_txt() #include "map/battle.h" #include "map/chrif.h" @@ -582,8 +583,8 @@ static int pet_catch_process2(struct map_session_data *sd, int target_id) pet_catch_rate = (pet->db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - get_percentage(md->status.hp, md->status.max_hp))/100; if(pet_catch_rate < 1) pet_catch_rate = 1; - if(battle_config.pet_catch_rate != 100) - pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100; + if(battle->bc->pet_catch_rate != 100) + pet_catch_rate = (pet_catch_rate*battle->bc->pet_catch_rate)/100; if(rnd()%10000 < pet_catch_rate) { @@ -592,6 +593,8 @@ static int pet_catch_process2(struct map_session_data *sd, int target_id) clif->pet_roulette(sd,1); intif->create_pet(sd->status.account_id,sd->status.char_id,pet->db[i].class_,mob->db(pet->db[i].class_)->lv, pet->db[i].EggID,0,pet->db[i].intimate,100,0,1,pet->db[i].jname); + + achievement->validate_taming(sd, pet->db[i].class_); } else { diff --git a/src/map/script.c b/src/map/script.c index 24d03bbe2..e38aae562 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -57,6 +57,7 @@ #include "map/status.h" #include "map/storage.h" #include "map/unit.h" +#include "map/achievement.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/db.h" @@ -9582,6 +9583,11 @@ static BUILDIN(successrefitem) clif->additem(sd,i,1,0); pc->equipitem(sd,i,ep); clif->misceffect(&sd->bl,3); + + achievement->validate_refine(sd, i, true); // Achievements [Smokexyz/Hercules] + + /* The following check is exclusive to characters (possibly only whitesmiths) + * that create equipments and refine them to level 10. */ if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == CARD0_FORGE && sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3]) @@ -9619,6 +9625,9 @@ static BUILDIN(failedrefitem) if (num > 0 && num <= ARRAYLENGTH(script->equip)) i=pc->checkequip(sd,script->equip[num-1]); if(i >= 0) { + // Call before changing refine to 0. + achievement->validate_refine(sd, i, false); + sd->status.inventory[i].refine = 0; pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); //recalculate bonus clif->refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure @@ -9666,6 +9675,9 @@ static BUILDIN(downrefitem) clif->additem(sd,i,1,0); pc->equipitem(sd,i,ep); + + achievement->validate_refine(sd, i, false); // Achievements [Smokexyz/Hercules] + clif->misceffect(&sd->bl,2); } @@ -21254,6 +21266,78 @@ static BUILDIN(showevent) } /*========================================== + * Achievement System [Smokexyz/Hercules] + *-----------------------------------------*/ +/** + * Validates an objective index for the given achievement. + * Can be used for any achievement type. + * @command achievement_progress(<ach_id>,<obj_idx>,<progress>,<incremental?>{,<char_id>}); + * @param aid - achievement ID + * @param obj_idx - achievement objective index. + * @param progress - objective progress towards goal. + * @Param incremental - (boolean) true to add the progress towards the goal, + * false to use the progress only as a comparand. + * @param account_id - (optional) character ID to perform on. + * @return true on success, false on failure. + * @push 1 on success, 0 on failure. + */ +static BUILDIN(achievement_progress) +{ + struct map_session_data *sd = script->rid2sd(st); + int aid = script_getnum(st, 2); + int obj_idx = script_getnum(st, 3); + int progress = script_getnum(st, 4); + int incremental = script_getnum(st, 5); + int account_id = script_hasdata(st, 6) ? script_getnum(st, 6) : 0; + const struct achievement_data *ad = NULL; + + if ((ad = achievement->get(aid)) == NULL) { + ShowError("buildin_achievement_progress: Invalid achievement ID %d received.\n", aid); + script_pushint(st, 0); + return false; + } + + if (obj_idx <= 0 || obj_idx > VECTOR_LENGTH(ad->objective)) { + ShowError("buildin_achievement_progress: Invalid objective index %d received. (min: %d, max: %d)\n", obj_idx, 0, VECTOR_LENGTH(ad->objective)); + script_pushint(st, 0); + return false; + } + + obj_idx--; // convert to array index. + + if (progress <= 0 || progress > VECTOR_INDEX(ad->objective, obj_idx).goal) { + ShowError("buildin_achievement_progress: Progress exceeds goal limit for achievement id %d.\n", aid); + script_pushint(st, 0); + return false; + } + + if (incremental < 0 || incremental > 1) { + ShowError("buildin_achievement_progress: Argument 4 expects boolean (0/1). provided value: %d\n", incremental); + script_pushint(st, 0); + return false; + } + + if (script_hasdata(st, 6)) { + if (account_id <= 0) { + ShowError("buildin_achievement_progress: Invalid Account id %d provided.\n", account_id); + script_pushint(st, 0); + return false; + } else if ((sd = map->id2sd(account_id)) == NULL) { + ShowError("buildin_achievement_progress: Account with id %d was not found.\n", account_id); + script_pushint(st, 0); + return false; + } + } + + if (achievement->validate(sd, aid, obj_idx, progress, incremental ? true : false)) + script_pushint(st, progress); + else + script_pushint(st, 0); + + return true; +} + +/*========================================== * BattleGround System *------------------------------------------*/ static BUILDIN(waitingroom2bg) @@ -25132,6 +25216,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(agitstart2,""), BUILDIN_DEF(agitend2,""), BUILDIN_DEF(agitcheck2,""), + // Achievements [Smokexyz/Hercules] + BUILDIN_DEF(achievement_progress, "iiii?"), // BattleGround BUILDIN_DEF(waitingroom2bg,"siiss?"), BUILDIN_DEF(waitingroom2bg_single,"isiis"), diff --git a/src/map/unit.c b/src/map/unit.c index 2fd8d6efb..9174bdccd 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2765,8 +2765,10 @@ static int unit_free(struct block_list *bl, clr_type clrtype) sd->instance = NULL; } VECTOR_CLEAR(sd->script_queues); + VECTOR_CLEAR(sd->achievement); // Achievement [Smokexyz/Hercules] VECTOR_CLEAR(sd->storage.item); VECTOR_CLEAR(sd->hatEffectId); + VECTOR_CLEAR(sd->title_ids); // Title [Dastgir/Hercules] sd->storage.received = false; if( sd->quest_log != NULL ) { aFree(sd->quest_log); diff --git a/src/plugins/HPMHooking.c b/src/plugins/HPMHooking.c index ca64aca6a..b477cb5c3 100644 --- a/src/plugins/HPMHooking.c +++ b/src/plugins/HPMHooking.c @@ -48,6 +48,7 @@ PRAGMA_GCC5(GCC diagnostic ignored "-Wdiscarded-qualifiers") #define HPM_SOURCES_INCLUDE "HPMHooking/HPMHooking_char.sources.inc" #include "char/char.h" #include "char/geoip.h" +#include "char/int_achievement.h" #include "char/int_auction.h" #include "char/int_clan.h" #include "char/int_elemental.h" @@ -71,6 +72,7 @@ PRAGMA_GCC5(GCC diagnostic ignored "-Wdiscarded-qualifiers") #define HPM_HOOKS_INCLUDE "HPMHooking/HPMHooking_map.Hooks.inc" #define HPM_POINTS_INCLUDE "HPMHooking/HPMHooking_map.HookingPoints.inc" #define HPM_SOURCES_INCLUDE "HPMHooking/HPMHooking_map.sources.inc" +#include "map/achievement.h" #include "map/atcommand.h" #include "map/battle.h" #include "map/battleground.h" diff --git a/vcproj-12/char-server.vcxproj b/vcproj-12/char-server.vcxproj index 3baf26e37..411191400 100644 --- a/vcproj-12/char-server.vcxproj +++ b/vcproj-12/char-server.vcxproj @@ -174,6 +174,7 @@ <ClInclude Include="..\src\char\HPMchar.h" /> <ClInclude Include="..\src\char\char.h" /> <ClInclude Include="..\src\char\geoip.h" /> + <ClInclude Include="..\src\char\int_achievement.h" /> <ClInclude Include="..\src\char\int_auction.h" /> <ClInclude Include="..\src\char\int_clan.h" /> <ClInclude Include="..\src\char\int_elemental.h" /> @@ -228,6 +229,7 @@ <ClCompile Include="..\src\char\HPMchar.c" /> <ClCompile Include="..\src\char\char.c" /> <ClCompile Include="..\src\char\geoip.c" /> + <ClCompile Include="..\src\char\int_achievement.c" /> <ClCompile Include="..\src\char\int_auction.c" /> <ClCompile Include="..\src\char\int_clan.c" /> <ClCompile Include="..\src\char\int_elemental.c" /> diff --git a/vcproj-12/char-server.vcxproj.filters b/vcproj-12/char-server.vcxproj.filters index af34d2efd..52d3bde16 100644 --- a/vcproj-12/char-server.vcxproj.filters +++ b/vcproj-12/char-server.vcxproj.filters @@ -67,6 +67,9 @@ <ClCompile Include="..\src\char\geoip.c"> <Filter>char</Filter> </ClCompile> + <ClCompile Include="..\src\char\int_achievement.c"> + <Filter>char</Filter> + </ClCompile> <ClCompile Include="..\src\char\int_auction.c"> <Filter>char</Filter> </ClCompile> @@ -219,6 +222,9 @@ <ClInclude Include="..\src\char\geoip.h"> <Filter>char</Filter> </ClInclude> + <ClInclude Include="..\src\char\int_achievement.h"> + <Filter>char</Filter> + </ClInclude> <ClInclude Include="..\src\char\int_auction.h"> <Filter>char</Filter> </ClInclude> diff --git a/vcproj-12/map-server.vcxproj b/vcproj-12/map-server.vcxproj index e9196c5df..4756cca2a 100644 --- a/vcproj-12/map-server.vcxproj +++ b/vcproj-12/map-server.vcxproj @@ -169,6 +169,7 @@ <ClInclude Include="..\src\common\timer.h" /> <ClInclude Include="..\src\common\utils.h" /> <ClInclude Include="..\src\common\winapi.h" /> + <ClInclude Include="..\src\map\achievement.h" /> <ClInclude Include="..\src\map\atcommand.h" /> <ClInclude Include="..\src\map\battle.h" /> <ClInclude Include="..\src\map\battleground.h" /> @@ -251,6 +252,7 @@ <ClCompile Include="..\src\common\thread.c" /> <ClCompile Include="..\src\common\timer.c" /> <ClCompile Include="..\src\common\utils.c" /> + <ClCompile Include="..\src\map\achievement.c" /> <ClCompile Include="..\src\map\atcommand.c" /> <ClCompile Include="..\src\map\battle.c" /> <ClCompile Include="..\src\map\battleground.c" /> diff --git a/vcproj-12/map-server.vcxproj.filters b/vcproj-12/map-server.vcxproj.filters index c8aaffc5d..e3bf78fb9 100644 --- a/vcproj-12/map-server.vcxproj.filters +++ b/vcproj-12/map-server.vcxproj.filters @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> + <ClCompile Include="..\src\map\achievement.c"> + <Filter>map</Filter> + </ClCompile> <ClCompile Include="..\src\map\atcommand.c"> <Filter>map</Filter> </ClCompile> @@ -213,6 +216,9 @@ </ClCompile> </ItemGroup> <ItemGroup> + <ClInclude Include="..\src\map\achievement.h"> + <Filter>map</Filter> + </ClInclude> <ClInclude Include="..\src\map\atcommand.h"> <Filter>map</Filter> </ClInclude> diff --git a/vcproj-14/char-server.vcxproj b/vcproj-14/char-server.vcxproj index a766d504d..692210a15 100644 --- a/vcproj-14/char-server.vcxproj +++ b/vcproj-14/char-server.vcxproj @@ -173,6 +173,7 @@ <ClInclude Include="..\src\char\HPMchar.h" /> <ClInclude Include="..\src\char\char.h" /> <ClInclude Include="..\src\char\geoip.h" /> + <ClInclude Include="..\src\char\int_achievement.h" /> <ClInclude Include="..\src\char\int_auction.h" /> <ClInclude Include="..\src\char\int_clan.h" /> <ClInclude Include="..\src\char\int_elemental.h" /> @@ -227,6 +228,7 @@ <ClCompile Include="..\src\char\HPMchar.c" /> <ClCompile Include="..\src\char\char.c" /> <ClCompile Include="..\src\char\geoip.c" /> + <ClCompile Include="..\src\char\int_achievement.c" /> <ClCompile Include="..\src\char\int_auction.c" /> <ClCompile Include="..\src\char\int_clan.c" /> <ClCompile Include="..\src\char\int_elemental.c" /> diff --git a/vcproj-14/char-server.vcxproj.filters b/vcproj-14/char-server.vcxproj.filters index af34d2efd..52d3bde16 100644 --- a/vcproj-14/char-server.vcxproj.filters +++ b/vcproj-14/char-server.vcxproj.filters @@ -67,6 +67,9 @@ <ClCompile Include="..\src\char\geoip.c"> <Filter>char</Filter> </ClCompile> + <ClCompile Include="..\src\char\int_achievement.c"> + <Filter>char</Filter> + </ClCompile> <ClCompile Include="..\src\char\int_auction.c"> <Filter>char</Filter> </ClCompile> @@ -219,6 +222,9 @@ <ClInclude Include="..\src\char\geoip.h"> <Filter>char</Filter> </ClInclude> + <ClInclude Include="..\src\char\int_achievement.h"> + <Filter>char</Filter> + </ClInclude> <ClInclude Include="..\src\char\int_auction.h"> <Filter>char</Filter> </ClInclude> diff --git a/vcproj-14/map-server.vcxproj b/vcproj-14/map-server.vcxproj index ccc80d15d..ae834de75 100644 --- a/vcproj-14/map-server.vcxproj +++ b/vcproj-14/map-server.vcxproj @@ -167,6 +167,7 @@ <ClInclude Include="..\src\common\timer.h" /> <ClInclude Include="..\src\common\utils.h" /> <ClInclude Include="..\src\common\winapi.h" /> + <ClInclude Include="..\src\map\achievement.h" /> <ClInclude Include="..\src\map\atcommand.h" /> <ClInclude Include="..\src\map\battle.h" /> <ClInclude Include="..\src\map\battleground.h" /> @@ -249,6 +250,7 @@ <ClCompile Include="..\src\common\thread.c" /> <ClCompile Include="..\src\common\timer.c" /> <ClCompile Include="..\src\common\utils.c" /> + <ClCompile Include="..\src\map\achievement.c" /> <ClCompile Include="..\src\map\atcommand.c" /> <ClCompile Include="..\src\map\battle.c" /> <ClCompile Include="..\src\map\battleground.c" /> diff --git a/vcproj-14/map-server.vcxproj.filters b/vcproj-14/map-server.vcxproj.filters index bf3004a77..9bf13d9f7 100644 --- a/vcproj-14/map-server.vcxproj.filters +++ b/vcproj-14/map-server.vcxproj.filters @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> + <ClCompile Include="..\src\map\achievement.c"> + <Filter>map</Filter> + </ClCompile> <ClCompile Include="..\src\map\atcommand.c"> <Filter>map</Filter> </ClCompile> @@ -213,6 +216,9 @@ </ClCompile> </ItemGroup> <ItemGroup> + <ClInclude Include="..\src\map\achievement.h"> + <Filter>map</Filter> + </ClInclude> <ClInclude Include="..\src\map\atcommand.h"> <Filter>map</Filter> </ClInclude> diff --git a/vcproj-15/char-server.vcxproj b/vcproj-15/char-server.vcxproj index ed5898a89..ad4de5be4 100644 --- a/vcproj-15/char-server.vcxproj +++ b/vcproj-15/char-server.vcxproj @@ -173,6 +173,7 @@ <ClInclude Include="..\src\char\HPMchar.h" /> <ClInclude Include="..\src\char\char.h" /> <ClInclude Include="..\src\char\geoip.h" /> + <ClInclude Include="..\src\char\int_achievement.h" /> <ClInclude Include="..\src\char\int_auction.h" /> <ClInclude Include="..\src\char\int_clan.h" /> <ClInclude Include="..\src\char\int_elemental.h" /> @@ -227,6 +228,7 @@ <ClCompile Include="..\src\char\HPMchar.c" /> <ClCompile Include="..\src\char\char.c" /> <ClCompile Include="..\src\char\geoip.c" /> + <ClCompile Include="..\src\char\int_achievement.c" /> <ClCompile Include="..\src\char\int_auction.c" /> <ClCompile Include="..\src\char\int_clan.c" /> <ClCompile Include="..\src\char\int_elemental.c" /> diff --git a/vcproj-15/char-server.vcxproj.filters b/vcproj-15/char-server.vcxproj.filters index af34d2efd..52d3bde16 100644 --- a/vcproj-15/char-server.vcxproj.filters +++ b/vcproj-15/char-server.vcxproj.filters @@ -67,6 +67,9 @@ <ClCompile Include="..\src\char\geoip.c"> <Filter>char</Filter> </ClCompile> + <ClCompile Include="..\src\char\int_achievement.c"> + <Filter>char</Filter> + </ClCompile> <ClCompile Include="..\src\char\int_auction.c"> <Filter>char</Filter> </ClCompile> @@ -219,6 +222,9 @@ <ClInclude Include="..\src\char\geoip.h"> <Filter>char</Filter> </ClInclude> + <ClInclude Include="..\src\char\int_achievement.h"> + <Filter>char</Filter> + </ClInclude> <ClInclude Include="..\src\char\int_auction.h"> <Filter>char</Filter> </ClInclude> diff --git a/vcproj-15/map-server.vcxproj b/vcproj-15/map-server.vcxproj index f3f170daa..fb9d57e9c 100644 --- a/vcproj-15/map-server.vcxproj +++ b/vcproj-15/map-server.vcxproj @@ -168,6 +168,7 @@ <ClInclude Include="..\src\common\timer.h" /> <ClInclude Include="..\src\common\utils.h" /> <ClInclude Include="..\src\common\winapi.h" /> + <ClInclude Include="..\src\map\achievement.h" /> <ClInclude Include="..\src\map\atcommand.h" /> <ClInclude Include="..\src\map\battle.h" /> <ClInclude Include="..\src\map\battleground.h" /> @@ -250,6 +251,7 @@ <ClCompile Include="..\src\common\thread.c" /> <ClCompile Include="..\src\common\timer.c" /> <ClCompile Include="..\src\common\utils.c" /> + <ClCompile Include="..\src\map\achievement.c" /> <ClCompile Include="..\src\map\atcommand.c" /> <ClCompile Include="..\src\map\battle.c" /> <ClCompile Include="..\src\map\battleground.c" /> diff --git a/vcproj-15/map-server.vcxproj.filters b/vcproj-15/map-server.vcxproj.filters index c8aaffc5d..8c91df7c7 100644 --- a/vcproj-15/map-server.vcxproj.filters +++ b/vcproj-15/map-server.vcxproj.filters @@ -1,6 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <ItemGroup> + <ClCompile Include="..\src\map\achievement.c"> + <Filter>map</Filter> + </ClCompile> <ClCompile Include="..\src\map\atcommand.c"> <Filter>map</Filter> </ClCompile> @@ -213,6 +216,9 @@ </ClCompile> </ItemGroup> <ItemGroup> + <ClInclude Include="..\src\map\achievement.h"> + <Filter>map</Filter> + </ClInclude> <ClInclude Include="..\src\map\atcommand.h"> <Filter>map</Filter> </ClInclude> |