diff options
Diffstat (limited to 'src/map')
56 files changed, 3356 insertions, 970 deletions
diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c index cbfa8e8c4..e89f47c12 100644 --- a/src/map/HPMmap.c +++ b/src/map/HPMmap.c @@ -81,6 +81,8 @@ #include "map/pet.h" #include "map/quest.h" #include "map/rodex.h" +#include "map/refine.h" +#include "map/refine.p.h" #include "map/script.h" #include "map/searchstore.h" #include "map/skill.h" diff --git a/src/map/Makefile.in b/src/map/Makefile.in index edb3fdaad..f851de756 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -44,7 +44,7 @@ MAP_C = achievement.c atcommand.c battle.c battleground.c buyingstore.c channel. 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 \ + pet.c quest.c refine.c rodex.c script.c searchstore.c skill.c status.c storage.c \ stylist.c trade.c unit.c vending.c MAP_OBJ = $(addprefix obj_sql/, $(patsubst %c,%o,$(MAP_C))) MAP_H = achievement.h atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \ @@ -54,9 +54,9 @@ MAP_H = achievement.h atcommand.h battle.h battleground.h buyingstore.h channel. messages_sak.h messages_zero.h mob.h npc.h packets.h packets_keys_main.h \ packets_keys_zero.h packets_shuffle_main.h packets_shuffle_re.h \ packets_shuffle_zero.h packets_struct.h party.h path.h pc.h pc_groups.h \ - pet.h quest.h rodex.h script.h searchstore.h skill.h status.h storage.h \ + pet.h quest.h refine.h rodex.h script.h searchstore.h skill.h status.h storage.h \ stylist.h trade.h unit.h vending.h -MAP_PH = +MAP_PH = refine.p.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) diff --git a/src/map/atcommand.c b/src/map/atcommand.c index a9bbff7bd..09303912b 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -48,6 +48,7 @@ #include "map/pc_groups.h" // groupid2name #include "map/pet.h" #include "map/quest.h" +#include "map/refine.h" #include "map/script.h" #include "map/searchstore.h" #include "map/skill.h" @@ -868,7 +869,7 @@ ACMD(speed) *------------------------------------------*/ ACMD(storage) { - if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.trading || sd->state.storage_flag) + if (sd->npc_id || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.trading || sd->state.storage_flag) return false; if (storage->open(sd) == 1) { //Already open. @@ -891,7 +892,7 @@ ACMD(guildstorage) return false; } - if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.trading) + if (sd->npc_id || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.trading) return false; if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) { @@ -1270,20 +1271,20 @@ ACMD(item2) struct item_data *item_data; char item_name[100]; int item_id, number = 0, bound = 0; - int identify = 0, refine = 0, attr = 0; + int identify = 0, refine_level = 0, attr = 0; int c1 = 0, c2 = 0, c3 = 0, c4 = 0; memset(item_name, '\0', sizeof(item_name)); if (!strcmpi(info->command,"itembound2") && (!*message || ( - sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 && - sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) { + sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine_level, &attr, &c1, &c2, &c3, &c4, &bound) < 10 && + sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine_level, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) { clif->message(fd, msg_fd(fd,296)); // Please enter all parameters (usage: @itembound2 <item name/ID> <quantity> clif->message(fd, msg_fd(fd,297)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4> <bound_type>). return false; } else if (!*message - || ( sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 - && sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 + || ( sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine_level, &attr, &c1, &c2, &c3, &c4) < 9 + && sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine_level, &attr, &c1, &c2, &c3, &c4) < 9 )) { clif->message(fd, msg_fd(fd,984)); // Please enter all parameters (usage: @item2 <item name/ID> <quantity> clif->message(fd, msg_fd(fd,985)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>). @@ -1319,20 +1320,20 @@ ACMD(item2) get_count = 1; if (item_data->type == IT_PETEGG) { identify = 1; - refine = 0; + refine_level = 0; } if (item_data->type == IT_PETARMOR) - refine = 0; + refine_level = 0; } else { identify = 1; - refine = attr = 0; + refine_level = attr = 0; } - refine = cap_value(refine, 0, MAX_REFINE); + refine_level = cap_value(refine_level, 0, MAX_REFINE); for (i = 0; i < loop; i++) { memset(&item_tmp, 0, sizeof(item_tmp)); item_tmp.nameid = item_id; item_tmp.identify = identify; - item_tmp.refine = refine; + item_tmp.refine = refine_level; item_tmp.attribute = attr; item_tmp.bound = (unsigned char)bound; item_tmp.card[0] = c1; @@ -2215,12 +2216,12 @@ ACMD(killmonster) *------------------------------------------*/ ACMD(refine) { - int j, position = 0, refine = 0, current_position, final_refine; + int j, position = 0, refine_level = 0, current_position, final_refine; int count; memset(atcmd_output, '\0', sizeof(atcmd_output)); - if (!*message || sscanf(message, "%12d %12d", &position, &refine) < 2) { + if (!*message || sscanf(message, "%12d %12d", &position, &refine_level) < 2) { clif->message(fd, msg_fd(fd,996)); // Please enter a position and an amount (usage: @refine <equip position> <+/- amount>). safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,997), EQP_HEAD_LOW); // %d: Lower Headgear clif->message(fd, atcmd_output); @@ -2245,7 +2246,7 @@ ACMD(refine) return false; } - refine = cap_value(refine, -MAX_REFINE, MAX_REFINE); + refine_level = cap_value(refine_level, -MAX_REFINE, MAX_REFINE); count = 0; for (j = 0; j < EQI_MAX; j++) { @@ -2263,7 +2264,7 @@ ACMD(refine) if(position && !(sd->status.inventory[idx].equip & position)) continue; - final_refine = cap_value(sd->status.inventory[idx].refine + refine, 0, MAX_REFINE); + final_refine = cap_value(sd->status.inventory[idx].refine + refine_level, 0, MAX_REFINE); if (sd->status.inventory[idx].refine != final_refine) { sd->status.inventory[idx].refine = final_refine; current_position = sd->status.inventory[idx].equip; @@ -5277,18 +5278,43 @@ ACMD(follow) } /*========================================== - * @dropall by [MouseJstr] - * Drop all your possession on the ground + * @dropall by [MouseJstr] and [Xantara] + * Drop all your possession on the ground based on item type *------------------------------------------*/ ACMD(dropall) { + int type = -1; + int count = 0; + + if (message[0] != '\0') { + type = atoi(message); + if (!((type >= IT_HEALING && type <= IT_DELAYCONSUME) || type == IT_CASH || type == -1)) { + clif->message(fd, msg_fd(fd, 1500)); + clif->message(fd, msg_fd(fd, 1501)); + return false; + } + } + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].amount) { - if(sd->status.inventory[i].equip != 0) - pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); - pc->dropitem(sd, i, sd->status.inventory[i].amount); + struct item_data *item_data = itemdb->exists(sd->status.inventory[i].nameid); + if (item_data == NULL) { + ShowWarning("Non-existant item %d on dropall list (account_id: %d, char_id: %d)\n", sd->status.inventory[i].nameid, sd->status.account_id, sd->status.char_id); + continue; + } + if (!pc->candrop(sd, &sd->status.inventory[i])) + continue; + if (type == -1 || type == item_data->type) { + if (sd->status.inventory[i].equip != 0) + pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC | PCUNEQUIPITEM_FORCE); + count += sd->status.inventory[i].amount; + pc->dropitem(sd, i, sd->status.inventory[i].amount); + } } } + + sprintf(atcmd_output, msg_fd(fd, 1502), count); // %d items are dropped! + clif->message(fd, atcmd_output); return true; } @@ -5400,7 +5426,7 @@ ACMD(clearcart) return false; } - if (sd->state.vending) { + if (sd->state.vending || sd->state.prevend) { clif->message(fd, msg_fd(fd,548)); // You can't clean a cart while vending! return false; } @@ -5599,9 +5625,9 @@ static void atcommand_getring(struct map_session_data *sd) memset(&item_tmp, 0, sizeof(item_tmp)); item_tmp.nameid = item_id; item_tmp.identify = 1; - item_tmp.card[0] = 255; - item_tmp.card[2] = sd->status.partner_id; - item_tmp.card[3] = sd->status.partner_id >> 16; + item_tmp.card[0] = CARD0_FORGE; + item_tmp.card[2] = GetWord(sd->status.partner_id, 0); + item_tmp.card[3] = GetWord(sd->status.partner_id, 1); if((flag = pc->additem(sd,&item_tmp,1,LOG_TYPE_COMMAND))) { clif->additem(sd,0,0,flag); @@ -9803,6 +9829,22 @@ ACMD(camerainfo) return true; } +ACMD(refineryui) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) { + clif->message(fd, msg_fd(fd, 453)); + return false; + } + + clif->OpenRefineryUI(sd); + return true; +#else + clif->message(fd, msg_fd(fd, 453)); + return false; +#endif +} + /** * Fills the reference of available commands in atcommand DBMap **/ @@ -10086,6 +10128,7 @@ static void atcommand_basecommands(void) ACMD_DEF(reloadclans), ACMD_DEF(setzone), ACMD_DEF(camerainfo), + ACMD_DEF(refineryui), }; int i; diff --git a/src/map/atcommand.h b/src/map/atcommand.h index 1783e5dc6..4fbf6b93a 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -41,7 +41,7 @@ struct config_setting_t; * Defines **/ #define ATCOMMAND_LENGTH 50 -#define MAX_MSG 1500 +#define MAX_MSG 1503 #define msg_txt(idx) atcommand->msg(idx) #define msg_sd(sd,msg_number) atcommand->msgsd((sd),(msg_number)) #define msg_fd(fd,msg_number) atcommand->msgfd((fd),(msg_number)) diff --git a/src/map/battle.c b/src/map/battle.c index fe7a64b51..c40c3afac 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7004,7 +7004,8 @@ static const struct battle_data { { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate, 100, 0, INT_MAX, }, { "defunit_not_enemy", &battle_config.defnotenemy, 0, 0, 1, }, { "gvg_traps_target_all", &battle_config.vs_traps_bctall, BL_PC, BL_NUL, BL_ALL, }, - { "traps_setting", &battle_config.traps_setting, 0, 0, 1, }, + { "trap_options/visibility", &battle_config.trap_visibility, 2, 0, 2, }, + { "trap_options/display_on_trigger", &battle_config.trap_trigger, 1, 0, 1, }, { "summon_flora_setting", &battle_config.summon_flora, 1|2, 0, 1|2, }, { "clear_skills_on_death", &battle_config.clear_unit_ondeath, BL_NUL, BL_NUL, BL_ALL, }, { "clear_skills_on_warp", &battle_config.clear_unit_onwarp, BL_ALL, BL_NUL, BL_ALL, }, @@ -7417,6 +7418,24 @@ static const struct battle_data { { "features/enable_achievement_system", &battle_config.feature_enable_achievement, 1, 0, 1, }, { "ping_timer_inverval", &battle_config.ping_timer_interval, 30, 0, 99999999, }, { "ping_time", &battle_config.ping_time, 20, 0, 99999999, }, + { "option_drop_max_loop", &battle_config.option_drop_max_loop, 10, 1, 100000, }, + { "drop_connection_on_quit", &battle_config.drop_connection_on_quit, 0, 0, 1, }, + { "features/enable_refinery_ui", &battle_config.enable_refinery_ui, 1, 0, 1, }, + { "features/replace_refine_npcs", &battle_config.replace_refine_npcs, 1, 0, 1, }, + { "batk_min_limit", &battle_config.batk_min, 0, 0, INT_MAX, }, + { "batk_max_limit", &battle_config.batk_max, USHRT_MAX, 1, INT_MAX, }, + { "matk_min_limit", &battle_config.matk_min, 0, 0, INT_MAX, }, + { "matk_max_limit", &battle_config.matk_max, USHRT_MAX, 1, INT_MAX, }, + { "watk_min_limit", &battle_config.watk_min, 0, 0, INT_MAX, }, + { "watk_max_limit", &battle_config.watk_max, USHRT_MAX, 1, INT_MAX, }, + { "flee_min_limit", &battle_config.flee_min, 1, 1, INT_MAX, }, + { "flee_max_limit", &battle_config.flee_max, SHRT_MAX, 1, INT_MAX, }, + { "flee2_min_limit", &battle_config.flee2_min, 10, 1, INT_MAX, }, + { "flee2_max_limit", &battle_config.flee2_max, SHRT_MAX, 1, INT_MAX, }, + { "critical_min_limit", &battle_config.critical_min, 10, 1, INT_MAX, }, + { "critical_max_limit", &battle_config.critical_max, SHRT_MAX, 1, INT_MAX, }, + { "hit_min_limit", &battle_config.hit_min, 1, 1, INT_MAX, }, + { "hit_max_limit", &battle_config.hit_max, SHRT_MAX, 1, INT_MAX, }, }; static bool battle_set_value_sub(int index, int value) @@ -7542,6 +7561,18 @@ static void battle_adjust_conf(void) } #endif +#if !(PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO)) + if (battle_config.enable_refinery_ui == 1) { + ShowWarning("conf/map/battle/feature.conf refinery ui is enabled but it requires PACKETVER 2016-11-09 RagexeRE/2016-11-30 Ragexe or newer, disabling...\n"); + battle_config.enable_refinery_ui = 0; + } + + if (battle_config.replace_refine_npcs == 1) { + ShowWarning("conf/map/battle/feature.conf replace refine npcs is enabled but it requires PACKETVER 2016-11-09 RagexeRE/2016-11-30 Ragexe or newer, disabling...\n"); + battle_config.replace_refine_npcs = 0; + } +#endif + #ifndef CELL_NOSTACK if (battle_config.custom_cell_stack_limit != 1) ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); @@ -7571,6 +7602,10 @@ static bool battle_config_read(const char *filename, bool imported) if (!imported) battle->config_set_defaults(); + if (libconfig->lookup(&config, "battle_configuration/traps_setting") != NULL) { + ShowError("The `traps_setting` battle conf option has been replaced by `trap_visibility`. Please see conf/map/battle/skill.conf.\n"); + } + for (i = 0; i < ARRAYLENGTH(battle_data); i++) { int type, val; char config_name[256]; diff --git a/src/map/battle.h b/src/map/battle.h index 723a86874..0ff5135d8 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -149,7 +149,8 @@ struct Battle_Config { int pc_damage_delay_rate; int defnotenemy; int vs_traps_bctall; - int traps_setting; + int trap_visibility; + int trap_trigger; int summon_flora; //[Skotlex] int clear_unit_ondeath; //[Skotlex] int clear_unit_onwarp; //[Skotlex] @@ -583,6 +584,27 @@ struct Battle_Config { int ping_timer_interval; int ping_time; + + int option_drop_max_loop; + + int drop_connection_on_quit; + int enable_refinery_ui; + int replace_refine_npcs; + + int batk_min; + int batk_max; + int matk_min; + int matk_max; + int watk_min; + int watk_max; + int flee_min; + int flee_max; + int flee2_min; + int flee2_max; + int critical_min; + int critical_max; + int hit_min; + int hit_max; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index df622e4ab..8cac65775 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -91,7 +91,7 @@ static void buyingstore_create(struct map_session_data *sd, int zenylimit, unsig return; } - if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] ) + if( !battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend || sd->buyingstore.slots == 0 || count > sd->buyingstore.slots || zenylimit <= 0 || zenylimit > sd->status.zeny || !storename[0] ) {// disabled or invalid input sd->buyingstore.slots = 0; clif->buyingstore_open_failed(sd, BUYINGSTORE_CREATE, 0); @@ -218,7 +218,7 @@ static void buyingstore_open(struct map_session_data *sd, int account_id) struct map_session_data* pl_sd; nullpo_retv(sd); - if( !battle_config.feature_buying_store || pc_istrading(sd) ) + if (!battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend) {// not allowed to sell return; } @@ -255,7 +255,7 @@ static void buyingstore_trade(struct map_session_data* sd, int account_id, unsig return; } - if( !battle_config.feature_buying_store || pc_istrading(sd) ) + if (!battle_config.feature_buying_store || pc_istrading(sd) || sd->state.prevend) {// not allowed to sell clif->buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, 0); return; diff --git a/src/map/chat.c b/src/map/chat.c index d9b642219..b650ff029 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -102,7 +102,7 @@ static bool chat_createpcchat(struct map_session_data *sd, const char *title, co if (sd->chat_id != 0) return false; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex] - if( sd->state.vending || sd->state.buyingstore ) + if (sd->state.vending || sd->state.prevend || sd->state.buyingstore) {// not chat, when you already have a store open return false; } @@ -147,7 +147,7 @@ static bool chat_joinchat(struct map_session_data *sd, int chatid, const char *p cd = map->id2cd(chatid); if (cd == NULL || cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m - || sd->state.vending || sd->state.buyingstore || sd->chat_id != 0 + || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->chat_id != 0 || ((cd->owner->type == BL_NPC) ? cd->users+1 : cd->users) >= cd->limit ) { clif->joinchatfail(sd,0); // room full @@ -255,6 +255,14 @@ static int chat_leavechat(struct map_session_data *sd, bool kicked) } if( leavechar == 0 && cd->owner->type == BL_PC ) { + + // check if new location are CELL_CHKNOCHAT + if (map->getcell(cd->usersd[0]->bl.m, NULL, cd->usersd[0]->bl.x, cd->usersd[0]->bl.y, CELL_CHKNOCHAT)) { + for (i = (cd->users - 1); i >= 0; i--) + chat->leave(cd->usersd[i], false); + return 2; + } + // Set and announce new owner cd->owner = &cd->usersd[0]->bl; clif->changechatowner(cd, cd->usersd[0]); diff --git a/src/map/clif.c b/src/map/clif.c index cd3131181..b93274d3d 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -49,6 +49,7 @@ #include "map/pet.h" #include "map/quest.h" #include "map/rodex.h" +#include "map/refine.h" #include "map/script.h" #include "map/skill.h" #include "map/status.h" @@ -431,8 +432,13 @@ static int clif_send_actual(int fd, void *buf, int len) *------------------------------------------*/ static bool clif_send(const void *buf, int len, struct block_list *bl, enum send_target type) { + if (type != ALL_CLIENT) + nullpo_retr(false, bl); + nullpo_retr(false, buf); + Assert_retr(false, len > 0); + int i; - struct map_session_data *sd, *tsd; + struct map_session_data *sd = BL_CAST(BL_PC, bl), *tsd; struct party_data *p = NULL; struct guild *g = NULL; struct battleground_data *bgd = NULL; @@ -440,11 +446,6 @@ static bool clif_send(const void *buf, int len, struct block_list *bl, enum send struct s_mapiterator* iter; int area_size; - if( type != ALL_CLIENT ) - nullpo_ret(bl); - - sd = BL_CAST(BL_PC, bl); - if (sd != NULL && pc_isinvisible(sd)) { if (type == AREA || type == BG || type == BG_AREA) type = SELF; @@ -2943,7 +2944,7 @@ static void clif_inventoryStart(struct map_session_data *sd, enum inventory_type p->invType = type; #endif #if PACKETVER_RE_NUM >= 20180919 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 - int strLen = (int)safestrnlen(name, 24); + int strLen = (int)safestrnlen(name, 24) + 1; if (strLen > 24) strLen = 24; const int len = sizeof(struct ZC_INVENTORY_START) + strLen; @@ -2977,28 +2978,21 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t nullpo_retv(sd); nullpo_retv(items); - int i = 0; - struct item_data *id; - - do { - int normal = 0, equip = 0, k = 0; - - for( ; i < items_length && k < 500; i++, k++ ) { - - if( items[i].nameid <= 0 ) - continue; + int normal_count = 0, equip_count = 0; + for (int i = 0; i < items_length; ++i) { + if (items[i].nameid == 0) + continue; - id = itemdb->search(items[i].nameid); + struct item_data *itd = itemdb->search(items[i].nameid); - if( !itemdb->isstackable2(id) ) //Non-stackable (Equippable) - clif->item_equip(i+1,&storelist_equip.list[equip++],&items[i],id,id->equip); - else //Stackable (Normal) - clif->item_normal(i+1,&storelist_normal.list[normal++],&items[i],id); - } + if (!itemdb->isstackable2(itd)) + clif->item_equip(i + 1, &storelist_equip.list[equip_count++], &items[i], itd, itd->equip); + else + clif->item_normal(i + 1, &storelist_normal.list[normal_count++], &items[i], itd); - if( normal ) { - storelist_normal.PacketType = storageListNormalType; - storelist_normal.PacketLength = ( sizeof( storelist_normal ) - sizeof( storelist_normal.list ) ) + (sizeof(struct NORMALITEM_INFO) * normal); + if (normal_count > 0 && (normal_count == MAX_STORAGE_ITEM_PACKET_NORMAL || i + 1 == items_length)) { + storelist_normal.PacketType = storageListNormalType; + storelist_normal.PacketLength = (sizeof(storelist_normal) - sizeof(storelist_normal.list)) + (sizeof(struct NORMALITEM_INFO) * normal_count); #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 storelist_normal.invType = type; @@ -3008,11 +3002,12 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t #endif clif->send(&storelist_normal, storelist_normal.PacketLength, &sd->bl, SELF); + normal_count = 0; } - if( equip ) { - storelist_equip.PacketType = storageListEquipType; - storelist_equip.PacketLength = ( sizeof( storelist_equip ) - sizeof( storelist_equip.list ) ) + (sizeof(struct EQUIPITEM_INFO) * equip); + if (equip_count > 0 && (equip_count == MAX_STORAGE_ITEM_PACKET_EQUIP || i + 1 == items_length)) { + storelist_equip.PacketType = storageListEquipType; + storelist_equip.PacketLength = (sizeof(storelist_equip) - sizeof(storelist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip_count); #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 storelist_equip.invType = type; @@ -3022,10 +3017,39 @@ static void clif_storageItems(struct map_session_data *sd, enum inventory_type t #endif clif->send(&storelist_equip, storelist_equip.PacketLength, &sd->bl, SELF); + equip_count = 0; } + } + + if (normal_count > 0) { + storelist_normal.PacketType = storageListNormalType; + storelist_normal.PacketLength = (sizeof(storelist_normal) - sizeof(storelist_normal.list)) + (sizeof(struct NORMALITEM_INFO) * normal_count); + +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 + storelist_normal.invType = type; +#endif +#if PACKETVER >= 20120925 && PACKETVER_RE_NUM < 20180829 && PACKETVER_ZERO_NUM < 20180919 && PACKETVER_MAIN_NUM < 20181002 + safestrncpy(storelist_normal.name, "Storage", NAME_LENGTH); +#endif + + clif->send(&storelist_normal, storelist_normal.PacketLength, &sd->bl, SELF); + normal_count = 0; + } + + if (equip_count > 0) { + storelist_equip.PacketType = storageListEquipType; + storelist_equip.PacketLength = (sizeof(storelist_equip) - sizeof(storelist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip_count); - } while ( i < items_length ); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 + storelist_equip.invType = type; +#endif +#if PACKETVER >= 20120925 && PACKETVER_RE_NUM < 20180829 && PACKETVER_ZERO_NUM < 20180919 && PACKETVER_MAIN_NUM < 20181002 + safestrncpy(storelist_equip.name, "Storage", NAME_LENGTH); +#endif + clif->send(&storelist_equip, storelist_equip.PacketLength, &sd->bl, SELF); + equip_count = 0; + } } static void clif_cartList(struct map_session_data *sd) @@ -4319,22 +4343,25 @@ static void clif_addchat(struct chat_data *cd, struct map_session_data *sd) /// role: /// 0 = owner (menu) /// 1 = normal -static void clif_changechatowner(struct chat_data *cd, struct map_session_data *sd) +static void clif_chatRoleChange(struct chat_data *cd, struct map_session_data *sd, struct block_list* bl, int isNotOwner) { - unsigned char buf[64]; - nullpo_retv(sd); - nullpo_retv(cd); + nullpo_retv(bl); + struct PACKET_ZC_ROLE_CHANGE p; - WBUFW(buf, 0) = 0xe1; - WBUFL(buf, 2) = 1; - memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH); + p.packetType = HEADER_ZC_ROLE_CHANGE; + p.flag = isNotOwner; + memcpy(&p.name, sd->status.name, NAME_LENGTH); + clif->send(&p, sizeof(struct PACKET_ZC_ROLE_CHANGE), bl, CHAT); +} - WBUFW(buf,30) = 0xe1; - WBUFL(buf,32) = 0; - memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH); +static void clif_changechatowner(struct chat_data *cd, struct map_session_data *sd) +{ + nullpo_retv(sd); + nullpo_retv(cd); - clif->send(buf,packet_len(0xe1)*2,&sd->bl,CHAT); + clif->chatRoleChange(cd, cd->usersd[0], &sd->bl, 1); + clif->chatRoleChange(cd, sd, &sd->bl, 0); } /// Notify about user leaving the chatroom (ZC_MEMBER_EXIT). @@ -5042,9 +5069,8 @@ static void clif_getareachar_skillunit(struct block_list *bl, struct skill_unit p.xPos = su->bl.x; p.yPos = su->bl.y; - //Use invisible unit id for traps. - if ((battle_config.traps_setting&1 && skill->get_inf2(su->group->skill_id)&INF2_TRAP) || - (skill->get_unit_flag(su->group->skill_id) & UF_RANGEDSINGLEUNIT && !(su->val2 & UF_RANGEDSINGLEUNIT))) + // Use invisible unit id for some ground skills. + if (skill->get_unit_flag(su->group->skill_id) & UF_RANGEDSINGLEUNIT && !(su->val2 & UF_RANGEDSINGLEUNIT)) p.job = UNT_DUMMYSKILL; else p.job = su->group->unit_id; @@ -5053,7 +5079,7 @@ static void clif_getareachar_skillunit(struct block_list *bl, struct skill_unit p.RadiusRange = (unsigned char)su->range; #endif - p.isVisible = 1; + p.isVisible = su->visible; #if PACKETVER >= 20130731 p.level = (unsigned char)su->group->skill_lv; @@ -7131,7 +7157,7 @@ static void clif_party_job_and_level(struct map_session_data *sd) WBUFW(buf, 6) = sd->status.class; WBUFW(buf, 8) = sd->status.base_level; - clif_send(buf, packet_len(0xabd), &sd->bl, PARTY); + clif->send(buf, packet_len(0xabd), &sd->bl, PARTY); #endif } @@ -7142,14 +7168,17 @@ static void clif_party_job_and_level(struct map_session_data *sd) /// 1 = auto-deny party invites static void clif_partyinvitationstate(struct map_session_data *sd) { +#if PACKETVER_MAIN_NUM >= 20070911 || defined(PACKETVER_RE) || PACKETVER_AD_NUM >= 20070911 || PACKETVER_SAK_NUM >= 20070904 || defined(PACKETVER_ZERO) int fd; nullpo_retv(sd); fd = sd->fd; - WFIFOHEAD(fd, packet_len(0x2c9)); - WFIFOW(fd, 0) = 0x2c9; - WFIFOB(fd, 2) = sd->status.allow_party ? 0 : 1; - WFIFOSET(fd, packet_len(0x2c9)); + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_PARTY_CONFIG)); + struct PACKET_ZC_PARTY_CONFIG *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_PARTY_CONFIG; + p->denyPartyInvites = sd->status.allow_party ? 1 : 0; + WFIFOSET(fd, sizeof(struct PACKET_ZC_PARTY_CONFIG)); +#endif } /// Party invitation request. @@ -8407,6 +8436,7 @@ static void clif_guild_expulsion(struct map_session_data *sd, const char *name, #endif safestrncpy(&p.reason[0], mes, 40); +// version unconfirmed #if PACKETVER < 20100803 memset(&p.account_name, 0, NAME_LENGTH); // account name (not used for security reasons) #endif @@ -8418,42 +8448,44 @@ static void clif_guild_expulsion(struct map_session_data *sd, const char *name, /// 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803) static void clif_guild_expulsionlist(struct map_session_data *sd) { -#if PACKETVER < 20100803 - const int offset = NAME_LENGTH*2+40; -#else - const int offset = NAME_LENGTH+40; -#endif - int fd, i, c = 0; - struct guild* g; - nullpo_retv(sd); - if( (g = sd->guild) == NULL ) + int c = 0; + + struct guild* g; + if ((g = sd->guild) == NULL) return; - fd = sd->fd; + int fd = sd->fd; - WFIFOHEAD(fd,4 + MAX_GUILDEXPULSION * offset); - WFIFOW(fd,0) = 0x163; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_BAN_LIST) + MAX_GUILDEXPULSION * sizeof(struct PACKET_ZC_BAN_LIST_sub)); + struct PACKET_ZC_BAN_LIST *packet = WFIFOP(fd, 0); + packet->packetType = HEADER_ZC_BAN_LIST; - for( i = 0; i < MAX_GUILDEXPULSION; i++ ) + for (int i = 0; i < MAX_GUILDEXPULSION; i++) { struct guild_expulsion* e = &g->expulsion[i]; - if( e->account_id > 0 ) + if (e->account_id > 0) { - memcpy(WFIFOP(fd,4 + c*offset), e->name, NAME_LENGTH); -#if PACKETVER < 20100803 - memset(WFIFOP(fd,4 + c*offset+24), 0, NAME_LENGTH); // account name (not used for security reasons) - memcpy(WFIFOP(fd,4 + c*offset+48), e->mes, 40); +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) + packet->chars[c].char_id = e->char_id; +// version unconfirmed +#elif PACKETVER >= 20100803 + memcpy(packet->chars[c].char_name, e->name, NAME_LENGTH); + #else - memcpy(WFIFOP(fd,4 + c*offset+24), e->mes, 40); + memcpy(packet->chars[c].char_name, e->name, NAME_LENGTH); + memset(packet->chars[c].account_name, 0, NAME_LENGTH); // account name (not used for security reasons) + #endif - c++; + memcpy(packet->chars[c].message, e->mes, 40); + + c ++; } } - WFIFOW(fd,2) = 4 + c*offset; - WFIFOSET(fd,WFIFOW(fd,2)); + packet->packetLen = sizeof(struct PACKET_ZC_BAN_LIST) + c * sizeof(struct PACKET_ZC_BAN_LIST_sub); + WFIFOSET(fd, packet->packetLen); } /// Guild chat message (ZC_GUILD_CHAT). @@ -9234,149 +9266,377 @@ static void clif_refresh(struct map_session_data *sd) clif->refresh_storagewindow(sd); } +static void clif_send_selforarea(int fd, struct block_list *bl, const void *buf, int len) +{ + // if no recipient specified just update nearby clients + // if no recipient specified just update nearby clients + if (fd == 0) { + clif->send(buf, len, bl, AREA); + } else { + struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; + if (sd != NULL) { + clif->send(buf, len, &sd->bl, SELF); + } else { + clif->send(buf, len, bl, SELF); + } + } +} + /// 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_blname_ack(int fd, struct block_list *bl) +static void clif_pcname_ack(int fd, struct block_list *bl) { - struct packet_reqnameall_ack packet = { 0 }; - int len = sizeof(struct packet_reqnameall_ack); - nullpo_retv(bl); + Assert_retv(bl->type == BL_PC); - packet.packet_id = reqName; + struct PACKET_ZC_ACK_REQNAMEALL packet = { 0 }; + int len = sizeof(struct PACKET_ZC_ACK_REQNAMEALL); packet.gid = bl->id; - switch(bl->type) { - case BL_PC: - { - const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); - const struct party_data *p = NULL; - const struct guild *g = NULL; - int ps = -1; + const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); + const struct party_data *p = NULL; + 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; - } + if (ssd->fakename[0] != '\0' && ssd->disguise != -1) { + packet.packet_id = reqName; + len = sizeof(struct packet_reqname_ack); + } else { + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; + len = sizeof(struct PACKET_ZC_ACK_REQNAMEALL); + } - //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise != -1) { - packet.gid = -bl->id; - } + //Requesting your own "shadow" name. [Skotlex] + if (ssd->fd == fd && ssd->disguise != -1) { + packet.gid = -bl->id; + } - if (ssd->fakename[0] != '\0') { - memcpy(packet.name, ssd->fakename, NAME_LENGTH); - break; - } + if (ssd->fakename[0] != '\0' && ssd->disguise != -1) { + memcpy(packet.name, ssd->fakename, NAME_LENGTH); + } else { +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + // 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 PACKETVER >= 20150503 - // Title System [Dastgir/Hercules] - if (ssd->status.title_id > 0) { - packet.title_id = ssd->status.title_id; + if (ssd->status.party_id != 0) { + p = party->search(ssd->status.party_id); + } + if (ssd->status.guild_id != 0) { + if ((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 (!battle_config.display_party_name && g == NULL) { + // do not display party unless the player is also in a guild + p = NULL; + } + + if (p != NULL) { + memcpy(packet.party_name, p->party.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); + } + } + + clif->send_selforarea(fd, bl, &packet, len); +} + +/// 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_homname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_HOM); + + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } #endif - memcpy(packet.name, ssd->status.name, NAME_LENGTH); + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} - if (ssd->status.party_id != 0) { - p = party->search(ssd->status.party_id); - } - if (ssd->status.guild_id != 0) { - if ((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; - } - } +/// 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_mername_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_MER); - if (!battle_config.display_party_name && g == NULL) { - // do not display party unless the player is also in a guild - p = NULL; - } + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif - if (p == NULL && g == NULL) - break; + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} - if (p != NULL) { - memcpy(packet.party_name, p->party.name, NAME_LENGTH); - } +/// 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_petname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_PET); - 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); - } - } + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} + +/// 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_npcname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_NPC); + + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} + +/// 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_mobname_guardian_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_MOB); + const struct mob_data *md = BL_UCCAST(BL_MOB, bl); + Assert_retv(md->guardian_data && md->guardian_data->g); + + struct PACKET_ZC_ACK_REQNAMEALL packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; + packet.gid = bl->id; + memcpy(packet.name, md->name, NAME_LENGTH); + memcpy(packet.guild_name, md->guardian_data->g->name, NAME_LENGTH); + memcpy(packet.position_name, md->guardian_data->castle->castle_name, NAME_LENGTH); + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAMEALL)); +} + +/// 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_mobname_normal_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_MOB); + + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_MOB, bl)->db->name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} + +/// 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_mobname_additional_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_MOB); + + struct PACKET_ZC_ACK_REQNAMEALL packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; + packet.gid = bl->id; + + const struct mob_data *md = BL_UCCAST(BL_MOB, bl); + + memcpy(packet.name, md->name, NAME_LENGTH); + char mobhp[100]; + char *str_p = mobhp; + if (battle_config.show_mob_info&4) + str_p += sprintf(str_p, "Lv. %d | ", md->level); + if (battle_config.show_mob_info&1) + str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp); + if (battle_config.show_mob_info&2) + str_p += sprintf(str_p, "HP: %u%% | ", get_percentage(md->status.hp, md->status.max_hp)); + //Even thought mobhp ain't a name, we send it as one so the client + //can parse it. [Skotlex] + if (str_p != mobhp) { + *(str_p-3) = '\0'; //Remove trailing space + pipe. + memcpy(packet.party_name, mobhp, NAME_LENGTH); + } + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAMEALL)); +} + +/// 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_mobname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_MOB); + + const struct mob_data *md = BL_UCCAST(BL_MOB, bl); + + if (md->guardian_data && md->guardian_data->g) { + clif->mobname_guardian_ack(fd, bl); + } else if (battle_config.show_mob_info) { + clif->mobname_additional_ack(fd, bl); + } else { + clif->mobname_normal_ack(fd, bl); + } +} + +/// 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_chatname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_CHAT); + + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + +#if 0 // Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex] + memcpy(packet.name, BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif +#endif + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} + +/// 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_elemname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_ELEM); + + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + memcpy(packet.name, BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 + struct unit_data *ud = unit->bl2ud(bl); + if (ud != NULL) { + memcpy(packet.title, ud->title, NAME_LENGTH); + packet.groupId = ud->groupId; + } +#endif + + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} + +static void clif_unknownname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + ShowError("clif_blname_ack: bad type %u(%d)\n", bl->type, bl->id); +} + +static void clif_blname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + + switch(bl->type) { + case BL_PC: + clif->pcname_ack(fd, bl); break; - //[blackhole89] case BL_HOM: - memcpy(packet.name, BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); + clif->homname_ack(fd, bl); break; case BL_MER: - memcpy(packet.name, BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); + clif->mername_ack(fd, bl); break; case BL_PET: - memcpy(packet.name, BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); + clif->petname_ack(fd, bl); break; case BL_NPC: - memcpy(packet.name, BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); + clif->npcname_ack(fd, bl); break; case BL_MOB: - { - const struct mob_data *md = BL_UCCAST(BL_MOB, bl); - - memcpy(packet.name, md->name, NAME_LENGTH); - if (md->guardian_data && md->guardian_data->g) { - 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; - 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) - str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp); - if (battle_config.show_mob_info&2) - str_p += sprintf(str_p, "HP: %u%% | ", get_percentage(md->status.hp, md->status.max_hp)); - //Even thought mobhp ain't a name, we send it as one so the client - //can parse it. [Skotlex] - if (str_p != mobhp) { - *(str_p-3) = '\0'; //Remove trailing space + pipe. - memcpy(packet.party_name, mobhp, NAME_LENGTH); - } - } - } + clif->mobname_ack(fd, bl); 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(packet.name, BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); + clif->chatname_ack(fd, bl); break; -#endif - return; case BL_ELEM: - memcpy(packet.name, BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); + clif->elemname_ack(fd, bl); break; default: - ShowError("clif_blname_ack: 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(&packet, len, bl, AREA); - } else { - 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); - } + clif->unknownname_ack(fd, bl); + break; } } @@ -9387,14 +9647,14 @@ static void clif_charnameupdate(struct map_session_data *ssd) int ps = -1; struct party_data *p = NULL; struct guild *g = NULL; - struct packet_reqnameall_ack packet = { 0 }; + struct PACKET_ZC_ACK_REQNAMEALL packet = { 0 }; nullpo_retv(ssd); if (ssd->fakename[0]) return; //No need to update as the party/guild was not displayed anyway. - packet.packet_id = reqNameAllType; + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; packet.gid = ssd->bl.id; memcpy(packet.name, ssd->status.name, NAME_LENGTH); @@ -9421,7 +9681,7 @@ static void clif_charnameupdate(struct map_session_data *ssd) memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } -#if PACKETVER >= 20150503 +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) // Achievement System [Dastgir/Hercules] if (ssd->status.title_id > 0) { packet.title_id = ssd->status.title_id; @@ -10530,15 +10790,7 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) // NPC Quest / Event Icon Check [Kisuka] #if PACKETVER >= 20090218 - { - int i; - for (i = 0; i < VECTOR_LENGTH(map->list[sd->bl.m].qi_data); i++) { - struct questinfo *qi = &VECTOR_INDEX(map->list[sd->bl.m].qi_data, i); - - if (quest->questinfo_validate(sd, qi)) - clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color); - } - } + quest->questinfo_refresh(sd); #endif } @@ -10750,8 +11002,10 @@ static void clif_parse_QuitGame(int fd, struct map_session_data *sd) /* Rovert's prevent logout option fixed [Valaris] */ if (!sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] && !sd->sc.data[SC__INVISIBILITY] && !sd->sc.data[SC_SUHIDE] && (!battle_config.prevent_logout || DIFF_TICK(timer->gettick(), sd->canlog_tick) > battle_config.prevent_logout)) { - sockt->eof(fd); clif->disconnect_ack(sd, 0); + sockt->flush(fd); + if (battle_config.drop_connection_on_quit) + sockt->eof(fd); } else { clif->disconnect_ack(sd, 1); } @@ -11543,7 +11797,7 @@ static void clif_parse_NpcClicked(int fd, struct map_session_data *sd) #endif return; } - if ( pc_cant_act2(sd) || !(bl = map->id2bl(RFIFOL(fd,2))) || sd->state.vending ) + if (pc_cant_act2(sd) || !(bl = map->id2bl(RFIFOL(fd,2))) || sd->state.vending || sd->state.prevend) return; switch (bl->type) { @@ -11915,7 +12169,7 @@ static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) __attr static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) { int flag = 0; - if (pc_istrading(sd)) + if (pc_istrading(sd) || sd->state.prevend) return; if (!pc_iscarton(sd)) return; @@ -11930,6 +12184,8 @@ static void clif_parse_GetItemFromCart(int fd, struct map_session_data *sd) __at /// 0127 <index>.W <amount>.L static void clif_parse_GetItemFromCart(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || sd->state.prevend) + return; if (!pc_iscarton(sd)) return; pc->getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4)); @@ -12587,10 +12843,19 @@ static void clif_parse_NpcAmountInput(int fd, struct map_session_data *sd) int npcid = RFIFOL(fd,2); int amount = RFIFOL(fd,6); - if (amount >= 0) + if (amount < sd->npc_amount_min) { + sd->npc_amount = sd->npc_amount_min; + sd->npc_input_capped_range = -1; + } + else if (amount > sd->npc_amount_max) { + sd->npc_amount = sd->npc_amount_max; + sd->npc_input_capped_range = 1; + } + else { sd->npc_amount = amount; - else - sd->npc_amount = 0; + sd->npc_input_capped_range = 0; + } + npc->scriptcont(sd, npcid, false); } @@ -12804,7 +13069,7 @@ static void clif_parse_MoveToKafra(int fd, struct map_session_data *sd) { int item_index, item_amount; - if (pc_istrading(sd)) + if (pc_istrading(sd) || sd->state.prevend) return; item_index = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[0])-2; @@ -12825,6 +13090,9 @@ static void clif_parse_MoveFromKafra(int fd, struct map_session_data *sd) __attr /// There are various variants of this packet, some of them have padding between fields. static void clif_parse_MoveFromKafra(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || sd->state.prevend) + return; + int item_index, item_amount; item_index = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[0])-1; @@ -12841,7 +13109,7 @@ static void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) /// 0129 <index>.W <amount>.L static void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd) { - if( sd->state.vending ) + if (pc_istrading(sd) || sd->state.prevend) return; if (!pc_iscarton(sd)) return; @@ -12857,7 +13125,7 @@ static void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) /// 0128 <index>.W <amount>.L static void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd) { - if( sd->state.vending ) + if (pc_istrading(sd) || sd->state.prevend) return; if (!pc_iscarton(sd)) return; @@ -16348,7 +16616,7 @@ static void clif_Auction_openwindow(struct map_session_data *sd) nullpo_retv(sd); fd = sd->fd; - if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->state.vending || sd->state.buyingstore || sd->state.trading) + if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.trading) return; if( !battle_config.feature_auction ) @@ -17039,7 +17307,14 @@ static void clif_parse_PartyTick(int fd, struct map_session_data *sd) __attribut static void clif_parse_PartyTick(int fd, struct map_session_data *sd) { const struct PACKET_CZ_PARTY_CONFIG *const p = RFIFOP(fd, 0); - sd->status.allow_party = p->refuseInvite ? false : true; + const bool newAllowParty = p->refuseInvite ? true : false; + if (newAllowParty != sd->status.allow_party) { + sd->status.allow_party = newAllowParty; + if ((map->save_settings & 512) != 0) + chrif->save(sd, 0); // send to char server + } else { + sd->status.allow_party = newAllowParty; + } clif->partytickack(sd, sd->status.allow_party); } @@ -18931,7 +19206,7 @@ static void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) if( sd->menuskill_id != SC_AUTOSHADOWSPELL ) return; - if( pc_istrading(sd) ) { + if (pc_istrading(sd) || sd->state.prevend) { clif->skill_fail(sd, sd->ud.skill_id, 0, 0, 0); clif_menuskill_clear(sd); return; @@ -19280,18 +19555,26 @@ static void clif_parse_CashShopBuy(int fd, struct map_session_data *sd) } else { result = CSBR_UNKONWN_ITEM; } - - WFIFOHEAD(fd, 16); - WFIFOW(fd, 0) = 0x849; - WFIFOL(fd, 2) = id; - WFIFOW(fd, 6) = result;/* result */ - WFIFOL(fd, 8) = sd->cashPoints;/* current cash point */ - WFIFOL(fd, 12) = sd->kafraPoints;// [Ryuuzaki] - WFIFOSET(fd, 16); + clif->cashShopBuyAck(fd, sd, id, result); } } +static void clif_cashShopBuyAck(int fd, struct map_session_data *sd, int itemId, enum CASH_SHOP_BUY_RESULT result) +{ +#if PACKETVER_MAIN_NUM >= 20101123 || PACKETVER_RE_NUM >= 20120328 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT)); + struct PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT *p = WFIFOP(fd, 0); + p->packetType = 0x849; + p->itemId = itemId; + p->result = result; + p->cashPoints = sd->cashPoints; + p->kafraPoints = sd->kafraPoints; + WFIFOSET(fd, sizeof(struct PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT)); +#endif +} + static void clif_parse_CashShopReqTab(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); /* [Ind/Hercules] */ static void clif_parse_CashShopReqTab(int fd, struct map_session_data *sd) @@ -20317,20 +20600,34 @@ static void clif_roulette_generate_ack(struct map_session_data *sd, enum GENERAT #endif } +static void clif_roulette_close(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20141008 || PACKETVER_RE_NUM >= 20140903 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + struct PACKET_ZC_ACK_CLOSE_ROULETTE p; + p.packetType = HEADER_ZC_ACK_CLOSE_ROULETTE; + p.result = 0; // close window + + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + /** * Stackable items merger */ static void clif_openmergeitem(int fd, struct map_session_data *sd) { -#if PACKETVER > 20120228 - int i = 0, n = 0, j = 0; +#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + int n = 0, j = 0; struct merge_item merge_items[MAX_INVENTORY]; struct merge_item *merge_items_[MAX_INVENTORY] = {0}; - nullpo_retv(sd); - memset(&merge_items,'\0',sizeof(merge_items)); + memset(&merge_items, '\0', sizeof(merge_items)); - for (i = 0; i < sd->status.inventorySize; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { struct item *item_data = &sd->status.inventory[i]; if (item_data->nameid == 0 || !itemdb->isstackable(item_data->nameid) || item_data->bound != IBT_NONE) @@ -20341,17 +20638,18 @@ static void clif_openmergeitem(int fd, struct map_session_data *sd) n++; } - qsort(merge_items,n,sizeof(struct merge_item),clif->comparemergeitem); + qsort(merge_items, n, sizeof(struct merge_item), clif->comparemergeitem); - for (i = 0, j = 0; i < n; i++) { - if (i > 0 && merge_items[i].nameid == merge_items[i-1].nameid) + j = 0; + for (int i = 0; i < n; i++) { + if (i > 0 && merge_items[i].nameid == merge_items[i - 1].nameid) { merge_items_[j] = &merge_items[i]; j++; continue; } - if (i < n - 1 && merge_items[i].nameid == merge_items[i+1].nameid) + if (i < n - 1 && merge_items[i].nameid == merge_items[i + 1].nameid) { merge_items_[j] = &merge_items[i]; j++; @@ -20359,12 +20657,14 @@ static void clif_openmergeitem(int fd, struct map_session_data *sd) } } - WFIFOHEAD(fd,2*j+4); - WFIFOW(fd,0) = 0x96d; - WFIFOW(fd,2) = 2*j+4; - for ( i = 0; i < j; i++ ) - WFIFOW(fd,i*2+4) = merge_items_[i]->position; - WFIFOSET(fd,2*j+4); + const int len = sizeof(struct PACKET_ZC_MERGE_ITEM_OPEN) + j * sizeof(struct PACKET_ZC_MERGE_ITEM_OPEN_sub); + WFIFOHEAD(fd, len); + struct PACKET_ZC_MERGE_ITEM_OPEN *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MERGE_ITEM_OPEN; + p->packetLen = len; + for (int i = 0; i < j; i++) + p->items[i].index = merge_items_[i]->position; + WFIFOSET(fd, len); #endif } @@ -20380,35 +20680,42 @@ static int clif_comparemergeitem(const void *a, const void *b) return a_->nameid > b_->nameid ? -1 : 1; } +static void clif_mergeitems(int fd, struct map_session_data *sd, int index, int amount, enum mergeitem_reason reason) +{ +#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO) + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_ACK_MERGE_ITEM)); + struct PACKET_ZC_ACK_MERGE_ITEM *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_ACK_MERGE_ITEM; + p->index = index + 2; + p->amount = amount; + p->reason = reason; + WFIFOSET(fd, sizeof(struct PACKET_ZC_ACK_MERGE_ITEM)); +#endif +} + static void clif_ackmergeitems(int fd, struct map_session_data *sd) { -#if PACKETVER > 20120228 - int i = 0, n = 0, length = 0, count = 0; +#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + int i = 0, n = 0, count = 0; int nameid = 0; int16 indexes[MAX_INVENTORY] = {0}, amounts[MAX_INVENTORY] = {0}; - struct item item_data; - nullpo_retv(sd); - length = (RFIFOW(fd,2) - 4)/2; + int length = (RFIFOW(fd, 2) - 4) / 2; if (length >= sd->status.inventorySize || length < 2) { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x96f; - WFIFOW(fd,2) = 0; - WFIFOW(fd,4) = 0; - WFIFOB(fd,6) = MERGEITEM_FAILD; - WFIFOSET(fd,7); + clif->mergeitems(fd, sd, 0, 0, MERGEITEM_FAILD); return; } for (i = 0, n = 0; i < length; i++) { - int16 idx = RFIFOW(fd,i*2+4) - 2; - struct item *it = NULL; + int16 idx = RFIFOW(fd, i * 2 + 4) - 2; if (idx < 0 || idx >= sd->status.inventorySize) continue; - it = &sd->status.inventory[idx]; + struct item *it = &sd->status.inventory[idx]; if (it->nameid == 0 || !itemdb->isstackable(it->nameid) || it->bound != IBT_NONE) continue; @@ -20426,43 +20733,29 @@ static void clif_ackmergeitems(int fd, struct map_session_data *sd) } if (n < 2 || count == 0) { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x96f; - WFIFOW(fd,2) = 0; - WFIFOW(fd,4) = 0; - WFIFOB(fd,6) = MERGEITEM_FAILD; - WFIFOSET(fd,7); + clif->mergeitems(fd, sd, 0, 0, MERGEITEM_FAILD); return; } if (count > MAX_AMOUNT) { - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x96f; - WFIFOW(fd,2) = 0; - WFIFOW(fd,4) = 0; - WFIFOB(fd,6) = MERGEITEM_MAXCOUNTFAILD; - WFIFOSET(fd,7); + clif->mergeitems(fd, sd, 0, 0, MERGEITEM_MAXCOUNTFAILD); return; } for (i = 0; i < n; i++) - pc->delitem(sd,indexes[i],amounts[i],0,DELITEM_NORMAL,LOG_TYPE_NPC); + pc->delitem(sd, indexes[i], amounts[i], 0, DELITEM_NORMAL, LOG_TYPE_NPC); - memset(&item_data,'\0',sizeof(item_data)); + struct item item_data; + memset(&item_data, '\0', sizeof(item_data)); item_data.nameid = nameid; item_data.identify = 1; item_data.unique_id = itemdb->unique_id(sd); - pc->additem(sd,&item_data,count,LOG_TYPE_NPC); + pc->additem(sd, &item_data, count, LOG_TYPE_NPC); ARR_FIND(0, sd->status.inventorySize, i, item_data.unique_id == sd->status.inventory[i].unique_id); - WFIFOHEAD(fd,7); - WFIFOW(fd,0) = 0x96f; - WFIFOW(fd,2) = i+2; - WFIFOW(fd,4) = count; - WFIFOB(fd,6) = MERGEITEM_SUCCESS; - WFIFOSET(fd,7); + clif->mergeitems(fd, sd, i, count, MERGEITEM_SUCCESS); #endif } @@ -21636,7 +21929,7 @@ static void clif_hat_effect_single(struct block_list *bl, uint16 effectId, bool WBUFB(buf,8) = enable; WBUFL(buf,9) = effectId; - clif_send(buf, 13, bl, AREA); + clif->send(buf, 13, bl, AREA); #endif } @@ -21843,6 +22136,17 @@ static void clif_parse_attendance_reward_request(int fd, struct map_session_data static void clif_parse_cz_blocking_play_cancel(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_cz_blocking_play_cancel(int fd, struct map_session_data *sd) { + clif->loadConfirm(sd); +} + +static void clif_loadConfirm(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190403 || PACKETVER_RE_NUM >= 20190320 || PACKETVER_ZERO_NUM >= 20190410 + nullpo_retv(sd); + struct PACKET_ZC_LOAD_CONFIRM p; + p.packetType = HEADER_ZC_LOAD_CONFIRM; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif } static void clif_ui_action(struct map_session_data *sd, int32 UIType, int32 data) @@ -22222,6 +22526,104 @@ static void clif_parse_ResetCooldown(int fd, struct map_session_data *sd) atcommand->exec(fd, sd, cmd, true); } +static void clif_OpenRefineryUI(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + if (battle_config.enable_refinery_ui == 0) + return; + + struct PACKET_ZC_REFINE_OPEN_WINDOW p; + p.packetType = HEADER_ZC_REFINE_OPEN_WINDOW; + clif->send(&p, sizeof(p), &sd->bl, SELF); + + sd->state.refine_ui = 1; +#endif +} + +static void clif_parse_AddItemRefineryUI(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_AddItemRefineryUI(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + const struct PACKET_CZ_REFINE_ADD_ITEM *p = RFIFO2PTR(fd); + refine->refinery_add_item(sd, p->index - 2); +#endif +} + +static void clif_AddItemRefineryUIAck(struct map_session_data *sd, int item_index, struct s_refine_requirement *req) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + nullpo_retv(req); + Assert_retv(item_index >= 0 && item_index < sd->status.inventorySize); + + if (battle_config.enable_refinery_ui == 0) + return; + + char buf[sizeof(struct PACKET_ZC_REFINE_ADD_ITEM) + sizeof(struct PACKET_ZC_REFINE_ADD_ITEM_SUB) * MAX_REFINE_REQUIREMENTS]; + struct PACKET_ZC_REFINE_ADD_ITEM *p = (struct PACKET_ZC_REFINE_ADD_ITEM *)buf; + + p->packetType = HEADER_ZC_REFINE_ADD_ITEM; + p->packtLength = sizeof(*p) + sizeof(p->req[0]) * req->req_count; + p->itemIndex = item_index + 2; + p->blacksmithBlessing = req->blacksmith_blessing; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid); + for (int i = 0; i < req->req_count; ++i) { + p->req[i].chance = refine->get_refine_chance(weapon_level, sd->status.inventory[item_index].refine, req->req[i].type); + p->req[i].itemId = req->req[i].nameid; + p->req[i].zeny = req->req[i].cost; + } + + clif->send(p, p->packtLength, &sd->bl, SELF); +#endif +} + +static void clif_parse_RefineryUIRefine(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_RefineryUIRefine(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + const struct PACKET_CZ_REFINE_ITEM_REQUEST *p = RFIFO2PTR(fd); + refine->refinery_refine_request(sd, p->index - 2, p->itemId, (p->blacksmithBlessing == 1) ? true : false); +#endif +} + +static void clif_parse_RefineryUIClose(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_RefineryUIClose(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + if (battle_config.enable_refinery_ui == 0) + return; + + sd->state.refine_ui = 0; + return; +#endif +} + +static void clif_announce_refine_status(struct map_session_data *sd, int item_id, int refine_level, bool success, enum send_target target) +{ +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + + Assert_retv(refine_level > 0 && refine_level <= INT8_MAX); + + struct PACKET_ZC_REFINE_STATUS p; + p.packetType = HEADER_ZC_REFINE_STATUS; + safestrncpy(p.name, sd->status.name, NAME_LENGTH); + p.itemId = item_id; + p.refine_level = refine_level; + p.status = (success) ? true : false; + clif->send(&p, sizeof(p), &sd->bl, target); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -22707,6 +23109,18 @@ void clif_defaults(void) clif->mvp_noitem = clif_mvp_noitem; clif->changed_dir = clif_changed_dir; clif->blname_ack = clif_blname_ack; + clif->pcname_ack = clif_pcname_ack; + clif->homname_ack = clif_homname_ack; + clif->mername_ack = clif_mername_ack; + clif->petname_ack = clif_petname_ack; + clif->npcname_ack = clif_npcname_ack; + clif->mobname_ack = clif_mobname_ack; + clif->mobname_guardian_ack = clif_mobname_guardian_ack; + clif->mobname_additional_ack = clif_mobname_additional_ack; + clif->mobname_normal_ack = clif_mobname_normal_ack; + clif->chatname_ack = clif_chatname_ack; + clif->elemname_ack = clif_elemname_ack; + clif->unknownname_ack = clif_unknownname_ack; clif->monster_hp_bar = clif_monster_hp_bar; clif->hpmeter = clif_hpmeter; clif->hpmeter_single = clif_hpmeter_single; @@ -22804,6 +23218,7 @@ void clif_defaults(void) clif->joinchatok = clif_joinchatok; clif->addchat = clif_addchat; clif->changechatowner = clif_changechatowner; + clif->chatRoleChange = clif_chatRoleChange; clif->clearchat = clif_clearchat; clif->leavechat = clif_leavechat; clif->changechatstatus = clif_changechatstatus; @@ -23066,11 +23481,13 @@ void clif_defaults(void) /* */ clif->parse_roulette_db = clif_parse_roulette_db; clif->roulette_generate_ack = clif_roulette_generate_ack; + clif->roulette_close = clif_roulette_close; /* Merge Items */ clif->openmergeitem = clif_openmergeitem; clif->cancelmergeitem = clif_cancelmergeitem; clif->comparemergeitem = clif_comparemergeitem; clif->ackmergeitems = clif_ackmergeitems; + clif->mergeitems = clif_mergeitems; /* Cart Deco */ clif->selectcart = clif_selectcart; /* */ @@ -23306,6 +23723,7 @@ void clif_defaults(void) clif->pCashShopReqTab = clif_parse_CashShopReqTab; clif->pCashShopSchedule = clif_parse_CashShopSchedule; clif->pCashShopBuy = clif_parse_CashShopBuy; + clif->cashShopBuyAck = clif_cashShopBuyAck; /* */ clif->pPartyTick = clif_parse_PartyTick; clif->pGuildInvite2 = clif_parse_GuildInvite2; @@ -23417,4 +23835,12 @@ void clif_defaults(void) clif->pingTimer = clif_pingTimer; clif->pingTimerSub = clif_pingTimerSub; clif->pResetCooldown = clif_parse_ResetCooldown; + clif->loadConfirm = clif_loadConfirm; + clif->send_selforarea = clif_send_selforarea; + clif->OpenRefineryUI = clif_OpenRefineryUI; + clif->pAddItemRefineryUI = clif_parse_AddItemRefineryUI; + clif->AddItemRefineryUIAck = clif_AddItemRefineryUIAck; + clif->pRefineryUIClose = clif_parse_RefineryUIClose; + clif->pRefineryUIRefine = clif_parse_RefineryUIRefine; + clif->announce_refine_status = clif_announce_refine_status; } diff --git a/src/map/clif.h b/src/map/clif.h index 6c9058cba..19c321ed3 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -55,6 +55,7 @@ struct skill_unit; struct unit_data; struct view_data; struct achievement_data; // map/achievement.h +struct s_refine_requirement; enum clif_messages; enum rodex_add_item; @@ -78,6 +79,11 @@ enum rodex_get_items; #define COLOR_YELLOW 0xffff00U #define COLOR_DEFAULT COLOR_GREEN +#define MAX_STORAGE_ITEM_PACKET_NORMAL ((INT16_MAX - (sizeof(struct ZC_STORE_ITEMLIST_NORMAL) - (sizeof(struct NORMALITEM_INFO) * MAX_ITEMLIST))) / sizeof(struct NORMALITEM_INFO)) +#define MAX_STORAGE_ITEM_PACKET_EQUIP ((INT16_MAX - (sizeof(struct ZC_STORE_ITEMLIST_EQUIP) - (sizeof(struct EQUIPITEM_INFO) * MAX_ITEMLIST))) / sizeof(struct EQUIPITEM_INFO)) +STATIC_ASSERT(MAX_STORAGE_ITEM_PACKET_NORMAL > 0, "Max items per storage item packet for normal items is less than 1, it's most likely to be a bug and shall not be ignored."); +STATIC_ASSERT(MAX_STORAGE_ITEM_PACKET_EQUIP > 0, "Max items per storage item packet for equip items is less than 1, it's most likely to be a bug and shall not be ignored."); + /** * Enumerations **/ @@ -405,6 +411,7 @@ enum CASH_SHOP_BUY_RESULT { CSBR_RUNE_OVERCOUNT = 0x9, CSBR_EACHITEM_OVERCOUNT = 0xa, CSBR_UNKNOWN = 0xb, + CSBR_BUSY = 0xc, }; enum BATTLEGROUNDS_QUEUE_ACK { @@ -884,6 +891,18 @@ struct clif_interface { void (*mvp_noitem) (struct map_session_data* sd); void (*changed_dir) (struct block_list *bl, enum send_target target); void (*blname_ack) (int fd, struct block_list *bl); + void (*pcname_ack) (int fd, struct block_list *bl); + void (*homname_ack) (int fd, struct block_list *bl); + void (*mername_ack) (int fd, struct block_list *bl); + void (*petname_ack) (int fd, struct block_list *bl); + void (*npcname_ack) (int fd, struct block_list *bl); + void (*mobname_ack) (int fd, struct block_list *bl); + void (*mobname_guardian_ack) (int fd, struct block_list *bl); + void (*mobname_additional_ack) (int fd, struct block_list *bl); + void (*mobname_normal_ack) (int fd, struct block_list *bl); + void (*chatname_ack) (int fd, struct block_list *bl); + void (*elemname_ack) (int fd, struct block_list *bl); + void (*unknownname_ack) (int fd, struct block_list *bl); void (*monster_hp_bar) ( struct mob_data* md, struct map_session_data *sd ); int (*hpmeter) (struct map_session_data *sd); void (*hpmeter_single) (int fd, int id, unsigned int hp, unsigned int maxhp); @@ -981,6 +1000,7 @@ struct clif_interface { void (*joinchatok) (struct map_session_data *sd,struct chat_data* cd); void (*addchat) (struct chat_data* cd,struct map_session_data *sd); void (*changechatowner) (struct chat_data* cd, struct map_session_data* sd); + void (*chatRoleChange) (struct chat_data *cd, struct map_session_data *sd, struct block_list* bl, int isNotOwner); void (*clearchat) (struct chat_data *cd,int fd); void (*leavechat) (struct chat_data* cd, struct map_session_data* sd, bool flag); void (*changechatstatus) (struct chat_data* cd); @@ -1244,11 +1264,13 @@ struct clif_interface { /* */ bool (*parse_roulette_db) (void); void (*roulette_generate_ack) (struct map_session_data *sd, enum GENERATE_ROULETTE_ACK result, short stage, short prizeIdx, int bonusItemID); + void (*roulette_close) (struct map_session_data *sd); /* Merge Items */ void (*openmergeitem) (int fd, struct map_session_data *sd); void (*cancelmergeitem) (int fd, struct map_session_data *sd); int (*comparemergeitem) (const void *a, const void *b); void (*ackmergeitems) (int fd, struct map_session_data *sd); + void (*mergeitems) (int fd, struct map_session_data *sd, int index, int amount, enum mergeitem_reason reason); /* */ 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); @@ -1478,6 +1500,7 @@ struct clif_interface { void (*pCashShopBuy) (int fd, struct map_session_data *sd); void (*pPartyTick) (int fd, struct map_session_data *sd); void (*pGuildInvite2) (int fd, struct map_session_data *sd); + void (*cashShopBuyAck) (int fd, struct map_session_data *sd, int itemId, enum CASH_SHOP_BUY_RESULT result); /* Group Search System Update */ void (*pPartyBookingAddFilter) (int fd, struct map_session_data *sd); void (*pPartyBookingSubFilter) (int fd, struct map_session_data *sd); @@ -1587,6 +1610,14 @@ struct clif_interface { int (*pingTimer) (int tid, int64 tick, int id, intptr_t data); int (*pingTimerSub) (struct map_session_data *sd, va_list ap); void (*pResetCooldown) (int fd, struct map_session_data *sd); + void (*loadConfirm) (struct map_session_data *sd); + void (*send_selforarea) (int fd, struct block_list *bl, const void *buf, int len); + void (*OpenRefineryUI) (struct map_session_data *sd); + void (*pAddItemRefineryUI) (int fd, struct map_session_data *sd); + void (*AddItemRefineryUIAck) (struct map_session_data *sd, int item_index, struct s_refine_requirement *req); + void (*pRefineryUIClose) (int fd, struct map_session_data *sd); + void (*pRefineryUIRefine) (int fd, struct map_session_data *sd); + void (*announce_refine_status) (struct map_session_data *sd, int item_id, int refine_level, bool success, enum send_target target); }; #ifdef HERCULES_CORE diff --git a/src/map/instance.c b/src/map/instance.c index 1e83b0b76..e87cc03bb 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -295,13 +295,6 @@ static int instance_add_map(const char *name, int instance_id, bool usebasename, } } - //Mimic questinfo - VECTOR_INIT(map->list[im].qi_data); - VECTOR_ENSURE(map->list[im].qi_data, VECTOR_LENGTH(map->list[m].qi_data), 1); - for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) { - VECTOR_PUSH(map->list[im].qi_data, VECTOR_INDEX(map->list[m].qi_data, i)); - } - map->list[im].m = im; map->list[im].instance_id = instance_id; map->list[im].instance_src_map = m; @@ -518,7 +511,7 @@ static void instance_del_map(int16 m) aFree(map->list[m].zone_mf); } - quest->questinfo_vector_clear(m); + VECTOR_CLEAR(map->list[m].qi_list); // Remove from instance for( i = 0; i < instance->list[map->list[m].instance_id].num_map; i++ ) { diff --git a/src/map/intif.c b/src/map/intif.c index 86bf07bce..e25f56b63 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -2754,16 +2754,21 @@ static void intif_parse_RodexNotifications(int fd) /// 2 - user got Items /// 3 - delete /// 4 - sender Read (returned mail) -static int intif_rodex_updatemail(int64 mail_id, int8 flag) +static int intif_rodex_updatemail(struct map_session_data *sd, int64 mail_id, uint8 opentype, int8 flag) { + nullpo_ret(sd); + if (intif->CheckForCharServer()) return 0; - WFIFOHEAD(inter_fd, 11); + WFIFOHEAD(inter_fd, 20); WFIFOW(inter_fd, 0) = 0x3097; - WFIFOQ(inter_fd, 2) = mail_id; - WFIFOB(inter_fd, 10) = flag; - WFIFOSET(inter_fd, 11); + WFIFOL(inter_fd, 2) = sd->status.account_id; + WFIFOL(inter_fd, 6) = sd->status.char_id; + WFIFOQ(inter_fd, 10) = mail_id; + WFIFOB(inter_fd, 18) = opentype; + WFIFOB(inter_fd, 19) = flag; + WFIFOSET(inter_fd, 20); return 0; } @@ -2855,6 +2860,35 @@ static void intif_parse_RodexCheckName(int fd) clif->rodex_checkname_result(sd, target_char_id, target_class, target_level, name); } +static void intif_parse_GetZenyAck(int fd) +{ + int char_id = RFIFOL(fd, 2); + int64 zeny = RFIFOL(fd, 6); + int64 mail_id = RFIFOQ(fd, 14); + uint8 opentype = RFIFOB(fd, 22); + struct map_session_data *sd = map->charid2sd(char_id); + + if (sd == NULL) // User is not online anymore + return; + rodex->getZenyAck(sd, mail_id, opentype, zeny); +} + +static void intif_parse_GetItemsAck(int fd) +{ + int char_id = RFIFOL(fd, 2); + + struct map_session_data *sd = map->charid2sd(char_id); + if (sd == NULL) // User is not online anymore + return; + + int64 mail_id = RFIFOQ(fd, 6); + uint8 opentype = RFIFOB(fd, 14); + int count = RFIFOB(fd, 15); + struct rodex_item items[RODEX_MAX_ITEM]; + memcpy(&items[0], RFIFOP(fd, 16), sizeof(struct rodex_item) * RODEX_MAX_ITEM); + rodex->getItemsAck(sd, mail_id, opentype, count, &items[0]); +} + //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2972,6 +3006,8 @@ static int intif_parse(int fd) case 0x3896: intif->pRodexHasNew(fd); break; case 0x3897: intif->pRodexSendMail(fd); break; case 0x3898: intif->pRodexCheckName(fd); break; + case 0x3899: intif->pGetZenyAck(fd); break; + case 0x389a: intif->pGetItemsAck(fd); break; // Clan System case 0x3858: intif->pRecvClanMemberAction(fd); break; @@ -3001,7 +3037,7 @@ void intif_defaults(void) -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] 14,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 - -1,-1, 7, 3, 0,-1, 7, 15,18 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] / RoDEX [KirieZ] + -1,-1, 7, 3, 0,-1, 7, 15,18 + NAME_LENGTH, 23, 16 + sizeof(struct rodex_item) * RODEX_MAX_ITEM, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] / RoDEX [KirieZ] }; intif = &intif_s; @@ -3171,6 +3207,8 @@ void intif_defaults(void) intif->pRodexHasNew = intif_parse_RodexNotifications; intif->pRodexSendMail = intif_parse_RodexSendMail; intif->pRodexCheckName = intif_parse_RodexCheckName; + intif->pGetZenyAck = intif_parse_GetZenyAck; + intif->pGetItemsAck = intif_parse_GetItemsAck; /* Clan System */ intif->pRecvClanMemberAction = intif_parse_RecvClanMemberAction; /* Achievement System */ diff --git a/src/map/intif.h b/src/map/intif.h index 425ab1d18..8df669217 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -135,9 +135,11 @@ struct intif_interface { // RoDEX int(*rodex_requestinbox) (int char_id, int account_id, int8 flag, int8 opentype, int64 mail_id); int(*rodex_checkhasnew) (struct map_session_data *sd); - int(*rodex_updatemail) (int64 mail_id, int8 flag); + int(*rodex_updatemail) (struct map_session_data *sd, int64 mail_id, uint8 opentype, int8 flag); int(*rodex_sendmail) (struct rodex_message *msg); int(*rodex_checkname) (struct map_session_data *sd, const char *name); + void (*pGetZenyAck) (int fd); + void (*pGetItemsAck) (int fd); /* Clan System */ int (*clan_kickoffline) (int clan_id, int kick_interval); int (*clan_membercount) (int clan_id, int kick_interval); diff --git a/src/map/itemdb.c b/src/map/itemdb.c index a61bbd008..11d778f8a 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -27,6 +27,7 @@ #include "map/map.h" #include "map/mob.h" // MAX_MOB_DB #include "map/pc.h" // W_MUSICAL, W_WHIP +#include "map/refine.h" #include "map/script.h" // item script processing #include "common/HPM.h" #include "common/conf.h" diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 315787993..e032def0c 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -130,6 +130,7 @@ enum item_itemid { ITEMID_INDIGO_PTS = 6361, ITEMID_YELLOW_WISH_PTS = 6362, ITEMID_LIME_GREEN_PTS = 6363, + ITEMID_BLACKSMITH_BLESSING = 6635, ITEMID_STONE = 7049, ITEMID_FIRE_BOTTLE = 7135, ITEMID_ACID_BOTTLE = 7136, diff --git a/src/map/log.c b/src/map/log.c index efb7fefbc..5bbca02a9 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -106,7 +106,7 @@ static char log_chattype2char(e_log_chat_type type) } /// check if this item should be logged according the settings -static bool should_log_item(int nameid, int amount, int refine, struct item_data *id) +static bool should_log_item(int nameid, int amount, int refine_level, struct item_data *id) { int filter = logs->config.filter; @@ -123,7 +123,7 @@ static bool should_log_item(int nameid, int amount, int refine, struct item_data ( filter&LOG_FILTER_PETITEM && ( id->type == IT_PETEGG || id->type == IT_PETARMOR ) ) || ( filter&LOG_FILTER_PRICE && id->value_buy >= logs->config.price_items_log ) || ( filter&LOG_FILTER_AMOUNT && abs(amount) >= logs->config.amount_items_log ) || - ( filter&LOG_FILTER_REFINE && refine >= logs->config.refine_items_log ) || + ( filter&LOG_FILTER_REFINE && refine_level >= logs->config.refine_items_log ) || ( filter&LOG_FILTER_CHANCE && ( ( id->maxchance != -1 && id->maxchance <= logs->config.rare_items_log ) || id->nameid == ITEMID_EMPERIUM ) ) ) return true; diff --git a/src/map/log.h b/src/map/log.h index db802575d..116d99bfd 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -155,7 +155,7 @@ struct log_interface { char (*picktype2char) (e_log_pick_type type); char (*chattype2char) (e_log_chat_type type); - bool (*should_log_item) (int nameid, int amount, int refine, struct item_data *id); + bool (*should_log_item) (int nameid, int amount, int refine_level, struct item_data *id); }; #ifdef HERCULES_CORE diff --git a/src/map/mail.c b/src/map/mail.c index 0a4b91e34..0a6603a45 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -177,7 +177,7 @@ static int mail_openmail(struct map_session_data *sd) { nullpo_ret(sd); - if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->state.vending || sd->state.buyingstore || sd->state.trading) + if (sd->state.storage_flag != STORAGE_FLAG_CLOSED || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.trading) return 0; clif->mail_window(sd->fd, 0); diff --git a/src/map/map.c b/src/map/map.c index 7d2cc87d4..71eab9286 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -58,6 +58,7 @@ #include "map/storage.h" #include "map/stylist.h" #include "map/rodex.h" +#include "map/refine.h" #include "map/trade.h" #include "map/unit.h" #include "map/achievement.h" @@ -3585,23 +3586,27 @@ static void map_zone_db_clear(void) } static void map_clean(int i) { - int v; Assert_retv(i >= 0 && i < map->count); - if(map->list[i].cell && map->list[i].cell != (struct mapcell *)0xdeadbeaf) aFree(map->list[i].cell); - if(map->list[i].block) aFree(map->list[i].block); - if(map->list[i].block_mob) aFree(map->list[i].block_mob); - if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random] - int j; - if(map->list[i].mob_delete_timer != INVALID_TIMER) + if (map->list[i].cell && map->list[i].cell != (struct mapcell *)0xdeadbeaf) + aFree(map->list[i].cell); + if (map->list[i].block) + aFree(map->list[i].block); + if (map->list[i].block_mob) + aFree(map->list[i].block_mob); + + if (battle_config.dynamic_mobs != 0) { //Dynamic mobs flag by [random] + if (map->list[i].mob_delete_timer != INVALID_TIMER) timer->delete(map->list[i].mob_delete_timer, map->removemobs_timer); - for (j=0; j<MAX_MOB_LIST_PER_MAP; j++) - if (map->list[i].moblist[j]) aFree(map->list[i].moblist[j]); + for (int j = 0; j < MAX_MOB_LIST_PER_MAP; j++) { + if (map->list[i].moblist[j] != NULL) + aFree(map->list[i].moblist[j]); + } } - if( map->list[i].unit_count ) { - if( map->list[i].units ) { - for(v = 0; v < map->list[i].unit_count; v++) { + if (map->list[i].unit_count != 0) { + if (map->list[i].units != NULL) { + for (int v = 0; v < map->list[i].unit_count; v++) { aFree(map->list[i].units[v]); } aFree(map->list[i].units); @@ -3610,98 +3615,44 @@ static void map_clean(int i) map->list[i].unit_count = 0; } - if( map->list[i].skill_count ) { - if( map->list[i].skills ) { - for(v = 0; v < map->list[i].skill_count; v++) { - aFree(map->list[i].skills[v]); - } + if (map->list[i].skill_count != 0) { + if (map->list[i].skills != NULL) { + for (int v = 0; v < map->list[i].skill_count; v++) { + aFree(map->list[i].skills[v]); + } aFree(map->list[i].skills); map->list[i].skills = NULL; } map->list[i].skill_count = 0; } - if( map->list[i].zone_mf_count ) { - if( map->list[i].zone_mf ) { - for(v = 0; v < map->list[i].zone_mf_count; v++) { - aFree(map->list[i].zone_mf[v]); - } + if (map->list[i].zone_mf_count != 0) { + if (map->list[i].zone_mf != NULL) { + for (int v = 0; v < map->list[i].zone_mf_count; v++) { + aFree(map->list[i].zone_mf[v]); + } aFree(map->list[i].zone_mf); map->list[i].zone_mf = NULL; } map->list[i].zone_mf_count = 0; } - if( map->list[i].channel ) + if (map->list[i].drop_list_count != 0) + map->list[i].drop_list_count = 0; + if (map->list[i].drop_list != NULL) + aFree(map->list[i].drop_list); + + if (map->list[i].channel != NULL) channel->delete(map->list[i].channel); + + VECTOR_CLEAR(map->list[i].qi_list); + HPM->data_store_destroy(&map->list[i].hdata); } static void do_final_maps(void) { - int i, v = 0; - - for( i = 0; i < map->count; i++ ) { - - if(map->list[i].cell && map->list[i].cell != (struct mapcell *)0xdeadbeaf ) aFree(map->list[i].cell); - if(map->list[i].block) aFree(map->list[i].block); - if(map->list[i].block_mob) aFree(map->list[i].block_mob); - - if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random] - int j; - if(map->list[i].mob_delete_timer != INVALID_TIMER) - timer->delete(map->list[i].mob_delete_timer, map->removemobs_timer); - for (j=0; j<MAX_MOB_LIST_PER_MAP; j++) - if (map->list[i].moblist[j]) aFree(map->list[i].moblist[j]); - } - - if( map->list[i].unit_count ) { - if( map->list[i].units ) { - for(v = 0; v < map->list[i].unit_count; v++) { - aFree(map->list[i].units[v]); - } - aFree(map->list[i].units); - map->list[i].units = NULL; - } - map->list[i].unit_count = 0; - } - - if( map->list[i].skill_count ) { - if( map->list[i].skills ) { - for(v = 0; v < map->list[i].skill_count; v++) { - aFree(map->list[i].skills[v]); - } - aFree(map->list[i].skills); - map->list[i].skills = NULL; - } - map->list[i].skill_count = 0; - } - - if( map->list[i].zone_mf_count ) { - if( map->list[i].zone_mf ) { - for(v = 0; v < map->list[i].zone_mf_count; v++) { - aFree(map->list[i].zone_mf[v]); - } - aFree(map->list[i].zone_mf); - map->list[i].zone_mf = NULL; - } - map->list[i].zone_mf_count = 0; - } - - if( map->list[i].drop_list_count ) { - map->list[i].drop_list_count = 0; - } - if( map->list[i].drop_list != NULL ) - aFree(map->list[i].drop_list); - - if( map->list[i].channel ) - channel->delete(map->list[i].channel); - - quest->questinfo_vector_clear(i); - - HPM->data_store_destroy(&map->list[i].hdata); - } - + for (int i = 0; i < map->count; i++) + map->clean(i); map->zone_db_clear(); - } static void map_zonedb_reload(void) @@ -3792,7 +3743,8 @@ static void map_flags_init(void) map->list[i].short_damage_rate = 100; map->list[i].long_damage_rate = 100; - VECTOR_INIT(map->list[i].qi_data); + VECTOR_CLEAR(map->list[i].qi_list); + VECTOR_INIT(map->list[i].qi_list); } } @@ -6028,28 +5980,30 @@ static int map_get_new_bonus_id(void) return map->bonus_id++; } -static void map_add_questinfo(int m, struct questinfo *qi) +static bool map_add_questinfo(int m, struct npc_data *nd) { - nullpo_retv(qi); - Assert_retv(m >= 0 && m < map->count); + nullpo_retr(false, nd); + Assert_retr(false, m >= 0 && m < map->count); - VECTOR_ENSURE(map->list[m].qi_data, 1, 1); - VECTOR_PUSH(map->list[m].qi_data, *qi); + if (&VECTOR_LAST(map->list[m].qi_list) == nd) + return false; + + VECTOR_ENSURE(map->list[m].qi_list, 1, 1); + VECTOR_PUSH(map->list[m].qi_list, *nd); + return true; } static bool map_remove_questinfo(int m, struct npc_data *nd) { - unsigned short i; nullpo_retr(false, nd); Assert_retr(false, m >= 0 && m < map->count); - for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) { - struct questinfo *qi_data = &VECTOR_INDEX(map->list[m].qi_data, i); - if (qi_data->nd == nd) { - VECTOR_ERASE(map->list[m].qi_data, i); - return true; - } + int i; + ARR_FIND(0, VECTOR_LENGTH(map->list[m].qi_list), i, &VECTOR_INDEX(map->list[m].qi_list, i) == nd); + if (i != VECTOR_LENGTH(map->list[m].qi_list)) { + VECTOR_ERASE(map->list[m].qi_list, i); + return true; } return false; } @@ -6190,6 +6144,7 @@ int do_final(void) atcommand->final_msg(); skill->final(); status->final(); + refine->final(); unit->final(); bg->final(); duel->final(); @@ -6407,6 +6362,7 @@ static void map_load_defaults(void) npc_chat_defaults(); rodex_defaults(); stylist_defaults(); + refine_defaults(); } /** * --run-once handler @@ -6711,6 +6667,7 @@ int do_init(int argc, char *argv[]) map->read_zone_db();/* read after item and skill initialization */ mob->init(minimal); pc->init(minimal); + refine->init(minimal); status->init(minimal); party->init(minimal); guild->init(minimal); diff --git a/src/map/map.h b/src/map/map.h index 1f70680e8..0330eccc4 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -705,41 +705,6 @@ struct map_drop_list { int drop_per; }; -struct questinfo_qreq { - int id; - int state; -}; - -struct questinfo_itemreq { - int nameid; - int min; - int max; -}; - -struct questinfo { - struct npc_data *nd; - unsigned short icon; - unsigned char color; - bool hasJob; - unsigned int job;/* perhaps a mapid mask would be most flexible? */ - bool sex_enabled; - int sex; - struct { - int min; - int max; - } base_level; - struct { - int min; - int max; - } job_level; - VECTOR_DECL(struct questinfo_itemreq) items; - struct s_homunculus homunculus; - int homunculus_type; - VECTOR_DECL(struct questinfo_qreq) quest_requirement; - int mercenary_class; -}; - - struct map_data { char name[MAP_NAME_LENGTH]; uint16 index; // The map index used by the mapindex* functions. @@ -877,8 +842,8 @@ struct map_data { int len; } cell_buf; - /* ShowEvent Data Cache */ - VECTOR_DECL(struct questinfo) qi_data; + /* questinfo entries list */ + VECTOR_DECL(struct npc_data) qi_list; /* speeds up clif_updatestatus processing by causing hpmeter to run only when someone with the permission can view it */ unsigned short hpmeter_visible; @@ -1312,7 +1277,7 @@ END_ZEROED_BLOCK; int (*abort_sub) (struct map_session_data *sd, va_list ap); void (*update_cell_bl) (struct block_list *bl, bool increase); int (*get_new_bonus_id) (void); - void (*add_questinfo) (int m, struct questinfo *qi); + bool (*add_questinfo) (int m, struct npc_data *nd); bool (*remove_questinfo) (int m, struct npc_data *nd); struct map_zone_data *(*merge_zone) (struct map_zone_data *main, struct map_zone_data *other); void (*zone_clear_single) (struct map_zone_data *zone); diff --git a/src/map/messages_main.h b/src/map/messages_main.h index 52c953ac8..802621eb2 100644 --- a/src/map/messages_main.h +++ b/src/map/messages_main.h @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20190403 +Latest version: 20190418 */ enum clif_messages { @@ -21589,6 +21589,32 @@ Please try again in a moment. */ MSG_ID_E13 = 0xe13, #endif +#if PACKETVER >= 20190417 +/*20190417 to latest +리딩 스펠북으로 저장된 마법이 없습니다. +*/ + MSG_ID_E14 = 0xe14, +/*20190417 to latest +이동 시 사용한 아이템은 재교환이 불가능합니다. +*/ + MSG_ID_E15 = 0xe15, +/*20190417 to latest +자유이동권을 구매 하였습니다 +*/ + MSG_ID_E16 = 0xe16, +/*20190417 to latest +자유이동권 사용 중, 관련 아이템을 소모하지 않습니다. +*/ + MSG_ID_E17 = 0xe17, +/*20190417 to latest +적용 +*/ + MSG_ID_E18 = 0xe18, +/*20190417 to latest +중지 +*/ + MSG_ID_E19 = 0xe19, +#endif }; #endif /* MAP_MESSAGES_MAIN_H */ diff --git a/src/map/messages_re.h b/src/map/messages_re.h index 3823720ad..b81476f9c 100644 --- a/src/map/messages_re.h +++ b/src/map/messages_re.h @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20190403 +Latest version: 20190418 */ enum clif_messages { @@ -21068,6 +21068,32 @@ Please try again in a moment. */ MSG_ID_E13 = 0xe13, #endif +#if PACKETVER >= 20190417 +/*20190417 to latest +리딩 스펠북으로 저장된 마법이 없습니다. +*/ + MSG_ID_E14 = 0xe14, +/*20190417 to latest +이동 시 사용한 아이템은 재교환이 불가능합니다. +*/ + MSG_ID_E15 = 0xe15, +/*20190417 to latest +자유이동권을 구매 하였습니다 +*/ + MSG_ID_E16 = 0xe16, +/*20190417 to latest +자유이동권 사용 중, 관련 아이템을 소모하지 않습니다. +*/ + MSG_ID_E17 = 0xe17, +/*20190417 to latest +적용 +*/ + MSG_ID_E18 = 0xe18, +/*20190417 to latest +중지 +*/ + MSG_ID_E19 = 0xe19, +#endif }; #endif /* MAP_MESSAGES_RE_H */ diff --git a/src/map/messages_zero.h b/src/map/messages_zero.h index 90c7e8895..ab48ae4ab 100644 --- a/src/map/messages_zero.h +++ b/src/map/messages_zero.h @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20190403 +Latest version: 20190502 */ enum clif_messages { @@ -17674,6 +17674,116 @@ Please try again in a moment. */ MSG_ID_E13 = 0xe13, #endif +#if PACKETVER >= 20190424 +/*20190424 to latest +리딩 스펠북으로 저장된 마법이 없습니다. +*/ + MSG_ID_E14 = 0xe14, +/*20190424 to latest +이동 시 사용한 아이템은 재교환이 불가능합니다. +*/ + MSG_ID_E15 = 0xe15, +/*20190424 to latest +자유이동권을 구매 하였습니다 +*/ + MSG_ID_E16 = 0xe16, +/*20190424 to latest +자유이동권 사용 중, 관련 아이템을 소모하지 않습니다. +*/ + MSG_ID_E17 = 0xe17, +/*20190424 to latest +적용 +*/ + MSG_ID_E18 = 0xe18, +/*20190424 to latest +중지 +*/ + MSG_ID_E19 = 0xe19, +/*20190424 to latest +자유이동권 +*/ + MSG_ID_E1A = 0xe1a, +/*20190424 to latest +%d시간 이용, %s %d개 +*/ + MSG_ID_E1B = 0xe1b, +/*20190424 to latest +자유이용권 사용중 +*/ + MSG_ID_E1C = 0xe1c, +/*20190424 to latest +특성 스테이터스 +*/ + MSG_ID_E1D = 0xe1d, +/*20190424 to latest +파워 파라메터 +^cc0000물리 공격력, 특성 공격력^ffffff 증가 +*/ + MSG_ID_E1E = 0xe1e, +/*20190424 to latest +스테미나 파라메터 +^cc0000물리 저항력^ffffff 증가 +*/ + MSG_ID_E1F = 0xe1f, +/*20190424 to latest +위즈덤 파라메터 +^cc0000마법 저항력^ffffff 증가 +*/ + MSG_ID_E20 = 0xe20, +/*20190424 to latest +스펠 파라메터 +^cc0000마법 공격력, 마법 공격력^ffffff 증가 +*/ + MSG_ID_E21 = 0xe21, +/*20190424 to latest +컨센트레이션 파라메터 +^cc0000명중률, 회피율, 특성 물리/마법 공격력^ffffff 증가 +*/ + MSG_ID_E22 = 0xe22, +/*20190424 to latest +크리에이티브 파라메터 +^cc0000특성 힐 회복량, 크리티컬 데미지 비율^ffffff 증가 +*/ + MSG_ID_E23 = 0xe23, +/*20190424 to latest +특성 물리 공격력 +*/ + MSG_ID_E24 = 0xe24, +/*20190424 to latest +특성 마법 공격력 +*/ + MSG_ID_E25 = 0xe25, +/*20190424 to latest +물리 저항력 +*/ + MSG_ID_E26 = 0xe26, +/*20190424 to latest +마법 저항력 +*/ + MSG_ID_E27 = 0xe27, +/*20190424 to latest +특성 힐 회복량 +*/ + MSG_ID_E28 = 0xe28, +/*20190424 to latest +크리티컬 데미지 비율 +*/ + MSG_ID_E29 = 0xe29, +/*20190424 to latest +특성 파라메터 레벨업에 사용되는 포인트 +*/ + MSG_ID_E2A = 0xe2a, +#endif +#if PACKETVER >= 20190502 +/*20190502 to latest +J.Lv +*/ + MSG_ID_E2B = 0xe2b, +/*20190502 to latest +AP +*/ + MSG_ID_E2C = 0xe2c, +#endif }; #endif /* MAP_MESSAGES_ZERO_H */ diff --git a/src/map/mob.c b/src/map/mob.c index fed4d6c60..aa938a1e7 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -1900,15 +1900,53 @@ static int mob_ai_hard(int tid, int64 tick, int id, intptr_t data) return 0; } +/** + * Adds random options of a given options drop group into item. + * + * @param item : item receiving random options + * @param options : Random Option Drop Group to be used + */ +static void mob_setdropitem_options(struct item *item, struct optdrop_group *options) +{ + nullpo_retv(item); + nullpo_retv(options); + + for (int i = 0; i < options->optslot_count; i++) { + if (rnd() % 10000 >= options->optslot_rate[i]) + continue; + + // count avoids a too long loop that would cause lag. + // if after option_drop_max_loop full iterations (running through all possibilities) + // it still fails to pick one, it'll stop at one random index in the next iteration + int count = battle_config.option_drop_max_loop * options->optslot[i].option_count + (rnd() % options->optslot[i].option_count); + int idx = 0; + while (count > 0 && rnd() % 10000 >= options->optslot[i].options[idx].rate) { + idx = (idx + 1) % options->optslot[i].option_count; + --count; + } + + item->option[i].index = options->optslot[i].options[idx].id; + + int min = options->optslot[i].options[idx].min; + int max = options->optslot[i].options[idx].max; + item->option[i].value = min + (rnd() % (max - min + 1)); + } +} + /*========================================== * Initializes the delay drop structure for mob-dropped items. *------------------------------------------*/ -static struct item_drop *mob_setdropitem(int nameid, int qty, struct item_data *data) +static struct item_drop *mob_setdropitem(int nameid, struct optdrop_group *options, int qty, struct item_data *data) { struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); drop->item_data.nameid = nameid; drop->item_data.amount = qty; drop->item_data.identify = data ? itemdb->isidentified2(data) : itemdb->isidentified(nameid); + + // Set item options [KirieZ] + if (options != NULL) + mob->setdropitem_options(&drop->item_data, options); + drop->showdropeffect = true; drop->next = NULL; return drop; @@ -2521,7 +2559,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) continue; } - ditem = mob->setdropitem(md->db->dropitem[i].nameid, 1, it); + ditem = mob->setdropitem(md->db->dropitem[i].nameid, md->db->dropitem[i].options, 1, it); // Official Drop Announce [Jedzkie] if (mvp_sd != NULL) { @@ -2538,7 +2576,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) // Ore Discovery [Celest] if (sd == mvp_sd && pc->checkskill(sd,BS_FINDINGORE) > 0) { if( (temp = itemdb->chain_item(itemdb->chain_cache[ECC_ORE],&i)) ) { - ditem = mob->setdropitem(temp, 1, NULL); + ditem = mob->setdropitem(temp, NULL, 1, NULL); mob->item_drop(md, dlist, ditem, 0, i, homkillonly); } } @@ -2570,7 +2608,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) continue; itemid = (!sd->add_drop[i].is_group) ? sd->add_drop[i].id : itemdb->chain_item(sd->add_drop[i].id, &drop_rate); if( itemid ) - mob->item_drop(md, dlist, mob->setdropitem(itemid,1,NULL), 0, drop_rate, homkillonly); + mob->item_drop(md, dlist, mob->setdropitem(itemid, NULL, 1, NULL), 0, drop_rate, homkillonly); } } @@ -2629,10 +2667,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) if (!(map->list[m].flag.nomvploot || type&1)) { /* pose them randomly in the list -- so on 100% drop servers it wont always drop the same item */ - struct { - int nameid; - int p; - } mdrop[MAX_MVP_DROP] = { { 0 } }; + struct mob_drop mdrop[MAX_MVP_DROP] = { { 0 } }; for (i = 0; i < MAX_MVP_DROP; i++) { int rpos; @@ -2644,6 +2679,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) mdrop[rpos].nameid = md->db->mvpitem[i].nameid; mdrop[rpos].p = md->db->mvpitem[i].p; + mdrop[rpos].options = md->db->mvpitem[i].options; } for (i = 0; i < MAX_MVP_DROP; i++) { @@ -2663,6 +2699,8 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) item.nameid = mdrop[i].nameid; item.identify = itemdb->isidentified2(data); + if (mdrop[i].options != NULL) + mob->setdropitem_options(&item, mdrop[i].options); clif->mvp_item(mvp_sd, item.nameid); log_mvp[0] = item.nameid; @@ -3861,6 +3899,212 @@ static inline int mob_parse_dbrow_cap_value(int class_, int min, int max, int va } /** + * Reads one possible option for a option slot in a option drop group + * @param option : Libconfig entry + * @param entry : memory db entry for current slot + * @param idx : index of entry where this option should be inserted at + * @param calc_rate : if rates should be recalculated after reading all entries + * @param slot : option group slot being read (for messages) + * @param group : option group being read (for messages) + * @return true if it successfully read the entry, false otherwise + */ +static bool mob_read_optdrops_option(struct config_setting_t *option, struct optdrop_group_optslot *entry, int *idx, bool *calc_rate, int slot, const char *group) +{ + nullpo_retr(false, option); + nullpo_retr(false, entry); + nullpo_retr(false, idx); + nullpo_retr(false, calc_rate); + nullpo_retr(false, group); + + const char *name = config_setting_name(option); + int opt_id; + + if (strncmp(name, "Rate", 4) == 0) + return true; + + if (script->get_constant(name, &opt_id) == false) { + ShowWarning("mob_read_optdrops_option: Invalid option \"%s\" for option slot %d of %s group, skipping.\n", name, slot, group); + return false; + } + + int min = 0, max = 0, opt_rate = 0; + if (config_setting_is_number(option)) { + // OptionName: value + min = libconfig->setting_get_int(option); + } else if (config_setting_is_array(option)) { + // OptionName: [min, max] + // OptionName: [min, max, rate] + int slen = libconfig->setting_length(option); + + if (slen >= 2) { + // [min, max,...] + min = libconfig->setting_get_int_elem(option, 0); + max = libconfig->setting_get_int_elem(option, 1); + } + + if (slen == 3) { + // [min, max, rate] + opt_rate = libconfig->setting_get_int_elem(option, 2); + } + } else { + ShowWarning("mob_read_optdrops_option: Invalid value \"%s\" for option slot %d of %s group, skipping.\n", name, slot, group); + return false; + } + + if (max < min) + max = min; + + entry->options[*idx].id = opt_id; + entry->options[*idx].min = min; + entry->options[*idx].max = max; + entry->options[*idx].rate = opt_rate; + + if (entry->options[*idx].rate == 0) + *calc_rate = true; + + (*idx)++; + + return true; +} + +/** + * Reads the settings for one random option slot of a random option drop group. + * @param optslot : The slot entry from config file + * @param n : slot index + * @param group_id : Group index + * @param group : group name (used in messages) + * @return true if it succesfully read, false otherwise + */ +static bool mob_read_optdrops_optslot(struct config_setting_t *optslot, int n, int group_id, const char *group) +{ + nullpo_retr(false, optslot); + nullpo_retr(false, group); + Assert_retr(false, group_id >= 0 && group_id < mob->opt_drop_groups_count); + Assert_retr(false, n >= 0 && n < MAX_ITEM_OPTIONS); + + // Structure: + // { + // Rate: chance of option 1 (int) + // OptionName1: value + // OptionName2: [min, max] + // OptionName3: [min, max, rate] + // .... + // } + + int drop_rate; // The rate for this option to be dropped (Rate field) + if (libconfig->setting_lookup_int(optslot, "Rate", &drop_rate) == CONFIG_FALSE) { + ShowWarning("mob_read_optdrops_optslot: Missing option %d rate in group %s, skipping.\n", n, group); + return false; + } + + int count = libconfig->setting_length(optslot); + if (count <= 1) { // 1 = Rate + ShowWarning("mob_read_optdrops_optslot: Option %d of %s group doesn't contain any possible options, skipping.\n", n, group); + return false; + } + + struct optdrop_group_optslot *entry = &(mob->opt_drop_groups[group_id].optslot[n]); + entry->options = aCalloc(sizeof(struct optdrop_group_option), count); + + int idx = 0; + int i = 0; + struct config_setting_t *opt = NULL; + bool calc_rate = false; + while (i < count && (opt = libconfig->setting_get_elem(optslot, i)) != NULL) { + ++i; + mob->read_optdrops_option(opt, entry, &idx, &calc_rate, n, group); + } + entry->option_count = idx; + mob->opt_drop_groups[group_id].optslot_count++; + mob->opt_drop_groups[group_id].optslot_rate[n] = drop_rate; + + // If there're empty rates, calculate them + if (calc_rate == true) { + for (int j = 0; j < idx; ++j) { + if (entry->options[j].rate == 0) + entry->options[j].rate = 10000 / idx; + } + } + + return true; +} + +/** + * Reads one random option drop group. + * @param group : Drop Group entry from config file + * @param n : group index + * @return true if it successfuly read, false otherwise + */ +static bool mob_read_optdrops_group(struct config_setting_t *group, int n) +{ + /* Structure: + <Group Name>: ( + { <Option 1 drop data> }, + { <Option 2 drop data> }, + ... // Up to MAX_ITEM_OPTIONS + ) + */ + nullpo_retr(false, group); + + const char *group_name = config_setting_name(group); + + if (group_name == NULL || *group_name == '\0') { + ShowWarning("mob_read_optdrops_group: Invalid name for random option drop group, skipping group %d...\n", n); + return false; + } + + script->set_constant2(group_name, n, false, false); + + int i = 0; + struct config_setting_t *drop_data = NULL; + while (i < MAX_ITEM_OPTIONS && (drop_data = libconfig->setting_get_elem(group, i)) != NULL) { + mob->read_optdrops_optslot(drop_data, i, n, group_name); + i++; + } + + return true; +} + +/** + * Reads random option drop groups database. + */ +static bool mob_read_optdrops_db(void) +{ + const char *filename = "option_drop_groups.conf"; // FIXME hardcoded name + + char filepath[256]; + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, filename); + + struct config_t option_groups; + if (libconfig->load_file(&option_groups, filepath) == CONFIG_FALSE) { + ShowError("Failed to load option drop groups\n"); + return false; + } + + struct config_setting_t *its = libconfig->lookup(&option_groups, "option_drop_group_db"); + struct config_setting_t *groups = NULL; + + int i = 0; + if (its != NULL && (groups = libconfig->setting_get_elem(its, 0)) != NULL) { + int count = libconfig->setting_length(groups); + mob->opt_drop_groups = aCalloc(sizeof(struct optdrop_group), count); + mob->opt_drop_groups_count = count; // maximum size (used by assertions) + + struct config_setting_t *group = NULL; + while ((group = libconfig->setting_get_elem(groups, i)) != NULL) { + mob->read_optdrops_group(group, i); + i++; + } + mob->opt_drop_groups_count = i; // number of entries used (should be the same amount) + } + + libconfig->destroy(&option_groups); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, filepath); + return true; +} + +/** * Processes the stats for a mob database entry. * * @param[in,out] entry The destination mob_db entry, already initialized @@ -3941,6 +4185,51 @@ static uint32 mob_read_db_mode_sub(struct mob_db *entry, struct config_setting_t } /** + * Process an entry of mob/mvp drops that contains a random option drop group. + * + * @param entry : mob db entry being read (used in error messages) + * @param item_name : AegisName of the item in this entry (used in error messages) + * @param drop : drop data entry + * @param drop_rate : used to return the entry drop_rate + * @returns a reference to the opt_drop_group to be used when creating this item drop + */ +static struct optdrop_group *mob_read_db_drops_option(struct mob_db *entry, const char *item_name, struct config_setting_t *drop, int *drop_rate) +{ + nullpo_retr(NULL, entry); + nullpo_retr(NULL, item_name); + nullpo_retr(NULL, drop); + nullpo_retr(NULL, drop_rate); + + // (Drop Rate, "Option Group") + if (!config_setting_is_list(drop) || config_setting_length(drop) != 2) { + ShowError("mob_read_db_optdrops: Invalid format for option drop group on item \"%s\" in monster %d, skipping.\n", item_name, entry->mob_id); + return NULL; + } + + int i32; + if (mob->get_const(libconfig->setting_get_elem(drop, 0), &i32) && i32 >= 0) + *drop_rate = i32; + + const char *group_name = libconfig->setting_get_string_elem(drop, 1); + if (group_name == NULL || *group_name == '\0') { + ShowError("mob_read_db_optdrops: Missing option drop group name on item \"%s\" in monster %d, skipping.\n", item_name, entry->mob_id); + return NULL; + } + + int opt_id; + if (script->get_constant(group_name, &opt_id) == false) { + ShowError("mob_read_db_optdrops: Invalid option drop group \"%s\" on item \"%s\" in monster %d, does this group really exists? Skipping...\n", group_name, item_name, entry->mob_id); + return NULL; + } + if (opt_id < 0 || opt_id >= mob->opt_drop_groups_count) { + ShowError("mob_read_db_optdrops: Invalid option drop group \"%s\" index \"%d\" on item \"%s\" in monster %d, does this group really exists? Skipping...\n", group_name, opt_id, item_name, entry->mob_id); + return NULL; + } + + return &mob->opt_drop_groups[opt_id]; +} + +/** * Processes the MVP drops for a mob_db entry. * * @param[in,out] entry The destination mob_db entry, already initialized @@ -3965,9 +4254,18 @@ static void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct config_setting i++; continue; } - if (mob->get_const(drop, &i32) && i32 >= 0) { - value = i32; + + struct optdrop_group *drop_option = NULL; + if (config_setting_is_number(drop)) { + // Setting is a number, item doesn't contain options + if (mob->get_const(drop, &i32) && i32 >= 0) { + value = i32; + } + } else { + // (Drop Rate, "Opt Drop Group") + drop_option = mob->read_db_drops_option(entry, name, drop, &value); } + if (value <= 0) { ShowWarning("mob_read_db: wrong drop chance %d for mvp drop item %s in monster %d\n", value, name, entry->mob_id); i++; @@ -3981,6 +4279,7 @@ static void mob_read_db_mvpdrops_sub(struct mob_db *entry, struct config_setting } mob->item_dropratio_adjust(entry->mvpitem[idx].nameid, entry->mob_id, &rate_adjust); entry->mvpitem[idx].p = mob->drop_adjust(value, rate_adjust, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); + entry->mvpitem[idx].options = drop_option; //calculate and store Max available drop chance of the MVP item if (entry->mvpitem[idx].p) { @@ -4024,9 +4323,18 @@ static void mob_read_db_drops_sub(struct mob_db *entry, struct config_setting_t i++; continue; } - if (mob->get_const(drop, &i32) && i32 >= 0) { - value = i32; + + struct optdrop_group *drop_option = NULL; + if (config_setting_is_number(drop)) { + // Setting is a number, item doesn't contain options + if (mob->get_const(drop, &i32) && i32 >= 0) { + value = i32; + } + } else { + // (Drop Rate, "Opt Drop Group") + drop_option = mob->read_db_drops_option(entry, name, drop, &value); } + if (value <= 0) { ShowWarning("mob_read_db: wrong drop chance %d for drop item %s in monster %d\n", value, name, entry->mob_id); i++; @@ -4034,6 +4342,7 @@ static void mob_read_db_drops_sub(struct mob_db *entry, struct config_setting_t } entry->dropitem[idx].nameid = id->nameid; + entry->dropitem[idx].options = drop_option; if (!entry->dropitem[idx].nameid) { entry->dropitem[idx].p = 0; //No drop. i++; @@ -4301,6 +4610,8 @@ static int mob_read_db_sub(struct config_setting_t *mobt, int n, const char *sou * } * Drops: { * AegisName: chance + * // or + * AegisName: (chance, "Option Drop Group") * ... * } */ @@ -5213,6 +5524,7 @@ static void mob_load(bool minimal) return; } sv->readdb(map->db_path, "mob_item_ratio.txt", ',', 2, 2+MAX_ITEMRATIO_MOBS, -1, mob->readdb_itemratio); // must be read before mobdb + mob->read_optdrops_db(); mob->readchatdb(); mob->readdb(); mob->readskilldb(); @@ -5254,6 +5566,8 @@ static void mob_reload(void) } mob->item_drop_ratio_other_db->clear(mob->item_drop_ratio_other_db, mob->final_ratio_sub); + mob->destroy_drop_groups(); + mob->load(false); } @@ -5309,6 +5623,22 @@ static void mob_destroy_mob_db(int index) mob->db_data[index] = NULL; } +/** + * Unloads option drop group database + */ +static void mob_destroy_drop_groups(void) +{ + for (int i = 0; i < mob->opt_drop_groups_count; i++) { + struct optdrop_group *group = &mob->opt_drop_groups[i]; + + for (int j = 0; j < group->optslot_count; j++) { + aFree(group->optslot[j].options); + } + } + + aFree(mob->opt_drop_groups); +} + /*========================================== * Clean memory usage. *------------------------------------------*/ @@ -5327,6 +5657,7 @@ static int do_final_mob(void) mob->destroy_mob_db(i); } } + mob->destroy_drop_groups(); for (i = 0; i <= MAX_MOB_CHAT; i++) { if (mob->chat_db[i] != NULL) @@ -5443,6 +5774,7 @@ void mob_defaults(void) mob->ai_sub_lazy = mob_ai_sub_lazy; mob->ai_lazy = mob_ai_lazy; mob->ai_hard = mob_ai_hard; + mob->setdropitem_options = mob_setdropitem_options; mob->setdropitem = mob_setdropitem; mob->setlootitem = mob_setlootitem; mob->delay_item_drop = mob_delay_item_drop; @@ -5476,6 +5808,10 @@ void mob_defaults(void) mob->clone_delete = mob_clone_delete; mob->drop_adjust = mob_drop_adjust; mob->item_dropratio_adjust = item_dropratio_adjust; + mob->read_optdrops_option = mob_read_optdrops_option; + mob->read_optdrops_optslot = mob_read_optdrops_optslot; + mob->read_optdrops_group = mob_read_optdrops_group; + mob->read_optdrops_db = mob_read_optdrops_db; mob->lookup_const = mob_lookup_const; mob->get_const = mob_get_const; mob->db_validate_entry = mob_db_validate_entry; @@ -5486,6 +5822,7 @@ void mob_defaults(void) mob->read_db_drops_sub = mob_read_db_drops_sub; mob->read_db_mvpdrops_sub = mob_read_db_mvpdrops_sub; mob->read_db_mode_sub = mob_read_db_mode_sub; + mob->read_db_drops_option = mob_read_db_drops_option; mob->read_db_stats_sub = mob_read_db_stats_sub; mob->name_constants = mob_name_constants; mob->readdb_mobavail = mob_readdb_mobavail; @@ -5501,6 +5838,7 @@ void mob_defaults(void) mob->final_ratio_sub = mob_final_ratio_sub; mob->clear_spawninfo = mob_clear_spawninfo; mob->destroy_mob_db = mob_destroy_mob_db; + mob->destroy_drop_groups = mob_destroy_drop_groups; mob->skill_db_libconfig = mob_skill_db_libconfig; mob->skill_db_libconfig_sub = mob_skill_db_libconfig_sub; mob->skill_db_libconfig_sub_skill = mob_skill_db_libconfig_sub_skill; diff --git a/src/map/mob.h b/src/map/mob.h index cdfc07c7d..b63efd272 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -152,6 +152,45 @@ struct spawn_info { unsigned short qty; }; +/** + * Information of one possible option that will fill + * an option slot (see optdrop_group_optslot) + */ +struct optdrop_group_option { + int id; //< Option ID + int min; //< Minimun value when this option drops + int max; //< Maximun value when this option drops + int rate; //< Chance of dropping this option +}; + +/** + * Information of possible options that will fill + * one option slot + */ +struct optdrop_group_optslot { + int option_count; //< Number of options in *options + struct optdrop_group_option *options; //< Array of possible options +}; + +/** + * A group of options to be random picked when + * dropping an item + */ +struct optdrop_group { + int optslot_count; //< How many option slots are configured by this group + int optslot_rate[MAX_ITEM_OPTIONS]; //< The rate to fill each of the configured slots + struct optdrop_group_optslot optslot[MAX_ITEM_OPTIONS]; //< Details of the options that will go in each slot +}; + +/** + * Stores data related to a monster drop (normal or mvp drop) + */ +struct mob_drop { + int nameid; //< Item ID + int p; //< Drop chance + struct optdrop_group *options; //< Option Drop Group associated with this drop (NULL if none) +}; + struct mob_db { int mob_id; char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH]; @@ -160,8 +199,8 @@ struct mob_db { short range2,range3; short race2; // celest unsigned short lv; - struct { int nameid,p; } dropitem[MAX_MOB_DROP]; - struct { int nameid,p; } mvpitem[MAX_MVP_DROP]; + struct mob_drop dropitem[MAX_MOB_DROP]; + struct mob_drop mvpitem[MAX_MVP_DROP]; struct status_data status; struct view_data vd; unsigned int option; @@ -436,6 +475,9 @@ struct mob_interface { struct mob_db *dummy; //Dummy mob to be returned when a non-existant one is requested. // Dynamic mob chat database struct mob_chat *chat_db[MAX_MOB_CHAT + 1]; + // Random Option Drop groups + struct optdrop_group *opt_drop_groups; + int opt_drop_groups_count; // Defines the Manuk/Splendide/Mora mob groups for the status reductions [Epoque & Frost] int manuk[8]; int splendide[5]; @@ -494,7 +536,8 @@ struct mob_interface { int (*ai_sub_lazy) (struct mob_data *md, va_list args); int (*ai_lazy) (int tid, int64 tick, int id, intptr_t data); int (*ai_hard) (int tid, int64 tick, int id, intptr_t data); - struct item_drop* (*setdropitem) (int nameid, int qty, struct item_data *data); + void (*setdropitem_options) (struct item *item, struct optdrop_group *options); + struct item_drop* (*setdropitem) (int nameid, struct optdrop_group *options, int qty, struct item_data *data); struct item_drop* (*setlootitem) (struct item *item); int (*delay_item_drop) (int tid, int64 tick, int id, intptr_t data); void (*item_drop) (struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate, unsigned short flag); @@ -527,6 +570,10 @@ struct mob_interface { int (*clone_delete) (struct mob_data *md); unsigned int (*drop_adjust) (int baserate, int rate_adjust, unsigned short rate_min, unsigned short rate_max); void (*item_dropratio_adjust) (int nameid, int mob_id, int *rate_adjust); + bool (*read_optdrops_option) (struct config_setting_t *option, struct optdrop_group_optslot *entry, int *idx, bool *calc_rate, int slot, const char *group); + bool (*read_optdrops_optslot) (struct config_setting_t *optslot, int n, int group_id, const char *group); + bool (*read_optdrops_group) (struct config_setting_t *group, int n); + bool (*read_optdrops_db) (void); void (*readdb) (void); bool (*lookup_const) (const struct config_setting_t *it, const char *name, int *value); bool (*get_const) (const struct config_setting_t *it, int *value); @@ -537,6 +584,7 @@ struct mob_interface { void (*read_db_drops_sub) (struct mob_db *entry, struct config_setting_t *t); void (*read_db_mvpdrops_sub) (struct mob_db *entry, struct config_setting_t *t); uint32 (*read_db_mode_sub) (struct mob_db *entry, struct config_setting_t *t); + struct optdrop_group *(*read_db_drops_option) (struct mob_db *entry, const char *item_name, struct config_setting_t *drop, int *drop_rate); void (*read_db_stats_sub) (struct mob_db *entry, struct config_setting_t *t); void (*name_constants) (void); bool (*readdb_mobavail) (char *str[], int columns, int current); @@ -552,6 +600,7 @@ struct mob_interface { void (*set_item_drop_ratio) (int nameid, struct item_drop_ratio *ratio); int (*final_ratio_sub) (union DBKey key, struct DBData *data, va_list ap); void (*destroy_mob_db) (int index); + void (*destroy_drop_groups) (void); bool (*skill_db_libconfig) (const char *filename, bool ignore_missing); bool (*skill_db_libconfig_sub) (struct config_setting_t *it, int n); bool (*skill_db_libconfig_sub_skill) (struct config_setting_t *it, int n, int mob_id); diff --git a/src/map/npc.c b/src/map/npc.c index 4b79a9fed..77087d6cb 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -36,6 +36,7 @@ #include "map/mob.h" #include "map/pc.h" #include "map/pet.h" +#include "map/quest.h" #include "map/script.h" #include "map/skill.h" #include "map/status.h" @@ -2638,8 +2639,9 @@ static int npc_unload(struct npc_data *nd, bool single) nd->path = NULL; } - if( single && nd->bl.m != -1 ) - map->remove_questinfo(nd->bl.m,nd); + if (single && nd->bl.m != -1) + map->remove_questinfo(nd->bl.m, nd); + npc->questinfo_clear(nd); if (nd->src_id == 0 && ( nd->subtype == SHOP || nd->subtype == CASHSHOP)) { //src check for duplicate shops [Orcao] @@ -2978,6 +2980,7 @@ static struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, i nd->class_ = class_; nd->speed = 200; nd->vd = npc_viewdb[0]; // Copy INVISIBLE_CLASS view data. Actual view data is set by npc->add_to_location() later. + VECTOR_INIT(nd->qi_data); return nd; } @@ -5331,6 +5334,18 @@ static void npc_debug_warps(void) npc->debug_warps_sub(map->list[m].npc[i]); } +static void npc_questinfo_clear(struct npc_data *nd) +{ + nullpo_retv(nd); + + for (int i = 0; i < VECTOR_LENGTH(nd->qi_data); i++) { + struct questinfo *qi = &VECTOR_INDEX(nd->qi_data, i); + VECTOR_CLEAR(qi->items); + VECTOR_CLEAR(qi->quest_requirement); + } + VECTOR_CLEAR(nd->qi_data); +} + /*========================================== * npc initialization *------------------------------------------*/ @@ -5551,4 +5566,5 @@ void npc_defaults(void) npc->barter_delfromsql_sub = npc_barter_delfromsql_sub; npc->db_checkid = npc_db_checkid; npc->refresh = npc_refresh; + npc->questinfo_clear = npc_questinfo_clear; } diff --git a/src/map/npc.h b/src/map/npc.h index 0eb8befd1..2819cbd87 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -129,6 +129,7 @@ struct npc_data { int spawn_timer; } tomb; } u; + VECTOR_DECL(struct questinfo) qi_data; struct hplugin_data_store *hdata; ///< HPM Plugin Data Store }; @@ -322,6 +323,7 @@ struct npc_interface { void (*barter_delfromsql_sub) (const char *npcname, int itemId, int itemId2, int amount2); bool (*db_checkid) (const int id); void (*refresh) (struct npc_data* nd); + void (*questinfo_clear) (struct npc_data *nd); /** * For the Secure NPC Timeout option (check config/Secure.h) [RR] **/ diff --git a/src/map/packets.h b/src/map/packets.h index a0b6b7f4b..bffec4f43 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -1805,6 +1805,12 @@ packet(0x96e,clif->ackmergeitems); packet(0x0a88,clif->pResetCooldown); #endif +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) + packet(0x0aa1, clif->pAddItemRefineryUI); + packet(0x0aa3, clif->pRefineryUIRefine); + packet(0x0aa4, clif->pRefineryUIClose); +#endif + // 2017-02-28aRagexeRE #if PACKETVER >= 20170228 // new packets @@ -1938,7 +1944,7 @@ packet(0x96e,clif->ackmergeitems); packet(0x0b12,clif->pNPCBarterClosed); #endif -#if PACKETVER_MAIN_NUM >= 20190403 || PACKETVER_RE_NUM >= 20190320 +#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220 packet(0x0b1c,clif->pPing); #endif diff --git a/src/map/packets_keys_main.h b/src/map/packets_keys_main.h index 9505fb454..8a6f32a54 100644 --- a/src/map/packets_keys_main.h +++ b/src/map/packets_keys_main.h @@ -37,7 +37,7 @@ packetKeys(0x49357d72,0x22c370a1,0x5f836591); #endif -// 2010-11-23aRagexeRE, 2010-11-24aRagexeRE, 2010-11-24bRagexeRE, 2010-11-25aRagexeRE, 2010-11-26aRagexeRE, 2010-11-30aRagexeRE, 2010-12-07aRagexeRE, 2010-12-14aRagexeRE, 2010-12-21aRagexeRE, 2010-12-23aRagexeRE, 2010-12-28aRagexeRE, 2011-01-04aRagexeRE, 2011-01-05aRagexeRE, 2011-01-11aRagexeRE, 2011-01-18aRagexeRE, 2011-01-25aRagexeRE, 2011-01-26aRagexeRE, 2011-01-26bRagexeRE, 2011-01-31aRagexeRE, 2011-01-31bRagexeRE, 2011-01-31cRagexeRE, 2011-02-08aRagexeRE, 2011-02-15aRagexeRE, 2011-02-22aRagexeRE, 2011-02-23aRagexeRE, 2011-02-23bRagexeRE, 2011-02-24aRagexeRE, 2011-02-25aRagexeRE, 2011-02-28aRagexeRE, 2011-03-08aRagexeRE, 2011-03-09aRagexeRE, 2011-03-09bRagexeRE, 2011-03-09cRagexeRE, 2011-03-09dRagexeRE, 2011-03-15aRagexeRE, 2011-03-22aRagexeRE, 2011-03-29aRagexeRE, 2011-03-30aRagexeRE, 2011-03-30cRagexeRE, 2011-04-05aRagexeRE, 2011-04-12aRagexeRE, 2011-04-19aRagexeRE, 2011-04-20aRagexeRE, 2011-04-26aRagexeRE, 2011-04-27aRagexeRE, 2011-05-03aRagexeRE, 2011-05-11aRagexeRE, 2011-05-17bRagexeRE, 2011-05-24aRagexeRE, 2011-05-26aRagexeRE, 2011-05-31aRagexeRE, 2011-06-07aRagexeRE, 2011-06-08aRagexeRE, 2011-06-08bRagexeRE, 2011-06-08cRagexeRE, 2011-06-09aRagexeRE, 2011-06-14bRagexeRE, 2011-06-22aRagexeRE, 2011-06-28aRagexeRE, 2011-07-06aRagexeRE, 2011-07-13aRagexeRE, 2011-07-13bRagexeRE, 2011-07-13cRagexeRE, 2011-07-19aRagexeRE, 2011-07-26aRagexeRE, 2011-08-03aRagexeRE, 2011-08-03bRagexeRE, 2011-08-10aRagexeRE, 2013-12-23aRagexeRE, 2014-05-08aRagexe, 2014-05-08aRagexeRE, 2014-06-11eRagexe, 2015-02-25hRagexe, 2018-03-15aRagexe, 2018-03-21aRagexe, 2018-03-21aRagexeRE, 2018-03-28bRagexe, 2018-03-28bRagexeRE, 2018-04-04bRagexe, 2018-04-04cRagexeRE, 2018-04-18aRagexe, 2018-04-18bRagexeRE, 2018-04-25cRagexe, 2018-04-25cRagexeRE, 2018-05-02bRagexe, 2018-05-02bRagexeRE, 2018-05-02dRagexeRE, 2018-05-09aRagexe, 2018-05-16cRagexe, 2018-05-16cRagexeRE, 2018-05-23aRagexe, 2018-05-23aRagexeRE, 2018-05-30aRagexe, 2018-05-30bRagexeRE, 2018-05-30cRagexeRE, 2018-06-05bRagexe, 2018-06-05bRagexeRE, 2018-06-12aRagexeRE, 2018-06-12bRagexeRE, 2018-06-20cRagexe, 2018-06-20dRagexeRE, 2018-06-20eRagexe, 2018-06-20eRagexeRE, 2018-06-21aRagexe, 2018-06-21aRagexeRE, 2018-07-04aRagexe, 2018-07-04aRagexeRE, 2018-07-11aRagexeRE, 2018-07-18bRagexe, 2018-07-18bRagexeRE, 2018-07-18bRagexeRE1, 2018-07-18cRagexe, 2018-07-18cRagexeRE, 2018-08-01cRagexe, 2018-08-01cRagexeRE, 2018-08-08bRagexe, 2018-08-08bRagexeRE, 2018-08-22cRagexe, 2018-08-22cRagexeRE, 2018-08-29aRagexe, 2018-08-29aRagexeRE, 2018-08-29bRagexeRE, 2018-08-31aRagexe, 2018-09-12dRagexe, 2018-09-12dRagexeRE, 2018-09-19aRagexe, 2018-09-19aRagexeRE, 2018-10-02aRagexe, 2018-10-02aRagexeRE, 2018-10-02bRagexe, 2018-10-02bRagexeRE, 2018-10-17_02aRagexe, 2018-10-17_02aRagexeRE, 2018-10-17_03aRagexe, 2018-10-17_03aRagexeRE, 2018-10-17bRagexe, 2018-10-17bRagexeRE, 2018-10-24bRagexe, 2018-10-31aRagexe, 2018-10-31bRagexe, 2018-10-31cRagexeRE, 2018-11-07aRagexe, 2018-11-07aRagexeRE, 2018-11-14cRagexe, 2018-11-14cRagexeRE, 2018-11-14dRagexe, 2018-11-14dRagexeRE, 2018-11-21bRagexe, 2018-11-21cRagexeRE, 2018-11-28aRagexe, 2018-11-28aRagexeRE, 2018-11-28bRagexe, 2018-11-28cRagexe, 2018-12-05aRagexe, 2018-12-05bRagexeRE, 2018-12-12aRagexe, 2018-12-12aRagexeRE, 2018-12-12bRagexe, 2018-12-12bRagexeRE, 2018-12-19bRagexe, 2018-12-19bRagexeRE, 2018-12-26aRagexe, 2018-12-26aRagexeRE, 2019-01-09aRagexe, 2019-01-09bRagexeRE, 2019-01-16bRagexe, 2019-01-16bRagexeRE, 2019-01-16cRagexe, 2019-01-16cRagexeRE, 2019-01-23dRagexe, 2019-01-23dRagexeRE, 2019-02-13IRagexeRE, 2019-02-13bRagexe, 2019-02-13eRagexe, 2019-02-20aRagexeRE, 2019-02-27aRagexe, 2019-02-27bRagexeRE, 2019-02-28aRagexe, 2019-02-28aRagexeRE, 2019-03-06bRagexe, 2019-03-06bRagexeRE, 2019-03-06cRagexe, 2019-03-06cRagexeRE, 2019-03-13aRagexe, 2019-03-20aRagexe, 2019-03-20aRagexeRE, 2019-03-22aRagexe, 2019-03-22aRagexeRE, 2019-03-27bRagexe, 2019-03-27bRagexeRE, 2019-04-03aRagexe, 2019-04-03bRagexeRE, 2019-04-03cRagexeRE +// 2010-11-23aRagexeRE, 2010-11-24aRagexeRE, 2010-11-24bRagexeRE, 2010-11-25aRagexeRE, 2010-11-26aRagexeRE, 2010-11-30aRagexeRE, 2010-12-07aRagexeRE, 2010-12-14aRagexeRE, 2010-12-21aRagexeRE, 2010-12-23aRagexeRE, 2010-12-28aRagexeRE, 2011-01-04aRagexeRE, 2011-01-05aRagexeRE, 2011-01-11aRagexeRE, 2011-01-18aRagexeRE, 2011-01-25aRagexeRE, 2011-01-26aRagexeRE, 2011-01-26bRagexeRE, 2011-01-31aRagexeRE, 2011-01-31bRagexeRE, 2011-01-31cRagexeRE, 2011-02-08aRagexeRE, 2011-02-15aRagexeRE, 2011-02-22aRagexeRE, 2011-02-23aRagexeRE, 2011-02-23bRagexeRE, 2011-02-24aRagexeRE, 2011-02-25aRagexeRE, 2011-02-28aRagexeRE, 2011-03-08aRagexeRE, 2011-03-09aRagexeRE, 2011-03-09bRagexeRE, 2011-03-09cRagexeRE, 2011-03-09dRagexeRE, 2011-03-15aRagexeRE, 2011-03-22aRagexeRE, 2011-03-29aRagexeRE, 2011-03-30aRagexeRE, 2011-03-30cRagexeRE, 2011-04-05aRagexeRE, 2011-04-12aRagexeRE, 2011-04-19aRagexeRE, 2011-04-20aRagexeRE, 2011-04-26aRagexeRE, 2011-04-27aRagexeRE, 2011-05-03aRagexeRE, 2011-05-11aRagexeRE, 2011-05-17bRagexeRE, 2011-05-24aRagexeRE, 2011-05-26aRagexeRE, 2011-05-31aRagexeRE, 2011-06-07aRagexeRE, 2011-06-08aRagexeRE, 2011-06-08bRagexeRE, 2011-06-08cRagexeRE, 2011-06-09aRagexeRE, 2011-06-14bRagexeRE, 2011-06-22aRagexeRE, 2011-06-28aRagexeRE, 2011-07-06aRagexeRE, 2011-07-13aRagexeRE, 2011-07-13bRagexeRE, 2011-07-13cRagexeRE, 2011-07-19aRagexeRE, 2011-07-26aRagexeRE, 2011-08-03aRagexeRE, 2011-08-03bRagexeRE, 2011-08-10aRagexeRE, 2013-12-23aRagexeRE, 2014-05-08aRagexe, 2014-05-08aRagexeRE, 2014-06-11eRagexe, 2015-02-25hRagexe, 2018-03-15aRagexe, 2018-03-21aRagexe, 2018-03-21aRagexeRE, 2018-03-28bRagexe, 2018-03-28bRagexeRE, 2018-04-04bRagexe, 2018-04-04cRagexeRE, 2018-04-18aRagexe, 2018-04-18bRagexeRE, 2018-04-25cRagexe, 2018-04-25cRagexeRE, 2018-05-02bRagexe, 2018-05-02bRagexeRE, 2018-05-02dRagexeRE, 2018-05-09aRagexe, 2018-05-16cRagexe, 2018-05-16cRagexeRE, 2018-05-23aRagexe, 2018-05-23aRagexeRE, 2018-05-30aRagexe, 2018-05-30bRagexeRE, 2018-05-30cRagexeRE, 2018-06-05bRagexe, 2018-06-05bRagexeRE, 2018-06-12aRagexeRE, 2018-06-12bRagexeRE, 2018-06-20cRagexe, 2018-06-20dRagexeRE, 2018-06-20eRagexe, 2018-06-20eRagexeRE, 2018-06-21aRagexe, 2018-06-21aRagexeRE, 2018-07-04aRagexe, 2018-07-04aRagexeRE, 2018-07-11aRagexeRE, 2018-07-18bRagexe, 2018-07-18bRagexeRE, 2018-07-18bRagexeRE1, 2018-07-18cRagexe, 2018-07-18cRagexeRE, 2018-08-01cRagexe, 2018-08-01cRagexeRE, 2018-08-08bRagexe, 2018-08-08bRagexeRE, 2018-08-22cRagexe, 2018-08-22cRagexeRE, 2018-08-29aRagexe, 2018-08-29aRagexeRE, 2018-08-29bRagexeRE, 2018-08-31aRagexe, 2018-09-12dRagexe, 2018-09-12dRagexeRE, 2018-09-19aRagexe, 2018-09-19aRagexeRE, 2018-10-02aRagexe, 2018-10-02aRagexeRE, 2018-10-02bRagexe, 2018-10-02bRagexeRE, 2018-10-17_02aRagexe, 2018-10-17_02aRagexeRE, 2018-10-17_03aRagexe, 2018-10-17_03aRagexeRE, 2018-10-17bRagexe, 2018-10-17bRagexeRE, 2018-10-24bRagexe, 2018-10-31aRagexe, 2018-10-31bRagexe, 2018-10-31cRagexeRE, 2018-11-07aRagexe, 2018-11-07aRagexeRE, 2018-11-14cRagexe, 2018-11-14cRagexeRE, 2018-11-14dRagexe, 2018-11-14dRagexeRE, 2018-11-21bRagexe, 2018-11-21cRagexeRE, 2018-11-28aRagexe, 2018-11-28aRagexeRE, 2018-11-28bRagexe, 2018-11-28cRagexe, 2018-12-05aRagexe, 2018-12-05bRagexeRE, 2018-12-12aRagexe, 2018-12-12aRagexeRE, 2018-12-12bRagexe, 2018-12-12bRagexeRE, 2018-12-19bRagexe, 2018-12-19bRagexeRE, 2018-12-26aRagexe, 2018-12-26aRagexeRE, 2019-01-09aRagexe, 2019-01-09bRagexeRE, 2019-01-16bRagexe, 2019-01-16bRagexeRE, 2019-01-16cRagexe, 2019-01-16cRagexeRE, 2019-01-23dRagexe, 2019-01-23dRagexeRE, 2019-02-13IRagexeRE, 2019-02-13bRagexe, 2019-02-13eRagexe, 2019-02-20aRagexeRE, 2019-02-27aRagexe, 2019-02-27bRagexeRE, 2019-02-28aRagexe, 2019-02-28aRagexeRE, 2019-03-06bRagexe, 2019-03-06bRagexeRE, 2019-03-06cRagexe, 2019-03-06cRagexeRE, 2019-03-13aRagexe, 2019-03-20aRagexe, 2019-03-20aRagexeRE, 2019-03-22aRagexe, 2019-03-22aRagexeRE, 2019-03-27bRagexe, 2019-03-27bRagexeRE, 2019-04-03aRagexe, 2019-04-03bRagexeRE, 2019-04-03cRagexeRE, 2019-04-17aRagexe, 2019-04-17cRagexeRE, 2019-04-18aRagexe, 2019-04-18aRagexeRE #if PACKETVER == 20101123 || \ PACKETVER == 20101124 || \ PACKETVER == 20101125 || \ @@ -145,7 +145,9 @@ PACKETVER == 20190320 || \ PACKETVER == 20190322 || \ PACKETVER == 20190327 || \ - PACKETVER >= 20190403 + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER >= 20190418 packetKeys(0x00000000,0x00000000,0x00000000); #endif diff --git a/src/map/packets_keys_zero.h b/src/map/packets_keys_zero.h index e36091424..5a9e65f14 100644 --- a/src/map/packets_keys_zero.h +++ b/src/map/packets_keys_zero.h @@ -30,7 +30,7 @@ /* This file is autogenerated, please do not commit manual changes */ -// 2017-10-18aRagexe_zero, 2017-10-19aRagexe_zero, 2017-10-23aRagexe_zero, 2017-10-23bRagexe_zero, 2017-10-23cRagexe_zero, 2017-10-24aRagexe_2_zero, 2017-10-24aRagexe_zero, 2017-10-25bRagexe_zero, 2017-10-27aRagexe_zero, 2017-10-27bRagexe_zero, 2017-10-30aRagexe_zero, 2017-10-31aRagexe_zero, 2017-11-09aRagexe_zero, 2017-11-13aRagexe_zero, 2017-11-13bRagexe_zero, 2018-03-15aRagexe_zero, 2018-03-21aRagexe_zero, 2018-03-21bRagexe_zero, 2018-03-28_1aRagexe_zero, 2018-03-28cRagexe_zero, 2018-04-11aRagexe_zero, 2018-04-25_3aRagexe_zero, 2018-05-09_3aRagexe_zero, 2018-05-23aRagexe_zero, 2018-06-05bRagexe_zero, 2018-06-05cRagexe_zero, 2018-06-27aRagexe_zero, 2018-07-03aRagexe_zero, 2018-07-11_2aRagexe_zero, 2018-07-25_2aRagexe_zero, 2018-08-01aRagexe_zero, 2018-08-08_2aRagexe_zero, 2018-08-22aRagexe_zero, 2018-08-29aRagexe_zero, 2018-09-05aRagexe_zero, 2018-09-12aRagexe_zero, 2018-09-19aRagexe_zero, 2018-09-28aRagexe_zero, 2018-10-10_2aRagexe_zero, 2018-10-24_2aRagexe_zero, 2018-11-14aRagexe_zero, 2018-11-20aRagexe_zero, 2018-11-28aRagexe_zero, 2018-12-12aRagexe_zero, 2018-12-19aRagexe_zero, 2018-12-26_2aRagexe_zero, 2019-01-16_2aRagexe_zero, 2019-01-17_1aRagexe_zero, 2019-01-30_2aRagexe_zero, 2019-02-13aRagexe_zero, 2019-02-20aRagexe_zero, 2019-02-27aRagexe_zero, 2019-03-13aRagexe_zero, 2019-03-27_2aRagexe_zero, 2019-03-27_3aRagexe_zero, 2019-04-03aRagexe_zero +// 2017-10-18aRagexe_zero, 2017-10-19aRagexe_zero, 2017-10-23aRagexe_zero, 2017-10-23bRagexe_zero, 2017-10-23cRagexe_zero, 2017-10-24aRagexe_2_zero, 2017-10-24aRagexe_zero, 2017-10-25bRagexe_zero, 2017-10-27aRagexe_zero, 2017-10-27bRagexe_zero, 2017-10-30aRagexe_zero, 2017-10-31aRagexe_zero, 2017-11-09aRagexe_zero, 2017-11-13aRagexe_zero, 2017-11-13bRagexe_zero, 2018-03-15aRagexe_zero, 2018-03-21aRagexe_zero, 2018-03-21bRagexe_zero, 2018-03-28_1aRagexe_zero, 2018-03-28cRagexe_zero, 2018-04-11aRagexe_zero, 2018-04-25_3aRagexe_zero, 2018-05-09_3aRagexe_zero, 2018-05-23aRagexe_zero, 2018-06-05bRagexe_zero, 2018-06-05cRagexe_zero, 2018-06-27aRagexe_zero, 2018-07-03aRagexe_zero, 2018-07-11_2aRagexe_zero, 2018-07-25_2aRagexe_zero, 2018-08-01aRagexe_zero, 2018-08-08_2aRagexe_zero, 2018-08-22aRagexe_zero, 2018-08-29aRagexe_zero, 2018-09-05aRagexe_zero, 2018-09-12aRagexe_zero, 2018-09-19aRagexe_zero, 2018-09-28aRagexe_zero, 2018-10-10_2aRagexe_zero, 2018-10-24_2aRagexe_zero, 2018-11-14aRagexe_zero, 2018-11-20aRagexe_zero, 2018-11-28aRagexe_zero, 2018-12-12aRagexe_zero, 2018-12-19aRagexe_zero, 2018-12-26_2aRagexe_zero, 2019-01-16_2aRagexe_zero, 2019-01-17_1aRagexe_zero, 2019-01-30_2aRagexe_zero, 2019-02-13aRagexe_zero, 2019-02-20aRagexe_zero, 2019-02-27aRagexe_zero, 2019-03-13aRagexe_zero, 2019-03-27_2aRagexe_zero, 2019-03-27_3aRagexe_zero, 2019-04-03aRagexe_zero, 2019-04-10bRagexe_zero, 2019-04-24aRagexe_zero, 2019-05-02aRagexe_zero #if PACKETVER == 20171018 || \ PACKETVER == 20171019 || \ PACKETVER == 20171023 || \ @@ -77,7 +77,10 @@ PACKETVER == 20190227 || \ PACKETVER == 20190313 || \ PACKETVER == 20190327 || \ - PACKETVER >= 20190403 + PACKETVER == 20190403 || \ + PACKETVER == 20190410 || \ + PACKETVER == 20190424 || \ + PACKETVER >= 20190502 packetKeys(0x00000000,0x00000000,0x00000000); #endif diff --git a/src/map/packets_shuffle_main.h b/src/map/packets_shuffle_main.h index d0c3099b9..a5ce83ebe 100644 --- a/src/map/packets_shuffle_main.h +++ b/src/map/packets_shuffle_main.h @@ -9727,7 +9727,7 @@ packet(0x0967,clif->pSolveCharName,2); // CZ_REQNAME_BYGID // 6 #endif -// 2018-11-21bRagexe, 2018-11-28aRagexe, 2018-11-28bRagexe, 2018-11-28cRagexe, 2018-12-05aRagexe, 2018-12-12aRagexe, 2018-12-12bRagexe, 2018-12-19bRagexe, 2018-12-26aRagexe, 2019-01-09aRagexe, 2019-01-16bRagexe, 2019-01-16cRagexe, 2019-01-23dRagexe, 2019-02-13bRagexe, 2019-02-13eRagexe, 2019-02-27aRagexe, 2019-02-28aRagexe, 2019-03-06bRagexe, 2019-03-06cRagexe, 2019-03-13aRagexe, 2019-03-20aRagexe, 2019-03-22aRagexe, 2019-03-27bRagexe, 2019-04-03aRagexe +// 2018-11-21bRagexe, 2018-11-28aRagexe, 2018-11-28bRagexe, 2018-11-28cRagexe, 2018-12-05aRagexe, 2018-12-12aRagexe, 2018-12-12bRagexe, 2018-12-19bRagexe, 2018-12-26aRagexe, 2019-01-09aRagexe, 2019-01-16bRagexe, 2019-01-16cRagexe, 2019-01-23dRagexe, 2019-02-13bRagexe, 2019-02-13eRagexe, 2019-02-27aRagexe, 2019-02-28aRagexe, 2019-03-06bRagexe, 2019-03-06cRagexe, 2019-03-13aRagexe, 2019-03-20aRagexe, 2019-03-22aRagexe, 2019-03-27bRagexe, 2019-04-03aRagexe, 2019-04-17aRagexe, 2019-04-18aRagexe #if PACKETVER == 20181121 || \ PACKETVER == 20181128 || \ PACKETVER == 20181205 || \ @@ -9745,7 +9745,9 @@ PACKETVER == 20190320 || \ PACKETVER == 20190322 || \ PACKETVER == 20190327 || \ - PACKETVER >= 20190403 + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER >= 20190418 packet(0x0202,clif->pFriendsListAdd,2); // CZ_ADD_FRIENDS // 26 packet(0x022d,clif->pHomMenu,2,4); // CZ_COMMAND_MER // 5 packet(0x023b,clif->pStoragePassword,0); // CZ_ACK_STORE_PASSWORD // 36 diff --git a/src/map/packets_shuffle_re.h b/src/map/packets_shuffle_re.h index c41095fb4..5808e4c17 100644 --- a/src/map/packets_shuffle_re.h +++ b/src/map/packets_shuffle_re.h @@ -9663,7 +9663,7 @@ packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 12 #endif -// 2018-07-04aRagexeRE, 2018-07-11aRagexeRE, 2018-07-18bRagexeRE, 2018-07-18bRagexeRE1, 2018-07-18cRagexeRE, 2018-08-01cRagexeRE, 2018-08-08bRagexeRE, 2018-08-22cRagexeRE, 2018-08-29aRagexeRE, 2018-08-29bRagexeRE, 2018-09-12dRagexeRE, 2018-09-19aRagexeRE, 2018-10-02aRagexeRE, 2018-10-02bRagexeRE, 2018-10-17_02aRagexeRE, 2018-10-17_03aRagexeRE, 2018-10-17bRagexeRE, 2018-10-31cRagexeRE, 2018-11-07aRagexeRE, 2018-11-14cRagexeRE, 2018-11-14dRagexeRE, 2018-11-21cRagexeRE, 2018-11-28aRagexeRE, 2018-12-05bRagexeRE, 2018-12-12aRagexeRE, 2018-12-12bRagexeRE, 2018-12-19bRagexeRE, 2018-12-26aRagexeRE, 2019-01-09bRagexeRE, 2019-01-16bRagexeRE, 2019-01-16cRagexeRE, 2019-01-23dRagexeRE, 2019-02-13IRagexeRE, 2019-02-20aRagexeRE, 2019-02-27bRagexeRE, 2019-02-28aRagexeRE, 2019-03-06bRagexeRE, 2019-03-06cRagexeRE, 2019-03-20aRagexeRE, 2019-03-22aRagexeRE, 2019-03-27bRagexeRE, 2019-04-03bRagexeRE, 2019-04-03cRagexeRE +// 2018-07-04aRagexeRE, 2018-07-11aRagexeRE, 2018-07-18bRagexeRE, 2018-07-18bRagexeRE1, 2018-07-18cRagexeRE, 2018-08-01cRagexeRE, 2018-08-08bRagexeRE, 2018-08-22cRagexeRE, 2018-08-29aRagexeRE, 2018-08-29bRagexeRE, 2018-09-12dRagexeRE, 2018-09-19aRagexeRE, 2018-10-02aRagexeRE, 2018-10-02bRagexeRE, 2018-10-17_02aRagexeRE, 2018-10-17_03aRagexeRE, 2018-10-17bRagexeRE, 2018-10-31cRagexeRE, 2018-11-07aRagexeRE, 2018-11-14cRagexeRE, 2018-11-14dRagexeRE, 2018-11-21cRagexeRE, 2018-11-28aRagexeRE, 2018-12-05bRagexeRE, 2018-12-12aRagexeRE, 2018-12-12bRagexeRE, 2018-12-19bRagexeRE, 2018-12-26aRagexeRE, 2019-01-09bRagexeRE, 2019-01-16bRagexeRE, 2019-01-16cRagexeRE, 2019-01-23dRagexeRE, 2019-02-13IRagexeRE, 2019-02-20aRagexeRE, 2019-02-27bRagexeRE, 2019-02-28aRagexeRE, 2019-03-06bRagexeRE, 2019-03-06cRagexeRE, 2019-03-20aRagexeRE, 2019-03-22aRagexeRE, 2019-03-27bRagexeRE, 2019-04-03bRagexeRE, 2019-04-03cRagexeRE, 2019-04-17cRagexeRE, 2019-04-18aRagexeRE #if PACKETVER == 20180704 || \ PACKETVER == 20180711 || \ PACKETVER == 20180718 || \ @@ -9695,7 +9695,9 @@ PACKETVER == 20190320 || \ PACKETVER == 20190322 || \ PACKETVER == 20190327 || \ - PACKETVER >= 20190403 + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER >= 20190418 packet(0x0202,clif->pFriendsListAdd,2); // CZ_ADD_FRIENDS // 26 packet(0x022d,clif->pHomMenu,2,4); // CZ_COMMAND_MER // 5 packet(0x023b,clif->pStoragePassword,0); // CZ_ACK_STORE_PASSWORD // 36 diff --git a/src/map/packets_shuffle_zero.h b/src/map/packets_shuffle_zero.h index ccf11a647..7fd34a51b 100644 --- a/src/map/packets_shuffle_zero.h +++ b/src/map/packets_shuffle_zero.h @@ -742,7 +742,7 @@ packet(0x0968,clif->pStoragePassword,0); // CZ_ACK_STORE_PASSWORD // 36 #endif -// 2018-11-14aRagexe_zero, 2018-11-20aRagexe_zero, 2018-11-28aRagexe_zero, 2018-12-12aRagexe_zero, 2018-12-19aRagexe_zero, 2018-12-26_2aRagexe_zero, 2019-01-16_2aRagexe_zero, 2019-01-17_1aRagexe_zero, 2019-01-30_2aRagexe_zero, 2019-02-13aRagexe_zero, 2019-02-20aRagexe_zero, 2019-02-27aRagexe_zero, 2019-03-13aRagexe_zero, 2019-03-27_2aRagexe_zero, 2019-03-27_3aRagexe_zero, 2019-04-03aRagexe_zero +// 2018-11-14aRagexe_zero, 2018-11-20aRagexe_zero, 2018-11-28aRagexe_zero, 2018-12-12aRagexe_zero, 2018-12-19aRagexe_zero, 2018-12-26_2aRagexe_zero, 2019-01-16_2aRagexe_zero, 2019-01-17_1aRagexe_zero, 2019-01-30_2aRagexe_zero, 2019-02-13aRagexe_zero, 2019-02-20aRagexe_zero, 2019-02-27aRagexe_zero, 2019-03-13aRagexe_zero, 2019-03-27_2aRagexe_zero, 2019-03-27_3aRagexe_zero, 2019-04-03aRagexe_zero, 2019-04-10bRagexe_zero, 2019-04-24aRagexe_zero, 2019-05-02aRagexe_zero #if PACKETVER == 20181114 || \ PACKETVER == 20181120 || \ PACKETVER == 20181128 || \ @@ -757,7 +757,10 @@ PACKETVER == 20190227 || \ PACKETVER == 20190313 || \ PACKETVER == 20190327 || \ - PACKETVER >= 20190403 + PACKETVER == 20190403 || \ + PACKETVER == 20190410 || \ + PACKETVER == 20190424 || \ + PACKETVER >= 20190502 packet(0x0202,clif->pFriendsListAdd,2); // CZ_ADD_FRIENDS // 26 packet(0x022d,clif->pHomMenu,2,4); // CZ_COMMAND_MER // 5 packet(0x023b,clif->pStoragePassword,0); // CZ_ACK_STORE_PASSWORD // 36 diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 96337d32b..33b7759ce 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -418,11 +418,6 @@ enum packet_headers { buyingStoreUpdateItemType = 0x81b, #endif reqName = 0x95, -#if PACKETVER >= 20150503 // Confirm this? - reqNameAllType = 0xA30, -#else - reqNameAllType = 0x195, -#endif #if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) skilWarpPointType = 0xabe, #else @@ -2787,17 +2782,46 @@ struct packet_reqname_ack { } __attribute__((packed)); // ZC_ACK_REQNAMEALL / ZC_ACK_REQNAMEALL2 -struct packet_reqnameall_ack { +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) +struct PACKET_ZC_ACK_REQNAMEALL { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; + char party_name[NAME_LENGTH]; + char guild_name[NAME_LENGTH]; + char position_name[NAME_LENGTH]; + int32 title_id; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_REQNAMEALL, 0x0a30); +#else +struct PACKET_ZC_ACK_REQNAMEALL { 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 +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_REQNAMEALL, 0x0195); #endif + +#if PACKETVER_MAIN_NUM >= 20180207 || PACKETVER_RE_NUM >= 20171129 || PACKETVER_ZERO_NUM >= 20171130 +struct PACKET_ZC_ACK_REQNAME_TITLE { + uint16 packet_id; + int32 gid; + int32 groupId; + char name[NAME_LENGTH]; + char title[NAME_LENGTH]; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_REQNAME_TITLE, 0x0adf); +#else +struct PACKET_ZC_ACK_REQNAME_TITLE { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_REQNAME_TITLE, 0x0095); +#endif struct PACKET_ZC_OVERWEIGHT_PERCENT { int16 packetType; @@ -3188,6 +3212,183 @@ struct PACKET_CZ_STYLE_CLOSE { DEFINE_PACKET_HEADER(CZ_STYLE_CLOSE, 0x0a48); #endif +#if PACKETVER_MAIN_NUM >= 20190403 || PACKETVER_RE_NUM >= 20190320 || PACKETVER_ZERO_NUM >= 20190410 +struct PACKET_ZC_LOAD_CONFIRM { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_LOAD_CONFIRM, 0x0b1b); +#endif + +#if PACKETVER_MAIN_NUM >= 20070911 || defined(PACKETVER_RE) || PACKETVER_AD_NUM >= 20070911 || PACKETVER_SAK_NUM >= 20070904 || defined(PACKETVER_ZERO) +struct PACKET_ZC_PARTY_CONFIG { + int16 packetType; + uint8 denyPartyInvites; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PARTY_CONFIG, 0x02c9); +#endif + +struct PACKET_ZC_ROLE_CHANGE { + int16 packetType; + int32 flag; + char name[NAME_LENGTH]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ROLE_CHANGE, 0x00e1); + +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) +struct PACKET_ZC_BAN_LIST_sub { + int char_id; + char message[40]; +} __attribute__((packed)); + +struct PACKET_ZC_BAN_LIST { + int16 packetType; + uint16 packetLen; + struct PACKET_ZC_BAN_LIST_sub chars[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_BAN_LIST, 0x0a87); +// version unconfirmed +#elif PACKETVER >= 20100803 +struct PACKET_ZC_BAN_LIST_sub { + char char_name[NAME_LENGTH]; + char message[40]; +} __attribute__((packed)); + +struct PACKET_ZC_BAN_LIST { + int16 packetType; + uint16 packetLen; + struct PACKET_ZC_BAN_LIST_sub chars[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_BAN_LIST, 0x0163); +#else +struct PACKET_ZC_BAN_LIST_sub { + char char_name[NAME_LENGTH]; + char account_name[NAME_LENGTH]; + char message[40]; +} __attribute__((packed)); + +struct PACKET_ZC_BAN_LIST { + int16 packetType; + uint16 packetLen; + struct PACKET_ZC_BAN_LIST_sub chars[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_BAN_LIST, 0x0163); +#endif + +#if PACKETVER_MAIN_NUM >= 20141008 || PACKETVER_RE_NUM >= 20140903 || defined(PACKETVER_ZERO) +struct PACKET_ZC_ACK_CLOSE_ROULETTE { + int16 packetType; + uint8 result; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_CLOSE_ROULETTE, 0x0a1e); +#endif + +#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO) +struct PACKET_ZC_ACK_MERGE_ITEM { + int16 packetType; + int16 index; + int16 amount; + uint8 reason; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_MERGE_ITEM, 0x096f); +#endif + +#if PACKETVER_MAIN_NUM >= 20120314 || PACKETVER_RE_NUM >= 20120221 || defined(PACKETVER_ZERO) +struct PACKET_ZC_MERGE_ITEM_OPEN_sub { + int16 index; +} __attribute__((packed)); + +struct PACKET_ZC_MERGE_ITEM_OPEN { + int16 packetType; + uint16 packetLen; + struct PACKET_ZC_MERGE_ITEM_OPEN_sub items[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_MERGE_ITEM_OPEN, 0x096d); +#endif + +#if PACKETVER_MAIN_NUM >= 20101123 || PACKETVER_RE_NUM >= 20120328 || defined(PACKETVER_ZERO) +struct PACKET_ZC_SE_PC_BUY_CASHITEM_RESULT { + int16 packetType; + uint32 itemId; // unused + uint16 result; + uint32 cashPoints; + uint32 kafraPoints; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SE_PC_BUY_CASHITEM_RESULT, 0x0849); +#endif + +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_OPEN_WINDOW { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_OPEN_WINDOW, 0x0aa0); +#endif + +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) +struct PACKET_CZ_REFINE_ADD_ITEM { + int16 packetType; + int16 index; +}; +DEFINE_PACKET_HEADER(CZ_REFINE_ADD_ITEM, 0x0aa1); +#endif + +#if PACKETVER_MAIN_NUM >= 20161130 || PACKETVER_RE_NUM >= 20161109 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_ADD_ITEM_SUB { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 chance; + int32 zeny; +} __attribute__((packed)); + +struct PACKET_ZC_REFINE_ADD_ITEM { + int16 packetType; + int16 packtLength; + int16 itemIndex; + int8 blacksmithBlessing; + struct PACKET_ZC_REFINE_ADD_ITEM_SUB req[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_ADD_ITEM, 0x0aa2); +#endif + +#if PACKETVER_MAIN_NUM >= 20161005 || PACKETVER_RE_NUM >= 20161005 || defined(PACKETVER_ZERO) +struct PACKET_CZ_REFINE_ITEM_REQUEST { + int16 packetType; + int16 index; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 blacksmithBlessing; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REFINE_ITEM_REQUEST, 0x0aa3); + +struct PACKET_CZ_REFINE_WINDOW_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REFINE_WINDOW_CLOSE, 0x0aa4); +#endif + +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) +struct PACKET_ZC_REFINE_STATUS { + int16 packetType; + char name[NAME_LENGTH]; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + int8 refine_level; + int8 status; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_REFINE_STATUS, 0x0ada); +#endif + #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 e4fb18c23..dc330d92d 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -424,6 +424,12 @@ static int party_invite(struct map_session_data *sd, struct map_session_data *ts return 0; } + if ((tsd->status.allow_party & 1) != 0) { + // party invite blocked by player + clif->party_inviteack(sd, tsd->status.name, 5); + return 0; + } + tsd->party_invite=sd->status.party_id; tsd->party_invite_account=sd->status.account_id; diff --git a/src/map/pc.c b/src/map/pc.c index 140cf7ac1..5416fbec2 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -4854,7 +4854,7 @@ static int pc_dropitem(struct map_session_data *sd, int n, int amount) if(sd->status.inventory[n].nameid <= 0 || sd->status.inventory[n].amount <= 0 || sd->status.inventory[n].amount < amount || - sd->state.trading || sd->state.vending || + sd->state.trading || sd->state.vending || sd->state.prevend || !sd->inventory_data[n] //pc->delitem would fail on this case. ) return 0; @@ -5472,7 +5472,7 @@ static int pc_putitemtocart(struct map_session_data *sd, int idx, int amount) item_data = &sd->status.inventory[idx]; - if( item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending ) + if (item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending || sd->state.prevend) return 1; if( (flag = pc->cart_additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0 ) @@ -5519,10 +5519,10 @@ static int pc_getitemfromcart(struct map_session_data *sd, int idx, int amount) item_data=&sd->status.cart[idx]; - if(item_data->nameid==0 || amount < 1 || item_data->amount<amount || sd->state.vending ) + if (item_data->nameid == 0 || amount < 1 || item_data->amount < amount || sd->state.vending || sd->state.prevend) return 1; - if((flag = pc->additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0) + if ((flag = pc->additem(sd,item_data,amount,LOG_TYPE_NONE)) == 0) return pc->cart_delitem(sd,idx,amount,0,LOG_TYPE_NONE); return flag; @@ -12356,7 +12356,7 @@ static bool pc_expandInventory(struct map_session_data *sd, int adjustSize) clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_MAX_SIZE); return false; } - if (pc_isdead(sd) || sd->state.vending || sd->state.buyingstore || sd->chat_id != 0 || sd->state.trading || sd->state.storage_flag || sd->state.prevend) { + if (pc_isdead(sd) || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->chat_id != 0 || sd->state.trading || sd->state.storage_flag || sd->state.prevend) { clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_OTHER_WORK); return false; } diff --git a/src/map/pc.h b/src/map/pc.h index b2069d4df..7c89f7f32 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -237,6 +237,7 @@ struct map_session_data { unsigned int standalone : 1;/* [Ind/Hercules <3] */ unsigned int loggingout : 1; unsigned int warp_clean : 1; + unsigned int refine_ui : 1; } state; struct { unsigned char no_weapon_damage, no_magic_damage, no_misc_damage; @@ -273,6 +274,9 @@ struct map_session_data { int npc_item_flag; //Marks the npc_id with which you can change equipments during interactions with said npc (see script command enable_itemuse) int npc_menu; // internal variable, used in npc menu handling int npc_amount; + int npc_amount_min; + int npc_amount_max; + int npc_input_capped_range; struct script_state *st; char npc_str[CHATBOX_SIZE]; // for passing npc input box text to script engine int npc_timer_id; //For player attached npc timers. [Skotlex] @@ -660,10 +664,10 @@ END_ZEROED_BLOCK; #define pc_issit(sd) ( (sd)->vd.dead_sit == 2 ) #define pc_isidle(sd) ( (sd)->chat_id != 0 || (sd)->state.vending || (sd)->state.buyingstore || DIFF_TICK(sockt->last_tick, (sd)->idletime) >= battle->bc->idle_no_share ) #define pc_istrading(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->state.trading ) -#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend ) +#define pc_cant_act(sd) ( (sd)->npc_id || (sd)->state.vending || (sd)->state.buyingstore || (sd)->chat_id != 0 || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1) /* equals pc_cant_act except it doesn't check for chat rooms */ -#define pc_cant_act2(sd) ( (sd)->npc_id || (sd)->state.buyingstore || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend ) +#define pc_cant_act2(sd) ( (sd)->npc_id || (sd)->state.buyingstore || ((sd)->sc.opt1 && (sd)->sc.opt1 != OPT1_BURNING) || (sd)->state.trading || (sd)->state.storage_flag || (sd)->state.prevend || (sd)->state.refine_ui == 1) #define pc_setdir(sd,b,h) ( (sd)->ud.dir = (b) ,(sd)->head_dir = (h) ) #define pc_setchatid(sd,n) ( (sd)->chat_id = (n) ) diff --git a/src/map/pet.c b/src/map/pet.c index 96fe63c51..ce26b6cb1 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -348,6 +348,21 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) if (i != sd->status.inventorySize) { sd->status.inventory[i].attribute &= ~ATTR_BROKEN; sd->status.inventory[i].bound = IBT_NONE; + } else { + // The pet egg wasn't found: it was probably hatched with the old system that deleted the egg. + struct item tmp_item = {0}; + int flag; + + tmp_item.nameid = pd->petDB->EggID; + tmp_item.identify = 1; + tmp_item.card[0] = CARD0_PET; + tmp_item.card[1] = GetWord(pd->pet.pet_id, 0); + tmp_item.card[2] = GetWord(pd->pet.pet_id, 1); + tmp_item.card[3] = pd->pet.rename_flag; + if ((flag = pc->additem(sd, &tmp_item, 1, LOG_TYPE_EGG)) != 0) { + clif->additem(sd, 0, 0, flag); + map->addflooritem(&sd->bl, &tmp_item, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); + } } #if PACKETVER >= 20180704 clif->inventoryList(sd); diff --git a/src/map/quest.c b/src/map/quest.c index 7a216095e..9540b411d 100644 --- a/src/map/quest.c +++ b/src/map/quest.c @@ -672,21 +672,22 @@ static int quest_questinfo_validate_icon(int icon) */ static void quest_questinfo_refresh(struct map_session_data *sd) { - int i; - nullpo_retv(sd); - for (i = 0; i < VECTOR_LENGTH(map->list[sd->bl.m].qi_data); i++) { - struct questinfo *qi = &VECTOR_INDEX(map->list[sd->bl.m].qi_data, i); - // Remove the bubbles if one of the conditions is no longer valid. - if (quest->questinfo_validate(sd, qi) == false) { + for (int i = 0; i < VECTOR_LENGTH(map->list[sd->bl.m].qi_list); i++) { + struct npc_data *nd = &VECTOR_INDEX(map->list[sd->bl.m].qi_list, i); + + int j; + ARR_FIND(0, VECTOR_LENGTH(nd->qi_data), j, quest->questinfo_validate(sd, &VECTOR_INDEX(nd->qi_data, j)) == true); + if (j != VECTOR_LENGTH(nd->qi_data)) { + struct questinfo *qi = &VECTOR_INDEX(nd->qi_data, j); + clif->quest_show_event(sd, &nd->bl, qi->icon, qi->color); + } else { #if PACKETVER >= 20120410 - clif->quest_show_event(sd, &qi->nd->bl, 9999, 0); + clif->quest_show_event(sd, &nd->bl, 9999, 0); #else - clif->quest_show_event(sd, &qi->nd->bl, 0, 0); + clif->quest_show_event(sd, &nd->bl, 0, 0); #endif - } else { - clif->quest_show_event(sd, &qi->nd->bl, qi->icon, qi->color); } } } @@ -927,26 +928,6 @@ static bool quest_questinfo_validate_mercenary_class(struct map_session_data *sd } /** - * Clears the questinfo data vector - * - * @param m mapindex. - * - */ -static void quest_questinfo_vector_clear(int m) -{ - int i; - - Assert_retv(m >= 0 && m < map->count); - - for (i = 0; i < VECTOR_LENGTH(map->list[m].qi_data); i++) { - struct questinfo *qi_data = &VECTOR_INDEX(map->list[m].qi_data, i); - VECTOR_CLEAR(qi_data->items); - VECTOR_CLEAR(qi_data->quest_requirement); - } - VECTOR_CLEAR(map->list[m].qi_data); -} - -/** * Initializes the quest interface. * * @param minimal Run in minimal mode (skips most of the loading) @@ -1020,5 +1001,4 @@ void quest_defaults(void) quest->questinfo_validate_homunculus_type = quest_questinfo_validate_homunculus_type; quest->questinfo_validate_quests = quest_questinfo_validate_quests; quest->questinfo_validate_mercenary_class = quest_questinfo_validate_mercenary_class; - quest->questinfo_vector_clear = quest_questinfo_vector_clear; } diff --git a/src/map/quest.h b/src/map/quest.h index 206a7902f..d60b9b33c 100644 --- a/src/map/quest.h +++ b/src/map/quest.h @@ -60,6 +60,39 @@ enum quest_check_type { HUNTING, ///< Check if the given hunting quest's requirements have been met }; +struct questinfo_qreq { + int id; + int state; +}; + +struct questinfo_itemreq { + int nameid; + int min; + int max; +}; + +struct questinfo { + unsigned short icon; + unsigned char color; + bool hasJob; + unsigned int job;/* perhaps a mapid mask would be most flexible? */ + bool sex_enabled; + int sex; + struct { + int min; + int max; + } base_level; + struct { + int min; + int max; + } job_level; + VECTOR_DECL(struct questinfo_itemreq) items; + struct s_homunculus homunculus; + int homunculus_type; + VECTOR_DECL(struct questinfo_qreq) quest_requirement; + int mercenary_class; +}; + struct quest_interface { struct quest_db **db_data; ///< Quest database struct quest_db dummy; ///< Dummy entry for invalid quest lookups @@ -93,7 +126,6 @@ struct quest_interface { bool (*questinfo_validate_homunculus_type) (struct map_session_data *sd, struct questinfo *qi); bool (*questinfo_validate_quests) (struct map_session_data *sd, struct questinfo *qi); bool (*questinfo_validate_mercenary_class) (struct map_session_data *sd, struct questinfo *qi); - void (*questinfo_vector_clear) (int m); }; #ifdef HERCULES_CORE diff --git a/src/map/refine.c b/src/map/refine.c new file mode 100644 index 000000000..4fe6e73c4 --- /dev/null +++ b/src/map/refine.c @@ -0,0 +1,669 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2019 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/>. +*/ + +#define HERCULES_CORE + +#include "refine.p.h" +#include "common/cbasetypes.h" +#include "common/nullpo.h" +#include "common/random.h" +#include "common/showmsg.h" +#include "common/strlib.h" +#include "common/utils.h" +#include "map/itemdb.h" +#include "map/map.h" +#include "map/pc.h" +#include "map/script.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** @file +* Implementation of the refine interface. +*/ + +static struct refine_interface refine_s; +static struct refine_interface_private refine_p; +static struct refine_interface_dbs refine_dbs; +struct refine_interface *refine; + +/// @copydoc refine_interface::refinery_refine_request() +static void refine_refinery_refine_request(struct map_session_data *sd, int item_index, int material_id, bool use_blacksmith_blessing) +{ + nullpo_retv(sd); + + if (item_index < 0 || item_index >= sd->status.inventorySize) + return; + + if (!refine->p->is_refinable(sd, item_index)) + return; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid); + int refine_level = sd->status.inventory[item_index].refine; + int i = 0; + const struct s_refine_requirement *req = &refine->p->dbs->refine_info[weapon_level].refine_requirements[refine_level]; + ARR_FIND(0, req->req_count, i, req->req[i].nameid == material_id); + + if (i == req->req_count) + return; + + if (use_blacksmith_blessing && req->blacksmith_blessing == 0) + return; + + if (sd->status.zeny < req->req[i].cost) + return; + + if (use_blacksmith_blessing) { + int count = 0; + for (int k = 0; k < sd->status.inventorySize; ++k) { + if (sd->status.inventory[k].nameid == ITEMID_BLACKSMITH_BLESSING) + count += sd->status.inventory[k].amount; + } + + if (count < req->blacksmith_blessing) + return; + } + + int idx; + if ((idx = pc->search_inventory(sd, req->req[i].nameid)) == INDEX_NOT_FOUND) + return; + + if (use_blacksmith_blessing) { + int amount = req->blacksmith_blessing; + for (int k = 0; k < sd->status.inventorySize; ++k) { + if (sd->status.inventory[k].nameid != ITEMID_BLACKSMITH_BLESSING) + continue; + + int delamount = (amount < sd->status.inventory[k].amount) ? amount : sd->status.inventory[k].amount; + if (pc->delitem(sd, k, delamount, 0, DELITEM_NORMAL, LOG_TYPE_REFINE) != 0) + break; + + amount -= delamount; + if (amount == 0) + break; + } + } + + if (pc->delitem(sd, idx, 1, 0, DELITEM_NORMAL, LOG_TYPE_REFINE) != 0) + return; + + if (pc->payzeny(sd, req->req[i].cost, LOG_TYPE_REFINE, NULL) != 0) + return; + + int refine_chance = refine->get_refine_chance(weapon_level, refine_level, req->req[i].type); + if (rnd() % 100 >= refine_chance) { + clif->misceffect(&sd->bl, 2); + + int failure_behabior = (use_blacksmith_blessing) ? REFINE_FAILURE_BEHAVIOR_KEEP : req->req[i].failure_behavior; + switch (failure_behabior) { + case REFINE_FAILURE_BEHAVIOR_KEEP: + clif->refine(sd->fd, 1, 0, sd->status.inventory[item_index].refine); + refine->refinery_add_item(sd, item_index); + break; + case REFINE_FAILURE_BEHAVIOR_DOWNGRADE: + sd->status.inventory[item_index].refine -= 1; + sd->status.inventory[item_index].refine = cap_value(sd->status.inventory[item_index].refine, 0, MAX_REFINE); + clif->refine(sd->fd, 2, item_index, sd->status.inventory[item_index].refine); + logs->pick_pc(sd, LOG_TYPE_REFINE, 1, &sd->status.inventory[item_index], sd->inventory_data[item_index]); + refine->refinery_add_item(sd, item_index); + break; + case REFINE_FAILURE_BEHAVIOR_DESTROY: + default: + clif->refine(sd->fd, 1, item_index, sd->status.inventory[item_index].refine); + pc->delitem(sd, item_index, 1, 0, DELITEM_FAILREFINE, LOG_TYPE_REFINE); + break; + } + + if ((req->announce & REFINE_ANNOUNCE_FAILURE) != 0) + clif->announce_refine_status(sd, sd->status.inventory[item_index].nameid, sd->status.inventory[item_index].refine, false, ALL_CLIENT); + } else { + sd->status.inventory[item_index].refine += 1; + sd->status.inventory[item_index].refine = cap_value(sd->status.inventory[item_index].refine, 0, MAX_REFINE); + + clif->misceffect(&sd->bl, 3); + clif->refine(sd->fd, 0, item_index, sd->status.inventory[item_index].refine); + logs->pick_pc(sd, LOG_TYPE_REFINE, 1, &sd->status.inventory[item_index], sd->inventory_data[item_index]); + refine->refinery_add_item(sd, item_index); + + if ((req->announce & REFINE_ANNOUNCE_SUCCESS) != 0) + clif->announce_refine_status(sd, sd->status.inventory[item_index].nameid, sd->status.inventory[item_index].refine, true, ALL_CLIENT); + } +} + +/// @copydoc refine_interface::refinery_add_item() +static void refine_refinery_add_item(struct map_session_data *sd, int item_index) +{ + nullpo_retv(sd); + + if (item_index < 0 || item_index >= sd->status.inventorySize) + return; + + if (!refine->p->is_refinable(sd, item_index)) + return; + + int weapon_level = itemdb_wlv(sd->status.inventory[item_index].nameid); + int refine_level = sd->status.inventory[item_index].refine; + clif->AddItemRefineryUIAck(sd, item_index, &refine->p->dbs->refine_info[weapon_level].refine_requirements[refine_level]); +} + +/// @copydoc refine_interface_private::is_refinable() +static bool refine_is_refinable(struct map_session_data *sd, int item_index) +{ + nullpo_retr(false, sd); + Assert_retr(false, item_index >= 0 && item_index < sd->status.inventorySize); + + if (sd->status.inventory[item_index].nameid == 0) + return false; + + struct item_data *itd = itemdb->search(sd->status.inventory[item_index].nameid); + + if (itd == &itemdb->dummy) + return false; + + if (itd->type != IT_WEAPON && itd->type != IT_ARMOR) + return false; + + if (itd->flag.no_refine == 1) + return false; + + if (sd->status.inventory[item_index].identify == 0) + return false; + + if (sd->status.inventory[item_index].refine >= MAX_REFINE || sd->status.inventory[item_index].expire_time > 0) + return false; + + if ((sd->status.inventory[item_index].attribute & ATTR_BROKEN) != 0) + return false; + + return true; +} + +/// @copydoc refine_interface::get_randombonus_max() +static int refine_get_randombonus_max(enum refine_type equipment_type, int refine_level) +{ + Assert_ret((int)equipment_type >= REFINE_TYPE_ARMOR && equipment_type < REFINE_TYPE_MAX); + Assert_ret(refine_level > 0 && refine_level <= MAX_REFINE); + + return refine->p->dbs->refine_info[equipment_type].randombonus_max[refine_level - 1]; +} + +/// @copydoc refine_interface::get_bonus() +static int refine_get_bonus(enum refine_type equipment_type, int refine_level) +{ + Assert_ret((int)equipment_type >= REFINE_TYPE_ARMOR && equipment_type < REFINE_TYPE_MAX); + Assert_ret(refine_level > 0 && refine_level <= MAX_REFINE); + + return refine->p->dbs->refine_info[equipment_type].bonus[refine_level - 1]; +} + +/// @copydoc refine_interface::get_refine_chance() +static int refine_get_refine_chance(enum refine_type wlv, int refine_level, enum refine_chance_type type) +{ + Assert_ret((int)wlv >= REFINE_TYPE_ARMOR && wlv < REFINE_TYPE_MAX); + + if (refine_level < 0 || refine_level >= MAX_REFINE) + return 0; + + if (type >= REFINE_CHANCE_TYPE_MAX) + return 0; + + return refine->p->dbs->refine_info[wlv].chance[type][refine_level]; +} + +/// @copydoc refine_interface_private::announce_behavior_string2enum() +static bool refine_announce_behavior_string2enum(const char *str, unsigned int *result) +{ + nullpo_retr(false, str); + nullpo_retr(false, result); + + if (strcasecmp(str, "Success") == 0) + *result = REFINE_ANNOUNCE_SUCCESS; + else if (strcasecmp(str, "Failure") == 0) + *result = REFINE_ANNOUNCE_FAILURE; + else if (strcasecmp(str, "Always") == 0) + *result = REFINE_ANNOUNCE_ALWAYS; + else + return false; + + return true; +} + +/// @copydoc refine_interface_private::failure_behavior_string2enum() +static bool refine_failure_behavior_string2enum(const char *str, enum refine_ui_failure_behavior *result) +{ + nullpo_retr(false, str); + nullpo_retr(false, result); + + if (strcasecmp(str, "Destroy") == 0) + *result = REFINE_FAILURE_BEHAVIOR_DESTROY; + else if (strcasecmp(str, "Keep") == 0) + *result = REFINE_FAILURE_BEHAVIOR_KEEP; + else if (strcasecmp(str, "Downgrade") == 0) + *result = REFINE_FAILURE_BEHAVIOR_DOWNGRADE; + else + return false; + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings_items() +static bool refine_readdb_refinery_ui_settings_items(const struct config_setting_t *elem, struct s_refine_requirement *req, const char *name, const char *source) +{ + nullpo_retr(false, elem); + nullpo_retr(false, req); + nullpo_retr(false, name); + nullpo_retr(false, source); + Assert_retr(false, req->req_count < MAX_REFINE_REQUIREMENTS); + + const char *aegis_name = config_setting_name(elem); + struct item_data *itd; + + if ((itd = itemdb->search_name(aegis_name)) == NULL) { + ShowWarning("refine_readdb_requirements_items: Invalid item '%s' passed to requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + + for (int i = 0; i < req->req_count; ++i) { + if (req->req[i].nameid == itd->nameid) { + ShowWarning("refine_readdb_requirements_items: Duplicated item '%s' passed to requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + } + + const char *type_string = NULL; + if (libconfig->setting_lookup_string(elem, "Type", &type_string) == CONFIG_FALSE) { + ShowWarning("refine_readdb_requirements_items: no type passed to item '%s' of requirements of '%s' in \"%s\" skipping...\n", aegis_name, name, source); + return false; + } + + int type; + if (!script->get_constant(type_string, &type)) { + ShowWarning("refine_readdb_requirements_items: invalid type '%s' passed to item '%s' of requirements of '%s' in \"%s\" skipping...\n", type_string, aegis_name, name, source); + return false; + } + + int cost = 0; + if (libconfig->setting_lookup_int(elem, "Cost", &cost) == CONFIG_TRUE) { + if (cost < 1) { + ShowWarning("refine_readdb_requirements_items: invalid cost value %d passed to item '%s' of requirements of '%s' in \"%s\" defaulting to 0...\n", cost, aegis_name, name, source); + cost = 0; + } + } + + enum refine_ui_failure_behavior behavior = REFINE_FAILURE_BEHAVIOR_DESTROY; + const char *behavior_string = NULL; + if (libconfig->setting_lookup_string(elem, "FailureBehavior", &behavior_string) != CONFIG_FALSE) { + if (!refine->p->failure_behavior_string2enum(behavior_string, &behavior)) { + ShowWarning("refine_readdb_requirements_items: invalid failure behavior value %s passed to item '%s' of requirements of '%s' in \"%s\" defaulting to 'Destroy'...\n", behavior_string, aegis_name, name, source); + } + } + + req->req[req->req_count].nameid = itd->nameid; + req->req[req->req_count].type = type; + req->req[req->req_count].cost = cost; + req->req[req->req_count].failure_behavior = behavior; + req->req_count++; + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings_sub() +static bool refine_readdb_refinery_ui_settings_sub(const struct config_setting_t *elem, int type, const char *name, const char *source) +{ + nullpo_retr(false, elem); + nullpo_retr(false, name); + nullpo_retr(false, source); + Assert_retr(0, type >= REFINE_TYPE_ARMOR && type < REFINE_TYPE_MAX); + + struct config_setting_t *level_t; + bool levels[MAX_REFINE] = {0}; + + if ((level_t = libconfig->setting_get_member(elem, "Level")) == NULL) { + ShowWarning("refine_readdb_requirements_sub: a requirements element missing level field for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + if (config_setting_is_scalar(level_t)) { + if (!config_setting_is_number(level_t)) { + ShowWarning("refine_readdb_requirements_sub: expected 'Level' field to be an integer '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + int refine_level = libconfig->setting_get_int(level_t); + if (refine_level < 1 || refine_level > MAX_REFINE) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' given value %d expected a value between %d and %d '%s' in \"%s\" skipping...\n", refine_level, 1, MAX_REFINE, name, source); + return false; + } + + levels[refine_level - 1] = true; + } else if (config_setting_is_aggregate(level_t)) { + if (libconfig->setting_length(level_t) != 2) { + ShowWarning("refine_readdb_requirements_sub: invalid length for Level array, expected 2 found %d for entry '%s' in \"%s\" skipping...\n", libconfig->setting_length(level_t), name, source); + return false; + } + + int levels_range[2]; + const struct config_setting_t *level_entry = NULL; + int i = 0, + k = 0; + while ((level_entry = libconfig->setting_get_elem(level_t, i++)) != NULL) { + if (!config_setting_is_number(level_entry)) { + ShowWarning("refine_readdb_requirements_sub: expected 'Level' array field to be an integer '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + levels_range[k] = libconfig->setting_get_int(level_entry); + if (levels_range[k] < 1 || levels_range[k] > MAX_REFINE) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' given value %d expected a value between %d and %d in entry'%s' in \"%s\" skipping...\n", levels_range[k], 1, MAX_REFINE, name, source); + return false; + } + + ++k; + } + + if (!(levels_range[0] < levels_range[1])) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'Level' range was given low %d high %d in entry'%s' in \"%s\" skipping...\n", levels_range[0], levels_range[1], name, source); + return false; + } + + for (i = levels_range[0] - 1; i < levels_range[1]; ++i) { + levels[i] = true; + } + } + + struct s_refine_requirement req = {0}; + if (libconfig->setting_lookup_int(elem, "BlacksmithBlessing", &req.blacksmith_blessing) == CONFIG_TRUE) { + if (req.blacksmith_blessing < 1 || req.blacksmith_blessing > INT8_MAX) { + ShowWarning("refine_readdb_requirements_sub: Invalid 'BlacksmithBlessing' amount was given value %d expected a value between %d and %d in entry'%s' in \"%s\" defaulting to 0...\n", req.blacksmith_blessing, 1, INT8_MAX, name, source); + req.blacksmith_blessing = 0; + } + } + + req.announce = 0; + const char *announce_behavior = NULL; + if (libconfig->setting_lookup_string(elem, "Announce", &announce_behavior) != CONFIG_FALSE) { + if (!refine->p->announce_behavior_string2enum(announce_behavior, &req.announce)) { + ShowWarning("refine_readdb_requirements_sub: invalid announce behavior value '%s' in entry '%s' in \"%s\" defaulting to not announce...\n", announce_behavior, name, source); + } + } + + struct config_setting_t *items_t; + if ((items_t = libconfig->setting_get_member(elem, "Items")) == NULL) { + ShowWarning("refine_readdb_requirements_sub: a requirements element missing Items element for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + if (libconfig->setting_length(items_t) < 1) { + ShowWarning("refine_readdb_requirements_sub: an Items element containing no items passed for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + int loaded_items = 0; + for (int i = 0; i < libconfig->setting_length(items_t); ++i) { + if (req.req_count >= MAX_REFINE_REQUIREMENTS) { + ShowWarning("refine_readdb_requirements_sub: Too many items passed to requirements maximum possible items is %d entry '%s' in \"%s\" skipping...\n", MAX_REFINE_REQUIREMENTS, name, source); + continue; + } + + struct config_setting_t *item_t = libconfig->setting_get_elem(items_t, i); + + if (!refine->p->readdb_refinery_ui_settings_items(item_t, &req, name, source)) + continue; + + loaded_items++; + } + + if (loaded_items == 0) { + ShowWarning("refine_readdb_requirements_sub: no valid items for requirements is passed for entry '%s' in \"%s\" skipping...\n", name, source); + return false; + } + + for (int i = 0; i < MAX_REFINE; ++i) { + if (!levels[i]) + continue; + + refine->p->dbs->refine_info[type].refine_requirements[i] = req; + } + + return true; +} + +/// @copydoc refine_interface_private::readdb_refinery_ui_settings() +static int refine_readdb_refinery_ui_settings(const struct config_setting_t *r, int type, const char *name, const char *source) +{ + nullpo_retr(0, r); + nullpo_retr(0, name); + nullpo_retr(0, source); + Assert_retr(0, type >= REFINE_TYPE_ARMOR && type < REFINE_TYPE_MAX); + + int i = 0; + const struct config_setting_t *elem = NULL; + while ((elem = libconfig->setting_get_elem(r, i++)) != NULL) { + refine->p->readdb_refinery_ui_settings_sub(elem, type, name, source); + } + + int retval = 0; + for (i = 0; i < MAX_REFINE; ++i) { + if (refine->p->dbs->refine_info[type].refine_requirements[i].req_count > 0) + retval++; + } + + return retval; +} + +/// @copydoc refine_interface_private::readdb_refine_libconfig_sub() +static int refine_readdb_refine_libconfig_sub(struct config_setting_t *r, const char *name, const char *source) +{ + struct config_setting_t *rate = NULL; + int type = REFINE_TYPE_ARMOR, bonus_per_level = 0, rnd_bonus_v = 0, rnd_bonus_lv = 0; + char lv[4]; + nullpo_ret(r); + nullpo_ret(name); + nullpo_ret(source); + + if (strncmp(name, "Armors", 6) == 0) { + type = REFINE_TYPE_ARMOR; + } else if (strncmp(name, "WeaponLevel", 11) != 0 || !strspn(&name[strlen(name)-1], "0123456789") || (type = atoi(strncpy(lv, name+11, 2))) == REFINE_TYPE_ARMOR) { + ShowError("status_readdb_refine_libconfig_sub: Invalid key name for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + if (type < REFINE_TYPE_ARMOR || type >= REFINE_TYPE_MAX) { + ShowError("status_readdb_refine_libconfig_sub: Out of range level for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + struct config_setting_t *refinery_ui_settings; + if ((refinery_ui_settings = libconfig->setting_get_member(r, "RefineryUISettings")) == NULL) { + ShowWarning("status_readdb_refine_libconfig_sub: Missing Requirements for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + if (refine->p->readdb_refinery_ui_settings(refinery_ui_settings, type, name, source) != MAX_REFINE) { + ShowWarning("status_readdb_refine_libconfig_sub: Not all refine levels have requrements entry for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + if (!libconfig->setting_lookup_int(r, "StatsPerLevel", &bonus_per_level)) { + ShowWarning("status_readdb_refine_libconfig_sub: Missing StatsPerLevel for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + if (!libconfig->setting_lookup_int(r, "RandomBonusStartLevel", &rnd_bonus_lv)) { + ShowWarning("status_readdb_refine_libconfig_sub: Missing RandomBonusStartLevel for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + if (!libconfig->setting_lookup_int(r, "RandomBonusValue", &rnd_bonus_v)) { + ShowWarning("status_readdb_refine_libconfig_sub: Missing RandomBonusValue for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + if ((rate=libconfig->setting_get_member(r, "Rates")) != NULL && config_setting_is_group(rate)) { + bool duplicate[MAX_REFINE]; + int bonus[MAX_REFINE], rnd_bonus[MAX_REFINE]; + int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; + + memset(&duplicate, 0, sizeof(duplicate)); + memset(&bonus, 0, sizeof(bonus)); + memset(&rnd_bonus, 0, sizeof(rnd_bonus)); + + for (int i = 0; i < REFINE_CHANCE_TYPE_MAX; i++) + for (int j = 0; j < MAX_REFINE; j++) + chance[i][j] = 100; // default value for all rates. + + struct config_setting_t *t = NULL; + for (int i = 0; (t = libconfig->setting_get_elem(rate, i)) != NULL && config_setting_is_group(t); ++i) { + int level = 0, i32; + char *rlvl = config_setting_name(t); + memset(&lv, 0, sizeof(lv)); + + if (!strspn(&rlvl[strlen(rlvl) - 1], "0123456789") || (level = atoi(strncpy(lv, rlvl + 2, 3))) <= 0) { + ShowError("status_readdb_refine_libconfig_sub: Invalid refine level format '%s' for entry %s in \"%s\"... skipping.\n", rlvl, name, source); + continue; + } + + if (level <= 0 || level > MAX_REFINE) { + ShowError("status_readdb_refine_libconfig_sub: Out of range refine level '%s' for entry %s in \"%s\"... skipping.\n", rlvl, name, source); + continue; + } + + level--; + + if (duplicate[level]) { + ShowWarning("status_readdb_refine_libconfig_sub: duplicate rate '%s' for entry %s in \"%s\", overwriting previous entry...\n", rlvl, name, source); + } else { + duplicate[level] = true; + } + + if (libconfig->setting_lookup_int(t, "NormalChance", &i32) != 0) + chance[REFINE_CHANCE_TYPE_NORMAL][level] = i32; + else + chance[REFINE_CHANCE_TYPE_NORMAL][level] = 100; + + if (libconfig->setting_lookup_int(t, "EnrichedChance", &i32) != 0) + chance[REFINE_CHANCE_TYPE_ENRICHED][level] = i32; + else + chance[REFINE_CHANCE_TYPE_ENRICHED][level] = level > 10 ? 0 : 100; // enriched ores up to +10 only. + + if (libconfig->setting_lookup_int(t, "EventNormalChance", &i32) != 0) + chance[REFINE_CHANCE_TYPE_E_NORMAL][level] = i32; + else + chance[REFINE_CHANCE_TYPE_E_NORMAL][level] = 100; + + if (libconfig->setting_lookup_int(t, "EventEnrichedChance", &i32) != 0) + chance[REFINE_CHANCE_TYPE_E_ENRICHED][level] = i32; + else + chance[REFINE_CHANCE_TYPE_E_ENRICHED][level] = level > 10 ? 0 : 100; // enriched ores up to +10 only. + + if (libconfig->setting_lookup_int(t, "Bonus", &i32) != 0) + bonus[level] += i32; + + if (level >= rnd_bonus_lv - 1) + rnd_bonus[level] = rnd_bonus_v * (level - rnd_bonus_lv + 2); + } + for (int i = 0; i < MAX_REFINE; i++) { + refine->p->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_NORMAL][i] = chance[REFINE_CHANCE_TYPE_NORMAL][i]; + refine->p->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_E_NORMAL][i] = chance[REFINE_CHANCE_TYPE_E_NORMAL][i]; + refine->p->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_ENRICHED][i] = chance[REFINE_CHANCE_TYPE_ENRICHED][i]; + refine->p->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_E_ENRICHED][i] = chance[REFINE_CHANCE_TYPE_E_ENRICHED][i]; + refine->p->dbs->refine_info[type].randombonus_max[i] = rnd_bonus[i]; + bonus[i] += bonus_per_level + (i > 0 ? bonus[i - 1] : 0); + refine->p->dbs->refine_info[type].bonus[i] = bonus[i]; + } + } else { + ShowWarning("status_readdb_refine_libconfig_sub: Missing refine rates for entry '%s' in \"%s\", skipping.\n", name, source); + return 0; + } + + return type + 1; +} + +/// @copydoc refine_interface_private::readdb_refine_libconfig() +static int refine_readdb_refine_libconfig(const char *filename) +{ + nullpo_retr(0, filename); + + bool duplicate[REFINE_TYPE_MAX]; + struct config_t refine_db_conf; + struct config_setting_t *r; + char filepath[256]; + int i = 0, count = 0; + + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, filename); + if (!libconfig->load_file(&refine_db_conf, filepath)) + return 0; + + memset(&duplicate, 0, sizeof(duplicate)); + + while((r = libconfig->setting_get_elem(refine_db_conf.root, i++))) { + char *name = config_setting_name(r); + int type = refine->p->readdb_refine_libconfig_sub(r, name, filename); + if (type != 0) { + if (duplicate[type - 1]) { + ShowWarning("status_readdb_refine_libconfig: duplicate entry for %s in \"%s\", overwriting previous entry...\n", name, filename); + } else { + duplicate[type - 1] = true; + } + count++; + } + } + libconfig->destroy(&refine_db_conf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + + return count; +} + +/// @copydoc refine_interface::init() +static int refine_init(bool minimal) +{ + if (minimal) + return 0; + + refine->p->readdb_refine_libconfig(DBPATH"refine_db.conf"); + return 0; +} + +/// @copydoc refine_interface::final() +static void refine_final(void) +{ +} + +void refine_defaults(void) +{ + refine = &refine_s; + refine->p = &refine_p; + refine->p->dbs = &refine_dbs; + + refine->p->readdb_refine_libconfig = refine_readdb_refine_libconfig; + refine->p->readdb_refine_libconfig_sub = refine_readdb_refine_libconfig_sub; + refine->p->announce_behavior_string2enum = refine_announce_behavior_string2enum; + refine->p->failure_behavior_string2enum = refine_failure_behavior_string2enum; + refine->p->readdb_refinery_ui_settings_items = refine_readdb_refinery_ui_settings_items; + refine->p->readdb_refinery_ui_settings_sub = refine_readdb_refinery_ui_settings_sub; + refine->p->readdb_refinery_ui_settings = refine_readdb_refinery_ui_settings; + refine->p->is_refinable = refine_is_refinable; + + refine->init = refine_init; + refine->final = refine_final; + refine->refinery_refine_request = refine_refinery_refine_request; + refine->refinery_add_item = refine_refinery_add_item; + refine->get_refine_chance = refine_get_refine_chance; + refine->get_bonus = refine_get_bonus; + refine->get_randombonus_max = refine_get_randombonus_max; +} diff --git a/src/map/refine.h b/src/map/refine.h new file mode 100644 index 000000000..410811e06 --- /dev/null +++ b/src/map/refine.h @@ -0,0 +1,148 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2019 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/>. +*/ + +#ifndef MAP_REFINE_H +#define MAP_REFINE_H + +/** @file + * Refine Interface. + **/ +#include "common/hercules.h" +#include "common/mmo.h" + +/* Defines */ +/** +* Max Refine available to your server +* Changing this limit requires edits to refine_db.conf +**/ +#ifdef RENEWAL + #define MAX_REFINE 20 +#else + #define MAX_REFINE 10 +#endif + +/* Forward Declarations */ +struct refine_interface_private; + +/* Enums */ +enum refine_type { + REFINE_TYPE_ARMOR = 0, + REFINE_TYPE_WEAPON1 = 1, + REFINE_TYPE_WEAPON2 = 2, + REFINE_TYPE_WEAPON3 = 3, + REFINE_TYPE_WEAPON4 = 4, +#ifndef REFINE_TYPE_MAX + REFINE_TYPE_MAX = 5 +#endif +}; + +enum refine_chance_type { + REFINE_CHANCE_TYPE_NORMAL = 0, // Normal Chance + REFINE_CHANCE_TYPE_ENRICHED = 1, // Enriched Ore Chance + REFINE_CHANCE_TYPE_E_NORMAL = 2, // Event Normal Ore Chance + REFINE_CHANCE_TYPE_E_ENRICHED = 3, // Event Enriched Ore Chance + REFINE_CHANCE_TYPE_MAX +}; + +enum refine_ui_failure_behavior { + REFINE_FAILURE_BEHAVIOR_DESTROY, + REFINE_FAILURE_BEHAVIOR_KEEP, + REFINE_FAILURE_BEHAVIOR_DOWNGRADE +}; + +/* Structure */ +struct s_refine_requirement { + int blacksmith_blessing; + int req_count; + unsigned int announce; + + struct { + int nameid; + int cost; + enum refine_chance_type type; + enum refine_ui_failure_behavior failure_behavior; + } req[MAX_REFINE_REQUIREMENTS]; +}; + +/** + * Refine Interface + **/ +struct refine_interface { + struct refine_interface_private *p; + + /** + * Initialize refine system + * @param minimal sets refine system to minimal mode in which it won't load or initialize itself + * @return returns 0 in-case of success 1 otherwise + **/ + int (*init)(bool minimal); + + /** + * Finalize refine system + **/ + void (*final)(void); + + /** + * Get the chance to upgrade a piece of equipment. + * @param wlv The weapon type of the item to refine (see see enum refine_type) + * @param refine The target refine level + * @return The chance to refine the item, in percent (0~100) + **/ + int (*get_refine_chance) (enum refine_type wlv, int refine_level, enum refine_chance_type type); + + /** + * Gets the attack/deffense bonus for the given equipment type and refine level + * @param equipment_type the equipment type + * @param refine_level the equipment refine level + * @return returns the bonus from refine db + **/ + int (*get_bonus) (enum refine_type equipment_type, int refine_level); + + /** + * Gets the maximum attack/deffense random bonus for the given equipment type and refine level + * @param equipment_type the equipment type + * @param refine_level the equipment refine level + * @return returns the bonus from refine db + **/ + int(*get_randombonus_max) (enum refine_type equipment_type, int refine_level); + + /** + * Validates and send Item addition packet to the client for refinery UI + * @param sd player session data. + * @param item_index the requested item index in inventory. + **/ + void (*refinery_add_item) (struct map_session_data *sd, int item_index); + + /** + * Processes an refine request through Refinery UI + * @param sd player session data + * @param item_index the index of the requested item + * @param material_id the refine material chosen by player + * @param use_blacksmith_blessing sets either if blacksmith blessing is requested to be used or not + **/ + void (*refinery_refine_request) (struct map_session_data *sd, int item_index, int material_id, bool use_blacksmith_blessing); +}; + +#ifdef HERCULES_CORE +void refine_defaults(void); +#endif + +HPShared struct refine_interface *refine; +#endif diff --git a/src/map/refine.p.h b/src/map/refine.p.h new file mode 100644 index 000000000..3247d15c9 --- /dev/null +++ b/src/map/refine.p.h @@ -0,0 +1,144 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2019 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/>. +*/ + +#ifndef MAP_REFINE_P_H +#define MAP_REFINE_P_H + +/** @file + * Private header for the refine interface. + **/ + +#include "refine.h" +#include "common/conf.h" +/* Enums */ +enum refine_announce_condition { + REFINE_ANNOUNCE_SUCCESS = 0x1, + REFINE_ANNOUNCE_FAILURE = 0x2, + REFINE_ANNOUNCE_ALWAYS = REFINE_ANNOUNCE_SUCCESS | REFINE_ANNOUNCE_FAILURE, +}; + +/* Structures */ +struct s_refine_info { + int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; //< success chance + int bonus[MAX_REFINE]; //< cumulative fixed bonus damage + int randombonus_max[MAX_REFINE]; //< cumulative maximum random bonus damage + struct s_refine_requirement refine_requirements[MAX_REFINE]; //< The requirements used for refinery UI +}; + +struct refine_interface_dbs { + struct s_refine_info refine_info[REFINE_TYPE_MAX]; +}; + +/** + * Refine Private Interface + **/ +struct refine_interface_private { + struct refine_interface_dbs *dbs; + + /** + * Processes a refine_db.conf entry. + * + * @param r Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param n Ordinal number of the entry, to be displayed in case of + * validation errors. + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return # of the validated entry, or 0 in case of failure. + **/ + int (*readdb_refine_libconfig_sub) (struct config_setting_t *r, const char *name, const char *source); + + /** + * Reads from a libconfig-formatted refine_db.conf file. + * + * @param *filename File name, relative to the database path. + * @return The number of found entries. + **/ + int (*readdb_refine_libconfig) (const char *filename); + + /** + * Converts refine database announce behvaior string to enum refine_announce_condition + * @param str the string to convert + * @param result pointer to where the converted value will be held + * @return true on success, false otherwise. + **/ + bool (*announce_behavior_string2enum) (const char *str, unsigned int *result); + + /** + * Converts refine database failure behvaior string to enum refine_ui_failure_behavior + * @param str the string to convert + * @param result pointer to where the converted value will be held + * @return true on success, false otherwise. + **/ + bool (*failure_behavior_string2enum) (const char *str, enum refine_ui_failure_behavior *result); + + /** + * Processes a refine_db.conf RefineryUISettings items entry. + * + * @param elem Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param req a pointer to requirements struct to fill with the item data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + bool (*readdb_refinery_ui_settings_items) (const struct config_setting_t *elem, struct s_refine_requirement *req, const char *name, const char *source); + + /** + * Processes a refine_db.conf RefineryUISettings entry. + * + * @param elem Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param type the type index in refine database to fill the data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + bool (*readdb_refinery_ui_settings_sub) (const struct config_setting_t *elem, int type, const char *name, const char *source); + + /** + * Reads a refine_db.conf RefineryUISettings entry and sends it to be processed. + * + * @param r Libconfig setting entry. It is expected to be valid and it + * won't be freed (it is care of the caller to do so if + * necessary) + * @param type the type index in refine database to fill the data + * @param name the current element name + * @param source Source of the entry (file name), to be displayed in case of + * validation errors. + * @return true on success, false otherwise. + **/ + int (*readdb_refinery_ui_settings) (const struct config_setting_t *r, int type, const char *name, const char *source); + + /** + * Checks if a given item in player's inventory is refineable. + * @param sd player session data. + * @param item_index the item index in player's inventory. + * @return true if item is refineable, false otherwise. + **/ + bool (*is_refinable) (struct map_session_data *sd, int item_index); +}; + +#endif diff --git a/src/map/rodex.c b/src/map/rodex.c index 33070c5f8..766fdc5ea 100644 --- a/src/map/rodex.c +++ b/src/map/rodex.c @@ -404,12 +404,12 @@ static void rodex_read_mail(struct map_session_data *sd, int64 mail_id) if (msg->opentype == RODEX_OPENTYPE_RETURN) { if (msg->sender_read == false) { - intif->rodex_updatemail(msg->id, 4); + intif->rodex_updatemail(sd, msg->id, 0, 4); msg->sender_read = true; } } else { if (msg->is_read == false) { - intif->rodex_updatemail(msg->id, 0); + intif->rodex_updatemail(sd, msg->id, 0, 0); msg->is_read = true; } } @@ -430,21 +430,36 @@ static void rodex_delete_mail(struct map_session_data *sd, int64 mail_id) nullpo_retv(msg); msg->is_deleted = true; - intif->rodex_updatemail(msg->id, 3); + intif->rodex_updatemail(sd, msg->id, 0, 3); clif->rodex_delete_mail(sd, msg->opentype, msg->id); } +/// give requested zeny from message to player +static void rodex_getZenyAck(struct map_session_data *sd, int64 mail_id, int8 opentype, int64 zeny) +{ + nullpo_retv(sd); + if (zeny <= 0) { + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); + return; + } + + if (pc->getzeny(sd, (int)zeny, LOG_TYPE_MAIL, NULL) != 0) { + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); + return; + } + + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_SUCCESS); +} + /// Gets attached zeny /// @param sd : Who's getting /// @param mail_id : Mail ID that we're getting zeny from static void rodex_get_zeny(struct map_session_data *sd, int8 opentype, int64 mail_id) { - struct rodex_message *msg; - nullpo_retv(sd); - msg = rodex->get_mail(sd, mail_id); + struct rodex_message *msg = rodex->get_mail(sd, mail_id); if (msg == NULL) { clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); @@ -456,16 +471,31 @@ static void rodex_get_zeny(struct map_session_data *sd, int8 opentype, int64 mai return; } - if (pc->getzeny(sd, (int)msg->zeny, LOG_TYPE_MAIL, NULL) != 0) { - clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); - return; - } - msg->type &= ~MAIL_TYPE_ZENY; msg->zeny = 0; - intif->rodex_updatemail(mail_id, 1); + intif->rodex_updatemail(sd, mail_id, opentype, 1); +} - clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_SUCCESS); +// give requested items from message to player +static void rodex_getItemsAck(struct map_session_data *sd, int64 mail_id, int8 opentype, int count, const struct rodex_item *items) +{ + nullpo_retv(sd); + nullpo_retv(items); + + for (int i = 0; i < count; ++i) { + const struct item *it = &items[i].item; + + if (it->nameid == 0) { + continue; + } + + if (pc->additem(sd, it, it->amount, LOG_TYPE_MAIL) != 0) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); + return; + } + } + + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEMS_SUCCESS); } /// Gets attached item @@ -473,14 +503,12 @@ static void rodex_get_zeny(struct map_session_data *sd, int8 opentype, int64 mai /// @param mail_id : Mail ID that we're getting items from static void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 mail_id) { - struct rodex_message *msg; - int weight = 0; - int empty_slots = 0, required_slots; - int i; - nullpo_retv(sd); - msg = rodex->get_mail(sd, mail_id); + int weight = 0; + int empty_slots = 0; + + struct rodex_message *msg = rodex->get_mail(sd, mail_id); if (msg == NULL) { clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FATAL_ERROR); @@ -492,7 +520,7 @@ static void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 ma return; } - for (i = 0; i < RODEX_MAX_ITEM; ++i) { + for (int i = 0; i < RODEX_MAX_ITEM; ++i) { if (msg->items[i].item.nameid != 0) { weight += itemdb->search(msg->items[i].item.nameid)->weight * msg->items[i].item.amount; } @@ -503,8 +531,8 @@ static void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 ma return; } - required_slots = msg->items_count; - for (i = 0; i < sd->status.inventorySize; ++i) { + int required_slots = msg->items_count; + for (int i = 0; i < sd->status.inventorySize; ++i) { if (sd->status.inventory[i].nameid == 0) { empty_slots++; } else if (itemdb->isstackable(sd->status.inventory[i].nameid) == 1) { @@ -529,27 +557,9 @@ static void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 ma return; } - for (i = 0; i < RODEX_MAX_ITEM; ++i) { - struct item *it = &msg->items[i].item; - - if (it->nameid == 0) { - continue; - } - - if (pc->additem(sd, it, it->amount, LOG_TYPE_MAIL) != 0) { - clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); - intif->rodex_updatemail(mail_id, 2); - return; - } else { - memset(it, 0x0, sizeof(*it)); - } - } - msg->type &= ~MAIL_TYPE_ITEM; msg->items_count = 0; - intif->rodex_updatemail(mail_id, 2); - - clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEMS_SUCCESS); + intif->rodex_updatemail(sd, mail_id, opentype, 2); } /// Cleans user's RoDEX related data @@ -668,4 +678,6 @@ void rodex_defaults(void) rodex->get_zeny = rodex_get_zeny; rodex->get_items = rodex_get_items; rodex->clean = rodex_clean; + rodex->getZenyAck = rodex_getZenyAck; + rodex->getItemsAck = rodex_getItemsAck; } diff --git a/src/map/rodex.h b/src/map/rodex.h index 56cc3a9d1..b6e7ca5b7 100644 --- a/src/map/rodex.h +++ b/src/map/rodex.h @@ -74,6 +74,8 @@ struct rodex_interface { void (*get_items) (struct map_session_data *sd, int8 opentype, int64 mail_id); void (*delete_mail) (struct map_session_data *sd, int64 mail_id); void (*clean) (struct map_session_data *sd, int8 flag); + void (*getZenyAck) (struct map_session_data *sd, int64 mail_id, int8 opentype, int64 zeny); + void (*getItemsAck) (struct map_session_data *sd, int64 mail_id, int8 opentype, int count, const struct rodex_item *items); }; #ifdef HERCULES_CORE diff --git a/src/map/script.c b/src/map/script.c index e99547fc7..60253bff8 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -52,6 +52,7 @@ #include "map/pet.h" #include "map/pet.h" #include "map/quest.h" +#include "map/refine.h" #include "map/skill.h" #include "map/status.h" #include "map/status.h" @@ -5961,6 +5962,19 @@ static BUILDIN(next) return true; } +/// Clears the NPC dialog and continues the script without press next button. +/// +/// mesclear(); +static BUILDIN(mesclear) +{ + struct map_session_data *sd = script->rid2sd(st); + + if (sd != NULL) + clif->scriptclear(sd, st->oid); + + return true; +} + /// Ends the script and displays the button 'close' on the npc dialog. /// The dialog is closed when the button is pressed. /// @@ -6947,59 +6961,60 @@ static BUILDIN(jobname) return true; } -/// Get input from the player. -/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if -/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. -/// For string inputs it returns 1 if the string was longer than 'max', -1 is -/// shorter than 'min' and 0 otherwise. -/// -/// input(<var>{,<min>{,<max>}}) -> <int> +/* + * Get input from the player. + * For numeric inputs the value is capped to the range [min,max]. Returns 1 if + * the value was higher than 'max', -1 if lower than 'min' and 0 otherwise. + * For string inputs it returns 1 if the string was longer than 'max', -1 is + * shorter than 'min' and 0 otherwise. + * + * input(<var>{,<min>{,<max>}}) -> <int> + */ static BUILDIN(input) { - struct script_data* data; - int64 uid; - const char* name; - int min; - int max; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - data = script_getdata(st,2); - if( !data_isreference(data) ) { + struct script_data *data = script_getdata(st, 2); + if (!data_isreference(data)) { ShowError("script:input: not a variable\n"); script->reportdata(data); st->state = END; return false; } - uid = reference_getuid(data); - name = reference_getname(data); - min = (script_hasdata(st,3) ? script_getnum(st,3) : script->config.input_min_value); - max = (script_hasdata(st,4) ? script_getnum(st,4) : script->config.input_max_value); + + int64 uid = reference_getuid(data); + const char *name = reference_getname(data); + int min = (script_hasdata(st, 3) ? script_getnum(st, 3) : script->config.input_min_value); + int max = (script_hasdata(st, 4) ? script_getnum(st, 4) : script->config.input_max_value); #ifdef SECURE_NPCTIMEOUT sd->npc_idle_type = NPCT_WAIT; #endif - if( !sd->state.menu_or_input ) { + if (!sd->state.menu_or_input) { // first invocation, display npc input box sd->state.menu_or_input = 1; st->state = RERUNLINE; - if( is_string_variable(name) ) - clif->scriptinputstr(sd,st->oid); - else - clif->scriptinput(sd,st->oid); + if (is_string_variable(name)) { + clif->scriptinputstr(sd, st->oid); + } else { + sd->npc_amount_min = min; + sd->npc_amount_max = max; + clif->scriptinput(sd, st->oid); + } } else { // take received text/value and store it in the designated variable sd->state.menu_or_input = 0; if (is_string_variable(name)) { int len = (int)strlen(sd->npc_str); - script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st,2)); + script->set_reg(st, sd, uid, name, sd->npc_str, script_getref(st, 2)); script_pushint(st, (len > max ? 1 : len < min ? -1 : 0)); } else { int amount = sd->npc_amount; script->set_reg(st, sd, uid, name, (const void *)h64BPTRSIZE(cap_value(amount,min,max)), script_getref(st,2)); - script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0)); + script_pushint(st, sd->npc_input_capped_range); } st->state = RUN; } @@ -8177,12 +8192,12 @@ static BUILDIN(getnameditem) } memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - item_tmp.amount=1; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] - item_tmp.card[2]=tsd->status.char_id; - item_tmp.card[3]=tsd->status.char_id >> 16; + item_tmp.nameid = nameid; + item_tmp.amount = 1; + item_tmp.identify = 1; + item_tmp.card[0] = CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] + item_tmp.card[2] = GetWord(tsd->status.char_id, 0); + item_tmp.card[3] = GetWord(tsd->status.char_id, 1); if(pc->additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) { script_pushint(st,0); return true; //Failed to add item, we will not drop if they don't fit @@ -9505,7 +9520,7 @@ static BUILDIN(getequippercentrefinery) if (i >= 0 && sd->status.inventory[i].nameid != 0 && sd->status.inventory[i].refine < MAX_REFINE) script_pushint(st, - status->get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int) sd->status.inventory[i].refine, (enum refine_chance_type) type)); + refine->get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int) sd->status.inventory[i].refine, (enum refine_chance_type) type)); else script_pushint(st, 0); @@ -13051,7 +13066,8 @@ enum mapinfo_info { MAPINFO_ID, MAPINFO_SIZE_X, MAPINFO_SIZE_Y, - MAPINFO_ZONE + MAPINFO_ZONE, + MAPINFO_NPC_COUNT }; static BUILDIN(getmapinfo) @@ -13076,7 +13092,7 @@ static BUILDIN(getmapinfo) } if (bl == NULL) { - ShowError("script:getmapinfo: map not supplied and NPC/PC not attached!\n"); + ShowError("buildin_getmapinfo: map not supplied and NPC/PC not attached!\n"); script_pushint(st, -3); return false; } @@ -13107,8 +13123,11 @@ static BUILDIN(getmapinfo) case MAPINFO_ZONE: script_pushstrcopy(st, map->list[m].zone->name); break; + case MAPINFO_NPC_COUNT: + script_pushint(st, map->list[m].npc_num); + break; default: - ShowError("script:getmapinfo: unknown option in second argument (%u).\n", mode); + ShowError("buildin_getmapinfo: unknown option in second argument (%u).\n", mode); script_pushint(st, -2); return false; } @@ -13914,7 +13933,8 @@ static BUILDIN(failedremovecards) if (sd->status.inventory[i].card[c] > 0 && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD) { cardflag = 1; - sd->status.inventory[i].card[c] = 0; + if (typefail == 1) + sd->status.inventory[i].card[c] = 0; if (typefail == 2) { // add cards to inventory, clear int flag; @@ -14832,48 +14852,55 @@ static BUILDIN(petloot) * Set arrays with info of all sd inventory : * @inventorylist_id, @inventorylist_amount, @inventorylist_equip, * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute, - * @inventorylist_card(0..3), @inventorylist_expire + * @inventorylist_card(0..3), + * @inventorylist_opt_id(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_val(0..MAX_ITEM_OPTIONS), + * @inventorylist_opt_param(0..MAX_ITEM_OPTIONS), + * @inventorylist_expire, @inventorylist_bound, @inventorylist_favorite, + * @inventorylist_idx * @inventorylist_count = scalar *------------------------------------------*/ static BUILDIN(getinventorylist) { struct map_session_data *sd = script->rid2sd(st); - char card_var[SCRIPT_VARNAME_LENGTH]; + char script_var[SCRIPT_VARNAME_LENGTH]; + int j = 0, k = 0; - int j=0,k; - if(!sd) return true; + if (sd == NULL) + return true; - for (int i = 0;i < sd->status.inventorySize; i++) { - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0) { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_id"), j),sd->status.inventory[i].nameid); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_amount"), j),sd->status.inventory[i].amount); - if(sd->status.inventory[i].equip) { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),pc->equippoint(sd,i)); + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0) { + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_id"), j), sd->status.inventory[i].nameid); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_amount"), j), sd->status.inventory[i].amount); + if (sd->status.inventory[i].equip != 0) { + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_equip"), j), pc->equippoint(sd, i)); } else { - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_equip"), j),0); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_equip"), j), 0); } - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_refine"), j),sd->status.inventory[i].refine); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_identify"), j),sd->status.inventory[i].identify); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_attribute"), j),sd->status.inventory[i].attribute); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_refine"), j), sd->status.inventory[i].refine); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_identify"), j), sd->status.inventory[i].identify); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_attribute"), j), sd->status.inventory[i].attribute); for (k = 0; k < MAX_SLOTS; k++) { - sprintf(card_var, "@inventorylist_card%d",k+1); - pc->setreg(sd,reference_uid(script->add_variable(card_var), j),sd->status.inventory[i].card[k]); + sprintf(script_var, "@inventorylist_card%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].card[k]); } for (k = 0; k < MAX_ITEM_OPTIONS; k++) { - sprintf(card_var, "@inventorylist_opt_id%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].index); - sprintf(card_var, "@inventorylist_opt_val%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].value); - sprintf(card_var, "@inventorylist_opt_param%d", k + 1); - pc->setreg(sd, reference_uid(script->add_variable(card_var), j), sd->status.inventory[i].option[k].param); + sprintf(script_var, "@inventorylist_opt_id%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].index); + sprintf(script_var, "@inventorylist_opt_val%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].value); + sprintf(script_var, "@inventorylist_opt_param%d", k + 1); + pc->setreg(sd, reference_uid(script->add_variable(script_var), j), sd->status.inventory[i].option[k].param); } - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); - pc->setreg(sd,reference_uid(script->add_variable("@inventorylist_bound"), j),sd->status.inventory[i].bound); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_expire"), j), sd->status.inventory[i].expire_time); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_bound"), j), sd->status.inventory[i].bound); + pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_favorite"), j), sd->status.inventory[i].favorite); pc->setreg(sd, reference_uid(script->add_variable("@inventorylist_idx"), j), i); j++; } } - pc->setreg(sd,script->add_variable("@inventorylist_count"),j); + pc->setreg(sd, script->add_variable("@inventorylist_count"), j); return true; } @@ -17778,6 +17805,17 @@ static BUILDIN(max) return true; } +static BUILDIN(cap_value) +{ + int value = script_getnum(st, 2); + int min = script_getnum(st, 3); + int max = script_getnum(st, 4); + + script_pushint(st, (int)cap_value(value, min, max)); + + return true; +} + static BUILDIN(md5) { const char *tmpstr; @@ -18962,6 +19000,20 @@ static BUILDIN(setunitdata) case UDT_ELELEVEL: setunitdata_check_bounds(4, 0, CHAR_MAX); break; + case UDT_GROUP: + { + setunitdata_check_bounds(4, 0, INT_MAX); + struct unit_data *ud = unit->bl2ud2(bl); + if (ud == NULL) { + ShowError("buildin_setunitdata: ud is NULL!\n"); + script_pushint(st, 0); + return false; + } + ud->groupId = script_getnum(st, 4); + clif->blname_ack(0, bl); // Send update to client. + script_pushint(st, 1); + return true; + } default: break; } @@ -19910,6 +19962,15 @@ static BUILDIN(getunitdata) return true;// no player attached } } + } else if (type == UDT_GROUP) { + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) { + ShowError("buildin_setunitdata: ud is NULL!\n"); + script_pushint(st, -1); + return false; + } + script_pushint(st, ud->groupId); + return true; } #define getunitdata_sub(idx__,var__) script->setd_sub(st,NULL,name,(idx__),(void *)h64BPTRSIZE((int)(var__)),data->ref); @@ -20373,6 +20434,46 @@ static BUILDIN(setunitname) return true; } +static BUILDIN(setunittitle) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); + if (bl == NULL) { + ShowWarning("buildin_setunittitle: Error in finding object with given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + struct unit_data *ud = unit->bl2ud2(bl); + if (ud == NULL) { + ShowWarning("buildin_setunittitle: Error in finding unit_data for given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + safestrncpy(ud->title, script_getstr(st, 3), NAME_LENGTH); + clif->blname_ack(0, bl); // Send update to client. + + return true; +} + +static BUILDIN(getunittitle) +{ + struct block_list *bl = map->id2bl(script_getnum(st, 2)); + if (bl == NULL) { + ShowWarning("buildin_getunitname: Error in finding object with given game ID %d!\n", script_getnum(st, 2)); + script_pushconststr(st, "Unknown"); + return false; + } + + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) { + ShowWarning("buildin_setunittitle: Error in finding unit_data for given game ID %d!\n", script_getnum(st, 2)); + return false; + } + + script_pushstrcopy(st, ud->title); + + return true; +} + /// Makes the unit walk to target position or target id /// Returns if it was successfull /// @@ -21201,7 +21302,6 @@ static BUILDIN(questinfo) return false; } - qi.nd = nd; qi.icon = quest->questinfo_validate_icon(icon); if (script_hasdata(st, 3)) { int color = script_getnum(st, 3); @@ -21213,7 +21313,9 @@ static BUILDIN(questinfo) qi.color = (unsigned char)color; } - map->add_questinfo(nd->bl.m, &qi); + VECTOR_ENSURE(nd->qi_data, 1, 1); + VECTOR_PUSH(nd->qi_data, qi); + map->add_questinfo(nd->bl.m, nd); return true; } @@ -21231,15 +21333,12 @@ static BUILDIN(setquestinfo) return false; } - qi = &VECTOR_LAST(map->list[nd->bl.m].qi_data); + qi = &VECTOR_LAST(nd->qi_data); if (qi == NULL) { ShowWarning("buildin_setquestinfo: no valide questinfo data has been found for this npc.\n"); return false; } - if (qi->nd != nd) { - ShowWarning("buildin_setquestinfo: invalid usage, setquestinfo must be used only after questinfo.\n"); - return false; - } + switch (type) { case QINFO_JOB: { @@ -24575,6 +24674,57 @@ static BUILDIN(getcalendartime) return true; } +enum consolemes_type { + CONSOLEMES_DEBUG = 0, + CONSOLEMES_ERROR = 1, + CONSOLEMES_WARNING = 2, + CONSOLEMES_INFO = 3, + CONSOLEMES_STATUS = 4, + CONSOLEMES_NOTICE = 5, +}; + +/*========================================== +* consolemes(<type>, "text") +*------------------------------------------*/ +static BUILDIN(consolemes) +{ + struct StringBuf buf; + StrBuf->Init(&buf); + int type = script_hasdata(st, 2) ? script_getnum(st, 2) : 0; + + if (!script->sprintf_helper(st, 3, &buf)) { + StrBuf->Destroy(&buf); + script_pushint(st, 0); + return false; + } + + switch (type) { + default: + case CONSOLEMES_DEBUG: + ShowDebug("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_ERROR: + ShowError("consolemes: (st->rid: %d) (st->oid: %d) %s\n", st->rid, st->oid, StrBuf->Value(&buf)); + break; + case CONSOLEMES_WARNING: + ShowWarning("consolemes: (st->rid: %d) (st->oid: %d) %s\n", st->rid, st->oid, StrBuf->Value(&buf)); + break; + case CONSOLEMES_INFO: + ShowInfo("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_STATUS: + ShowStatus("consolemes: %s\n", StrBuf->Value(&buf)); + break; + case CONSOLEMES_NOTICE: + ShowNotice("consolemes: %s\n", StrBuf->Value(&buf)); + break; + } + + StrBuf->Destroy(&buf); + script_pushint(st, 1); + return true; +} + /** place holder for the translation macro **/ static BUILDIN(_) { @@ -25170,6 +25320,35 @@ static BUILDIN(getInventorySize) return true; } +// force close roulette window if it opened +static BUILDIN(closeroulette) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + clif->roulette_close(sd); + return true; +} + +static BUILDIN(openrefineryui) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + if (battle_config.enable_refinery_ui == 0) { + script_pushint(st, 0); + return true; + } + + clif->OpenRefineryUI(sd); + script_pushint(st, 1); + return true; +} + /** * Adds a built-in script function. * @@ -25334,6 +25513,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(mes, "?"), BUILDIN_DEF(mesf, "s*"), BUILDIN_DEF(next,""), + BUILDIN_DEF(mesclear,""), BUILDIN_DEF(close,""), BUILDIN_DEF(close2,""), BUILDIN_DEF(menu,"sl*"), @@ -25494,7 +25674,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(sc_end,"i?"), BUILDIN_DEF(getstatus, "i?"), BUILDIN_DEF(getscrate,"ii?"), - BUILDIN_DEF(debugmes,"v*"), + BUILDIN_DEF_DEPRECATED(debugmes,"v*"), + BUILDIN_DEF(consolemes,"iv*"), BUILDIN_DEF2(catchpet,"pet","i"), BUILDIN_DEF2(birthpet,"bpet",""), BUILDIN_DEF(resetlvl,"i"), @@ -25661,6 +25842,7 @@ static void script_parse_builtin(void) // <--- List of mathematics commands BUILDIN_DEF(min, "i*"), BUILDIN_DEF(max, "i*"), + BUILDIN_DEF(cap_value, "iii"), BUILDIN_DEF(md5,"s"), BUILDIN_DEF(swap,"rr"), // [zBuffer] List of dynamic var commands ---> @@ -25705,6 +25887,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(getunitdata,"ii?"), BUILDIN_DEF(getunitname,"i"), BUILDIN_DEF(setunitname,"is"), + BUILDIN_DEF(getunittitle,"i"), + BUILDIN_DEF(setunittitle,"is"), BUILDIN_DEF(unitwalk,"ii?"), BUILDIN_DEF(unitkill,"i"), BUILDIN_DEF(unitwarp,"isii"), @@ -25918,6 +26102,9 @@ static void script_parse_builtin(void) BUILDIN_DEF(expandInventoryResult, "i"), BUILDIN_DEF(expandInventory, "i"), BUILDIN_DEF(getInventorySize, ""), + + BUILDIN_DEF(closeroulette, ""), + BUILDIN_DEF(openrefineryui, ""), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -25970,6 +26157,7 @@ static void script_hardcoded_constants(void) script->set_constant("MAX_MENU_LENGTH", MAX_MENU_LENGTH, false, false); script->set_constant("MOB_CLONE_START", MOB_CLONE_START, false, false); script->set_constant("MOB_CLONE_END", MOB_CLONE_END, false, false); + script->set_constant("MAX_NPC_PER_MAP", MAX_NPC_PER_MAP, false, false); script->constdb_comment("status options"); script->set_constant("Option_Nothing",OPTION_NOTHING,false, false); @@ -26220,6 +26408,15 @@ static void script_hardcoded_constants(void) script->set_constant("MAPINFO_SIZE_X", MAPINFO_SIZE_X, false, false); script->set_constant("MAPINFO_SIZE_Y", MAPINFO_SIZE_Y, false, false); script->set_constant("MAPINFO_ZONE", MAPINFO_ZONE, false, false); + script->set_constant("MAPINFO_NPC_COUNT", MAPINFO_NPC_COUNT, false, false); + + script->constdb_comment("consolemes options"); + script->set_constant("CONSOLEMES_DEBUG", CONSOLEMES_DEBUG, false, false); + script->set_constant("CONSOLEMES_ERROR", CONSOLEMES_ERROR, false, false); + script->set_constant("CONSOLEMES_WARNING", CONSOLEMES_WARNING, false, false); + script->set_constant("CONSOLEMES_INFO", CONSOLEMES_INFO, false, false); + script->set_constant("CONSOLEMES_STATUS", CONSOLEMES_STATUS, false, false); + script->set_constant("CONSOLEMES_NOTICE", CONSOLEMES_NOTICE, false, false); script->constdb_comment("set/getiteminfo options"); script->set_constant("ITEMINFO_BUYPRICE", ITEMINFO_BUYPRICE, false, false); @@ -26391,6 +26588,66 @@ static void script_hardcoded_constants(void) script->set_constant("NST_CUSTOM", NST_CUSTOM, false, false); script->set_constant("NST_BARTER", NST_BARTER, false, false); + script->constdb_comment("script unit data types"); + script->set_constant("UDT_TYPE", UDT_TYPE, false, false); + script->set_constant("UDT_SIZE", UDT_SIZE, false, false); + script->set_constant("UDT_LEVEL", UDT_LEVEL, false, false); + script->set_constant("UDT_HP", UDT_HP, false, false); + script->set_constant("UDT_MAXHP", UDT_MAXHP, false, false); + script->set_constant("UDT_SP", UDT_SP, false, false); + script->set_constant("UDT_MAXSP", UDT_MAXSP, false, false); + script->set_constant("UDT_MASTERAID", UDT_MASTERAID, false, false); + script->set_constant("UDT_MASTERCID", UDT_MASTERCID, false, false); + script->set_constant("UDT_MAPIDXY", UDT_MAPIDXY, false, true); // for setunitdata use *unitwarp, for getunitdata use *getmapxy + script->set_constant("UDT_WALKTOXY", UDT_WALKTOXY, false, true); // use *unitwalk + script->set_constant("UDT_SPEED", UDT_SPEED, false, false); + script->set_constant("UDT_MODE", UDT_MODE, false, false); + script->set_constant("UDT_AI", UDT_AI, false, false); + script->set_constant("UDT_SCOPTION", UDT_SCOPTION, false, false); + script->set_constant("UDT_SEX", UDT_SEX, false, false); + script->set_constant("UDT_CLASS", UDT_CLASS, false, false); + script->set_constant("UDT_HAIRSTYLE", UDT_HAIRSTYLE, false, false); + script->set_constant("UDT_HAIRCOLOR", UDT_HAIRCOLOR, false, false); + script->set_constant("UDT_HEADBOTTOM", UDT_HEADBOTTOM, false, false); + script->set_constant("UDT_HEADMIDDLE", UDT_HEADMIDDLE, false, false); + script->set_constant("UDT_HEADTOP", UDT_HEADTOP, false, false); + script->set_constant("UDT_CLOTHCOLOR", UDT_CLOTHCOLOR, false, false); + script->set_constant("UDT_SHIELD", UDT_SHIELD, false, false); + script->set_constant("UDT_WEAPON", UDT_WEAPON, false, false); + script->set_constant("UDT_LOOKDIR", UDT_LOOKDIR, false, false); + script->set_constant("UDT_CANMOVETICK", UDT_CANMOVETICK, false, false); + script->set_constant("UDT_STR", UDT_STR, false, false); + script->set_constant("UDT_AGI", UDT_AGI, false, false); + script->set_constant("UDT_VIT", UDT_VIT, false, false); + script->set_constant("UDT_INT", UDT_INT, false, false); + script->set_constant("UDT_DEX", UDT_DEX, false, false); + script->set_constant("UDT_LUK", UDT_LUK, false, false); + script->set_constant("UDT_ATKRANGE", UDT_ATKRANGE, false, false); + script->set_constant("UDT_ATKMIN", UDT_ATKMIN, false, false); + script->set_constant("UDT_ATKMAX", UDT_ATKMAX, false, false); + script->set_constant("UDT_MATKMIN", UDT_MATKMIN, false, false); + script->set_constant("UDT_MATKMAX", UDT_MATKMAX, false, false); + script->set_constant("UDT_DEF", UDT_DEF, false, false); + script->set_constant("UDT_MDEF", UDT_MDEF, false, false); + script->set_constant("UDT_HIT", UDT_HIT, false, false); + script->set_constant("UDT_FLEE", UDT_FLEE, false, false); + script->set_constant("UDT_PDODGE", UDT_PDODGE, false, false); + script->set_constant("UDT_CRIT", UDT_CRIT, false, false); + script->set_constant("UDT_RACE", UDT_RACE, false, false); + script->set_constant("UDT_ELETYPE", UDT_ELETYPE, false, false); + script->set_constant("UDT_ELELEVEL", UDT_ELELEVEL, false, false); + script->set_constant("UDT_AMOTION", UDT_AMOTION, false, false); + script->set_constant("UDT_ADELAY", UDT_ADELAY, false, false); + script->set_constant("UDT_DMOTION", UDT_DMOTION, false, false); + script->set_constant("UDT_HUNGER", UDT_HUNGER, false, false); + script->set_constant("UDT_INTIMACY", UDT_INTIMACY, false, false); + script->set_constant("UDT_LIFETIME", UDT_LIFETIME, false, false); + script->set_constant("UDT_MERC_KILLCOUNT", UDT_MERC_KILLCOUNT, false, false); + script->set_constant("UDT_STATPOINT", UDT_STATPOINT, false, false); + script->set_constant("UDT_ROBE", UDT_ROBE, false, false); + script->set_constant("UDT_BODY2", UDT_BODY2, false, false); + script->set_constant("UDT_GROUP", UDT_GROUP, false, false); + script->constdb_comment("Renewal"); #ifdef RENEWAL script->set_constant("RENEWAL", 1, false, false); diff --git a/src/map/script.h b/src/map/script.h index 008da9c3c..4c1cc168d 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -431,6 +431,7 @@ enum script_unit_data_types { UDT_STATPOINT, UDT_ROBE, UDT_BODY2, + UDT_GROUP, UDT_MAX }; diff --git a/src/map/searchstore.c b/src/map/searchstore.c index 0c6fa3555..c991e38c4 100644 --- a/src/map/searchstore.c +++ b/src/map/searchstore.c @@ -345,7 +345,7 @@ static void searchstore_clearremote(struct map_session_data *sd) } /// receives results from a store-specific callback -static bool searchstore_result(struct map_session_data *sd, unsigned int store_id, int account_id, const char *store_name, int nameid, unsigned short amount, unsigned int price, const int *card, unsigned char refine, const struct item_option *option) +static bool searchstore_result(struct map_session_data *sd, unsigned int store_id, int account_id, const char *store_name, int nameid, unsigned short amount, unsigned int price, const int *card, unsigned char refine_level, const struct item_option *option) { struct s_search_store_info_item* ssitem; @@ -364,7 +364,7 @@ static bool searchstore_result(struct map_session_data *sd, unsigned int store_i ssitem->amount = amount; ssitem->price = price; memcpy(ssitem->card, card, sizeof(ssitem->card)); - ssitem->refine = refine; + ssitem->refine = refine_level; memcpy(ssitem->option, option, sizeof(ssitem->option)); return true; diff --git a/src/map/searchstore.h b/src/map/searchstore.h index e5ccfd300..71d562679 100644 --- a/src/map/searchstore.h +++ b/src/map/searchstore.h @@ -109,7 +109,7 @@ struct searchstore_interface { void (*click) (struct map_session_data* sd, int account_id, int store_id, int nameid); bool (*queryremote) (struct map_session_data* sd, int account_id); void (*clearremote) (struct map_session_data* sd); - bool (*result) (struct map_session_data* sd, unsigned int store_id, int account_id, const char* store_name, int nameid, unsigned short amount, unsigned int price, const int* card, unsigned char refine, const struct item_option *option); + bool (*result) (struct map_session_data* sd, unsigned int store_id, int account_id, const char* store_name, int nameid, unsigned short amount, unsigned int price, const int* card, unsigned char refine_level, const struct item_option *option); }; #ifdef HERCULES_CORE diff --git a/src/map/skill.c b/src/map/skill.c index 633a73d67..a259829ef 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -44,6 +44,7 @@ #include "map/path.h" #include "map/pc.h" #include "map/pet.h" +#include "map/refine.h" #include "map/script.h" #include "map/status.h" #include "map/unit.h" @@ -4155,10 +4156,9 @@ static int skill_reveal_trap(struct block_list *bl, va_list ap) Assert_ret(bl->type == BL_SKILL); su = BL_UCAST(BL_SKILL, bl); - if (su->alive && su->group && skill->get_inf2(su->group->skill_id)&INF2_TRAP) { //Reveal trap. - //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] - //clif->changetraplook(bl, su->group->unit_id); - clif->getareachar_skillunit(&su->bl,su,AREA); + if (su->alive && su->group && skill->get_inf2(su->group->skill_id) & INF2_HIDDEN_TRAP) { //Reveal trap. + su->visible = true; + clif->skillunit_update(bl); return 1; } return 0; @@ -11044,9 +11044,10 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill map->foreachinarea(status->change_timer_sub, src->m, x-r, y-r, x+r,y+r,BL_CHAR, src,NULL,SC_SIGHT,tick); - if(battle_config.traps_setting&1) - map->foreachinarea(skill_reveal_trap, - src->m, x-r, y-r, x+r, y+r, BL_SKILL); + if (battle_config.trap_visibility != 0) { + map->foreachinarea(skill_reveal_trap, + src->m, x - r, y - r, x + r, y + r, BL_SKILL); + } break; case SR_RIDEINLIGHTNING: @@ -12763,6 +12764,13 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR,0)-1); } + if (sg->skill_id == HT_ANKLESNARE + || (battle_config.trap_trigger == 1 && skill->get_inf2(sg->skill_id) & INF2_HIDDEN_TRAP) + ) { + src->visible = true; + clif->skillunit_update(&src->bl); + } + switch (sg->unit_id) { case UNT_FIREWALL: case UNT_KAEN: { @@ -12915,10 +12923,11 @@ static int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *b clif->fixpos(bl); } sg->val2 = bl->id; - } else + } else { sec = 3000; //Couldn't trap it? + } + if( sg->unit_id == UNT_ANKLESNARE ) { - clif->skillunit_update(&src->bl); /** * If you're snared from a trap that was invisible this makes the trap be * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE") @@ -16204,7 +16213,7 @@ static void skill_weaponrefine(struct map_session_data *sd, int idx) return; } - per = status->get_refine_chance(ditem->wlv, (int)item->refine, REFINE_CHANCE_TYPE_NORMAL) * 10; + per = refine->get_refine_chance(ditem->wlv, (int)item->refine, REFINE_CHANCE_TYPE_NORMAL) * 10; // Aegis leaked formula. [malufett] if (sd->status.class == JOB_MECHANIC_T) @@ -17075,6 +17084,14 @@ static struct skill_unit *skill_initunit(struct skill_unit_group *group, int idx su->val1=val1; su->val2 = val2; su->prev = 0; + su->visible = true; + + if (skill->get_inf2(group->skill_id) & INF2_HIDDEN_TRAP + && ((battle_config.trap_visibility == 1 && map_flag_vs(group->map)) // invisible in PvP/GvG + || battle_config.trap_visibility == 2 // always invisible + )) { + su->visible = false; + } idb_put(skill->unit_db, su->bl.id, su); map->addiddb(&su->bl); @@ -20240,6 +20257,12 @@ static void skill_validate_skillinfo(struct config_setting_t *conf, struct s_ski } else { sk->inf2 &= ~INF2_ALLOW_REPRODUCE; } + } else if (strcmpi(type, "HiddenTrap") == 0) { + if (on) { + sk->inf2 |= INF2_HIDDEN_TRAP; + } else { + sk->inf2 &= ~INF2_HIDDEN_TRAP; + } } else if (strcmpi(type, "None") != 0) { skilldb_invalid_error(type, config_setting_name(t), sk->nameid); } diff --git a/src/map/skill.h b/src/map/skill.h index 0ace19927..5da37d129 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -124,6 +124,7 @@ enum e_skill_inf2 { INF2_FREE_CAST_REDUCED = 0x10000, INF2_SHOW_SKILL_SCALE = 0x20000, INF2_ALLOW_REPRODUCE = 0x40000, + INF2_HIDDEN_TRAP = 0x80000, // Traps that are hidden (based on trap_visiblity battle conf) }; @@ -1806,6 +1807,7 @@ struct skill_unit { int limit; int val1,val2; + bool visible; short alive,range; int prev; }; diff --git a/src/map/status.c b/src/map/status.c index f06bb0330..63e71c9dc 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -38,6 +38,7 @@ #include "map/path.h" #include "map/pc.h" #include "map/pet.h" +#include "map/refine.h" #include "map/script.h" #include "map/skill.h" #include "map/skill.h" @@ -2577,18 +2578,18 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o r = 0; if (r) - wa->atk2 = status->dbs->refine_info[wlv].bonus[r-1] / 100; + wa->atk2 = refine->get_bonus(wlv, r) / 100; #ifdef RENEWAL wa->matk += sd->inventory_data[index]->matk; wa->wlv = wlv; if( r && sd->weapontype1 != W_BOW ) // renewal magic attack refine bonus - wa->matk += status->dbs->refine_info[wlv].bonus[r-1] / 100; + wa->matk += refine->get_bonus(wlv, r) / 100; #endif //Overrefined bonus. if (r) - wd->overrefine = status->dbs->refine_info[wlv].randombonus_max[r-1] / 100; + wd->overrefine = refine->get_randombonus_max(wlv, r) / 100; wa->range += sd->inventory_data[index]->range; if(sd->inventory_data[index]->script) { @@ -2623,7 +2624,7 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o r = 0; if (r) - refinedef += status->dbs->refine_info[REFINE_TYPE_ARMOR].bonus[r-1]; + refinedef += refine->get_bonus(REFINE_TYPE_ARMOR, r); if(sd->inventory_data[index]->script) { if( i == EQI_HAND_L ) //Shield @@ -3853,7 +3854,7 @@ static void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag temp = bst->batk - status->base_atk(bl,bst); if (temp) { temp += st->batk; - st->batk = cap_value(temp, 0, USHRT_MAX); + st->batk = cap_value(temp, battle_config.batk_min, battle_config.batk_max); } st->batk = status->calc_batk(bl, sc, st->batk, true); } @@ -4448,7 +4449,7 @@ static int status_base_amotion_pc(struct map_session_data *sd, struct status_dat return amotion; } -static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *st) +static int status_base_atk(const struct block_list *bl, const struct status_data *st) { int flag = 0, str, dex, dstr; @@ -4505,42 +4506,50 @@ static unsigned short status_base_atk(const struct block_list *bl, const struct if (bl->type == BL_PC) str += dex / 5 + st->luk / 5; #endif // RENEWAL - return cap_value(str, 0, USHRT_MAX); + return cap_value(str, battle_config.batk_min, battle_config.batk_max); } -static unsigned short status_base_matk_min(const struct status_data *st) +static int status_base_matk_min(const struct status_data *st) { nullpo_ret(st); #ifdef RENEWAL Assert_ret(0); return 0; #else // not RENEWAL - return st->int_ + (st->int_ / 7) * (st->int_ / 7); + int matk = st->int_ + (st->int_ / 7) * (st->int_ / 7); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); #endif // RENEWAL } -static unsigned short status_base_matk_max(const struct status_data *st) +static int status_base_matk_max(const struct status_data *st) { nullpo_ret(st); - return st->int_ + (st->int_ / 5)*(st->int_ / 5); + int matk = st->int_ + (st->int_ / 5) * (st->int_ / 5); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); } -static unsigned short status_base_matk(struct block_list *bl, const struct status_data *st, int level) +static int status_base_matk(struct block_list *bl, const struct status_data *st, int level) { #ifdef RENEWAL nullpo_ret(bl); nullpo_ret(st); - switch ( bl->type ) { + int matk = 0; + switch (bl->type) { case BL_MOB: - return st->int_ + level; + matk = st->int_ + level; + break; case BL_HOM: - return status_get_homint(st, BL_UCCAST(BL_HOM, bl)) + level; + matk = status_get_homint(st, BL_UCCAST(BL_HOM, bl)) + level; + break; case BL_MER: - return st->int_ + st->int_ / 5 * st->int_ / 5; + matk = st->int_ + st->int_ / 5 * st->int_ / 5; + break; case BL_PC: default: // temporary until all are formulated - return st->int_ + (st->int_ / 2) + (st->dex / 5) + (st->luk / 3) + (level / 4); + matk = st->int_ + (st->int_ / 2) + (st->dex / 5) + (st->luk / 3) + (level / 4); + break; } + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); #else Assert_ret(0); return 0; @@ -4597,7 +4606,7 @@ static void status_calc_misc(struct block_list *bl, struct status_data *st, int if ( st->batk ) { int temp = st->batk + status->base_atk(bl, st); - st->batk = cap_value(temp, 0, USHRT_MAX); + st->batk = cap_value(temp, battle_config.batk_min, battle_config.batk_max); } else st->batk = status->base_atk(bl, st); if ( st->cri ) { @@ -4994,17 +5003,17 @@ static unsigned short status_calc_luk(struct block_list *bl, struct status_chang return (unsigned short)cap_value(luk, 0, USHRT_MAX); } -static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk, bool viewable) +static int status_calc_batk(struct block_list *bl, struct status_change *sc, int batk, bool viewable) { nullpo_ret(bl); if(!sc || !sc->count) - return cap_value(batk,0,USHRT_MAX); + return cap_value(batk, battle_config.batk_min, battle_config.batk_max); if( !viewable ){ /* some statuses that are hidden in the status window */ if(sc->data[SC_PLUSATTACKPOWER]) batk += sc->data[SC_PLUSATTACKPOWER]->val1; - return (unsigned short)cap_value(batk,0,USHRT_MAX); + return cap_value(batk, battle_config.batk_min, battle_config.batk_max); } #ifndef RENEWAL if(sc->data[SC_PLUSATTACKPOWER]) @@ -5087,14 +5096,14 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan if (sc->data[SC_SHRIMP]) batk += batk * sc->data[SC_SHRIMP]->val2 / 100; - return (unsigned short)cap_value(batk,0,USHRT_MAX); + return cap_value(batk, battle_config.batk_min, battle_config.batk_max); } -static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk, bool viewable) +static int status_calc_watk(struct block_list *bl, struct status_change *sc, int watk, bool viewable) { nullpo_ret(bl); if(!sc || !sc->count) - return cap_value(watk,0,USHRT_MAX); + return cap_value(watk, battle_config.watk_min, battle_config.watk_max); if( !viewable ){ /* some statuses that are hidden in the status window */ @@ -5102,7 +5111,7 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan watk -= sc->data[SC_WATER_BARRIER]->val3; if(sc->data[SC_GENTLETOUCH_CHANGE] && sc->data[SC_GENTLETOUCH_CHANGE]->val2) watk += sc->data[SC_GENTLETOUCH_CHANGE]->val2; - return (unsigned short)cap_value(watk,0,USHRT_MAX); + return cap_value(watk, battle_config.watk_min, battle_config.watk_max); } #ifndef RENEWAL if(sc->data[SC_IMPOSITIO]) @@ -5180,14 +5189,14 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan if (sc->data[SC_CATNIPPOWDER]) watk -= watk * sc->data[SC_CATNIPPOWDER]->val2 / 100; - return (unsigned short)cap_value(watk,0,USHRT_MAX); + return cap_value(watk, battle_config.watk_min, battle_config.watk_max); } -static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk) +static int status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk) { #ifdef RENEWAL if (!sc || !sc->count) - return cap_value(matk,0,USHRT_MAX); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); if (sc->data[SC_PLUSMAGICPOWER]) matk += sc->data[SC_PLUSMAGICPOWER]->val1; if (sc->data[SC_MATKFOOD]) @@ -5208,22 +5217,22 @@ static unsigned short status_calc_ematk(struct block_list *bl, struct status_cha matk += 25 * sc->data[SC_IZAYOI]->val1; if (sc->data[SC_SHRIMP]) matk += matk * sc->data[SC_SHRIMP]->val2 / 100; - return (unsigned short)cap_value(matk,0,USHRT_MAX); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); #else return 0; #endif } -static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk, bool viewable) +static int status_calc_matk(struct block_list *bl, struct status_change *sc, int matk, bool viewable) { if (!sc || !sc->count) - return cap_value(matk,0,USHRT_MAX); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); if (!viewable) { /* some statuses that are hidden in the status window */ if (sc->data[SC_MINDBREAKER]) matk += matk * sc->data[SC_MINDBREAKER]->val2 / 100; - return (unsigned short)cap_value(matk, 0, USHRT_MAX); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); } #ifndef RENEWAL @@ -5281,17 +5290,17 @@ static unsigned short status_calc_matk(struct block_list *bl, struct status_chan if (sc->data[SC_MAGIC_CANDY]) matk += sc->data[SC_MAGIC_CANDY]->val1; - return (unsigned short)cap_value(matk, 0, USHRT_MAX); + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); } -static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical, bool viewable) +static int status_calc_critical(struct block_list *bl, struct status_change *sc, int critical, bool viewable) { if (!sc || !sc->count) - return cap_value(critical, 10, SHRT_MAX); + return cap_value(critical, battle_config.critical_min, battle_config.critical_max); if (!viewable) { /* some statuses that are hidden in the status window */ - return (short)cap_value(critical, 10, SHRT_MAX); + return cap_value(critical, battle_config.critical_min, battle_config.critical_max); } if (sc->data[SC_CRITICALPERCENT]) @@ -5322,20 +5331,20 @@ static signed short status_calc_critical(struct block_list *bl, struct status_ch if (sc->data[SC_BUCHEDENOEL]) critical += sc->data[SC_BUCHEDENOEL]->val4 * 10; - return (short)cap_value(critical, 10, SHRT_MAX); + return cap_value(critical, battle_config.critical_min, battle_config.critical_max); } -static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit, bool viewable) +static int status_calc_hit(struct block_list *bl, struct status_change *sc, int hit, bool viewable) { if (!sc || !sc->count) - return cap_value(hit, 1, SHRT_MAX); + return cap_value(hit, battle_config.hit_min, battle_config.hit_max); if (!viewable) { /* some statuses that are hidden in the status window */ if (sc->data[SC_MTF_ASPD]) hit += sc->data[SC_MTF_ASPD]->val2; - return (short)cap_value(hit, 1, SHRT_MAX); + return cap_value(hit, battle_config.hit_min, battle_config.hit_max); } if (sc->data[SC_INCHIT]) @@ -5377,26 +5386,26 @@ static signed short status_calc_hit(struct block_list *bl, struct status_change if (sc->data[SC_BUCHEDENOEL]) hit += sc->data[SC_BUCHEDENOEL]->val3; - return (short)cap_value(hit, 1, SHRT_MAX); + return cap_value(hit, battle_config.hit_min, battle_config.hit_max); } -static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee, bool viewable) +static int status_calc_flee(struct block_list *bl, struct status_change *sc, int flee, bool viewable) { nullpo_retr(1, bl); if (bl->type == BL_PC) { if (map_flag_gvg2(bl->m)) flee -= flee * battle_config.gvg_flee_penalty / 100; - else if( map->list[bl->m].flag.battleground ) + else if (map->list[bl->m].flag.battleground) flee -= flee * battle_config.bg_flee_penalty / 100; } if (!sc || !sc->count) - return cap_value(flee, 1, SHRT_MAX); + return cap_value(flee, battle_config.flee_min, battle_config.flee_max); if (!viewable) { /* some statuses that are hidden in the status window */ - return (short)cap_value(flee, 1, SHRT_MAX); + return cap_value(flee, battle_config.flee_min, battle_config.flee_max); } if (sc->data[SC_INCFLEE]) @@ -5474,17 +5483,17 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change if (sc->data[SC_MYSTICPOWDER]) flee += sc->data[SC_MYSTICPOWDER]->val2; - return (short)cap_value(flee, 1, SHRT_MAX); + return cap_value(flee, battle_config.flee_min, battle_config.flee_max); } -static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2, bool viewable) +static int status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2, bool viewable) { if(!sc || !sc->count) - return cap_value(flee2,10,SHRT_MAX); + return cap_value(flee2, battle_config.flee2_min, battle_config.flee2_max); if( !viewable ){ /* some statuses that are hidden in the status window */ - return (short)cap_value(flee2,10,SHRT_MAX); + return cap_value(flee2, battle_config.flee2_min, battle_config.flee2_max); } if(sc->data[SC_PLUSAVOIDVALUE]) @@ -5496,7 +5505,7 @@ static signed short status_calc_flee2(struct block_list *bl, struct status_chang if (sc->data[SC_FREYJASCROLL]) flee2 += sc->data[SC_FREYJASCROLL]->val2; - return (short)cap_value(flee2,10,SHRT_MAX); + return cap_value(flee2, battle_config.flee2_min, battle_config.flee2_max); } static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def, bool viewable) @@ -8474,12 +8483,12 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = 0; val4 = 0; max_stat = (status->get_lv(bl)-10<50)?status->get_lv(bl)-10:50; - stat = max(0, max_stat - status2->str ); val3 |= cap_value(stat,0,0xFF)<<16; - stat = max(0, max_stat - status2->agi ); val3 |= cap_value(stat,0,0xFF)<<8; - stat = max(0, max_stat - status2->vit ); val3 |= cap_value(stat,0,0xFF); - stat = max(0, max_stat - status2->int_); val4 |= cap_value(stat,0,0xFF)<<16; - stat = max(0, max_stat - status2->dex ); val4 |= cap_value(stat,0,0xFF)<<8; - stat = max(0, max_stat - status2->luk ); val4 |= cap_value(stat,0,0xFF); + stat = max(0, max_stat - (int)status2->str ); val3 |= cap_value(stat,0,0xFF)<<16; + stat = max(0, max_stat - (int)status2->agi ); val3 |= cap_value(stat,0,0xFF)<<8; + stat = max(0, max_stat - (int)status2->vit ); val3 |= cap_value(stat,0,0xFF); + stat = max(0, max_stat - (int)status2->int_); val4 |= cap_value(stat,0,0xFF)<<16; + stat = max(0, max_stat - (int)status2->dex ); val4 |= cap_value(stat,0,0xFF)<<8; + stat = max(0, max_stat - (int)status2->luk ); val4 |= cap_value(stat,0,0xFF); } break; case SC_SWORDREJECT: @@ -12508,10 +12517,10 @@ static int status_get_weapon_atk(struct block_list *bl, struct weapon_atk *watk, if ( bl->type == BL_PC && !(flag & 2) ) { const struct map_session_data *sd = BL_UCCAST(BL_PC, bl); - short index = sd->equip_index[EQI_HAND_R], refine; + short index = sd->equip_index[EQI_HAND_R], refine_level; if ( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON - && (refine = sd->status.inventory[index].refine) < 16 && refine ) { - int r = status->dbs->refine_info[watk->wlv].randombonus_max[refine + (4 - watk->wlv)] / 100; + && (refine_level = sd->status.inventory[index].refine) < 16 && refine_level) { + int r = refine->get_randombonus_max(watk->wlv, refine_level + (4 - watk->wlv) + 1) / 100; if ( r ) max += (rnd() % 100) % r + 1; } @@ -12623,10 +12632,10 @@ static void status_get_matk_sub(struct block_list *bl, int flag, unsigned short #ifdef RENEWAL if ( sd && !(flag & 2) ) { - short index = sd->equip_index[EQI_HAND_R], refine; + short index = sd->equip_index[EQI_HAND_R], refine_level; if ( index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON - && (refine = sd->status.inventory[index].refine) < 16 && refine ) { - int r = status->dbs->refine_info[sd->inventory_data[index]->wlv].randombonus_max[refine + (4 - sd->inventory_data[index]->wlv)] / 100; + && (refine_level = sd->status.inventory[index].refine) < 16 && refine_level) { + int r = refine->get_randombonus_max(sd->inventory_data[index]->wlv, refine_level + (4 - sd->inventory_data[index]->wlv) + 1) / 100; if ( r ) *matk_max += (rnd() % 100) % r + 1; } @@ -13076,25 +13085,6 @@ static int status_natural_heal_timer(int tid, int64 tick, int id, intptr_t data) return 0; } -/** - * Get the chance to upgrade a piece of equipment. - * @param wlv The weapon type of the item to refine (see see enum refine_type) - * @param refine The target refine level - * @return The chance to refine the item, in percent (0~100) - */ -static int status_get_refine_chance(enum refine_type wlv, int refine, enum refine_chance_type type) -{ - Assert_ret((int)wlv >= REFINE_TYPE_ARMOR && wlv < REFINE_TYPE_MAX); - - if (refine < 0 || refine >= MAX_REFINE) - return 0; - - if (type >= REFINE_CHANCE_TYPE_MAX) - return 0; - - return status->dbs->refine_info[wlv].chance[type][refine]; -} - static int status_get_sc_type(sc_type type) { @@ -13405,171 +13395,6 @@ static bool status_readdb_sizefix(char *fields[], int columns, int current) return true; } -/** - * Processes a refine_db.conf entry. - * - * @param r Libconfig setting entry. It is expected to be valid and it - * won't be freed (it is care of the caller to do so if - * necessary) - * @param n Ordinal number of the entry, to be displayed in case of - * validation errors. - * @param source Source of the entry (file name), to be displayed in case of - * validation errors. - * @return # of the validated entry, or 0 in case of failure. - */ -static int status_readdb_refine_libconfig_sub(struct config_setting_t *r, const char *name, const char *source) -{ - struct config_setting_t *rate = NULL; - int type = REFINE_TYPE_ARMOR, bonus_per_level = 0, rnd_bonus_v = 0, rnd_bonus_lv = 0; - char lv[4]; - nullpo_ret(r); - nullpo_ret(name); - nullpo_ret(source); - - if (strncmp(name, "Armors", 6) == 0) { - type = REFINE_TYPE_ARMOR; - } else if (strncmp(name, "WeaponLevel", 11) != 0 || !strspn(&name[strlen(name)-1], "0123456789") || (type = atoi(strncpy(lv, name+11, 2))) == REFINE_TYPE_ARMOR) { - ShowError("status_readdb_refine_libconfig_sub: Invalid key name for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - if (type < REFINE_TYPE_ARMOR || type >= REFINE_TYPE_MAX) { - ShowError("status_readdb_refine_libconfig_sub: Out of range level for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - if (!libconfig->setting_lookup_int(r, "StatsPerLevel", &bonus_per_level)) { - ShowWarning("status_readdb_refine_libconfig_sub: Missing StatsPerLevel for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - if (!libconfig->setting_lookup_int(r, "RandomBonusStartLevel", &rnd_bonus_lv)) { - ShowWarning("status_readdb_refine_libconfig_sub: Missing RandomBonusStartLevel for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - if (!libconfig->setting_lookup_int(r, "RandomBonusValue", &rnd_bonus_v)) { - ShowWarning("status_readdb_refine_libconfig_sub: Missing RandomBonusValue for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - - if ((rate=libconfig->setting_get_member(r, "Rates")) != NULL && config_setting_is_group(rate)) { - struct config_setting_t *t = NULL; - bool duplicate[MAX_REFINE]; - int bonus[MAX_REFINE], rnd_bonus[MAX_REFINE]; - int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; - int i, j; - - memset(&duplicate, 0, sizeof(duplicate)); - memset(&bonus, 0, sizeof(bonus)); - memset(&rnd_bonus, 0, sizeof(rnd_bonus)); - - for (i = 0; i < REFINE_CHANCE_TYPE_MAX; i++) - for (j = 0; j < MAX_REFINE; j++) - chance[i][j] = 100; // default value for all rates. - - i = 0; - j = 0; - while ((t = libconfig->setting_get_elem(rate,i++)) != NULL && config_setting_is_group(t)) { - int level = 0, i32; - char *rlvl = config_setting_name(t); - memset(&lv, 0, sizeof(lv)); - - if (!strspn(&rlvl[strlen(rlvl) - 1], "0123456789") || (level = atoi(strncpy(lv, rlvl + 2, 3))) <= 0) { - ShowError("status_readdb_refine_libconfig_sub: Invalid refine level format '%s' for entry %s in \"%s\"... skipping.\n", rlvl, name, source); - continue; - } - - if (level <= 0 || level > MAX_REFINE) { - ShowError("status_readdb_refine_libconfig_sub: Out of range refine level '%s' for entry %s in \"%s\"... skipping.\n", rlvl, name, source); - continue; - } - - level--; - - if (duplicate[level]) { - ShowWarning("status_readdb_refine_libconfig_sub: duplicate rate '%s' for entry %s in \"%s\", overwriting previous entry...\n", rlvl, name, source); - } else { - duplicate[level] = true; - } - - if (libconfig->setting_lookup_int(t, "NormalChance", &i32) != 0) - chance[REFINE_CHANCE_TYPE_NORMAL][level] = i32; - else - chance[REFINE_CHANCE_TYPE_NORMAL][level] = 100; - - if (libconfig->setting_lookup_int(t, "EnrichedChance", &i32) != 0) - chance[REFINE_CHANCE_TYPE_ENRICHED][level] = i32; - else - chance[REFINE_CHANCE_TYPE_ENRICHED][level] = level > 10 ? 0 : 100; // enriched ores up to +10 only. - - if (libconfig->setting_lookup_int(t, "EventNormalChance", &i32) != 0) - chance[REFINE_CHANCE_TYPE_E_NORMAL][level] = i32; - else - chance[REFINE_CHANCE_TYPE_E_NORMAL][level] = 100; - - if (libconfig->setting_lookup_int(t, "EventEnrichedChance", &i32) != 0) - chance[REFINE_CHANCE_TYPE_E_ENRICHED][level] = i32; - else - chance[REFINE_CHANCE_TYPE_E_ENRICHED][level] = level > 10 ? 0 : 100; // enriched ores up to +10 only. - - if (libconfig->setting_lookup_int(t, "Bonus", &i32) != 0) - bonus[level] += i32; - - if (level >= rnd_bonus_lv - 1) - rnd_bonus[level] = rnd_bonus_v * (level - rnd_bonus_lv + 2); - } - for (i = 0; i < MAX_REFINE; i++) { - status->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_NORMAL][i] = chance[REFINE_CHANCE_TYPE_NORMAL][i]; - status->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_E_NORMAL][i] = chance[REFINE_CHANCE_TYPE_E_NORMAL][i]; - status->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_ENRICHED][i] = chance[REFINE_CHANCE_TYPE_ENRICHED][i]; - status->dbs->refine_info[type].chance[REFINE_CHANCE_TYPE_E_ENRICHED][i] = chance[REFINE_CHANCE_TYPE_E_ENRICHED][i]; - status->dbs->refine_info[type].randombonus_max[i] = rnd_bonus[i]; - bonus[i] += bonus_per_level + (i > 0 ? bonus[i - 1] : 0); - status->dbs->refine_info[type].bonus[i] = bonus[i]; - } - } else { - ShowWarning("status_readdb_refine_libconfig_sub: Missing refine rates for entry '%s' in \"%s\", skipping.\n", name, source); - return 0; - } - - return type + 1; -} - -/** - * Reads from a libconfig-formatted refine_db.conf file. - * - * @param *filename File name, relative to the database path. - * @return The number of found entries. - */ -static int status_readdb_refine_libconfig(const char *filename) -{ - bool duplicate[REFINE_TYPE_MAX]; - struct config_t refine_db_conf; - struct config_setting_t *r; - char filepath[256]; - int i = 0, count = 0; - - safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, filename); - if (!libconfig->load_file(&refine_db_conf, filepath)) - return 0; - - memset(&duplicate,0,sizeof(duplicate)); - - while((r = libconfig->setting_get_elem(refine_db_conf.root,i++))) { - char *name = config_setting_name(r); - int type = status->readdb_refine_libconfig_sub(r, name, filename); - if (type != 0) { - if (duplicate[type-1]) { - ShowWarning("status_readdb_refine_libconfig: duplicate entry for %s in \"%s\", overwriting previous entry...\n", name, filename); - } else { - duplicate[type-1] = true; - } - count++; - } - } - libconfig->destroy(&refine_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); - - return count; -} - static bool status_readdb_scconfig(char *fields[], int columns, int current) { int val = 0; @@ -13627,7 +13452,6 @@ static int status_readdb(void) // sv->readdb(map->db_path, "job_db2.txt", ',', 1, 1+MAX_LEVEL, -1, status->readdb_job2); sv->readdb(map->db_path, DBPATH"size_fix.txt", ',', MAX_SINGLE_WEAPON_TYPE, MAX_SINGLE_WEAPON_TYPE, ARRAYLENGTH(status->dbs->atkmods), status->readdb_sizefix); - status->readdb_refine_libconfig(DBPATH"refine_db.conf"); sv->readdb(map->db_path, "sc_config.txt", ',', 2, 2, SC_MAX, status->readdb_scconfig); status->read_job_db(); @@ -13685,7 +13509,6 @@ void status_defaults(void) status->natural_heal_prev_tick = 0; status->natural_heal_diff_tick = 0; /* funcs */ - status->get_refine_chance = status_get_refine_chance; // for looking up associated data status->skill2sc = status_skill2sc; status->sc2skill = status_sc2skill; @@ -13819,8 +13642,6 @@ void status_defaults(void) status->natural_heal_timer = status_natural_heal_timer; status->readdb_job2 = status_readdb_job2; status->readdb_sizefix = status_readdb_sizefix; - status->readdb_refine_libconfig = status_readdb_refine_libconfig; - status->readdb_refine_libconfig_sub = status_readdb_refine_libconfig_sub; status->readdb_scconfig = status_readdb_scconfig; status->read_job_db = status_read_job_db; status->read_job_db_sub = status_read_job_db_sub; diff --git a/src/map/status.h b/src/map/status.h index 17af22703..5f53b715a 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -49,27 +49,6 @@ struct pet_data; ) /** - * Max Refine available to your server - * Changing this limit requires edits to refine_db.txt - **/ -#ifdef RENEWAL - #define MAX_REFINE 20 -#else - #define MAX_REFINE 10 -#endif - -enum refine_type { - REFINE_TYPE_ARMOR = 0, - REFINE_TYPE_WEAPON1 = 1, - REFINE_TYPE_WEAPON2 = 2, - REFINE_TYPE_WEAPON3 = 3, - REFINE_TYPE_WEAPON4 = 4, -#ifndef REFINE_TYPE_MAX - REFINE_TYPE_MAX = 5 -#endif -}; - -/** * SC configuration type * @see db/sc_config.txt for more information **/ @@ -2066,29 +2045,27 @@ enum e_status_calc_opt { //Required because players have two of these, one in status_data //and another for their left hand weapon. typedef struct weapon_atk { - unsigned short atk, atk2; + unsigned int atk, atk2; unsigned short range; unsigned char ele; #ifdef RENEWAL - unsigned short matk; + unsigned int matk; unsigned char wlv; #endif } weapon_atk; //For holding basic status (which can be modified by status changes) struct status_data { - unsigned int + uint32 hp, sp, // see status_cpy before adding members before hp and sp - max_hp, max_sp; - unsigned short + max_hp, max_sp, str, agi, vit, int_, dex, luk, batk, matk_min, matk_max, speed, - amotion, adelay, dmotion; - uint32 mode; - short - hit, flee, cri, flee2, + amotion, adelay, dmotion, + mode; + int32 hit, flee, cri, flee2, def2, mdef2, #ifdef RENEWAL_ASPD aspd_rate2, @@ -2249,21 +2226,6 @@ struct status_change { #define status_calc_elemental(ed, opt) (status->calc_bl_(&(ed)->bl, SCB_ALL, (opt))) #define status_calc_npc(nd, opt) (status->calc_bl_(&(nd)->bl, SCB_ALL, (opt))) -enum refine_chance_type { - REFINE_CHANCE_TYPE_NORMAL = 0, // Normal Chance - REFINE_CHANCE_TYPE_ENRICHED = 1, // Enriched Ore Chance - REFINE_CHANCE_TYPE_E_NORMAL = 2, // Event Normal Ore Chance - REFINE_CHANCE_TYPE_E_ENRICHED = 3, // Event Enriched Ore Chance - REFINE_CHANCE_TYPE_MAX -}; - -// bonus values and upgrade chances for refining equipment -struct s_refine_info { - int chance[REFINE_CHANCE_TYPE_MAX][MAX_REFINE]; // success chance - int bonus[MAX_REFINE]; // cumulative fixed bonus damage - int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage -}; - struct s_status_dbs { BEGIN_ZEROED_BLOCK; /* Everything within this block will be memset to 0 when status_defaults() is executed */ int max_weight_base[CLASS_COUNT]; @@ -2277,8 +2239,6 @@ BEGIN_ZEROED_BLOCK; /* Everything within this block will be memset to 0 when sta int RelevantBLTypes[SI_MAX]; // "icon" -> enum bl_type (for clif->status_change to identify for which bl types to send packets) bool DisplayType[SC_MAX]; /* */ - struct s_refine_info refine_info[REFINE_TYPE_MAX]; - /* */ int atkmods[3][MAX_SINGLE_WEAPON_TYPE];//ATK weapon modification for size (size_fix.txt) char job_bonus[CLASS_COUNT][MAX_LEVEL]; sc_conf_type sc_conf[SC_MAX]; @@ -2307,7 +2267,6 @@ struct status_interface { int (*init) (bool minimal); void (*final) (void); /* funcs */ - int (*get_refine_chance) (enum refine_type wlv, int refine, enum refine_chance_type type); // for looking up associated data sc_type (*skill2sc) (int skill_id); int (*sc2skill) (sc_type sc); @@ -2379,8 +2338,8 @@ struct status_interface { short (*calc_def2) (struct block_list *bl, struct status_change *sc, int def2, bool viewable); defType (*calc_mdef) (struct block_list *bl, struct status_change *sc, int mdef, bool viewable); short (*calc_mdef2) (struct block_list *bl, struct status_change *sc, int mdef2, bool viewable); - unsigned short (*calc_batk)(struct block_list *bl, struct status_change *sc, int batk, bool viewable); - unsigned short(*base_matk) (struct block_list *bl, const struct status_data *st, int level); + int (*calc_batk)(struct block_list *bl, struct status_change *sc, int batk, bool viewable); + int (*base_matk) (struct block_list *bl, const struct status_data *st, int level); int (*get_weapon_atk) (struct block_list *src, struct weapon_atk *watk, int flag); int (*get_total_mdef) (struct block_list *src); int (*get_total_def) (struct block_list *src); @@ -2391,7 +2350,7 @@ struct status_interface { void (*initChangeTables) (void); void (*initDummyData) (void); int (*base_amotion_pc) (struct map_session_data *sd, struct status_data *st); - unsigned short (*base_atk) (const struct block_list *bl, const struct status_data *st); + int (*base_atk) (const struct block_list *bl, const struct status_data *st); unsigned int (*get_base_maxhp) (const struct map_session_data *sd, const struct status_data *st); unsigned int (*get_base_maxsp) (const struct map_session_data *sd, const struct status_data *st); unsigned int (*get_restart_hp) (const struct map_session_data *sd, const struct status_data *st); @@ -2403,12 +2362,12 @@ struct status_interface { unsigned short (*calc_int) (struct block_list *bl, struct status_change *sc, int int_); unsigned short (*calc_dex) (struct block_list *bl, struct status_change *sc, int dex); unsigned short (*calc_luk) (struct block_list *bl, struct status_change *sc, int luk); - unsigned short (*calc_watk) (struct block_list *bl, struct status_change *sc, int watk, bool viewable); - unsigned short (*calc_matk) (struct block_list *bl, struct status_change *sc, int matk, bool viewable); - signed short (*calc_hit) (struct block_list *bl, struct status_change *sc, int hit, bool viewable); - signed short (*calc_critical) (struct block_list *bl, struct status_change *sc, int critical, bool viewable); - signed short (*calc_flee) (struct block_list *bl, struct status_change *sc, int flee, bool viewable); - signed short (*calc_flee2) (struct block_list *bl, struct status_change *sc, int flee2, bool viewable); + int (*calc_watk) (struct block_list *bl, struct status_change *sc, int watk, bool viewable); + int (*calc_matk) (struct block_list *bl, struct status_change *sc, int matk, bool viewable); + signed int (*calc_hit) (struct block_list *bl, struct status_change *sc, int hit, bool viewable); + signed int (*calc_critical) (struct block_list *bl, struct status_change *sc, int critical, bool viewable); + signed int (*calc_flee) (struct block_list *bl, struct status_change *sc, int flee, bool viewable); + signed int (*calc_flee2) (struct block_list *bl, struct status_change *sc, int flee2, bool viewable); unsigned short (*calc_speed) (struct block_list *bl, struct status_change *sc, int speed); short (*calc_aspd_rate) (struct block_list *bl, struct status_change *sc, int aspd_rate); unsigned short (*calc_dmotion) (struct block_list *bl, struct status_change *sc, int dmotion); @@ -2419,7 +2378,7 @@ struct status_interface { unsigned char (*calc_element) (struct block_list *bl, struct status_change *sc, int element); unsigned char (*calc_element_lv) (struct block_list *bl, struct status_change *sc, int lv); uint32 (*calc_mode) (const struct block_list *bl, const struct status_change *sc, uint32 mode); - unsigned short (*calc_ematk) (struct block_list *bl, struct status_change *sc, int matk); + int (*calc_ematk) (struct block_list *bl, struct status_change *sc, int matk); void (*calc_bl_main) (struct block_list *bl, int flag); void (*display_add) (struct map_session_data *sd, enum sc_type type, int dval1, int dval2, int dval3); void (*display_remove) (struct map_session_data *sd, enum sc_type type); @@ -2427,15 +2386,13 @@ struct status_interface { int (*natural_heal_timer) (int tid, int64 tick, int id, intptr_t data); bool (*readdb_job2) (char *fields[], int columns, int current); bool (*readdb_sizefix) (char *fields[], int columns, int current); - int (*readdb_refine_libconfig) (const char *filename); - int (*readdb_refine_libconfig_sub) (struct config_setting_t *r, const char *name, const char *source); bool (*readdb_scconfig) (char *fields[], int columns, int current); void (*read_job_db) (void); void (*read_job_db_sub) (int idx, const char *name, struct config_setting_t *jdb); void (*set_sc) (uint16 skill_id, sc_type sc, int icon, unsigned int flag); void (*copy) (struct status_data *a, const struct status_data *b); - unsigned short (*base_matk_min) (const struct status_data *st); - unsigned short (*base_matk_max) (const struct status_data *st); + int (*base_matk_min) (const struct status_data *st); + int (*base_matk_max) (const struct status_data *st); }; #ifdef HERCULES_CORE diff --git a/src/map/trade.c b/src/map/trade.c index 9c284ba23..cef14ffe6 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -163,8 +163,8 @@ static void trade_tradeack(struct map_session_data *sd, int type) } //Check if you can start trade. - if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.storage_flag != STORAGE_FLAG_CLOSED - || tsd->npc_id || tsd->state.vending || tsd->state.buyingstore || tsd->state.storage_flag != STORAGE_FLAG_CLOSED + if (sd->npc_id || sd->state.vending || sd->state.prevend || sd->state.buyingstore || sd->state.storage_flag != STORAGE_FLAG_CLOSED + || tsd->npc_id || tsd->state.vending || tsd->state.prevend || tsd->state.buyingstore || tsd->state.storage_flag != STORAGE_FLAG_CLOSED ) { //Fail clif->tradestart(sd, 2); diff --git a/src/map/unit.c b/src/map/unit.c index 68e6aeec1..45cb7dffd 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -957,7 +957,7 @@ static int unit_warp(struct block_list *bl, short m, short x, short y, enum clr_ return 2; } - } else if (map->getcell(m, bl, x, y, CELL_CHKNOREACH)) { + } else if (bl->type != BL_NPC && map->getcell(m, bl, x, y, CELL_CHKNOREACH)) { //Invalid target cell ShowWarning("unit_warp: Specified non-walkable target cell: %d (%s) at [%d,%d]\n", m, map->list[m].name, x,y); @@ -1090,6 +1090,7 @@ static int unit_can_move(struct block_list *bl) if (sd && ( pc_issit(sd) || sd->state.vending || + sd->state.prevend || sd->state.buyingstore || sd->block_action.move )) diff --git a/src/map/unit.h b/src/map/unit.h index 5c01cdc2e..3209351e3 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -44,6 +44,7 @@ enum unit_stopwalking_flag { struct unit_data { struct block_list *bl; + char title[NAME_LENGTH]; struct walkpath_data walkpath; struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL]; struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP]; @@ -61,6 +62,7 @@ struct unit_data { int chaserange; bool stepaction; //Action should be executed on step [Playtester] int steptimer; //Timer that triggers the action [Playtester] + int groupId; // id of client side group (works for npc and may be other) [4144] uint16 stepskill_id,stepskill_lv; //Remembers skill that should be casted on step [Playtester] int64 attackabletime; int64 canact_tick; |