diff options
Diffstat (limited to 'src/map')
107 files changed, 21848 insertions, 10546 deletions
diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c index 6eff37df8..42c7b3c5f 100644 --- a/src/map/HPMmap.c +++ b/src/map/HPMmap.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 @@ -81,11 +81,14 @@ #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" #include "map/status.h" #include "map/storage.h" +#include "map/stylist.h" #include "map/trade.h" #include "map/unit.h" #include "map/vending.h" diff --git a/src/map/HPMmap.h b/src/map/HPMmap.h index a9d2a454e..52ab36cce 100644 --- a/src/map/HPMmap.h +++ b/src/map/HPMmap.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 diff --git a/src/map/Makefile.in b/src/map/Makefile.in index 3705fda0e..75093662f 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -1,8 +1,8 @@ # This file is part of Hercules. # http://herc.ws - http://github.com/HerculesWS/Hercules # -# Copyright (C) 2012-2018 Hercules Dev Team -# Copyright (C) Athena Dev Teams +# Copyright (C) 2012-2020 Hercules Dev Team +# Copyright (C) Athena Dev Teams # # Hercules is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -36,6 +36,19 @@ LIBCONFIG_OBJ = $(addprefix $(LIBCONFIG_D)/, libconfig.o grammar.o scanctx.o \ LIBCONFIG_H = $(addprefix $(LIBCONFIG_D)/, libconfig.h grammar.h parsectx.h \ scanctx.h scanner.h strbuf.h wincompat.h) +ifeq (@USE_LIBBACKTRACE@,yes) + LIBBACKTRACE_D = $(THIRDPARTY_D)/libbacktrace + LIBBACKTRACE_OBJ = $(addprefix $(LIBBACKTRACE_D)/, atomic.o backtrace.o \ + dwarf.o @LIBBACKTRACE_FORMAT_FILE@ fileline.o mmapio.o mmap.o posix.o print.o \ + simple.o sort.o state.o) + LIBBACKTRACE_H = $(addprefix $(LIBBACKTRACE_D)/, backtrace.h \ + backtrace-supported.h config.h filenames.h internal.h) +else + LIBBACKTRACE_D = + LIBBACKTRACE_OBJ = + LIBBACKTRACE_H = +endif + MT19937AR_D = $(THIRDPARTY_D)/mt19937ar MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o MT19937AR_H = $(MT19937AR_D)/mt19937ar.h @@ -44,8 +57,8 @@ 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 \ - trade.c unit.c vending.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 \ chrif.h clan.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \ @@ -54,13 +67,13 @@ 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 \ - trade.h unit.h vending.h -MAP_PH = + 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 = refine.p.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) - MAP_SERVER_SQL_DEPENDS=$(MAP_OBJ) $(COMMON_D)/obj_all/common.a $(COMMON_D)/obj_sql/common_sql.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) $(SYSINFO_INC) + MAP_SERVER_SQL_DEPENDS=$(MAP_OBJ) $(COMMON_D)/obj_all/common.a $(COMMON_D)/obj_sql/common_sql.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) $(LIBBACKTRACE_OBJ) $(SYSINFO_INC) else MAP_SERVER_SQL_DEPENDS=needs_mysql endif @@ -99,7 +112,7 @@ help: Makefile: Makefile.in @$(MAKE) -C ../.. src/map/Makefile -$(SYSINFO_INC): $(MAP_C) $(MAP_PH) $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) +$(SYSINFO_INC): $(MAP_C) $(MAP_PH) $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) $(LIBBACKTRACE_H) @echo " MAKE $@" @$(MAKE) -C ../.. sysinfo @@ -120,7 +133,7 @@ map-server: ../../map-server@EXEEXT@ ../../map-server@EXEEXT@: $(MAP_SERVER_SQL_DEPENDS) Makefile @echo " LD $(notdir $@)" @$(CC) @STATIC@ @LDFLAGS@ -o ../../map-server@EXEEXT@ $(MAP_OBJ) $(COMMON_D)/obj_all/common.a $(COMMON_D)/obj_sql/common_sql.a \ - $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @MYSQL_LIBS@ + $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) $(LIBBACKTRACE_OBJ) @LIBS@ @MYSQL_LIBS@ # missing object files $(COMMON_D)/obj_all/common.a: @@ -139,10 +152,14 @@ $(LIBCONFIG_OBJ): @echo " MAKE $@" @$(MAKE) -C $(LIBCONFIG_D) +$(LIBBACKTRACE_OBJ): + @echo " MAKE $@" + @$(MAKE) -C $(LIBBACKTRACE_D) + .SECONDEXPANSION: # map object files -obj_sql/%.o: %.c $$(filter %.p.h, $(MAP_PH)) $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) | obj_sql +obj_sql/%.o: %.c $$(filter %.p.h, $(MAP_PH)) $(MAP_H) $(COMMON_H) $(CONFIG_H) $(MT19937AR_H) $(LIBCONFIG_H) $(LIBBACKTRACE_H) | obj_sql @echo " CC $<" @$(CC) @CFLAGS@ @DEFS@ $(COMMON_INCLUDE) $(THIRDPARTY_INCLUDE) @MYSQL_CFLAGS@ @CPPFLAGS@ -c $(OUTPUT_OPTION) $< diff --git a/src/map/achievement.c b/src/map/achievement.c index 7148acae8..e6a9ae3be 100644 --- a/src/map/achievement.c +++ b/src/map/achievement.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * -* Copyright (C) 2017 Hercules Dev Team +* Copyright (C) 2017-2020 Hercules Dev Team * Copyright (C) Smokexyz * Copyright (C) Dastgir * @@ -301,6 +301,9 @@ static int achievement_validate_type(struct map_session_data *sd, enum achieveme Assert_ret(criteria->goal != 0); + if (battle_config.feature_enable_achievement == 0) + return 0; + if (type == ACH_QUEST) { ShowError("achievement_validate_type: ACH_QUEST is not handled by this function. (use achievement_validate())\n"); return 0; @@ -358,6 +361,9 @@ static bool achievement_validate(struct map_session_data *sd, int aid, unsigned Assert_retr(false, progress > 0); Assert_retr(false, obj_idx < MAX_ACHIEVEMENT_OBJECTIVES); + if (battle_config.feature_enable_achievement == 0) + return false; + if ((ad = achievement->get(aid)) == NULL) { ShowError("achievement_validate: Invalid Achievement %d provided.", aid); return false; @@ -760,14 +766,13 @@ static void achievement_validate_refine(struct map_session_data *sd, unsigned in struct item_data *id = NULL; nullpo_retv(sd); - Assert_retv(idx < MAX_INVENTORY); + Assert_retv(idx < sd->status.inventorySize); id = itemdb->exists(sd->status.inventory[idx].nameid); if (sd->achievements_received == false) return; - Assert_retv(idx < MAX_INVENTORY); Assert_retv(id != NULL); criteria.goal = sd->status.inventory[idx].refine; @@ -1046,10 +1051,10 @@ static void achievement_get_rewards_items(struct map_session_data *sd, const str if (!itemdb->isstackable(it.nameid)) { it.amount = 1; for (int j = 0; j < total; ++j) - pc->additem(sd, &it, 1, LOG_TYPE_SCRIPT); + pc->additem(sd, &it, 1, LOG_TYPE_ACHIEVEMENT); } else { it.amount = total; - pc->additem(sd, &it, total, LOG_TYPE_SCRIPT); + pc->additem(sd, &it, total, LOG_TYPE_ACHIEVEMENT); } } } @@ -1094,7 +1099,8 @@ static bool achievement_get_rewards(struct map_session_data *sd, const struct ac */ static void achievement_readdb_ranks(void) { - const char *filename = "db/achievement_rank_db.conf"; + char filename[256]; + libconfig->format_db_path("achievement_rank_db.conf", filename, sizeof(filename)); struct config_t ar_conf = { 0 }; struct config_setting_t *ardb = NULL, *conf = NULL; int entry = 0; @@ -1772,7 +1778,8 @@ static void achievement_readdb_additional_fields(const struct config_setting_t * */ static void achievement_readb(void) { - const char *filename = "db/"DBPATH"achievement_db.conf"; + char filename[256]; + libconfig->format_db_path(DBPATH"achievement_db.conf", filename, sizeof(filename)); struct config_t ach_conf = { 0 }; struct config_setting_t *achdb = NULL, *conf = NULL; int entry = 0, count = 0; diff --git a/src/map/achievement.h b/src/map/achievement.h index de5eaa060..dd80f7a23 100644 --- a/src/map/achievement.h +++ b/src/map/achievement.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * -* Copyright (C) 2018 Hercules Dev Team +* Copyright (C) 2018-2020 Hercules Dev Team * Copyright (C) Smokexyz * * Hercules is free software: you can redistribute it and/or modify diff --git a/src/map/atcommand.c b/src/map/atcommand.c index 6cfa86163..3fe6c8581 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -510,7 +511,7 @@ ACMD(where) return false; } - pl_sd = map->nick2sd(atcmd_player_name); + pl_sd = map->nick2sd(atcmd_player_name, true); if (pl_sd == NULL || strncmp(pl_sd->status.name, atcmd_player_name, NAME_LENGTH) != 0 || (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > pc_get_group_level(sd) && !pc_has_permission(sd, PC_PERM_WHO_DISPLAY_AID)) @@ -547,7 +548,7 @@ ACMD(jumpto) return false; } - if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { + if ((pl_sd=map->nick2sd(message, true)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -868,9 +869,14 @@ 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 (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nostorage & 1)) { // mapflag nostorage already defined? can't open :c + clif->message(fd, msg_fd(fd, 1161)); // You currently cannot open your storage. + return false; + } + if (storage->open(sd) == 1) { //Already open. clif->message(fd, msg_fd(fd,250)); // You have already opened your storage. Close it first. return false; @@ -891,7 +897,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) { @@ -904,6 +910,11 @@ ACMD(guildstorage) return false; } + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nogstorage & 1)) { // mapflag nogstorage already defined? can't open :c + clif->message(fd, msg_fd(fd, 1161)); // You currently cannot open your storage. (there is no other messages...) + return false; + } + if( gstorage->open(sd) ) { clif->message(fd, msg_fd(fd,1201)); // Your guild's storage has already been opened by another member, try again later. return false; @@ -951,39 +962,10 @@ ACMD(option) *------------------------------------------*/ ACMD(hide) { - if (pc_isinvisible(sd)) { - sd->sc.option &= ~OPTION_INVISIBLE; - if (sd->disguise != -1 ) - status->set_viewdata(&sd->bl, sd->disguise); - else - status->set_viewdata(&sd->bl, sd->status.class); - clif->message(fd, msg_fd(fd,10)); // Invisible: Off - - // increment the number of pvp players on the map - map->list[sd->bl.m].users_pvp++; - - if( map->list[sd->bl.m].flag.pvp && !map->list[sd->bl.m].flag.pvp_nocalcrank ) { - // register the player for ranking calculations - sd->pvp_timer = timer->add( timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0 ); - } - //bugreport:2266 - map->foreachinmovearea(clif->insight, &sd->bl, AREA_SIZE, sd->bl.x, sd->bl.y, BL_ALL, &sd->bl); - } else { - clif->clearunit_area(&sd->bl, CLR_OUTSIGHT); - sd->sc.option |= OPTION_INVISIBLE; - sd->vd.class = INVISIBLE_CLASS; - clif->message(fd, msg_fd(fd,11)); // Invisible: On - - // decrement the number of pvp players on the map - map->list[sd->bl.m].users_pvp--; - - if( map->list[sd->bl.m].flag.pvp && !map->list[sd->bl.m].flag.pvp_nocalcrank && sd->pvp_timer != INVALID_TIMER ) { - // unregister the player for ranking - timer->delete( sd->pvp_timer, pc->calc_pvprank_timer ); - sd->pvp_timer = INVALID_TIMER; - } - } - clif->changeoption(&sd->bl); + if (pc_isinvisible(sd)) + pc->unhide(sd, true); + else + pc->hide(sd, true); return true; } @@ -1096,8 +1078,10 @@ ACMD(kami) sscanf(message, "%199[^\n]", atcmd_output); if (stristr(info->command, "l") != NULL) clif->broadcast(&sd->bl, atcmd_output, (int)strlen(atcmd_output) + 1, BC_DEFAULT, ALL_SAMEMAP); + else if (info->command[4] == 'b' || info->command[4] == 'B') + clif->broadcast(NULL, atcmd_output, (int)strlen(atcmd_output) + 1, BC_BLUE, ALL_CLIENT); else - intif->broadcast(atcmd_output, (int)strlen(atcmd_output) + 1, (*(info->command + 4) == 'b' || *(info->command + 4) == 'B') ? BC_BLUE : BC_YELLOW); + clif->broadcast(NULL, atcmd_output, (int)strlen(atcmd_output) + 1, BC_YELLOW, ALL_CLIENT); } else { if(!*message || (sscanf(message, "%10u %199[^\n]", &color, atcmd_output) < 2)) { clif->message(fd, msg_fd(fd,981)); // Please enter color and message (usage: @kamic <color> <message>). @@ -1108,7 +1092,7 @@ ACMD(kami) clif->message(fd, msg_fd(fd,982)); // Invalid color. return false; } - intif->broadcast2(atcmd_output, (int)strlen(atcmd_output) + 1, color, 0x190, 12, 0, 0); + clif->broadcast2(NULL, atcmd_output, (int)strlen(atcmd_output) + 1, color, 0x190, 12, 0, 0, ALL_CLIENT); } return true; } @@ -1270,20 +1254,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 +1303,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; @@ -1359,9 +1343,7 @@ ACMD(item2) *------------------------------------------*/ ACMD(itemreset) { - int i; - - for (i = 0; i < MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].amount && sd->status.inventory[i].equip == 0) { pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_COMMAND); } @@ -2217,59 +2199,100 @@ 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) { - 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 + 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>). +#if PACKETVER > 20100707 + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1515), -3); // %d: Refine All Equip (Shadow) + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1514), -2); // %d: Refine All Equip (Costume) + clif->message(fd, atcmd_output); +#endif + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1513), -1); // %d: Refine All Equip (General) + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 997), EQP_HEAD_LOW); // %d: Headgear (Low) + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 998), EQP_HAND_R); // Hand (Right) + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 999), EQP_GARMENT); // %d: Garment + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1000), EQP_ACC_L); // Accessory (Left) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,998), EQP_HAND_R); // %d: Right Hand + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1001), EQP_ARMOR); // %d: Body Armor clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,999), EQP_GARMENT); // %d: Garment + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1002), EQP_HAND_L); // Hand (Left) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1000), EQP_ACC_L); // %d: Left Accessory + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1003), EQP_SHOES); // %d: Shoes clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1001), EQP_ARMOR); // %d: Body Armor + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1004), EQP_ACC_R); // Accessory (Right) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1002), EQP_HAND_L); // %d: Left Hand + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1005), EQP_HEAD_TOP); // %d: Headgear (Top) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1003), EQP_SHOES); // %d: Shoes + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1006), EQP_HEAD_MID); // %d: Headgear (Mid) +#if PACKETVER > 20100707 + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1503), EQP_COSTUME_HEAD_TOP); // %d: Costume Headgear (Top) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1004), EQP_ACC_R); // %d: Right Accessory + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1504), EQP_COSTUME_HEAD_MID); // %d: Costume Headgear (Mid) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1005), EQP_HEAD_TOP); // %d: Top Headgear + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1505), EQP_COSTUME_HEAD_LOW); // %d: Costume Headgear (Low) clif->message(fd, atcmd_output); - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1006), EQP_HEAD_MID); // %d: Mid Headgear + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1506), EQP_COSTUME_GARMENT); // %d: Costume Garment + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1507), EQP_SHADOW_ARMOR); // %d: Shadow Armor + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1508), EQP_SHADOW_WEAPON); // %d: Shadow Weapon + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1509), EQP_SHADOW_SHIELD); // %d: Shadow Shield + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1510), EQP_SHADOW_SHOES); // %d: Shadow Shoes + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1511), EQP_SHADOW_ACC_R); // %d: Shadow Accessory (Right) + clif->message(fd, atcmd_output); + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 1512), EQP_SHADOW_ACC_L); // %d: Shadow Accessory (Left) + clif->message(fd, atcmd_output); +#endif clif->message(fd, atcmd_output); 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++) { int idx = sd->equip_index[j]; if (idx < 0) continue; - if(j == EQI_AMMO) continue; /* can't equip ammo */ - if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == idx) + if (j == EQI_AMMO) + continue; /* can't equip ammo */ + if (j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == idx) continue; - if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == idx) + if (j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == idx) continue; - if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == idx || sd->equip_index[EQI_HEAD_LOW] == idx)) + if (j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == idx || sd->equip_index[EQI_HEAD_LOW] == idx)) + continue; + if (j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == idx) + continue; + if (j == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == idx || sd->equip_index[EQI_COSTUME_LOW] == idx)) continue; - if(position && !(sd->status.inventory[idx].equip & position)) + if (position == -3 && !itemdb_is_shadowequip(sd->status.inventory[idx].equip)) + continue; + else if (position == -2 && !itemdb_is_costumeequip(sd->status.inventory[idx].equip)) + continue; + else if (position == -1 && (itemdb_is_costumeequip(sd->status.inventory[idx].equip) || itemdb_is_shadowequip(sd->status.inventory[idx].equip))) + continue; + else 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; - pc->unequipitem(sd, idx, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); + pc->unequipitem(sd, idx, PCUNEQUIPITEM_RECALC | PCUNEQUIPITEM_FORCE); clif->refine(fd, 0, idx, sd->status.inventory[idx].refine); clif->delitem(sd, idx, 1, DELITEM_MATERIALCHANGE); clif->additem(sd, idx, 1, 0); @@ -2280,11 +2303,11 @@ ACMD(refine) } if (count == 0) - clif->message(fd, msg_fd(fd,166)); // No item has been refined. + clif->message(fd, msg_fd(fd, 166)); // No item has been refined. else if (count == 1) - clif->message(fd, msg_fd(fd,167)); // 1 item has been refined. + clif->message(fd, msg_fd(fd, 167)); // 1 item has been refined. else { - safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,168), count); // %d items have been refined. + safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd, 168), count); // %d items have been refined. clif->message(fd, atcmd_output); } @@ -2425,7 +2448,7 @@ ACMD(displaystatus) if( flag == 0 ) clif->sc_end(&sd->bl,sd->bl.id,AREA,type); else - clif->status_change(&sd->bl, type, flag, tick, val1, val2, val3); + clif->status_change(&sd->bl, type, BL_PC, flag, tick, val1, val2, val3); return true; } @@ -2692,42 +2715,49 @@ ACMD(guildlevelup) return true; } -/*========================================== +/** + * Creates a pet egg in the character's inventory. * - *------------------------------------------*/ + * @code{.herc} + * @makeegg <pet> + * @endcode + * + **/ ACMD(makeegg) { - struct item_data *item_data; - int id, pet_id; - - if (!*message) { - clif->message(fd, msg_fd(fd,1015)); // Please enter a monster/egg name/ID (usage: @makeegg <pet>). + if (*message == '\0') { + clif->message(fd, msg_fd(fd, 1015)); // Please enter a monster/egg name/ID (usage: @makeegg <pet>). return false; } - if ((item_data = itemdb->search_name(message)) != NULL) // for egg name + struct item_data *item_data = itemdb->search_name(message); + int id; + + if (item_data != NULL) { // Egg name. id = item_data->nameid; - else - if ((id = mob->db_searchname(message)) != 0) // for monster name - ; - else - id = atoi(message); + } else { + id = mob->db_searchname(message); // Monster name. + + if (id == 0) + id = atoi(message); // Egg/monster ID. + } - pet_id = pet->search_petDB_index(id, PET_CLASS); - if (pet_id < 0) + int pet_id = pet->search_petDB_index(id, PET_CLASS); + + if (pet_id == INDEX_NOT_FOUND) pet_id = pet->search_petDB_index(id, PET_EGG); - if (pet_id >= 0) { - sd->catch_target_class = pet->db[pet_id].class_; - intif->create_pet( - sd->status.account_id, sd->status.char_id, - (short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv, - pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate, - 100, 0, 1, pet->db[pet_id].jname); - } else { - clif->message(fd, msg_fd(fd,180)); // The monster/egg name/id doesn't exist. + + if (pet_id == INDEX_NOT_FOUND) { + clif->message(fd, msg_fd(fd, 180)); // The monster/egg name/ID doesn't exist. return false; } + sd->catch_target_class = pet->db[pet_id].class_; + intif->create_pet(sd->status.account_id, sd->status.char_id, pet->db[pet_id].class_, + mob->db(pet->db[pet_id].class_)->lv, pet->db[pet_id].EggID, 0, + (short)pet->db[pet_id].intimate, PET_HUNGER_STUFFED, + 0, 1,pet->db[pet_id].jname); + return true; } @@ -2746,72 +2776,90 @@ ACMD(hatch) return true; } -/*========================================== +/** + * Sets a pet's intimacy value. * - *------------------------------------------*/ + * @code{.herc} + * @petfriendly <0-1000> + * @endcode + * + **/ ACMD(petfriendly) { - int friendly; - struct pet_data *pd; - - if (!*message || (friendly = atoi(message)) < 0) { - clif->message(fd, msg_fd(fd,1016)); // Please enter a valid value (usage: @petfriendly <0-1000>). + if (*message == '\0' || (atoi(message) == 0 && isdigit(*message) == 0)) { + clif->message(fd, msg_fd(fd, 1016)); // Please enter a valid value (usage: @petfriendly <0-1000>). return false; } - pd = sd->pd; - if (!pd) { - clif->message(fd, msg_fd(fd,184)); // Sorry, but you have no pet. + int friendly = atoi(message); + + if (friendly < PET_INTIMACY_NONE || friendly > PET_INTIMACY_MAX) { + clif->message(fd, msg_fd(fd, 1016)); // Please enter a valid value (usage: @petfriendly <0-1000>). return false; } - if (friendly < 0 || friendly > 1000) - { - clif->message(fd, msg_fd(fd,37)); // An invalid number was specified. + struct pet_data *pd = sd->pd; + + if (sd->status.pet_id == 0 || pd == NULL) { + clif->message(fd, msg_fd(fd, 184)); // Sorry, but you have no pet. return false; } - if (friendly == pd->pet.intimate) { - clif->message(fd, msg_fd(fd,183)); // Pet intimacy is already at maximum. + if (friendly == pd->pet.intimate && friendly == PET_INTIMACY_MAX) { + clif->message(fd, msg_fd(fd, 183)); // Pet intimacy is already at maximum. return false; } - pet->set_intimate(pd, friendly); - clif->send_petstatus(sd); - clif->message(fd, msg_fd(fd,182)); // Pet intimacy changed. + if (friendly != pd->pet.intimate) { // No need to update the pet's status if intimacy value won't change. + pet->set_intimate(pd, friendly); + clif->send_petstatus(sd); + } + + clif->message(fd, msg_fd(fd, 182)); // Pet intimacy changed. (Send message regardless of value has changed or not.) + return true; } -/*========================================== +/** + * Sets a pet's hunger value. * - *------------------------------------------*/ + * @code{.herc} + * @pethungry <0-100> + * @endcode + * + **/ ACMD(pethungry) { - int hungry; - struct pet_data *pd; - - if (!*message || (hungry = atoi(message)) < 0) { - clif->message(fd, msg_fd(fd,1017)); // Please enter a valid number (usage: @pethungry <0-100>). + if (*message == '\0' || (atoi(message) == 0 && isdigit(*message) == 0)) { + clif->message(fd, msg_fd(fd, 1017)); // Please enter a valid number (usage: @pethungry <0-100>). return false; } - pd = sd->pd; - if (!sd->status.pet_id || !pd) { - clif->message(fd, msg_fd(fd,184)); // Sorry, but you have no pet. + int hungry = atoi(message); + + if (hungry < PET_HUNGER_STARVING || hungry > PET_HUNGER_STUFFED) { + clif->message(fd, msg_fd(fd, 1017)); // Please enter a valid number (usage: @pethungry <0-100>). return false; } - if (hungry < 0 || hungry > 100) { - clif->message(fd, msg_fd(fd,37)); // An invalid number was specified. + + struct pet_data *pd = sd->pd; + + if (sd->status.pet_id == 0 || pd == NULL) { + clif->message(fd, msg_fd(fd, 184)); // Sorry, but you have no pet. return false; } - if (hungry == pd->pet.hungry) { - clif->message(fd, msg_fd(fd,186)); // Pet hunger is already at maximum. + + if (hungry == pd->pet.hungry && hungry == PET_HUNGER_STUFFED) { + clif->message(fd, msg_fd(fd, 186)); // Pet hunger is already at maximum. return false; } - pd->pet.hungry = hungry; - clif->send_petstatus(sd); - clif->message(fd, msg_fd(fd,185)); // Pet hunger changed. + if (hungry != pd->pet.hungry) { // No need to update the pet's status if hunger value won't change. + pd->pet.hungry = hungry; + clif->send_petstatus(sd); + } + + clif->message(fd, msg_fd(fd, 185)); // Pet hunger changed. (Send message regardless of value has changed or not.) return true; } @@ -2852,7 +2900,7 @@ ACMD(recall) return false; } - if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { + if ((pl_sd=map->nick2sd(message, true)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -3177,7 +3225,7 @@ ACMD(kick) return false; } - if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { + if ((pl_sd=map->nick2sd(message, true)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -3507,7 +3555,7 @@ ACMD(idsearch) safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,77), item_name); // Search results for '%s' (name: id): clif->message(fd, atcmd_output); - match = itemdb->search_name_array(item_array, MAX_SEARCH, item_name, 0); + match = itemdb->search_name_array(item_array, MAX_SEARCH, item_name, IT_SEARCH_NAME_PARTIAL); if (match > MAX_SEARCH) { safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,269), MAX_SEARCH, match); clif->message(fd, atcmd_output); @@ -4071,16 +4119,36 @@ ACMD(mapinfo) for (i = 0; i < map->list[m_id].npc_num;) { struct npc_data *nd = map->list[m_id].npc[i]; switch(nd->dir) { - case 0: strcpy(direction, msg_fd(fd,1101)); break; // North - case 1: strcpy(direction, msg_fd(fd,1102)); break; // North West - case 2: strcpy(direction, msg_fd(fd,1103)); break; // West - case 3: strcpy(direction, msg_fd(fd,1104)); break; // South West - case 4: strcpy(direction, msg_fd(fd,1105)); break; // South - case 5: strcpy(direction, msg_fd(fd,1106)); break; // South East - case 6: strcpy(direction, msg_fd(fd,1107)); break; // East - case 7: strcpy(direction, msg_fd(fd,1108)); break; // North East - case 9: strcpy(direction, msg_fd(fd,1109)); break; // North - default: strcpy(direction, msg_fd(fd,1110)); break; // Unknown + case UNIT_DIR_NORTH: + strcpy(direction, msg_fd(fd, 1101)); // North + break; + case UNIT_DIR_NORTHWEST: + strcpy(direction, msg_fd(fd, 1102)); // North West + break; + case UNIT_DIR_WEST: + strcpy(direction, msg_fd(fd, 1103)); // West + break; + case UNIT_DIR_SOUTHWEST: + strcpy(direction, msg_fd(fd, 1104)); // South West + break; + case UNIT_DIR_SOUTH: + strcpy(direction, msg_fd(fd, 1105)); // South + break; + case UNIT_DIR_SOUTHEAST: + strcpy(direction, msg_fd(fd, 1106)); // South East + break; + case UNIT_DIR_EAST: + strcpy(direction, msg_fd(fd, 1107)); // East + break; + case UNIT_DIR_NORTHEAST: + strcpy(direction, msg_fd(fd, 1108)); // North East + break; + case 9: // is this actually used? [skyleo] + strcpy(direction, msg_fd(fd, 1109)); // North + break; + default: + strcpy(direction, msg_fd(fd, 1110)); // Unknown + break; } if(strcmp(nd->name,nd->exname) == 0) safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1111), // NPC %d: %s | Direction: %s | Sprite: %d | Location: %d %d @@ -4160,12 +4228,19 @@ ACMD(mount_peco) return true; } if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { + int mtype = MADO_ROBOT; + if (!*message) + sscanf(message, "%d", &mtype); + if (mtype < MADO_ROBOT || mtype >= MADO_MAX) { + clif->message(fd, msg_fd(fd, 173)); // Please enter a valid madogear type. + return false; + } if (!pc_ismadogear(sd)) { clif->message(sd->fd,msg_fd(fd,1123)); // You have mounted your Mado Gear. - pc->setmadogear(sd, true); + pc->setmadogear(sd, true, (enum mado_type)mtype); } else { clif->message(sd->fd,msg_fd(fd,1124)); // You have released your Mado Gear. - pc->setmadogear(sd, false); + pc->setmadogear(sd, false, (enum mado_type)mtype); } return true; } @@ -4274,10 +4349,8 @@ ACMD(partyspy) *------------------------------------------*/ ACMD(repairall) { - int count, i; - - count = 0; - for (i = 0; i < MAX_INVENTORY; i++) { + int count = 0; + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].card[0] == CARD0_PET) continue; if (sd->status.inventory[i].nameid && (sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { @@ -4314,7 +4387,7 @@ ACMD(nuke) return false; } - if ((pl_sd = map->nick2sd(atcmd_player_name)) != NULL) { + if ((pl_sd = map->nick2sd(atcmd_player_name, true)) != NULL) { if (pc_get_group_level(sd) >= pc_get_group_level(pl_sd)) { // you can kill only lower or same GM level skill->castend_nodamage_id(&pl_sd->bl, &pl_sd->bl, NPC_SELFDESTRUCTION, 99, timer->gettick(), 0); clif->message(fd, msg_fd(fd,109)); // Player has been nuked! @@ -4433,27 +4506,90 @@ ACMD(loadnpc) return true; } +/** + * Unloads a specific NPC. + * + * @code{.herc} + * @unloadnpc <NPC_name> {<flag>} + * @endcode + * + **/ ACMD(unloadnpc) { - struct npc_data *nd; - char NPCname[NAME_LENGTH+1]; + char npc_name[NAME_LENGTH + 1] = {'\0'}; + int flag = 1; - memset(NPCname, '\0', sizeof(NPCname)); + if (*message == '\0' || sscanf(message, "%24s %1d", npc_name, &flag) < 1) { + clif->message(fd, msg_fd(fd, 1133)); /// Please enter a NPC name (Usage: @unloadnpc <NPC_name> {<flag>}). + return false; + } + + struct npc_data *nd = npc->name2id(npc_name); + + if (nd == NULL) { + clif->message(fd, msg_fd(fd, 111)); /// This NPC doesn't exist. + return false; + } + + npc->unload_duplicates(nd, (flag != 0)); + npc->unload(nd, true, (flag != 0)); + npc->read_event_script(); + clif->message(fd, msg_fd(fd, 112)); /// Npc Disabled. + return true; +} + +/** + * Unloads a script file and reloads it. + * Note: Be aware that some changes made by NPC are not reverted on unload. See doc/atcommands.txt for details. + * + * @code{.herc} + * @reloadnpc <path> {<flag>} + * @endcode + * + **/ +ACMD(reloadnpc) +{ + char format[20]; + + snprintf(format, sizeof(format), "%%%ds %%1d", MAX_DIR_PATH); + + char file_path[MAX_DIR_PATH + 1] = {'\0'}; + int flag = 1; - if (!*message || sscanf(message, "%24[^\n]", NPCname) < 1) { - clif->message(fd, msg_fd(fd,1133)); // Please enter a NPC name (usage: @npcoff <NPC_name>). + if (*message == '\0' || (sscanf(message, format, file_path, &flag) < 1)) { + clif->message(fd, msg_fd(fd, 1516)); /// Usage: @reloadnpc <path> {<flag>} return false; } - if ((nd = npc->name2id(NPCname)) == NULL) { - clif->message(fd, msg_fd(fd,111)); // This NPC doesn't exist. + if (!exists(file_path)) { + clif->message(fd, msg_fd(fd, 1387)); /// File not found. + return false; + } + + if (!is_file(file_path)) { + clif->message(fd, msg_fd(fd, 1518)); /// Not a file. + return false; + } + + FILE *fp = fopen(file_path, "r"); + + if (fp == NULL) { + clif->message(fd, msg_fd(fd, 1519)); /// Can't open file. return false; } - npc->unload_duplicates(nd); - npc->unload(nd,true); + fclose(fp); + + if (!npc->unloadfile(file_path, (flag != 0))) { + clif->message(fd, msg_fd(fd, 1517)); /// Script could not be unloaded. + return false; + } + + clif->message(fd, msg_fd(fd, 1386)); /// File unloaded. Be aware that... + npc->addsrcfile(file_path); + npc->parsesrcfile(file_path, true); npc->read_event_script(); - clif->message(fd, msg_fd(fd,112)); // Npc Disabled. + clif->message(fd, msg_fd(fd, 262)); /// Script loaded. return true; } @@ -4602,7 +4738,7 @@ ACMD(jail) return false; } - if ((pl_sd = map->nick2sd(atcmd_player_name)) == NULL) { + if ((pl_sd = map->nick2sd(atcmd_player_name, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -4654,7 +4790,7 @@ ACMD(unjail) return false; } - if ((pl_sd = map->nick2sd(atcmd_player_name)) == NULL) { + if ((pl_sd = map->nick2sd(atcmd_player_name, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -4733,7 +4869,7 @@ ACMD(jailfor) return false; } - if ((pl_sd = map->nick2sd(atcmd_player_name)) == NULL) { + if ((pl_sd = map->nick2sd(atcmd_player_name, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -5045,7 +5181,7 @@ ACMD(broadcast) } safesnprintf(atcmd_output, sizeof(atcmd_output), "%s: %s", sd->status.name, message); - intif->broadcast(atcmd_output, (int)strlen(atcmd_output) + 1, BC_DEFAULT); + clif->broadcast(NULL, atcmd_output, (int)strlen(atcmd_output) + 1, BC_DEFAULT, ALL_CLIENT); return true; } @@ -5264,7 +5400,7 @@ ACMD(follow) return true; } - if ((pl_sd = map->nick2sd(message)) == NULL) { + if ((pl_sd = map->nick2sd(message, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -5281,20 +5417,49 @@ 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 i; + int type = -1; - for (i = 0; i < MAX_INVENTORY; 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); + 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; + } + } + + int count = 0, count_skipped = 0; + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].amount > 0) { + 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); + + int amount = sd->status.inventory[i].amount; + if (pc->dropitem(sd, i, amount) != 0) + count += amount; + else + count_skipped += amount; + } } } + + sprintf(atcmd_output, msg_fd(fd, 1502), count, count_skipped); // %d items are dropped (%d skipped)! + clif->message(fd, atcmd_output); return true; } @@ -5304,8 +5469,6 @@ ACMD(dropall) *------------------------------------------*/ ACMD(storeall) { - int i; - if (sd->state.storage_flag != STORAGE_FLAG_NORMAL) { //Open storage. if (storage->open(sd) == 1) { @@ -5319,7 +5482,7 @@ ACMD(storeall) return false; } - for (i = 0; i < MAX_INVENTORY; i++) { + 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); @@ -5408,7 +5571,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; } @@ -5493,7 +5656,7 @@ ACMD(useskill) if (!strcmp(target,"self")) pl_sd = sd; //quick keyword - else if ((pl_sd = map->nick2sd(target)) == NULL) { + else if ((pl_sd = map->nick2sd(target, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -5504,6 +5667,8 @@ ACMD(useskill) return false; } + pc->autocast_clear(sd); + if (skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE+MAX_HOMUNSKILL && sd->hd && homun_alive(sd->hd)) // (If used with @useskill, put the homunc as dest) bl = &sd->hd->bl; @@ -5561,7 +5726,7 @@ ACMD(skilltree) return false; } - if ( (pl_sd = map->nick2sd(target)) == NULL ) { + if ( (pl_sd = map->nick2sd(target, true)) == NULL ) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -5607,9 +5772,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); @@ -5631,7 +5796,7 @@ ACMD(marry) return false; } - if ((pl_sd = map->nick2sd(player_name)) == NULL) { + if ((pl_sd = map->nick2sd(player_name, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); return false; } @@ -5761,7 +5926,7 @@ ACMD(changegm) return false; } - if ((pl_sd=map->nick2sd(message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) { + if ((pl_sd=map->nick2sd(message, true)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) { clif->message(fd, msg_fd(fd,1184)); // Target character must be online and be a guild member. return false; } @@ -5782,7 +5947,7 @@ ACMD(changeleader) return false; } - if (party->changeleader(sd, map->nick2sd(message))) + if (party->changeleader(sd, map->nick2sd(message, true))) return true; return false; } @@ -6246,7 +6411,7 @@ ACMD(mobsearch) } if (mob_id == atoi(mob_name)) { strcpy(mob_name,mob->db(mob_id)->jname); // DEFAULT_MOB_JNAME - //strcpy(mob_name,mob_db(mob_id)->name); // DEFAULT_MOB_NAME + //strcpy(mob_name,mob->db(mob_id)->name); // DEFAULT_MOB_NAME } snprintf(atcmd_output, sizeof atcmd_output, msg_fd(fd,1220), mob_name, mapindex_id2name(sd->mapindex)); // Mob Search... %s %s @@ -6467,47 +6632,52 @@ ACMD(reset) /*========================================== * *------------------------------------------*/ + +/** + * Spawns mobs which treats the invoking as its master. + * + * @code{.herc} + * @summon <monster name/ID> {<duration>} + * @endcode + * + **/ ACMD(summon) { - char name[NAME_LENGTH]; - int mob_id = 0; + char name[NAME_LENGTH + 1] = {'\0'}; int duration = 0; - struct mob_data *md; - int64 tick=timer->gettick(); - if (!*message || sscanf(message, "%23s %12d", name, &duration) < 1) - { - clif->message(fd, msg_fd(fd,1225)); // Please enter a monster name (usage: @summon <monster name> {duration}). + if (*message == '\0' || sscanf(message, "%24s %12d", name, &duration) < 1) { + clif->message(fd, msg_fd(fd, 1225)); /// Please enter a monster name (usage: @summon <monster name> {duration}). return false; } - if (duration < 1) - duration =1; - else if (duration > 60) - duration =60; + int mob_id = atoi(name); - if ((mob_id = atoi(name)) == 0) + if (mob_id == 0) mob_id = mob->db_searchname(name); - if(mob_id == 0 || mob->db_checkid(mob_id) == 0) - { - clif->message(fd, msg_fd(fd,40)); // Invalid monster ID or name. + + if (mob_id == 0 || mob->db_checkid(mob_id) == 0) { + clif->message(fd, msg_fd(fd, 40)); /// Invalid monster ID or name. return false; } - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, -1, -1, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(&sd->bl, sd->bl.m, -1, -1, DEFAULT_MOB_JNAME, mob_id, "", + SZ_SMALL, AI_NONE, 0); - if(!md) + if (md == NULL) return false; md->master_id = sd->bl.id; md->special_state.ai = AI_ATTACK; - md->deletetimer = timer->add(tick+(duration*60000),mob->timer_delete,md->bl.id,0); - clif->specialeffect(&md->bl,344,AREA); - mob->spawn(md); - sc_start4(NULL,&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); - clif->skill_poseffect(&sd->bl,AM_CALLHOMUN,1,md->bl.x,md->bl.y,tick); - clif->message(fd, msg_fd(fd,39)); // All monster summoned! + const int64 tick = timer->gettick(); + + md->deletetimer = timer->add(tick + (int64)cap_value(duration, 1, 60) * 60000, mob->timer_delete, md->bl.id, 0); + clif->specialeffect(&md->bl, 344, AREA); + mob->spawn(md); + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); + clif->skill_poseffect(&sd->bl, AM_CALLHOMUN, 1, md->bl.x, md->bl.y, tick); + clif->message(fd, msg_fd(fd, 39)); /// All monster summoned! return true; } @@ -6548,7 +6718,7 @@ ACMD(trade) return false; } - if ((pl_sd = map->nick2sd(message)) == NULL) { + if ((pl_sd = map->nick2sd(message, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -6592,7 +6762,7 @@ ACMD(unmute) return false; } - if ((pl_sd = map->nick2sd(message)) == NULL) { + if ((pl_sd = map->nick2sd(message, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -6647,6 +6817,18 @@ ACMD(changesex) return true; } +ACMD(changecharsex) +{ + int i; + + pc->resetskill(sd, PCRESETSKILL_CHSEX); + // to avoid any problem with equipment and invalid sex, equipment is unequipped. + for (i=0; i<EQI_MAX; i++) + if (sd->equip_index[i] >= 0) pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); + chrif->changesex(sd, false); + return true; +} + /*================================================ * @mute - Mutes a player for a set amount of time *------------------------------------------------*/ @@ -6660,7 +6842,7 @@ ACMD(mute) return false; } - if ((pl_sd = map->nick2sd(atcmd_player_name)) == NULL) { + if ((pl_sd = map->nick2sd(atcmd_player_name, true)) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -6692,6 +6874,9 @@ ACMD(mute) *------------------------------------------*/ ACMD(refresh) { + if (sd->npc_id > 0) + return false; + clif->refresh(sd); return true; } @@ -6703,29 +6888,41 @@ ACMD(refreshall) iter = mapit_getallusers(); for (iter_sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); iter_sd = BL_UCAST(BL_PC, mapit->next(iter))) - clif->refresh(iter_sd); + if (iter_sd->npc_id <= 0) + clif->refresh(iter_sd); mapit->free(iter); return true; } /*========================================== - * @identify + * @identify / @identifyall * => GM's magnifier. *------------------------------------------*/ ACMD(identify) { - int i,num; + int num = 0; + bool identifyall = (strcmpi(info->command, "identifyall") == 0); - for (i=num=0;i<MAX_INVENTORY;i++) { - if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify!=1){ - num++; + if (!identifyall) { + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify != 1) { + num++; + } } - } - if (num > 0) { - clif->item_identify_list(sd); } else { - clif->message(fd,msg_fd(fd,1238)); // There are no items to appraise. + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify != 1) { + skill->identify(sd, i); + num++; + } + } } + + if (num == 0) + clif->message(fd,msg_fd(fd,1238)); // There are no items to appraise. + else if (!identifyall) + clif->item_identify_list(sd); + return true; } @@ -6942,7 +7139,7 @@ ACMD(showmobs) if (mob_id == atoi(mob_name)) { strcpy(mob_name,mob->db(mob_id)->jname); // DEFAULT_MOB_JNAME - //strcpy(mob_name,mob_db(mob_id)->name); // DEFAULT_MOB_NAME + //strcpy(mob_name,mob->db(mob_id)->name); // DEFAULT_MOB_NAME } snprintf(atcmd_output, sizeof atcmd_output, msg_fd(fd,1252), // Mob Search... %s %s @@ -7323,7 +7520,7 @@ ACMD(iteminfo) return false; } if ((item_array[0] = itemdb->exists(atoi(message))) == NULL) - count = itemdb->search_name_array(item_array, MAX_SEARCH, message, 0); + count = itemdb->search_name_array(item_array, MAX_SEARCH, message, IT_SEARCH_NAME_PARTIAL); if (!count) { clif->message(fd, msg_fd(fd,19)); // Invalid item ID or name. @@ -7374,7 +7571,7 @@ ACMD(whodrops) return false; } if ((item_array[0] = itemdb->exists(atoi(message))) == NULL) - count = itemdb->search_name_array(item_array, MAX_SEARCH, message, 0); + count = itemdb->search_name_array(item_array, MAX_SEARCH, message, IT_SEARCH_NAME_PARTIAL); if (!count) { clif->message(fd, msg_fd(fd,19)); // Invalid item ID or name. @@ -7666,39 +7863,61 @@ ACMD(monsterignore) return true; } -/*========================================== - * @fakename - * => Gives your character a fake name. [Valaris] - *------------------------------------------*/ + +/** + * Temporarily changes the character's name to the specified string. + * + * @code{.herc} + * @fakename {<options>} {<fake_name>} + * @endcode + * + **/ ACMD(fakename) { - if (!*message) - { - if (sd->fakename[0]) - { + if (*message == '\0') { + if (sd->fakename[0] != '\0') { sd->fakename[0] = '\0'; - clif->charnameack(0, &sd->bl); - if( sd->disguise ) - clif->charnameack(sd->fd, &sd->bl); - clif->message(sd->fd, msg_fd(fd,1307)); // Returned to real name. + sd->fakename_options = FAKENAME_OPTION_NONE; + clif->blname_ack(0, &sd->bl); + + if (sd->disguise != 0) // Another packet should be sent so the client updates the name for sd. + clif->blname_ack(sd->fd, &sd->bl); + + clif->message(sd->fd, msg_fd(fd, 1307)); // Returned to real name. return true; } - clif->message(sd->fd, msg_fd(fd,1308)); // You must enter a name. + clif->message(sd->fd, msg_fd(fd, 1308)); // You must enter a name. return false; } - if (strlen(message) < 2) - { - clif->message(sd->fd, msg_fd(fd,1309)); // Fake name must be at least two characters. + int options = FAKENAME_OPTION_NONE; + char buf[NAME_LENGTH] = {'\0'}; + const char *fake_name = NULL; + + if (sscanf(message, "%d %23[^\n]", &options, buf) == 2) { + fake_name = buf; + } else { + options = FAKENAME_OPTION_NONE; + fake_name = message; + } + + if (strlen(fake_name) < 2) { + clif->message(sd->fd, msg_fd(fd, 1309)); // Fake name must be at least two characters. return false; } - safestrncpy(sd->fakename, message, sizeof(sd->fakename)); - clif->charnameack(0, &sd->bl); - if (sd->disguise) // Another packet should be sent so the client updates the name for sd - clif->charnameack(sd->fd, &sd->bl); - clif->message(sd->fd, msg_fd(fd,1310)); // Fake name enabled. + if (options < FAKENAME_OPTION_NONE) + options = FAKENAME_OPTION_NONE; + + safestrncpy(sd->fakename, fake_name, sizeof(sd->fakename)); + sd->fakename_options = options; + clif->blname_ack(0, &sd->bl); + + if (sd->disguise != 0) // Another packet should be sent so the client updates the name for sd. + clif->blname_ack(sd->fd, &sd->bl); + + clif->message(sd->fd, msg_fd(fd, 1310)); // Fake name enabled. return true; } @@ -7739,6 +7958,7 @@ ACMD(mapflag) CHECKFLAG(nodrop); CHECKFLAG(novending); CHECKFLAG(loadevent); CHECKFLAG(nochat); CHECKFLAG(partylock); CHECKFLAG(guildlock); CHECKFLAG(src4instance); CHECKFLAG(notomb); CHECKFLAG(nocashshop); CHECKFLAG(noviewid); CHECKFLAG(town); + CHECKFLAG(nostorage); CHECKFLAG(nogstorage); clif->message(sd->fd," "); clif->message(sd->fd,msg_fd(fd,1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On) clif->message(sd->fd,msg_fd(fd,1313)); // Type "@mapflag available" to list the available mapflags. @@ -7780,7 +8000,7 @@ ACMD(mapflag) SETFLAG(nomvploot); SETFLAG(nightenabled); SETFLAG(nodrop); SETFLAG(novending); SETFLAG(loadevent); SETFLAG(nochat); SETFLAG(partylock); SETFLAG(guildlock); SETFLAG(src4instance); SETFLAG(notomb); SETFLAG(nocashshop); SETFLAG(noviewid); - SETFLAG(town); + SETFLAG(town); SETFLAG(nostorage); SETFLAG(nogstorage); clif->message(sd->fd, msg_fd(fd, 1314)); // Invalid flag name or flag. @@ -7793,7 +8013,7 @@ ACMD(mapflag) clif->message(sd->fd, "nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,"); clif->message(sd->fd, "fog, fireworks, sakura, leaves, nobaseexp, nojobexp, nomobloot,"); clif->message(sd->fd, "nomvploot, nightenabled, nodrop, novending, loadevent, nochat, partylock,"); - clif->message(sd->fd, "guildlock, src4instance, notomb, nocashshop, noviewid"); + clif->message(sd->fd, "guildlock, src4instance, notomb, nocashshop, noviewid, nostorage, nogstorage"); #undef CHECKFLAG #undef SETFLAG @@ -7854,7 +8074,7 @@ ACMD(showdelay) ACMD(invite) { unsigned int did = sd->duel_group; - struct map_session_data *target_sd = map->nick2sd(message); + struct map_session_data *target_sd = map->nick2sd(message, true); if (did == 0) { @@ -7911,10 +8131,11 @@ ACMD(duel) return false; } - if (!duel->checktime(sd)) { + int64 diff = duel->difftime(sd); + if (diff > 0) { char output[CHAT_SIZE_MAX]; - // "Duel: You can take part in duel only one time per %d minutes." - sprintf(output, msg_fd(fd,356), battle_config.duel_time_interval); + // "Duel: You can take part in duel again after %d secconds." + sprintf(output, msg_fd(fd,356), (int)diff); clif->message(fd, output); return false; } @@ -7927,7 +8148,7 @@ ACMD(duel) } duel->create(sd, maxpl); } else { - struct map_session_data *target_sd = map->nick2sd(message); + struct map_session_data *target_sd = map->nick2sd(message, true); if (target_sd != NULL) { unsigned int newduel; if ((newduel = duel->create(sd, 2)) != -1) { @@ -7964,10 +8185,11 @@ ACMD(leave) ACMD(accept) { - if (!duel->checktime(sd)) { + int64 diff = duel->difftime(sd); + if (diff > 0) { char output[CHAT_SIZE_MAX]; - // "Duel: You can take part in duel only one time per %d minutes." - sprintf(output, msg_fd(fd,356), battle_config.duel_time_interval); + // "Duel: You can take part in duel again after %d seconds." + sprintf(output, msg_fd(fd,356), (int)diff); clif->message(fd, output); return false; } @@ -8072,7 +8294,7 @@ ACMD(clone) return false; } - if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { + if ((pl_sd=map->nick2sd(message, true)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) { clif->message(fd, msg_fd(fd,3)); // Character not found. return false; } @@ -8146,7 +8368,7 @@ ACMD(request) } safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,278), message); // (@request): %s - intif->wis_message_to_gm(sd->status.name, PC_PERM_RECEIVE_REQUESTS, atcmd_output); + pc->wis_message_to_gm(sd->status.name, PC_PERM_RECEIVE_REQUESTS, atcmd_output); clif_disp_onlyself(sd, atcmd_output); clif->message(sd->fd,msg_fd(fd,279)); // @request sent. return true; @@ -8163,6 +8385,15 @@ ACMD(feelreset) return true; } +// Reset hatred targets [Wolfie] +ACMD(hatereset) +{ + pc->resethate(sd); + clif->message(fd, msg_fd(fd, 979)); // Reset 'Hatred' targets. + + return true; +} + /*========================================== * AUCTION SYSTEM *------------------------------------------*/ @@ -8256,7 +8487,7 @@ ACMD(itemlist) } else if( strcmpi(info->command, "itemlist") == 0 ) { location = "inventory"; items = sd->status.inventory; - size = MAX_INVENTORY; + size = sd->status.inventorySize; } else return false; @@ -8861,19 +9092,54 @@ ACMD(addperm) return true; } +/** + * Unloads a script file. + * Note: Be aware that some changes made by NPC are not reverted on unload. See doc/atcommands.txt for details. + * + * @code{.herc} + * @unloadnpcfile <path> {<flag>} + * @endcode + * + **/ ACMD(unloadnpcfile) { - if (!*message) { - clif->message(fd, msg_fd(fd,1385)); // Usage: @unloadnpcfile <file name> + char format[20]; + + snprintf(format, sizeof(format), "%%%ds %%1d", MAX_DIR_PATH); + + char file_path[MAX_DIR_PATH + 1] = {'\0'}; + int flag = 1; + + if (*message == '\0' || (sscanf(message, format, file_path, &flag) < 1)) { + clif->message(fd, msg_fd(fd, 1385)); /// Usage: @unloadnpcfile <path> {<flag>} return false; } - if (npc->unloadfile(message)) { - clif->message(fd, msg_fd(fd,1386)); // File unloaded. Be aware that mapflags and monsters spawned directly are not removed. - } else { - clif->message(fd, msg_fd(fd,1387)); // File not found. + if (!exists(file_path)) { + clif->message(fd, msg_fd(fd, 1387)); /// File not found. + return false; + } + + if (!is_file(file_path)) { + clif->message(fd, msg_fd(fd, 1518)); /// Not a file. + return false; + } + + FILE *fp = fopen(file_path, "r"); + + if (fp == NULL) { + clif->message(fd, msg_fd(fd, 1519)); /// Can't open file. + return false; + } + + fclose(fp); + + if (!npc->unloadfile(file_path, (flag != 0))) { + clif->message(fd, msg_fd(fd, 1517)); /// Script could not be unloaded. return false; } + + clif->message(fd, msg_fd(fd, 1386)); /// File unloaded. Be aware that... return true; } @@ -9190,7 +9456,7 @@ ACMD(channel) return false; } - if (sub4[0] == '\0' || (pl_sd = map->nick2sd(sub4)) == NULL) { + if (sub4[0] == '\0' || (pl_sd = map->nick2sd(sub4, true)) == NULL) { safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1434), sub4);// Player '%s' was not found clif->message(fd, atcmd_output); return false; @@ -9237,7 +9503,7 @@ ACMD(channel) clif->message(fd, atcmd_output); return false; } - if (sub4[0] == '\0' || (pl_sd = map->nick2sd(sub4)) == NULL) { + if (sub4[0] == '\0' || (pl_sd = map->nick2sd(sub4, true)) == NULL) { safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1434), sub4);// Player '%s' was not found clif->message(fd, atcmd_output); return false; @@ -9811,6 +10077,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 **/ @@ -9980,10 +10262,12 @@ static void atcommand_basecommands(void) ACMD_DEF(clearweather), ACMD_DEF(uptime), ACMD_DEF(changesex), + ACMD_DEF(changecharsex), ACMD_DEF(mute), ACMD_DEF(refresh), ACMD_DEF(refreshall), ACMD_DEF(identify), + ACMD_DEF2("identifyall", identify), ACMD_DEF(misceffect), ACMD_DEF(mobsearch), ACMD_DEF(cleanmap), @@ -10046,6 +10330,7 @@ static void atcommand_basecommands(void) ACMD_DEF(homshuffle), ACMD_DEF(showmobs), ACMD_DEF(feelreset), + ACMD_DEF(hatereset), ACMD_DEF(auction), ACMD_DEF(mail), ACMD_DEF2("noks", ksprotection), @@ -10073,6 +10358,7 @@ static void atcommand_basecommands(void) ACMD_DEF(addperm), ACMD_DEF2("rmvperm", addperm), ACMD_DEF(unloadnpcfile), + ACMD_DEF(reloadnpc), ACMD_DEF(cart), ACMD_DEF(cashmount), ACMD_DEF(join), @@ -10094,6 +10380,7 @@ static void atcommand_basecommands(void) ACMD_DEF(reloadclans), ACMD_DEF(setzone), ACMD_DEF(camerainfo), + ACMD_DEF(refineryui), }; int i; @@ -10278,9 +10565,9 @@ static bool atcommand_exec(const int fd, struct map_session_data *sd, const char clif->message(fd, msg_fd(fd,143)); return false; } + if (sd->block_action.commands) // *pcblock script command + return false; } - if (sd->block_action.commands) // *pcblock script command - return false; if (*message == atcommand->char_symbol) is_atcommand = false; @@ -10312,7 +10599,7 @@ static bool atcommand_exec(const int fd, struct map_session_data *sd, const char return true; } - ssd = map->nick2sd(charname); + ssd = map->nick2sd(charname, true); if (ssd == NULL) { sprintf(output, msg_fd(fd,1389), command); // %s failed. Player not found. clif->message(fd, output); @@ -10724,6 +11011,9 @@ void atcommand_defaults(void) { atcommand = &atcommand_s; + atcommand->atcmd_output = &atcmd_output; + atcommand->atcmd_player_name = &atcmd_player_name; + atcommand->db = NULL; atcommand->alias_db = NULL; diff --git a/src/map/atcommand.h b/src/map/atcommand.h index 3bbbefa20..f3a5155ab 100644 --- a/src/map/atcommand.h +++ b/src/map/atcommand.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,9 +21,11 @@ #ifndef MAP_ATCOMMAND_H #define MAP_ATCOMMAND_H +#include "map/mapdefines.h" #include "map/pc_groups.h" #include "common/hercules.h" #include "common/db.h" +#include "common/mmo.h" #include <stdarg.h> @@ -39,7 +41,7 @@ struct config_setting_t; * Defines **/ #define ATCOMMAND_LENGTH 50 -#define MAX_MSG 1500 +#define MAX_MSG 1520 #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)) @@ -52,6 +54,16 @@ typedef enum { COMMAND_CHARCOMMAND = 2, } AtCommandType; +/** @fakename display option flags **/ +enum fakename_option_flag { + FAKENAME_OPTION_NONE = 0x00, + FAKENAME_OPTION_SHOW_PARTYNAME = 0x01, + FAKENAME_OPTION_SHOW_GUILDNAME = 0x02, + FAKENAME_OPTION_SHOW_GUILDPOSITION = 0x04, + FAKENAME_OPTION_SHOW_CLANPOSITION = 0x08, + FAKENAME_OPTION_SHOW_TITLE = 0x10 +}; + /** * Typedef **/ @@ -90,6 +102,8 @@ struct atcmd_binding_data { * Interface **/ struct atcommand_interface { + char (*atcmd_output)[CHAT_SIZE_MAX]; + char (*atcmd_player_name)[NAME_LENGTH]; unsigned char at_symbol; unsigned char char_symbol; /* atcommand binding */ diff --git a/src/map/battle.c b/src/map/battle.c index f51240810..c8cd71b94 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1019,7 +1019,7 @@ static int64 battle_calc_cardfix(int attack_type, struct block_list *src, struct { struct map_session_data *sd, *tsd; int cardfix = 1000; - short t_class, s_class, s_race2, t_race2; + int t_class, s_class, s_race2, t_race2; struct status_data *sstatus, *tstatus; int i; @@ -3231,12 +3231,11 @@ static int64 battle_calc_damage(struct block_list *src, struct block_list *bl, s if (!damage) return 0; if( (sce = sc->data[SC_LIGHTNINGWALK]) && flag&BF_LONG && rnd()%100 < sce->val1 ) { - int dx[8]={0,-1,-1,-1,0,1,1,1}; - int dy[8]={1,1,0,-1,-1,-1,0,1}; - uint8 dir = map->calc_dir(bl, src->x, src->y); - if( unit->movepos(bl, src->x-dx[dir], src->y-dy[dir], 1, 1) ) { - clif->slide(bl,src->x-dx[dir],src->y-dy[dir]); - unit->setdir(bl, dir); + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); + Assert_ret(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); + if (unit->movepos(bl, src->x - dirx[dir], src->y - diry[dir], 1, 1)) { + clif->slide(bl, src->x - dirx[dir], src->y - diry[dir]); + unit->set_dir(bl, dir); } d->dmg_lv = ATK_DEF; status_change_end(bl, SC_LIGHTNINGWALK, INVALID_TIMER); @@ -3467,11 +3466,6 @@ static int64 battle_calc_gvg_damage(struct block_list *src, struct block_list *b case NC_SELFDESTRUCTION: break; default: - /* Uncomment if you want god-mode Emperiums at 100 defense. [Kisuka] - if (md && md->guardian_data) { - damage -= damage * (md->guardian_data->castle->defense/100) * battle_config.castle_defense_rate/100; - } - */ break; } return damage; @@ -3756,7 +3750,7 @@ static struct Damage battle_calc_magic_attack(struct block_list *src, struct blo if (sc){ if( sc->data[SC_TELEKINESIS_INTENSE] && s_ele == ELE_GHOST ) - ad.damage += sc->data[SC_TELEKINESIS_INTENSE]->val3; + ad.damage += ad.damage * sc->data[SC_TELEKINESIS_INTENSE]->val3 / 100; } switch(skill_id){ case MG_FIREBOLT: @@ -4130,13 +4124,6 @@ static struct Damage battle_calc_misc_attack(struct block_list *src, struct bloc case NPC_EVILLAND: md.damage = skill->calc_heal(src,target,skill_id,skill_lv,false); break; - case RK_DRAGONBREATH: - case RK_DRAGONBREATH_WATER: - md.damage = ((status_get_hp(src) / 50) + (status_get_max_sp(src) / 4)) * skill_lv; - RE_LVL_MDMOD(150); - if (sd) md.damage = md.damage * (95 + 5 * pc->checkskill(sd,RK_DRAGONTRAINING)) / 100; - md.flag |= BF_LONG|BF_WEAPON; - break; /** * Ranger **/ @@ -4343,7 +4330,7 @@ static struct Damage battle_calc_misc_attack(struct block_list *src, struct bloc } break; } - + battle->reflect_trap(target, src, &md, skill_id); return md; @@ -4666,13 +4653,17 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl { short cri = sstatus->cri; if (sd != NULL) { + // Racial crit bonuses are affected by katar's crit bonus. + if (battle_config.show_katar_crit_bonus && sd->weapontype == W_KATAR) + cri += sd->critaddrace[tstatus->race] * 2; + else + cri += sd->critaddrace[tstatus->race]; + // if show_katar_crit_bonus is enabled, it already done the calculation in status.c if (!battle_config.show_katar_crit_bonus && sd->weapontype == W_KATAR) { cri <<= 1; } - cri+= sd->critaddrace[tstatus->race]; - if (flag.arrow) { cri += sd->bonus.arrow_cri; } @@ -4960,6 +4951,12 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl break; } break; + case RK_DRAGONBREATH: + case RK_DRAGONBREATH_WATER: + wd.damage = ((status_get_hp(src) / 50) + (status_get_max_sp(src) / 4)) * skill_lv; + wd.damage = wd.damage * status->get_lv(src) / 150; + if (sd) wd.damage = wd.damage * (95 + 5 * pc->checkskill(sd, RK_DRAGONTRAINING)) / 100; + break; default: { i = (flag.cri @@ -4978,6 +4975,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl switch (sd->weapontype) { case W_BOW: case W_REVOLVER: + case W_RIFLE: case W_GATLING: case W_SHOTGUN: case W_GRENADE: @@ -5552,7 +5550,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src, struct bl #endif if( flag.infdef ) { //Plants receive 1 damage when hit - short class_ = status->get_class(target); + int class_ = status->get_class(target); if( flag.hit || wd.damage > 0 ) wd.damage = wd.div_; // In some cases, right hand no need to have a weapon to increase damage if( flag.lh && (flag.hit || wd.damage2 > 0) ) @@ -5858,10 +5856,10 @@ static void battle_reflect_damage(struct block_list *target, struct block_list * if( wd->flag & BF_SHORT ) { if( !is_boss(src) ) { if( sc->data[SC_DEATHBOUND] && skill_id != WS_CARTTERMINATION ) { - uint8 dir = map->calc_dir(target,src->x,src->y), - t_dir = unit->getdir(target); + enum unit_dir dir = map->calc_dir(target, src->x, src->y); + enum unit_dir t_dir = unit->getdir(target); - if( !map->check_dir(dir,t_dir) ) { + if (map->check_dir(dir, t_dir) == 0) { int64 rd1 = damage * sc->data[SC_DEATHBOUND]->val2 / 100; // Amplify damage. trdamage += rdamage = rd1 - (damage = rd1 * 30 / 100); // not normalized as intended. @@ -5929,21 +5927,21 @@ static void battle_reflect_damage(struct block_list *target, struct block_list * delay += 100;/* gradual increase so the numbers don't clip in the client */ } if( sc->data[SC_LG_REFLECTDAMAGE] && rnd()%100 < (30 + 10*sc->data[SC_LG_REFLECTDAMAGE]->val1) ) { - bool change = false; - NORMALIZE_RDAMAGE(damage * sc->data[SC_LG_REFLECTDAMAGE]->val2 / 100); trdamage -= rdamage;/* wont count towards total */ - if( sd && !sd->state.autocast ) { - change = true; - sd->state.autocast = 1; + enum autocast_type ac_type; + + if (sd != NULL) { + ac_type = sd->autocast.type; + sd->autocast.type = AUTOCAST_TEMP; } map->foreachinshootrange(battle->damage_area,target,skill->get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,target,delay,wd->dmotion,rdamage,status_get_race(target)); - if( change ) - sd->state.autocast = 0; + if (sd != NULL) + sd->autocast.type = ac_type; delay += 150;/* gradual increase so the numbers don't clip in the client */ @@ -6133,7 +6131,7 @@ static int battle_damage_area(struct block_list *bl, va_list ap) else status_fix_damage(src,bl,damage,0); clif->damage(bl,bl,amotion,dmotion,damage,1,BDT_ENDURE,0); - if (src->type != BL_PC || !BL_UCCAST(BL_PC, src)->state.autocast) + if (src->type != BL_PC || BL_UCCAST(BL_PC, src)->autocast.type != AUTOCAST_TEMP) skill->additional_effect(src, bl, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick); map->freeblock_unlock(); } @@ -6229,10 +6227,10 @@ static enum damage_lv battle_weapon_attack(struct block_list *src, struct block_ status_change_end(src, SC_CLOAKINGEXCEED, INVALID_TIMER); } if( tsc && tsc->data[SC_AUTOCOUNTER] && status->check_skilluse(target, src, KN_AUTOCOUNTER, 1) ) { - uint8 dir = map->calc_dir(target,src->x,src->y); - int t_dir = unit->getdir(target); + enum unit_dir dir = map->calc_dir(target, src->x, src->y); + enum unit_dir t_dir = unit->getdir(target); int dist = distance_bl(src, target); - if(dist <= 0 || (!map->check_dir(dir,t_dir) && dist <= tstatus->rhw.range+1)) { + if(dist <= 0 || (map->check_dir(dir, t_dir) == 0 && dist <= tstatus->rhw.range + 1)) { uint16 skill_lv = tsc->data[SC_AUTOCOUNTER]->val1; clif->skillcastcancel(target); //Remove the casting bar. [Skotlex] clif->damage(src, target, sstatus->amotion, 1, 0, 1, BDT_NORMAL, 0); //Display MISS. @@ -6321,6 +6319,18 @@ static enum damage_lv battle_weapon_attack(struct block_list *src, struct block_ if (sd && sd->state.arrow_atk) //Consume arrow. battle->consume_ammo(sd, 0, 0); + if (target->type == BL_MOB) { + struct mob_data *md = BL_CAST(BL_MOB, target); + if (md != NULL) { + if (md->db->dmg_taken_rate != 100) { + if (wd.damage > 0) + wd.damage = apply_percentrate64(wd.damage, md->db->dmg_taken_rate, 100); + if (wd.damage2 > 0) + wd.damage2 = apply_percentrate64(wd.damage2, md->db->dmg_taken_rate, 100); + } + } + } + damage = wd.damage + wd.damage2; if( damage > 0 && src != target ) { if( sc && sc->data[SC_DUPLELIGHT] && (wd.flag&BF_SHORT) && rnd()%100 <= 10+2*sc->data[SC_DUPLELIGHT]->val1 ){ @@ -6361,7 +6371,7 @@ static enum damage_lv battle_weapon_attack(struct block_list *src, struct block_ if (d_bl != NULL && ((d_bl->type == BL_MER && d_md->master != NULL && d_md->master->bl.id == target->id) - || (d_bl->type == BL_PC && d_sd->devotion[sce->val2] == target->id) + || (d_sd != NULL && d_bl->type == BL_PC && d_sd->devotion[sce->val2] == target->id) ) && check_distance_bl(target, d_bl, sce->val3) ) { @@ -6445,12 +6455,12 @@ static enum damage_lv battle_weapon_attack(struct block_list *src, struct block_ } } - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(sd,r_skill,r_lv,3); skill->castend_type(type, src, target, r_skill, r_lv, tick, flag); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; sd->ud.canact_tick = tick + skill->delay_fix(src, r_skill, r_lv); - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, r_skill, r_lv), 0, 0, 1); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, r_skill, r_lv), 0, 0, 1); } } @@ -6589,10 +6599,6 @@ static int battle_check_target(struct block_list *src, struct block_list *target m = target->m; - if (flag & BCT_ENEMY && (map->getcell(m, src, src->x, src->y, CELL_CHKBASILICA) || map->getcell(m, src, target->x, target->y, CELL_CHKBASILICA))) { - return -1; - } - //t_bl/s_bl hold the 'master' of the attack, while src/target are the actual //objects involved. if( (t_bl = battle->get_master(target)) == NULL ) @@ -6601,6 +6607,11 @@ static int battle_check_target(struct block_list *src, struct block_list *target if( (s_bl = battle->get_master(src)) == NULL ) s_bl = src; + if ((flag & BCT_ENEMY) != 0 && (status_get_mode(s_bl) & MD_BOSS) == 0 && (map->getcell(m, src, src->x, src->y, CELL_CHKBASILICA) != 0 + || map->getcell(m, src, target->x, target->y, CELL_CHKBASILICA) != 0)) { + return -1; + } + if (s_bl->type == BL_PC) { const struct map_session_data *s_sd = BL_UCCAST(BL_PC, s_bl); switch (t_bl->type) { @@ -7004,7 +7015,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, }, @@ -7072,16 +7084,15 @@ static const struct battle_data { { "guild_emperium_check", &battle_config.guild_emperium_check, 1, 0, 1, }, { "guild_exp_limit", &battle_config.guild_exp_limit, 50, 0, 99, }, { "player_invincible_time", &battle_config.pc_invincible_time, 5000, 0, INT_MAX, }, + { "pet_catch_rate_official_formula", &battle_config.pet_catch_rate_official_formula, 1, 0, 1, }, { "pet_catch_rate", &battle_config.pet_catch_rate, 100, 0, INT_MAX, }, { "pet_rename", &battle_config.pet_rename, 0, 0, 1, }, { "pet_friendly_rate", &battle_config.pet_friendly_rate, 100, 0, INT_MAX, }, { "pet_hungry_delay_rate", &battle_config.pet_hungry_delay_rate, 100, 10, INT_MAX, }, - { "pet_hungry_friendly_decrease", &battle_config.pet_hungry_friendly_decrease, 5, 0, INT_MAX, }, { "pet_status_support", &battle_config.pet_status_support, 0, 0, 1, }, { "pet_attack_support", &battle_config.pet_attack_support, 0, 0, 1, }, { "pet_damage_support", &battle_config.pet_damage_support, 0, 0, 1, }, { "pet_support_min_friendly", &battle_config.pet_support_min_friendly, 900, 0, 950, }, - { "pet_equip_min_friendly", &battle_config.pet_equip_min_friendly, 900, 0, 950, }, { "pet_support_rate", &battle_config.pet_support_rate, 100, 0, INT_MAX, }, { "pet_attack_exp_to_master", &battle_config.pet_attack_exp_to_master, 0, 0, 1, }, { "pet_attack_exp_rate", &battle_config.pet_attack_exp_rate, 100, 0, INT_MAX, }, @@ -7175,7 +7186,6 @@ static const struct battle_data { { "skill_removetrap_type", &battle_config.skill_removetrap_type, 0, 0, 1, }, { "disp_experience", &battle_config.disp_experience, 0, 0, 1, }, { "disp_zeny", &battle_config.disp_zeny, 0, 0, 1, }, - { "castle_defense_rate", &battle_config.castle_defense_rate, 100, 0, 100, }, { "bone_drop", &battle_config.bone_drop, 0, 0, 2, }, { "buyer_name", &battle_config.buyer_name, 1, 0, 1, }, { "skill_wall_check", &battle_config.skill_wall_check, 1, 0, 1, }, @@ -7308,6 +7318,7 @@ static const struct battle_data { { "mob_remove_delay", &battle_config.mob_remove_delay, 60000, 1000, INT_MAX, }, { "mob_active_time", &battle_config.mob_active_time, 0, 0, INT_MAX, }, { "boss_active_time", &battle_config.boss_active_time, 0, 0, INT_MAX, }, + { "slave_chase_masters_chasetarget", &battle_config.slave_chase_masters_chasetarget, 1, 0, 1, }, { "sg_miracle_skill_duration", &battle_config.sg_miracle_skill_duration, 3600000, 0, INT_MAX, }, { "hvan_explosion_intimate", &battle_config.hvan_explosion_intimate, 45000, 0, 100000, }, { "quest_exp_rate", &battle_config.quest_exp_rate, 100, 0, INT_MAX, }, @@ -7413,6 +7424,30 @@ static const struct battle_data { { "min_item_buy_price", &battle_config.min_item_buy_price, 1, 0, INT_MAX, }, { "min_item_sell_price", &battle_config.min_item_sell_price, 0, 0, INT_MAX, }, { "display_fake_hp_when_dead", &battle_config.display_fake_hp_when_dead, 1, 0, 1, }, + { "magicrod_type", &battle_config.magicrod_type, 0, 0, 1, }, + { "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, }, + { "autoloot_adjust", &battle_config.autoloot_adjust, 0, 0, 1, }, + { "hom_bonus_exp_from_master", &battle_config.hom_bonus_exp_from_master, 10, 0, 100, }, }; static bool battle_set_value_sub(int index, int value) @@ -7538,6 +7573,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"); @@ -7567,6 +7614,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 750753c08..bb907d5b9 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -103,8 +103,8 @@ enum e_battle_check_target { //New definitions [Skotlex] enum battle_dmg_type { BDT_NORMAL = 0, // Normal attack //BDT_PICKUP = 1, // Pick up item - //BDT_SITDOWN = 2, // Sit down - //BDT_STANDUP = 3, // Stand up + BDT_SITDOWN = 2, // Sit down + BDT_STANDUP = 3, // Stand up BDT_ENDURE = 4, // Damage (endure) BDT_SPLASH = 5, // Splash BDT_SKILL = 6, // Skill @@ -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] @@ -211,16 +212,15 @@ struct Battle_Config { int guild_aura; int pc_invincible_time; + int pet_catch_rate_official_formula; int pet_catch_rate; int pet_rename; int pet_friendly_rate; int pet_hungry_delay_rate; - int pet_hungry_friendly_decrease; int pet_status_support; int pet_attack_support; int pet_damage_support; int pet_support_min_friendly; //[Skotlex] - int pet_equip_min_friendly; int pet_support_rate; int pet_attack_exp_to_master; int pet_attack_exp_rate; @@ -341,7 +341,6 @@ struct Battle_Config { int skill_removetrap_type; int disp_experience; int disp_zeny; - int castle_defense_rate; int backstab_bow_penalty; int hp_rate; int sp_rate; @@ -404,6 +403,7 @@ struct Battle_Config { int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex] int mob_active_time; //Duration through which mobs execute their Hard AI after players leave their area of sight. int boss_active_time; + int slave_chase_masters_chasetarget; int show_hp_sp_drain, show_hp_sp_gain; //[Skotlex] int show_katar_crit_bonus; @@ -477,6 +477,7 @@ struct Battle_Config { int client_emblem_max_blank_percent; int hom_max_level; int hom_S_max_level; + int hom_bonus_exp_from_master; // [BattleGround Settings] int bg_update_interval; @@ -576,6 +577,36 @@ struct Battle_Config { int min_item_sell_price; int display_fake_hp_when_dead; + + int magicrod_type; + + int feature_enable_achievement; + + 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; + + int autoloot_adjust; }; /* criteria for battle_config.idletime_critera */ @@ -590,6 +621,7 @@ enum e_battle_config_idletime { BCIDLE_EMOTION = 0x080, BCIDLE_DROPITEM = 0x100, BCIDLE_ATCOMMAND = 0x200, + BCIDLE_SCRIPT = 0x400, }; // Damage delayed info diff --git a/src/map/battleground.c b/src/map/battleground.c index c2772a2b9..7a9d54266 100644 --- a/src/map/battleground.c +++ b/src/map/battleground.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -169,13 +169,13 @@ static int bg_team_leave(struct map_session_data *sd, enum bg_team_leave_type fl switch (flag) { default: case BGTL_QUIT: - sprintf(output, "Server : %s has quit the game...", sd->status.name); + sprintf(output, msg_txt(464), sd->status.name); // Server : %s has quit the game... break; case BGTL_LEFT: - sprintf(output, "Server : %s is leaving the battlefield...", sd->status.name); + sprintf(output, msg_txt(454), sd->status.name); // Server : %s is leaving the battlefield... break; case BGTL_AFK: - sprintf(output, "Server : %s has been afk-kicked from the battlefield...", sd->status.name); + sprintf(output, msg_txt(455), sd->status.name); // Server : %s has been afk-kicked from the battlefield... break; } clif->bg_message(bgd, 0, "Server", output); @@ -860,9 +860,9 @@ static enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, str if ( ( tick = pc_readglobalreg(sd, script->add_variable(bg->gdelay_var)) ) && tsec < tick ) { char response[100]; if( (tick-tsec) > 60 ) - sprintf(response, "You are a deserter! Wait %u minute(s) before you can apply again", (tick - tsec) / 60); + sprintf(response, msg_sd(sd, 456), (tick - tsec) / 60); // You are a deserter! Wait %u minute(s) before you can apply again else - sprintf(response, "You are a deserter! Wait %u seconds before you can apply again", (tick - tsec)); + sprintf(response, msg_sd(sd, 457), (tick - tsec)); // You are a deserter! Wait %u seconds before you can apply again clif->messagecolor_self(sd->fd, COLOR_RED, response); return BGQA_FAIL_DESERTER; } @@ -870,9 +870,9 @@ static enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, str if ( ( tick = pc_readglobalreg(sd, script->add_variable(arena->delay_var)) ) && tsec < tick ) { char response[100]; if( (tick-tsec) > 60 ) - sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %u minute(s)", (tick - tsec) / 60); + sprintf(response, msg_sd(sd, 458), (tick - tsec) / 60); // You can't reapply to this arena so fast. Apply to the different arena or wait %u minute(s) else - sprintf(response, "You can't reapply to this arena so fast. Apply to the different arena or wait %u seconds", (tick - tsec)); + sprintf(response, msg_sd(sd, 459), (tick - tsec)); // You can't reapply to this arena so fast. Apply to the different arena or wait %u seconds clif->messagecolor_self(sd->fd, COLOR_RED, response); return BGQA_FAIL_COOLDOWN; } @@ -894,9 +894,9 @@ static enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, str if ( count < arena->min_team_players ) { char response[121]; if( count != sd->guild->connect_member && sd->guild->connect_member >= arena->min_team_players ) - sprintf(response, "Can't apply: not enough members in your team/guild that have not entered the queue in individual mode, minimum is %d", arena->min_team_players); + sprintf(response, msg_sd(sd, 460), arena->min_team_players); // Can't apply: not enough members in your team/guild that have not entered the queue in individual mode, minimum is %d else - sprintf(response, "Can't apply: not enough members in your team/guild, minimum is %d", arena->min_team_players); + sprintf(response, msg_sd(sd, 461), arena->min_team_players); // Can't apply: not enough members in your team/guild, minimum is %d clif->messagecolor_self(sd->fd, COLOR_RED, response); return BGQA_FAIL_TEAM_COUNT; } @@ -926,9 +926,9 @@ static enum BATTLEGROUNDS_QUEUE_ACK bg_canqueue(struct map_session_data *sd, str if( count < arena->min_team_players ) { char response[121]; if( count != p->party.count && p->party.count >= arena->min_team_players ) - sprintf(response, "Can't apply: not enough members in your team/party that have not entered the queue in individual mode, minimum is %d", arena->min_team_players); + sprintf(response, msg_sd(sd, 462), arena->min_team_players); // Can't apply: not enough members in your team/party that have not entered the queue in individual mode, minimum is %d else - sprintf(response, "Can't apply: not enough members in your team/party, minimum is %d",arena->min_team_players); + sprintf(response, msg_sd(sd, 463), arena->min_team_players); // Can't apply: not enough members in your team/party, minimum is %d clif->messagecolor_self(sd->fd, COLOR_RED, response); return BGQA_FAIL_TEAM_COUNT; } diff --git a/src/map/battleground.h b/src/map/battleground.h index 4f6e5507e..b567dddd4 100644 --- a/src/map/battleground.h +++ b/src/map/battleground.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/buyingstore.c b/src/map/buyingstore.c index df622e4ab..2c2fc13ae 100644 --- a/src/map/buyingstore.c +++ b/src/map/buyingstore.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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/buyingstore.h b/src/map/buyingstore.h index 63762f321..a35167fbc 100644 --- a/src/map/buyingstore.h +++ b/src/map/buyingstore.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/channel.c b/src/map/channel.c index e27e9fb0b..43e903fc9 100644 --- a/src/map/channel.c +++ b/src/map/channel.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 @@ -324,7 +324,7 @@ static void channel_join_sub(struct channel_data *chan, struct map_session_data if (!stealth && (chan->options&HCS_OPT_ANNOUNCE_JOIN)) { char message[60]; - sprintf(message, "#%s '%s' joined",chan->name,sd->status.name); + sprintf(message, msg_txt(897), chan->name, sd->status.name); // #%s '%s' joined clif->channel_msg(chan,sd,message); } @@ -442,7 +442,7 @@ static void channel_leave(struct channel_data *chan, struct map_session_data *sd channel->delete(chan); } else if (!channel->config->closing && (chan->options & HCS_OPT_ANNOUNCE_JOIN)) { char message[60]; - sprintf(message, "#%s '%s' left",chan->name,sd->status.name); + sprintf(message, msg_txt(898), chan->name, sd->status.name); // #%s '%s' left clif->channel_msg(chan,sd,message); } diff --git a/src/map/channel.h b/src/map/channel.h index c56227c66..8f91b0777 100644 --- a/src/map/channel.h +++ b/src/map/channel.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 diff --git a/src/map/chat.c b/src/map/chat.c index d9b642219..097fbdca4 100644 --- a/src/map/chat.c +++ b/src/map/chat.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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/chat.h b/src/map/chat.h index 9bea51a82..6c571c62e 100644 --- a/src/map/chat.h +++ b/src/map/chat.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/chrif.c b/src/map/chrif.c index af3504ca2..b131907e0 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -975,14 +975,14 @@ static bool chrif_divorceack(int char_id, int partner_id) if( ( sd = map->charid2sd(char_id) ) != NULL && sd->status.partner_id == partner_id ) { sd->status.partner_id = 0; - for(i = 0; i < MAX_INVENTORY; i++) + for (i = 0; i < sd->status.inventorySize; i++) if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); } if( ( sd = map->charid2sd(partner_id) ) != NULL && sd->status.partner_id == char_id ) { sd->status.partner_id = 0; - for(i = 0; i < MAX_INVENTORY; i++) + for (i = 0; i < sd->status.inventorySize; i++) if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); } @@ -1226,6 +1226,7 @@ static bool chrif_save_scdata(struct map_session_data *sd) } else { data.tick = INFINITE_DURATION; } + data.total_tick = sc->data[i]->total_tick; data.type = i; data.val1 = sc->data[i]->val1; data.val2 = sc->data[i]->val2; @@ -1273,8 +1274,8 @@ static bool chrif_load_scdata(int fd) for (i = 0; i < count; i++) { const struct status_change_data *data = RFIFOP(fd,14 + i*sizeof(struct status_change_data)); - status->change_start(NULL, &sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, - data->tick, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_LOADED|SCFLAG_FIXEDRATE); + status->change_start_sub(NULL, &sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, + data->tick, data->total_tick, SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_LOADED|SCFLAG_FIXEDRATE); } pc->scdata_received(sd); diff --git a/src/map/chrif.h b/src/map/chrif.h index 78910d24b..e2e82c6cb 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/clan.c b/src/map/clan.c index 3d5c70eb1..041ba1d6d 100644 --- a/src/map/clan.c +++ b/src/map/clan.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2017 Hercules Dev Team + * Copyright (C) 2017-2020 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 diff --git a/src/map/clan.h b/src/map/clan.h index 15ed52680..95ea8565c 100644 --- a/src/map/clan.h +++ b/src/map/clan.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2017 Hercules Dev Team + * Copyright (C) 2017-2020 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 diff --git a/src/map/clif.c b/src/map/clif.c index 7d539b3cd..7be5c6978 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,9 +49,11 @@ #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" +#include "map/stylist.h" #include "map/storage.h" #include "map/trade.h" #include "map/unit.h" @@ -90,10 +92,8 @@ static struct packet_itemlist_equip itemlist_equip; static struct ZC_STORE_ITEMLIST_NORMAL storelist_normal; static struct ZC_STORE_ITEMLIST_EQUIP storelist_equip; static struct packet_viewequip_ack viewequip_list; -#if PACKETVER >= 20131223 -static struct packet_npc_market_result_ack npcmarket_result; -static struct packet_npc_market_open npcmarket_open; -#endif +// temporart buffer for send big packets +char packet_buf[0xffff]; //#define DUMP_UNKNOWN_PACKET //#define DUMP_INVALID_PACKET @@ -429,8 +429,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; @@ -438,11 +443,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; @@ -890,7 +890,7 @@ static void clif_clearflooritem(struct flooritem_data *fitem, int fd) /// 2 = logged out /// 3 = teleport /// 4 = trickdead -static void clif_clearunit_single(int id, clr_type type, int fd) +static void clif_clearunit_single(int id, enum clr_type type, int fd) { WFIFOHEAD(fd, packet_len(0x80)); WFIFOW(fd,0) = 0x80; @@ -907,7 +907,7 @@ static void clif_clearunit_single(int id, clr_type type, int fd) /// 2 = logged out /// 3 = teleport /// 4 = trickdead -static void clif_clearunit_area(struct block_list *bl, clr_type type) +static void clif_clearunit_area(struct block_list *bl, enum clr_type type) { unsigned char buf[8]; @@ -936,12 +936,12 @@ static void clif_clearunit_area(struct block_list *bl, clr_type type) static int clif_clearunit_delayed_sub(int tid, int64 tick, int id, intptr_t data) { struct block_list *bl = (struct block_list *)data; - clif->clearunit_area(bl, (clr_type) id); + clif->clearunit_area(bl, (enum clr_type) id); ers_free(clif->delay_clearunit_ers,bl); return 0; } -static void clif_clearunit_delayed(struct block_list *bl, clr_type type, int64 tick) +static void clif_clearunit_delayed(struct block_list *bl, enum clr_type type, int64 tick) { struct block_list *tbl; @@ -1116,7 +1116,7 @@ static void clif_set_unit_idle(struct block_list *bl, struct map_session_data *t p.head = vd->hair_style; p.weapon = vd->weapon; p.accessory = vd->head_bottom; -#if PACKETVER < 7 || PACKETVER_RE_NUM >= 20180704 +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 p.shield = vd->shield; #endif p.accessory2 = vd->head_top; @@ -1273,7 +1273,7 @@ static void clif_spawn_unit(struct block_list *bl, enum send_target target) p.head = vd->hair_style; p.weapon = vd->weapon; p.accessory = vd->head_bottom; -#if PACKETVER < 7 || PACKETVER_RE_NUM >= 20180704 +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 p.shield = vd->shield; #endif p.accessory2 = vd->head_top; @@ -1382,7 +1382,7 @@ static void clif_set_unit_walking(struct block_list *bl, struct map_session_data p.weapon = vd->weapon; p.accessory = vd->head_bottom; p.moveStartTime = (unsigned int)timer->gettick(); -#if PACKETVER < 7 || PACKETVER_RE_NUM >= 20180704 +#if PACKETVER < 7 || PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 p.shield = vd->shield; #endif p.accessory2 = vd->head_top; @@ -1586,7 +1586,8 @@ static bool clif_spawn(struct block_list *bl) if (sd->bg_id != 0 && map->list[sd->bl.m].flag.battleground) clif->sendbgemblem_area(sd); for (i = 0; i < sd->sc_display_count; i++) { - clif->sc_continue(&sd->bl, sd->bl.id,AREA,status->dbs->IconChangeTable[sd->sc_display[i]->type],sd->sc_display[i]->val1,sd->sc_display[i]->val2,sd->sc_display[i]->val3); + clif->sc_continue(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(sd->sc_display[i]->type), sd->sc_display[i]->val1, sd->sc_display[i]->val2, sd->sc_display[i]->val3); + } if (sd->charm_type != CHARM_TYPE_NONE && sd->charm_count > 0) clif->spiritcharm(sd); @@ -1612,7 +1613,7 @@ static bool clif_spawn(struct block_list *bl) else if (nd->size == SZ_MEDIUM) clif->specialeffect(&nd->bl,421,AREA); if (nd->clan_id > 0) - clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, nd->clan_id, 0); + clif->sc_load(&nd->bl, nd->bl.id, AREA, status->get_sc_icon(SC_CLAN_INFO), 0, nd->clan_id, 0); } break; case BL_PET: @@ -1627,6 +1628,7 @@ static bool clif_spawn(struct block_list *bl) /// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W static void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag) { +#if PACKETVER_MAIN_NUM >= 20101005 || PACKETVER_RE_NUM >= 20080827 || defined(PACKETVER_ZERO) struct status_data *hstatus; enum homun_type htype; struct PACKET_ZC_PROPERTY_HOMUN p; @@ -1638,14 +1640,16 @@ static void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int htype = homun->class2type(hd->homunculus.class_); memset(&p, 0, sizeof(p)); - p.packetType = hominfoType; + p.packetType = HEADER_ZC_PROPERTY_HOMUN; memcpy(p.name, hd->homunculus.name, NAME_LENGTH); // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) p.flags = (!battle_config.hom_rename && hd->homunculus.rename_flag ? 0x1 : 0x0) | (hd->homunculus.vaporize == HOM_ST_REST ? 0x2 : 0) | (hd->homunculus.hp > 0 ? 0x4 : 0); p.level = hd->homunculus.level; p.hunger = hd->homunculus.hunger; p.intimacy = hd->homunculus.intimacy / 100; +#if !(PACKETVER_MAIN_NUM >= 20190619 || PACKETVER_RE_NUM >= 20190605 || PACKETVER_ZERO_NUM >= 20190626) p.itemId = 0; // equip id +#endif #ifdef RENEWAL p.atk2 = cap_value(hstatus->rhw.atk2, 0, INT16_MAX); #else @@ -1704,6 +1708,7 @@ static void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int p.skillPoints = hd->homunculus.skillpts; p.range = status_get_range(&hd->bl); clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif } /// Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER). @@ -2160,20 +2165,23 @@ static void clif_buylist(struct map_session_data *sd, struct npc_data *nd) /// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }* static void clif_selllist(struct map_session_data *sd) { - int fd,i,c=0,val; + int c = 0, val; nullpo_retv(sd); - fd=sd->fd; - WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4); + int fd = sd->fd; + WFIFOHEAD(fd, sd->status.inventorySize * 10 + 4); WFIFOW(fd,0)=0xc7; - for( i = 0; i < MAX_INVENTORY; i++ ) + for (int i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] ) { if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) ) continue; + if (sd->status.inventory[i].favorite != 0) + continue; // Cannot Sell Favorite item + if( sd->status.inventory[i].expire_time ) continue; // Cannot Sell Rental Items @@ -2216,6 +2224,8 @@ static void clif_scriptmes(struct map_session_data *sd, int npcid, const char *m slen = (int)strlen(mes) + 9; Assert_retv(slen <= INT16_MAX); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + sd->state.dialog = 1; WFIFOHEAD(fd, slen); @@ -2252,6 +2262,8 @@ static void clif_scriptnext(struct map_session_data *sd, int npcid) nullpo_retv(sd); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + fd=sd->fd; WFIFOHEAD(fd, packet_len(0xb5)); WFIFOW(fd,0)=0xb5; @@ -2280,6 +2292,8 @@ static void clif_scriptclose(struct map_session_data *sd, int npcid) nullpo_retv(sd); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + fd=sd->fd; WFIFOHEAD(fd, packet_len(0xb6)); WFIFOW(fd,0)=0xb6; @@ -2351,6 +2365,8 @@ static void clif_scriptmenu(struct map_session_data *sd, int npcid, const char * bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) clif->sendfakenpc(sd, npcid); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + WFIFOHEAD(fd, slen); WFIFOW(fd,0) = 0xb7; WFIFOW(fd,2) = slen; @@ -2382,6 +2398,8 @@ static void clif_scriptinput(struct map_session_data *sd, int npcid) bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) clif->sendfakenpc(sd, npcid); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + fd=sd->fd; WFIFOHEAD(fd, packet_len(0x142)); WFIFOW(fd,0)=0x142; @@ -2412,6 +2430,8 @@ static void clif_scriptinputstr(struct map_session_data *sd, int npcid) bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)))) clif->sendfakenpc(sd, npcid); + pc->update_idle_time(sd, BCIDLE_SCRIPT); + fd=sd->fd; WFIFOHEAD(fd, packet_len(0x1d4)); WFIFOW(fd,0)=0x1d4; @@ -2582,7 +2602,7 @@ static void clif_additem(struct map_session_data *sd, int n, int amount, int fai p.count = amount; if( !fail ) { - if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL ) + if (n < 0 || n >= sd->status.inventorySize || sd->status.inventory[n].nameid <= 0 || sd->inventory_data[n] == NULL) return; if (sd->inventory_data[n]->view_id > 0) @@ -2804,7 +2824,7 @@ static void clif_inventoryItems(struct map_session_data *sd, enum inventory_type int i, normal = 0, equip = 0; nullpo_retv(sd); - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) continue; @@ -2838,7 +2858,7 @@ static void clif_inventoryItems(struct map_session_data *sd, enum inventory_type } /* on 20120925 onwards this is a field on clif_item_equip/normal */ #if PACKETVER >= 20111122 && PACKETVER < 20120925 - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) continue; @@ -2866,7 +2886,7 @@ static void clif_equipItems(struct map_session_data *sd, enum inventory_type typ int i, equip = 0; nullpo_retv(sd); - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) continue; @@ -2886,7 +2906,7 @@ static void clif_equipItems(struct map_session_data *sd, enum inventory_type typ /* on 20120925 onwards this is a field on clif_item_equip */ #if PACKETVER >= 20111122 && PACKETVER < 20120925 - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL ) continue; @@ -2919,22 +2939,22 @@ static void clif_inventoryStart(struct map_session_data *sd, enum inventory_type nullpo_retv(sd); nullpo_retv(name); - char buf[sizeof(struct ZC_INVENTORY_START) + 24]; + char buf[sizeof(struct PACKET_ZC_INVENTORY_START) + 24]; memset(buf, 0, sizeof(buf)); - struct ZC_INVENTORY_START *p = (struct ZC_INVENTORY_START *)buf; - p->packetType = 0xb08; + struct PACKET_ZC_INVENTORY_START *p = (struct PACKET_ZC_INVENTORY_START *)buf; + p->packetType = HEADER_ZC_INVENTORY_START; #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 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; + const int len = sizeof(struct PACKET_ZC_INVENTORY_START) + strLen; p->packetLength = len; safestrncpy(p->name, name, strLen); #else - const int len = sizeof(struct ZC_INVENTORY_START); + const int len = sizeof(struct PACKET_ZC_INVENTORY_START); safestrncpy(p->name, name, NAME_LENGTH); #endif clif->send(p, len, &sd->bl, SELF); @@ -2946,8 +2966,8 @@ static void clif_inventoryEnd(struct map_session_data *sd, enum inventory_type t #if PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 nullpo_retv(sd); - struct ZC_INVENTORY_END p; - p.packetType = 0xb0b; + struct PACKET_ZC_INVENTORY_END p; + p.packetType = HEADER_ZC_INVENTORY_END; #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 p.invType = type; #endif @@ -2961,28 +2981,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; @@ -2992,11 +3005,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; @@ -3006,10 +3020,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 - } while ( i < items_length ); + 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); + +#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) @@ -3062,6 +3105,121 @@ static void clif_cartItems(struct map_session_data *sd, enum inventory_type type } } +static void clif_inventoryExpansionInfo(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 + nullpo_retv(sd); + + const int fd = sd->fd; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_INVENTORY_EXPANSION_INFO)); + struct PACKET_ZC_INVENTORY_EXPANSION_INFO *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_INVENTORY_EXPANSION_INFO; + p->expansionSize = sd->status.inventorySize - FIXED_INVENTORY_SIZE; + WFIFOSET(fd, sizeof(struct PACKET_ZC_INVENTORY_EXPANSION_INFO)); +#endif +} + +static void clif_inventoryExpandAck(struct map_session_data *sd, enum expand_inventory result, int itemId) +{ +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 + nullpo_retv(sd); + + const int fd = sd->fd; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_ACK_INVENTORY_EXPAND)); + struct PACKET_ZC_ACK_INVENTORY_EXPAND *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_ACK_INVENTORY_EXPAND; + p->result = result; + p->itemId = itemId; + WFIFOSET(fd, sizeof(struct PACKET_ZC_ACK_INVENTORY_EXPAND)); +#endif +} + +static void clif_inventoryExpandResult(struct map_session_data *sd, enum expand_inventory_result result) +{ +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 + nullpo_retv(sd); + + const int fd = sd->fd; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_ACK_INVENTORY_EXPAND_RESULT)); + struct PACKET_ZC_ACK_INVENTORY_EXPAND_RESULT *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_ACK_INVENTORY_EXPAND_RESULT; + p->result = result; + WFIFOSET(fd, sizeof(struct PACKET_ZC_ACK_INVENTORY_EXPAND_RESULT)); +#endif +} + +static void clif_parse_inventoryExpansion(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_inventoryExpansion(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 + if (pc_isdead(sd) || pc_cant_act(sd)) { + clif->inventoryExpandAck(sd, EXPAND_INVENTORY_OTHER_WORK, 0); + return; + } + if (sd->status.inventorySize == MAX_INVENTORY) { + clif->inventoryExpandAck(sd, EXPAND_INVENTORY_MAX_SIZE, 0); + return; + } + + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev = NULL; + + safestrncpy(evname, "inventory_expansion::OnInvExpandRequest", EVENT_NAME_LENGTH); + if ((ev = strdb_get(npc->ev_db, evname))) { + script->run_npc(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id); + } else { + ShowError("clif_parse_inventoryExpansion: event '%s' not found, operation failed.\n", evname); + } +#endif +} + +static void clif_parse_inventoryExpansionConfirmed(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_inventoryExpansionConfirmed(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 + if (pc_isdead(sd) || pc_cant_act(sd)) { + clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_OTHER_WORK); + return; + } + if (sd->status.inventorySize == MAX_INVENTORY) { + clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_MAX_SIZE); + return; + } + + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev = NULL; + + safestrncpy(evname, "inventory_expansion::OnInvExpandConfirmed", EVENT_NAME_LENGTH); + if ((ev = strdb_get(npc->ev_db, evname))) { + script->run_npc(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id); + } else { + ShowError("clif_parse_inventoryExpansionConfirmed: event '%s' not found, operation failed.\n", evname); + } +#endif +} + +static void clif_parse_inventoryExpansionRejected(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_inventoryExpansionRejected(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 + char evname[EVENT_NAME_LENGTH]; + struct event_data *ev = NULL; + + safestrncpy(evname, "inventory_expansion::OnInvExpandRejected", EVENT_NAME_LENGTH); + if ((ev = strdb_get(npc->ev_db, evname))) { + script->run_npc(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id); + } else { + ShowError("clif_parse_inventoryExpansionRejected: event '%s' not found, operation failed.\n", evname); + } +#endif +} + +// CZ_REQ_REMAINTIME +static void clif_parse_reqRemainTime(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_reqRemainTime(int fd, struct map_session_data *sd) +{ +} + /// Removes cart (ZC_CARTOFF). /// 012b /// Client behavior: @@ -3719,7 +3877,7 @@ static void clif_arrowequip(struct map_session_data *sd, int val) nullpo_retv(sd); #if PACKETVER >= 20121128 - clif->status_change(&sd->bl, SI_CLIENT_ONLY_EQUIP_ARROW, 1, INVALID_TIMER, 0, 0, 0); + clif->status_change(&sd->bl, status->get_sc_icon(SC_CLIENT_ONLY_EQUIP_ARROW), status->get_sc_relevant_bl_types(SC_CLIENT_ONLY_EQUIP_ARROW), 1, INVALID_TIMER, 0, 0, 0); #endif fd=sd->fd; WFIFOHEAD(fd, packet_len(0x013c)); @@ -3754,20 +3912,16 @@ static void clif_arrow_fail(struct map_session_data *sd, int type) /// 01ad <packet len>.W { <name id>.W }* static void clif_arrow_create_list(struct map_session_data *sd) { - int i, c; - int fd; - int len; - struct PACKET_ZC_MAKINGARROW_LIST *p; - nullpo_retv(sd); - fd = sd->fd; - len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + int fd = sd->fd; + int len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); WFIFOHEAD(fd, len); - p = WFIFOP(fd, 0); - p->packetType = 0x1ad; + struct PACKET_ZC_MAKINGARROW_LIST *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MAKINGARROW_LIST; - for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) { + int c = 0; + for (int i = 0; i < MAX_SKILL_ARROW_DB; i++) { int j; if (skill->dbs->arrow_db[i].nameid > 0 && (j = pc->search_inventory(sd, skill->dbs->arrow_db[i].nameid)) != INDEX_NOT_FOUND @@ -3798,17 +3952,16 @@ static void clif_arrow_create_list(struct map_session_data *sd) /// 1 = success static void clif_statusupack(struct map_session_data *sd, int type, int ok, int val) { - int fd; - nullpo_retv(sd); + int fd = sd->fd; - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0xbc)); - WFIFOW(fd,0)=0xbc; - WFIFOW(fd,2)=type; - WFIFOB(fd,4)=ok; - WFIFOB(fd,5)=cap_value(val,0,UINT8_MAX); - WFIFOSET(fd,packet_len(0xbc)); + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_STATUS_CHANGE_ACK)); + struct PACKET_ZC_STATUS_CHANGE_ACK *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_STATUS_CHANGE_ACK; + p->sp = type; + p->ok = ok; + p->value = cap_value(val, 0, UINT8_MAX); + WFIFOSET(fd, sizeof(struct PACKET_ZC_STATUS_CHANGE_ACK)); } /// Notifies the client about the result of a request to equip an item (ZC_REQ_WEAR_EQUIP_ACK). @@ -3824,6 +3977,7 @@ static void clif_equipitemack(struct map_session_data *sd, int n, int pos, enum p.index = n+2; p.wearLocation = pos; #if PACKETVER >= 20100629 + Assert_retv(n >= 0 && n < sd->status.inventorySize); if (result == EIA_SUCCESS && sd->inventory_data[n]->equip&EQP_VISIBLE) p.wItemSpriteNumber = sd->inventory_data[n]->view_sprite; else @@ -3881,51 +4035,58 @@ static void clif_misceffect(struct block_list *bl, int type) /// 0229 <id>.L <body state>.W <health state>.W <effect state>.L <pk mode>.B (ZC_STATE_CHANGE3) static void clif_changeoption(struct block_list *bl) { - unsigned char buf[32]; - struct status_change *sc; - struct map_session_data* sd; - nullpo_retv(bl); - if ( !(sc = status->get_sc(bl)) && bl->type != BL_NPC ) return; //How can an option change if there's no sc? + struct status_change *sc = status->get_sc(bl); - sd = BL_CAST(BL_PC, bl); + if (sc == NULL && bl->type != BL_NPC) // How can an option change if there's no sc? + return; -#if PACKETVER >= 7 - WBUFW(buf,0) = 0x229; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = (sc) ? sc->opt1 : 0; - WBUFW(buf,8) = (sc) ? sc->opt2 : 0; - WBUFL(buf,10) = (sc != NULL) ? sc->option : ((bl->type == BL_NPC) ? BL_UCCAST(BL_NPC, bl)->option : 0); - WBUFB(buf,14) = (sd)? sd->status.karma : 0; + struct map_session_data *sd = BL_CAST(BL_PC, bl); + struct PACKET_ZC_STATE_CHANGE p; + p.packetType = HEADER_ZC_STATE_CHANGE; + p.AID = bl->id; + p.bodyState = (sc != NULL) ? sc->opt1 : 0; + p.healthState = (sc != NULL) ? sc->opt2 : 0; + p.effectState = (sc != NULL) ? sc->option : BL_UCCAST(BL_NPC, bl)->option; + p.isPKModeON = (sd != NULL) ? sd->status.karma : 0; if (clif->isdisguised(bl)) { - clif->send(buf,packet_len(0x229),bl,AREA_WOS); - WBUFL(buf,2) = -bl->id; - clif->send(buf,packet_len(0x229),bl,SELF); - WBUFL(buf,2) = bl->id; - WBUFL(buf,10) = OPTION_INVISIBLE; - clif->send(buf,packet_len(0x229),bl,SELF); + clif->send(&p, sizeof(p), bl, AREA_WOS); + p.AID = -bl->id; + clif->send(&p, sizeof(p), bl, SELF); + p.AID = bl->id; + p.effectState = OPTION_INVISIBLE; + clif->send(&p, sizeof(p), bl, SELF); } else { - clif->send(buf,packet_len(0x229),bl,AREA); + clif->send(&p, sizeof(p), bl, AREA); } -#else - WBUFW(buf,0) = 0x119; - WBUFL(buf,2) = bl->id; - WBUFW(buf,6) = (sc) ? sc->opt1 : 0; - WBUFW(buf,8) = (sc) ? sc->opt2 : 0; - WBUFL(buf,10) = (sc != NULL) ? sc->option : ((bl->type == BL_NPC) ? BL_UCCAST(BL_NPC, bl)->option : 0); - WBUFB(buf,12) = (sd)? sd->status.karma : 0; +} + +static void clif_changeoption_target(struct block_list *bl, struct block_list *target_bl, enum send_target target) +{ + nullpo_retv(bl); + nullpo_retv(target_bl); + + struct status_change *sc = status->get_sc(bl); + + if (sc == NULL && bl->type != BL_NPC) // How can an option change if there's no sc? + return; + + struct map_session_data *sd = BL_CAST(BL_PC, bl); + struct PACKET_ZC_STATE_CHANGE p; + p.packetType = HEADER_ZC_STATE_CHANGE; + p.AID = bl->id; + p.bodyState = (sc != NULL) ? sc->opt1 : 0; + p.healthState = (sc != NULL) ? sc->opt2 : 0; + p.effectState = (sc != NULL) ? sc->option : BL_UCCAST(BL_NPC, bl)->option; + p.isPKModeON = (sd != NULL) ? sd->status.karma : 0; if (clif->isdisguised(bl)) { - clif->send(buf,packet_len(0x119),bl,AREA_WOS); - WBUFL(buf,2) = -bl->id; - clif->send(buf,packet_len(0x119),bl,SELF); - WBUFL(buf,2) = bl->id; - WBUFW(buf,10) = OPTION_INVISIBLE; - clif->send(buf,packet_len(0x119),bl,SELF); - } else { - clif->send(buf,packet_len(0x119),bl,AREA); + p.AID = -bl->id; + clif->send(&p, sizeof(p), target_bl, target); + p.AID = bl->id; + p.effectState = OPTION_INVISIBLE; } -#endif + clif->send(&p, sizeof(p), target_bl, target); } /// Displays status change effects on NPCs/monsters (ZC_NPC_SHOWEFST_UPDATE). @@ -3965,7 +4126,7 @@ static void clif_useitemack(struct map_session_data *sd, int index, int amount, nullpo_retv(sd); - if (index < 0 || index >= MAX_INVENTORY) + if (index < 0 || index >= sd->status.inventorySize) return; fd = sd->fd; @@ -4187,22 +4348,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). @@ -4314,7 +4478,7 @@ static void clif_tradeadditem(struct map_session_data *sd, struct map_session_da if (index != 0) { index -= 2; //index fix - Assert_retv(index >= 0 && index < MAX_INVENTORY); + Assert_retv(index >= 0 && index < sd->status.inventorySize); if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0) p.itemId = sd->inventory_data[index]->view_id; else @@ -4523,7 +4687,7 @@ static void clif_getareachar_pc(struct map_session_data *sd, struct map_session_ clif->charm_single(sd->fd, dstsd); for( i = 0; i < dstsd->sc_display_count; i++ ) { - clif->sc_continue(&sd->bl,dstsd->bl.id,SELF,status->dbs->IconChangeTable[dstsd->sc_display[i]->type],dstsd->sc_display[i]->val1,dstsd->sc_display[i]->val2,dstsd->sc_display[i]->val3); + clif->sc_continue(&sd->bl, dstsd->bl.id, SELF, status->get_sc_icon(dstsd->sc_display[i]->type), dstsd->sc_display[i]->val1, dstsd->sc_display[i]->val2, dstsd->sc_display[i]->val3); } if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting. (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround @@ -4604,7 +4768,7 @@ static void clif_getareachar_unit(struct map_session_data *sd, struct block_list else if (nd->size == SZ_MEDIUM) clif->specialeffect_single(bl,421,sd->fd); if (nd->clan_id > 0) - clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, nd->clan_id, 0); + clif->sc_load(&nd->bl, nd->bl.id, AREA, status->get_sc_icon(SC_CLAN_INFO), 0, nd->clan_id, 0); } break; case BL_MOB: @@ -4636,9 +4800,9 @@ static void clif_getareachar_unit(struct map_session_data *sd, struct block_list //Modifies the type of damage according to status changes [Skotlex] //Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit. -static inline int clif_calc_delay(int type, int div, int damage, int delay) +static inline enum battle_dmg_type clif_calc_delay(enum battle_dmg_type type, int div, int damage, int delay) { - return ( delay == 0 && damage > 0 ) ? ( div > 1 ? 9 : 4 ) : type; + return ( delay == 0 && damage > 0 ) ? ( div > 1 ? BDT_MULTIENDURE : BDT_ENDURE ) : type; } /*========================================== @@ -4669,7 +4833,7 @@ static int clif_calc_walkdelay(struct block_list *bl, int delay, int type, int d /// 08c8 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <IsSPDamage>.B <div>.W <type>.B <damage2>.L (ZC_NOTIFY_ACT2) /// type: @see enum battle_dmg_type /// for BDT_NORMAL: [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ] -static int clif_damage(struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, unsigned char type, int64 in_damage2) +static int clif_damage(struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, enum battle_dmg_type type, int64 in_damage2) { struct packet_damage p; struct status_change *sc; @@ -4739,7 +4903,7 @@ static int clif_damage(struct block_list *src, struct block_list *dst, int sdela } if(src == dst) { - unit->setdir(src,unit->getdir(src)); + unit->set_dir(src, unit->getdir(src)); } //Return adjusted can't walk delay for further processing. @@ -4910,9 +5074,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; @@ -4921,7 +5084,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; @@ -5099,58 +5262,79 @@ static int clif_insight(struct block_list *bl, va_list ap) return 0; } +static void clif_playerSkillToPacket(struct map_session_data *sd, struct SKILLDATA *skillData, int skillId, int idx, bool newSkill) +{ + nullpo_retv(sd); + nullpo_retv(skillData); + Assert_retv(idx >= 0 && idx < MAX_SKILL_DB); + + int skill_lv = sd->status.skill[idx].lv; + skillData->id = skillId; + skillData->inf = skill->get_inf(skillId); + skillData->level = skill_lv; + if (skill_lv > 0) { + skillData->sp = skill->get_sp(skillId, skill_lv); + skillData->range2 = skill->get_range2(&sd->bl, skillId, skill_lv); + } else { + skillData->sp = 0; + skillData->range2 = 0; + } +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 + if (newSkill) + skillData->level2 = 0; + else + skillData->level2 = skill_lv; +#else + safestrncpy(skillData->name, skill->get_name(skillId), NAME_LENGTH); +#endif + if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) + skillData->upFlag = (skill_lv < skill->tree_get_max(skillId, sd->status.class)) ? 1 : 0; + else + skillData->upFlag = 0; +} + /// Updates whole skill tree (ZC_SKILLINFO_LIST). /// 010f <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }* static void clif_skillinfoblock(struct map_session_data *sd) { - int fd; - int i,len,id; - nullpo_retv(sd); - fd=sd->fd; - if (!fd) return; + int fd = sd->fd; + if (!fd) + return; + + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_SKILLINFO_LIST) + MAX_SKILL_DB * sizeof(struct SKILLDATA)); + struct PACKET_ZC_SKILLINFO_LIST *p = WFIFOP(fd, 0); - WFIFOHEAD(fd, MAX_SKILL_DB * 37 + 4); - WFIFOW(fd,0) = 0x10f; - for ( i = 0, len = 4; i < MAX_SKILL_DB; i++) { - if( (id = sd->status.skill[i].id) != 0 ) { - int level; + p->packetType = HEADER_ZC_SKILLINFO_LIST; + int skillIndex = 0; + int len = sizeof(struct PACKET_ZC_SKILLINFO_LIST); + int i; + for (i = 0; i < MAX_SKILL_DB; i++) { + int id = sd->status.skill[i].id; + if (id != 0) { // workaround for bugreport:5348 - if (len + 37 > 8192) + if (len + sizeof(struct SKILLDATA) > 8192) break; - WFIFOW(fd, len) = id; - WFIFOL(fd, len + 2) = skill->get_inf(id); - level = sd->status.skill[i].lv; - WFIFOW(fd, len + 6) = level; - if (level) { - WFIFOW(fd, len + 8) = skill->get_sp(id, level); - WFIFOW(fd, len + 10)= skill->get_range2(&sd->bl, id, level); - } - else { - WFIFOW(fd, len + 8) = 0; - WFIFOW(fd, len + 10)= 0; - } - safestrncpy(WFIFOP(fd,len+12), skill->get_name(id), NAME_LENGTH); - if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) - WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill->tree_get_max(id, sd->status.class))? 1:0; - else - WFIFOB(fd,len+36) = 0; - len += 37; + clif->playerSkillToPacket(sd, &p->skills[skillIndex], id, i, false); + len += sizeof(struct SKILLDATA); + skillIndex++; } } - WFIFOW(fd,2)=len; - WFIFOSET(fd,len); + p->packetLength = len; + WFIFOSET(fd, len); // workaround for bugreport:5348; send the remaining skills one by one to bypass packet size limit - for ( ; i < MAX_SKILL_DB; i++) { - if( (id = sd->status.skill[i].id) != 0 ) { + for (; i < MAX_SKILL_DB; i++) { + int id = sd->status.skill[i].id; + if (id != 0) { clif->addskill(sd, id); clif->skillinfo(sd, id, 0); } } } + /** * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item) **/ @@ -5159,36 +5343,21 @@ static void clif_skillinfoblock(struct map_session_data *sd) /// 0111 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B static void clif_addskill(struct map_session_data *sd, int id) { - int fd, skill_lv, idx = skill->get_index(id); - nullpo_retv(sd); - fd = sd->fd; - if (!fd) return; + int fd = sd->fd; + if (!fd) + return; + int idx = skill->get_index(id); if (sd->status.skill[idx].id <= 0) return; - skill_lv = sd->status.skill[idx].lv; - - WFIFOHEAD(fd, packet_len(0x111)); - WFIFOW(fd,0) = 0x111; - WFIFOW(fd,2) = id; - WFIFOL(fd,4) = skill->get_inf(id); - WFIFOW(fd,8) = skill_lv; - if (skill_lv > 0) { - WFIFOW(fd,10) = skill->get_sp(id, skill_lv); - WFIFOW(fd,12) = skill->get_range2(&sd->bl, id, skill_lv); - } else { - WFIFOW(fd,10) = 0; - WFIFOW(fd,12) = 0; - } - safestrncpy(WFIFOP(fd,14), skill->get_name(id), NAME_LENGTH); - if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) - WFIFOB(fd,38) = (skill_lv < skill->tree_get_max(id, sd->status.class))? 1:0; - else - WFIFOB(fd,38) = 0; - WFIFOSET(fd,packet_len(0x111)); + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_ADD_SKILL)); + struct PACKET_ZC_ADD_SKILL *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_ADD_SKILL; + clif->playerSkillToPacket(sd, &p->skill, id, idx, true); + WFIFOSET(fd, sizeof(struct PACKET_ZC_ADD_SKILL)); } /// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE). @@ -5241,32 +5410,34 @@ static void clif_skillup(struct map_session_data *sd, uint16 skill_id, int skill /// 07e1 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <upgradable>.B static void clif_skillinfo(struct map_session_data *sd, int skill_id, int inf) { + nullpo_retv(sd); + const int fd = sd->fd; int idx = skill->get_index(skill_id); - int skill_lv; - - nullpo_retv(sd); Assert_retv(idx >= 0 && idx < MAX_SKILL_DB); - skill_lv = sd->status.skill[idx].lv; - - WFIFOHEAD(fd,packet_len(0x7e1)); - WFIFOW(fd,0) = 0x7e1; - WFIFOW(fd,2) = skill_id; - WFIFOL(fd,4) = inf?inf:skill->get_inf(skill_id); - WFIFOW(fd,8) = skill_lv; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_SKILLINFO_UPDATE2)); + struct PACKET_ZC_SKILLINFO_UPDATE2 *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_SKILLINFO_UPDATE2; + int skill_lv = sd->status.skill[idx].lv; + p->id = skill_id; + p->inf = skill->get_inf(skill_id); + p->level = skill_lv; if (skill_lv > 0) { - WFIFOW(fd,10) = skill->get_sp(skill_id, skill_lv); - WFIFOW(fd,12) = skill->get_range2(&sd->bl, skill_id, skill_lv); + p->sp = skill->get_sp(skill_id, skill_lv); + p->range2 = skill->get_range2(&sd->bl, skill_id, skill_lv); } else { - WFIFOW(fd,10) = 0; - WFIFOW(fd,12) = 0; + p->sp = 0; + p->range2 = 0; } +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 + p->level2 = skill_lv; +#endif if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) - WFIFOB(fd,14) = (skill_lv < skill->tree_get_max(skill_id, sd->status.class))? 1:0; + p->upFlag = (skill_lv < skill->tree_get_max(skill_id, sd->status.class)) ? 1 : 0; else - WFIFOB(fd,14) = 0; - WFIFOSET(fd,packet_len(0x7e1)); + p->upFlag = 0; + WFIFOSET(fd, sizeof(struct PACKET_ZC_SKILLINFO_UPDATE2)); } /// Notifies clients in area, that an object is about to use a skill. @@ -5286,32 +5457,31 @@ static void clif_skillinfo(struct map_session_data *sd, int skill_id, int inf) /// 1 = no text static void clif_useskill(struct block_list *bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, uint16 skill_lv, int casttime) { -#if PACKETVER < 20091124 - const int cmd = 0x13e; -#else - const int cmd = 0x7fb; -#endif - unsigned char buf[32]; - int property = skill->get_ele(skill_id, skill_lv); + nullpo_retv(bl); - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = src_id; - WBUFL(buf,6) = dst_id; - WBUFW(buf,10) = dst_x; - WBUFW(buf,12) = dst_y; - WBUFW(buf,14) = skill_id; - WBUFL(buf,16) = property<0?0:property; //Avoid sending negatives as element [Skotlex] - WBUFL(buf,20) = casttime; -#if PACKETVER >= 20091124 - WBUFB(buf,24) = 0; // isDisposable + const int element = skill->get_ele(skill_id, skill_lv); + struct PACKET_ZC_USESKILL_ACK p; + p.packetType = HEADER_ZC_USESKILL_ACK; + p.srcId = src_id; + p.dstId = dst_id; + p.x = dst_x; + p.y = dst_y; + p.skillId = skill_id; + p.element = element < 0 ? 0 : element; //Avoid sending negatives as element [Skotlex] + p.delayTime = casttime; +#if PACKETVER_MAIN_NUM >= 20091124 || PACKETVER_RE_NUM >= 20091124 || defined(PACKETVER_ZERO) + p.disposable = 0; +#endif +#if PACKETVER_MAIN_NUM >= 20181212 || PACKETVER_RE_NUM >= 20181212 || PACKETVER_ZERO_NUM >= 20190130 + p.unknown = 0; #endif if (clif->isdisguised(bl)) { - clif->send(buf,packet_len(cmd), bl, AREA_WOS); - WBUFL(buf,2) = -src_id; - clif->send(buf,packet_len(cmd), bl, SELF); + clif->send(&p, sizeof(p), bl, AREA_WOS); + p.srcId = -src_id; + clif->send(&p, sizeof(p), bl, SELF); } else { - clif->send(buf,packet_len(cmd), bl, AREA); + clif->send(&p, sizeof(p), bl, AREA); } #if PACKETVER >= 20151223 if ((skill->get_inf2(skill_id) & INF2_SHOW_SKILL_SCALE) != 0) @@ -5414,7 +5584,7 @@ static void clif_skill_cooldown(struct map_session_data *sd, uint16 skill_id, un /// Skill attack effect and damage. /// 0114 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.W <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL) /// 01de <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.L <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL2) -static int clif_skill_damage(struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int64 in_damage, int div, uint16 skill_id, uint16 skill_lv, int type) +static int clif_skill_damage(struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int64 in_damage, int div, uint16 skill_id, uint16 skill_lv, enum battle_dmg_type type) { unsigned char buf[64]; struct status_change *sc; @@ -5516,7 +5686,7 @@ static int clif_skill_damage(struct block_list *src, struct block_list *dst, int /// Ground skill attack effect and damage (ZC_NOTIFY_SKILL_POSITION). /// 0115 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <x>.W <y>.W <damage>.W <level>.W <div>.W <type>.B #if 0 -static int clif_skill_damage2(struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int damage, int div, uint16 skill_id, uint16 skill_lv, int type) +static int clif_skill_damage2(struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int damage, int div, uint16 skill_id, uint16 skill_lv, enum battle_dmg_type type) { unsigned char buf[64]; struct status_change *sc; @@ -5652,7 +5822,7 @@ static void clif_skill_warppoint(struct map_session_data *sd, uint16 skill_id, u nullpo_retv(sd); fd = sd->fd; #if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) - len = sizeof(struct PACKET_ZC_WARPLIST) + sizeof(struct PACKET_ZC_WARPLIST_sub) * mapsCount; + len = sizeof(struct PACKET_ZC_WARPLIST) + sizeof(struct PACKET_ZC_WARPLIST_sub) * 6; #else len = sizeof(struct PACKET_ZC_WARPLIST); #endif @@ -5740,7 +5910,7 @@ static void clif_skill_estimation(struct map_session_data *sd, struct block_list { struct status_data *dstatus; unsigned char buf[64]; - int i;//, fix; + int i, fix; nullpo_retv(sd); nullpo_retv(dst); @@ -5762,9 +5932,9 @@ static void clif_skill_estimation(struct map_session_data *sd, struct block_list + ((battle_config.estimation_type&2) ? dstatus->mdef2 : 0); WBUFW(buf,18) = dstatus->def_ele; for(i=0;i<9;i++) { - WBUFB(buf,20+i)= (unsigned char)battle->attr_ratio(i+1,dstatus->def_ele, dstatus->ele_lv); + // WBUFB(buf,20+i)= (unsigned char)battle->attr_ratio(i+1,dstatus->def_ele, dstatus->ele_lv); // The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex] - //WBUFB(buf,20+i)= (unsigned char)((fix=battle->attr_ratio(i+1,dstatus->def_ele, dstatus->ele_lv))<0?0:fix); + WBUFB(buf,20+i)= (unsigned char)((fix=battle->attr_ratio(i+1,dstatus->def_ele, dstatus->ele_lv))<0?0:fix); } clif->send(buf,packet_len(0x18c),&sd->bl,sd->status.party_id>0?PARTY_SAMEMAP:SELF); @@ -5839,7 +6009,7 @@ static void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 s len = sizeof(struct PACKET_ZC_MAKINGITEM_LIST) + MAX_SKILL_PRODUCE_DB * sizeof(struct PACKET_ZC_MAKINGITEM_LIST_sub); WFIFOHEAD(fd, len); p = WFIFOP(fd, 0); - p->packetType = 0x25a; + p->packetType = HEADER_ZC_MAKINGITEM_LIST; p->makeItem = list_type; // list type c = 0; @@ -5882,7 +6052,7 @@ static void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 s } } -static void clif_status_change_notick(struct block_list *bl, int type, int flag, int tick, int val1, int val2, int val3) +static void clif_status_change_notick(struct block_list *bl, int type, int relevant_bl, int flag, int tick, int total_tick, int val1, int val2, int val3) { struct packet_sc_notick p; struct map_session_data *sd; @@ -5892,7 +6062,7 @@ static void clif_status_change_notick(struct block_list *bl, int type, int flag, if (type == SI_BLANK) //It shows nothing on the client... return; - if (!(status->type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client + if (!(relevant_bl & bl->type)) // only send status changes that actually matter to the client return; sd = BL_CAST(BL_PC, bl); @@ -5911,7 +6081,7 @@ static void clif_status_change_notick(struct block_list *bl, int type, int flag, /// 08ff <id>.L <index>.W <remain msec>.L { <val>.L }*3 (PACKETVER >= 20111108) /// 0983 <index>.W <id>.L <state>.B <total msec>.L <remain msec>.L { <val>.L }*3 (PACKETVER >= 20120618) /// 0984 <id>.L <index>.W <total msec>.L <remain msec>.L { <val>.L }*3 (PACKETVER >= 20120618) -static void clif_status_change(struct block_list *bl, int type, int flag, int tick, int val1, int val2, int val3) +static void clif_status_change_sub(struct block_list *bl, int type, int relevant_bl, int flag, int tick, int total_tick, int val1, int val2, int val3) { struct packet_status_change p; struct map_session_data *sd; @@ -5921,7 +6091,7 @@ static void clif_status_change(struct block_list *bl, int type, int flag, int ti nullpo_retv(bl); - if (!(status->type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client + if (!(relevant_bl & bl->type)) // only send status changes that actually matter to the client return; if ( tick < 0 ) @@ -5935,7 +6105,7 @@ static void clif_status_change(struct block_list *bl, int type, int flag, int ti p.state = (unsigned char)flag; #if PACKETVER >= 20120618 - p.Total = tick; /* at this stage remain and total are the same value I believe */ + p.Total = total_tick; #endif #if PACKETVER >= 20090121 p.Left = tick; @@ -5946,6 +6116,13 @@ static void clif_status_change(struct block_list *bl, int type, int flag, int ti clif->send(&p,sizeof(p), bl, (sd && sd->status.option&OPTION_INVISIBLE) ? SELF : AREA); } +/// Notifies clients of a status change. +/// @see clif_status_change_sub +static void clif_status_change(struct block_list *bl, int type, int relevant_bl, int flag, int total_tick, int val1, int val2, int val3) +{ + clif->status_change_sub(bl, type, relevant_bl, flag, total_tick, total_tick, val1, val2, val3); +} + /// Send message (modified by [Yor]) (ZC_NOTIFY_PLAYERCHAT). /// 008e <packet len>.W <message>.?B static void clif_displaymessage(const int fd, const char *mes) @@ -6299,7 +6476,7 @@ static void clif_wis_message(int fd, const char *nick, const char *mes, int mes_ safestrncpy(WFIFOP(fd,28), mes, mes_len + 1); WFIFOSET(fd,WFIFOW(fd,2)); #else - ssd = map->nick2sd(nick); + ssd = map->nick2sd(nick, false); WFIFOHEAD(fd, mes_len + NAME_LENGTH + 9); WFIFOW(fd,0) = 0x97; @@ -6377,10 +6554,10 @@ static void clif_use_card(struct map_session_data *sd, int idx) if (!pc->can_insert_card(sd, idx)) return; - WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4); + WFIFOHEAD(fd, sd->status.inventorySize * 2 + 4); WFIFOW(fd, 0) = 0x17b; - for (i = c = 0; i < MAX_INVENTORY; i++) { + for (i = c = 0; i < sd->status.inventorySize; i++) { if (!pc->can_insert_card_into(sd, idx, i)) continue; WFIFOW(fd, 4 + c * 2) = i + 2; @@ -6424,9 +6601,9 @@ static void clif_item_identify_list(struct map_session_data *sd) fd=sd->fd; - WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4); + WFIFOHEAD(fd, sd->status.inventorySize * 2 + 4); WFIFOW(fd,0)=0x177; - for(i=c=0;i<MAX_INVENTORY;i++){ + for (i = c = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && !sd->status.inventory[i].identify){ WFIFOW(fd,c*2+4)=i+2; c++; @@ -6471,11 +6648,11 @@ static void clif_item_repair_list(struct map_session_data *sd, struct map_sessio fd = sd->fd; - len = MAX_INVENTORY * sizeof(struct PACKET_ZC_REPAIRITEMLIST_sub) + sizeof(struct PACKET_ZC_REPAIRITEMLIST); + len = dstsd->status.inventorySize * sizeof(struct PACKET_ZC_REPAIRITEMLIST_sub) + sizeof(struct PACKET_ZC_REPAIRITEMLIST); WFIFOHEAD(fd, len); p = WFIFOP(fd, 0); p->packetType = 0x1fc; - for (i = c = 0; i < MAX_INVENTORY; i++) { + for (i = c = 0; i < sd->status.inventorySize; i++) { int nameid = dstsd->status.inventory[i].nameid; if (nameid > 0 && (dstsd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { // && skill_can_repair(sd,nameid)) { p->items[c].index = i; @@ -6551,11 +6728,11 @@ static void clif_item_refine_list(struct map_session_data *sd) skill_lv = pc->checkskill(sd, WS_WEAPONREFINE); fd = sd->fd; - len = MAX_INVENTORY * sizeof(struct PACKET_ZC_NOTIFY_WEAPONITEMLIST_sub) + sizeof(struct PACKET_ZC_NOTIFY_WEAPONITEMLIST); + len = sd->status.inventorySize * sizeof(struct PACKET_ZC_NOTIFY_WEAPONITEMLIST_sub) + sizeof(struct PACKET_ZC_NOTIFY_WEAPONITEMLIST); WFIFOHEAD(fd, len); p = WFIFOP(fd, 0); p->packetType = 0x221; - for (i = c = 0; i < MAX_INVENTORY; i++) { + for (i = c = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].identify && itemdb_wlv(sd->status.inventory[i].nameid) >= 1 && !sd->inventory_data[i]->flag.no_refine @@ -6580,21 +6757,28 @@ static void clif_item_refine_list(struct map_session_data *sd) /// 0147 <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradeable>.B static void clif_item_skill(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv) { - int fd; - nullpo_retv(sd); - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x147)); - WFIFOW(fd, 0)=0x147; - WFIFOW(fd, 2)=skill_id; - WFIFOL(fd, 4)=skill->get_inf(skill_id); - WFIFOW(fd, 8)=skill_lv; - WFIFOW(fd,10)=skill->get_sp(skill_id,skill_lv); - WFIFOW(fd,12)=skill->get_range2(&sd->bl, skill_id,skill_lv); - safestrncpy(WFIFOP(fd,14),skill->get_name(skill_id),NAME_LENGTH); - WFIFOB(fd,38)=0; - WFIFOSET(fd,packet_len(0x147)); + int fd = sd->fd; + + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_AUTORUN_SKILL)); + + struct PACKET_ZC_AUTORUN_SKILL *p = WFIFOP(fd, 0); + int type = skill->get_inf(skill_id); + + if (sd->autocast.itemskill_cast_on_self && sd->autocast.type == AUTOCAST_ITEM) + type = INF_SELF_SKILL; + + p->packetType = HEADER_ZC_AUTORUN_SKILL; + p->skill_id = skill_id; + p->skill_type = type; + p->skill_lv = skill_lv; + p->skill_sp = skill->get_sp(skill_id, skill_lv); + p->skill_range = skill->get_range2(&sd->bl, skill_id, skill_lv); + safestrncpy(p->skill_name, skill->get_name(skill_id), NAME_LENGTH); + p->up_flag = 0; + + WFIFOSET(fd, sizeof(struct PACKET_ZC_AUTORUN_SKILL)); } /// Adds an item to character's cart. @@ -7000,7 +7184,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 } @@ -7011,14 +7195,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 ? 1 : 0; - 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. @@ -7385,9 +7572,9 @@ static void clif_sendegg(struct map_session_data *sd) return; } - WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4); + WFIFOHEAD(fd, sd->status.inventorySize * 2 + 4); WFIFOW(fd,0) = 0x1a6; - for (i = n = 0; i < MAX_INVENTORY; i++) { + for (i = n = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL || sd->inventory_data[i]->type!=IT_PETEGG || sd->status.inventory[i].amount <= 0) continue; WFIFOW(fd, n * 2 + 4) = i + 2; @@ -7509,10 +7696,11 @@ static void clif_pet_food(struct map_session_data *sd, int foodid, int fail) /// 01cd { <skill id>.L }*7 static void clif_autospell(struct map_session_data *sd, uint16 skill_lv) { +#if PACKETVER_MAIN_NUM >= 20090406 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_SAK_NUM >= 20080618 nullpo_retv(sd); int fd = sd->fd; -#if PACKETVER_RE_NUM >= 20181031 +#if PACKETVER_MAIN_NUM >= 20181128 || PACKETVER_RE_NUM >= 20181031 // reserve space for 7 skills WFIFOHEAD(fd, sizeof(struct PACKET_ZC_AUTOSPELLLIST) + 4 * 7); #else @@ -7520,7 +7708,7 @@ static void clif_autospell(struct map_session_data *sd, uint16 skill_lv) #endif struct PACKET_ZC_AUTOSPELLLIST *p = WFIFOP(fd, 0); memset(p, 0, sizeof(struct PACKET_ZC_AUTOSPELLLIST)); - p->packetType = autoSpellList; + p->packetType = HEADER_ZC_AUTOSPELLLIST; int index = 0; if (skill_lv > 0 && pc->checkskill(sd, MG_NAPALMBEAT) > 0) @@ -7538,7 +7726,7 @@ static void clif_autospell(struct map_session_data *sd, uint16 skill_lv) if (skill_lv > 9 && pc->checkskill(sd, MG_FROSTDIVER) > 0) p->skills[index++] = MG_FROSTDIVER; -#if PACKETVER_RE_NUM >= 20181031 +#if PACKETVER_MAIN_NUM >= 20181128 || PACKETVER_RE_NUM >= 20181031 const int len = sizeof(struct PACKET_ZC_AUTOSPELLLIST) + index * 4; p->packetLength = len; #else @@ -7548,6 +7736,7 @@ static void clif_autospell(struct map_session_data *sd, uint16 skill_lv) sd->menuskill_id = SA_AUTOSPELL; sd->menuskill_val = skill_lv; +#endif } /// Devotion's visual effect (ZC_DEVOTIONLIST). @@ -7606,17 +7795,27 @@ static void clif_devotion(struct block_list *src, struct map_session_data *tsd) static void clif_spiritball(struct block_list *bl) { unsigned char buf[16]; - struct map_session_data *sd = BL_CAST(BL_PC,bl); - struct homun_data *hd = BL_CAST(BL_HOM,bl); nullpo_retv(bl); WBUFW(buf, 0) = 0x1d0; WBUFL(buf, 2) = bl->id; WBUFW(buf, 6) = 0; //init to 0 - switch(bl->type){ - case BL_PC: WBUFW(buf, 6) = sd->spiritball; break; - case BL_HOM: WBUFW(buf, 6) = hd->homunculus.spiritball; break; + switch (bl->type) { + case BL_PC: + { + struct map_session_data *sd = BL_CAST(BL_PC, bl); + nullpo_retv(sd); + WBUFW(buf, 6) = sd->spiritball; + break; + } + case BL_HOM: + { + struct homun_data *hd = BL_CAST(BL_HOM, bl); + nullpo_retv(hd); + WBUFW(buf, 6) = hd->homunculus.spiritball; + break; + } } clif->send(buf, packet_len(0x1d0), bl, AREA); } @@ -7932,6 +8131,54 @@ static void clif_guild_allianceinfo(struct map_session_data *sd) WFIFOSET(fd,WFIFOW(fd,2)); } +static void clif_guild_castlelist(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 + nullpo_retv(sd); + + struct guild *g = sd->guild; + if (g == NULL) + return; + + int castle_count = guild->checkcastles(g); + if (castle_count > 0) { + int len = sizeof(struct PACKET_ZC_GUILD_CASTLE_LIST) + castle_count; + struct PACKET_ZC_GUILD_CASTLE_LIST *p = aMalloc(len); + p->packetType = HEADER_ZC_GUILD_CASTLE_LIST; + p->packetLength = len; + + int i = 0; + struct DBIterator *iter = db_iterator(guild->castle_db); + for (struct guild_castle *gc = dbi_first(iter); dbi_exists(iter); gc = dbi_next(iter)) { + if (gc->guild_id == g->guild_id) { + p->castle_list[i] = gc->castle_id; + ++i; + } + } + dbi_destroy(iter); + + clif->send(p, len, &sd->bl, SELF); + aFree(p); + } +#endif +} + +static void clif_guild_castleinfo(struct map_session_data *sd, struct guild_castle *gc) +{ +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 + + nullpo_retv(sd); + nullpo_retv(gc); + + struct PACKET_ZC_CASTLE_INFO p = { 0 }; + p.packetType = HEADER_ZC_CASTLE_INFO; + p.castle_id = gc->castle_id; + p.economy = gc->economy; + p.defense = gc->defense; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + /// Guild member manager information (ZC_MEMBERMGR_INFO). /// 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }* /// state: @@ -8264,6 +8511,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 @@ -8275,42 +8523,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). @@ -8512,14 +8762,14 @@ static void clif_emotion(struct block_list *bl, int type) /// 0191 <id>.L <contents>.80B static void clif_talkiebox(struct block_list *bl, const char *talkie) { - unsigned char buf[MESSAGE_SIZE+6]; nullpo_retv(bl); nullpo_retv(talkie); + struct PACKET_ZC_TALKBOX_CHATCONTENTS p; - WBUFW(buf,0) = 0x191; - WBUFL(buf,2) = bl->id; - safestrncpy(WBUFP(buf,6),talkie,MESSAGE_SIZE); - clif->send(buf,packet_len(0x191),bl,AREA); + p.PacketType = HEADER_ZC_TALKBOX_CHATCONTENTS; + p.aid = bl->id; + safestrncpy(&p.message[0], talkie, TALKBOX_MESSAGE_SIZE); + clif->send(&p, sizeof(struct PACKET_ZC_TALKBOX_CHATCONTENTS), bl, AREA); } /// Displays wedding effect centered on an object (ZC_CONGRATULATION). @@ -8854,21 +9104,44 @@ static void clif_specialeffect_single(struct block_list *bl, int type, int fd) /// @see doc/effect_list.txt /// num data: /// effect-dependent value -static void clif_specialeffect_value(struct block_list *bl, int effect_id, int num, send_target target) -{ - uint8 buf[14]; - - WBUFW(buf,0) = 0x284; - WBUFL(buf,2) = bl->id; - WBUFL(buf,6) = effect_id; - WBUFL(buf,10) = num; +static void clif_specialeffect_value(struct block_list *bl, int effect_id, uint64 num, send_target target) +{ +#if PACKETVER_MAIN_NUM >= 20060911 || PACKETVER_AD_NUM >= 20060911 || PACKETVER_SAK_NUM >= 20060911 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) + struct PACKET_ZC_NOTIFY_EFFECT3 packet; + packet.packetType = HEADER_ZC_NOTIFY_EFFECT3; + packet.aid = bl->id; + packet.effectId = effect_id; +#if PACKETVER >= 20191127 + packet.num = num; +#else + packet.num = (uint32)num; +#endif - clif->send(buf, packet_len(0x284), bl, target); + clif->send(&packet, sizeof(struct PACKET_ZC_NOTIFY_EFFECT3), bl, target); if (clif->isdisguised(bl)) { - WBUFL(buf,2) = -bl->id; - clif->send(buf, packet_len(0x284), bl, SELF); + packet.aid = -bl->id; + clif->send(&packet, sizeof(struct PACKET_ZC_NOTIFY_EFFECT3), bl, SELF); } +#endif +} + +static void clif_specialeffect_value_single(struct block_list *bl, int effect_id, uint64 num, int fd) +{ +#if PACKETVER_MAIN_NUM >= 20060911 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_NOTIFY_EFFECT3)); + + struct PACKET_ZC_NOTIFY_EFFECT3 *packet = WFIFOP(fd, 0); + packet->packetType = HEADER_ZC_NOTIFY_EFFECT3; + packet->aid = bl->id; + packet->effectId = effect_id; +#if PACKETVER >= 20191127 + packet->num = num; +#else + packet->num = (uint32)num; +#endif + WFIFOSET(fd, sizeof(struct PACKET_ZC_NOTIFY_EFFECT3)); +#endif } /// Remove special effects (ZC_REMOVE_EFFECT). @@ -8968,6 +9241,34 @@ static void clif_messagecolor(struct block_list *bl, uint32 color, const char *m clif->send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC); } +// Message without owner, not logged in chat +static void clif_serviceMessageColor(struct map_session_data *sd, uint32 color, const char *msg) +{ +#if PACKETVER_MAIN_NUM >= 20170830 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) + nullpo_retv(sd); + nullpo_retv(msg); + + int msg_len = (int)strlen(msg) + 1; + + if (msg_len > 512) { + ShowWarning("clif_serviceMessageColor: Truncating too long message '%s' (len=%d).\n", msg, msg_len); + msg_len = 512; + } + + const int len = sizeof(struct PACKET_ZC_SERVICE_MESSAGE_COLOR) + msg_len; + const int fd = sd->fd; + WFIFOHEAD(fd, len); + struct PACKET_ZC_SERVICE_MESSAGE_COLOR *p = WFIFOP(fd, 0); + + p->packetType = HEADER_ZC_SERVICE_MESSAGE_COLOR; + p->packetLength = len; + p->color = RGB2BGR(color); + safestrncpy(p->message, msg, msg_len); + + WFIFOSET(fd, len); +#endif +} + /** * Notifies the client that the storage window is still open * @@ -9054,6 +9355,8 @@ static void clif_refresh(struct map_session_data *sd) mail->clear(sd); + clif->loadConfirm(sd); + if (clif->isdisguised(&sd->bl)) {/* refresh-da */ short disguise = sd->disguise; pc->disguise(sd, -1); @@ -9063,202 +9366,505 @@ 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 a character's name on client. + * + * @code + * 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) + * @endcode + * + * @param fd The incoming file descriptor. + * @param bl The related character's block list. + * + **/ +static void clif_pcname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + Assert_retv(bl->type == BL_PC); + + struct PACKET_ZC_ACK_REQNAMEALL packet = {0}; + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; + packet.gid = bl->id; + + const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); + + if (ssd->fd == fd && ssd->disguise != -1) // Requesting your own "shadow" name. + packet.gid = -bl->id; + + if (ssd->fakename[0] != '\0') + memcpy(packet.name, ssd->fakename, NAME_LENGTH); + else + memcpy(packet.name, ssd->status.name, NAME_LENGTH); + + const struct party_data *p = NULL; + + if (ssd->status.party_id != 0) + p = party->search(ssd->status.party_id); + + const struct guild *g = NULL; + int pos_idx = INDEX_NOT_FOUND; + + if (ssd->status.guild_id != 0 && (g = ssd->guild) != NULL) { + int i; + int acc_id = ssd->status.account_id; + int chr_id = ssd->status.char_id; + + ARR_FIND(0, g->max_member, i, g->member[i].account_id == acc_id && g->member[i].char_id == chr_id); + + if (i < g->max_member) + pos_idx = g->member[i].position; + } + + if (battle_config.display_party_name == 0 && g == NULL) + p = NULL; // Do not display party name, unless the character is also in a guild. + + if (p != NULL) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_PARTYNAME) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.party_name, p->party.name, NAME_LENGTH); + } + } + + if (g != NULL && pos_idx >= 0 && pos_idx < MAX_GUILDPOSITION) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_GUILDNAME) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.guild_name, g->name,NAME_LENGTH); + } + + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_GUILDPOSITION) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.position_name, g->position[pos_idx].name, NAME_LENGTH); + } + } else if (ssd->status.clan_id != 0) { + struct clan *c = clan->search(ssd->status.clan_id); + + if (c != 0) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_CLANPOSITION) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.position_name, c->name, NAME_LENGTH); + } + } + } + +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) // Title system. + if (ssd->status.title_id > 0) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_TITLE) != 0) + || ssd->fakename[0] == '\0') { + packet.title_id = ssd->status.title_id; + } + } +#endif + + 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_charnameack(int fd, struct block_list *bl) +static void clif_homname_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_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 + 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_mername_ack(int fd, struct block_list *bl) +{ nullpo_retv(bl); + Assert_retv(bl->type == BL_MER); - packet.packet_id = reqName; + 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 - 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; + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} - if (ssd->fakename[0] != '\0' || ssd->status.guild_id > 0 || ssd->status.party_id > 0 || ssd->status.title_id > 0) { - packet.packet_id = reqNameAllType; - } +/// 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); - //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise != -1) { - packet.gid = -bl->id; - } + 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 - if (ssd->fakename[0] != '\0') { - memcpy(packet.name, ssd->fakename, NAME_LENGTH); - break; - } + clif->send_selforarea(fd, bl, &packet, sizeof(struct PACKET_ZC_ACK_REQNAME_TITLE)); +} -#if PACKETVER >= 20150503 - // Title System [Dastgir/Hercules] - if (ssd->status.title_id > 0) { - packet.title_id = ssd->status.title_id; - } +/// 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 - 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_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); - 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_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)); +} - if (p == NULL && g == NULL) - break; +/// 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); - if (p != NULL) { - memcpy(packet.party_name, p->party.name, NAME_LENGTH); - } + struct PACKET_ZC_ACK_REQNAME_TITLE packet = { 0 }; + packet.packet_id = HEADER_ZC_ACK_REQNAME_TITLE; + packet.gid = bl->id; + const struct mob_data *md = BL_UCCAST(BL_MOB, bl); + memcpy(packet.name, md->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 (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, 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_skillname_ack(int fd, struct block_list *bl) +{ +} + +static void clif_itemname_ack(int fd, struct block_list *bl) +{ + nullpo_retv(bl); + ShowError("clif_itemname_ack: bad type %u(%d)\n", bl->type, bl->id); + Assert_retv(0); +} + +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); + Assert_retv(0); +} + +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; + case BL_ITEM: + clif->itemname_ack(fd, bl); + break; + case BL_SKILL: + clif->skillname_ack(fd, bl); break; default: - ShowError("clif_charnameack: bad type %u(%d)\n", bl->type, bl->id); - return; - } - - if (packet.packet_id == reqName) { - len = sizeof(struct packet_reqname_ack); - } - // if no recipient specified just update nearby clients - // if no recipient specified just update nearby clients - if (fd == 0) { - clif->send(&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; } } -//Used to update when a char leaves a party/guild. [Skotlex] -//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent. +/** + * Updates a character's name on client when leaving a party/guild. + * + * @code + * 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) + * @endcode + * + * @param ssd The related character. + * + **/ static void clif_charnameupdate(struct map_session_data *ssd) { - int ps = -1; + nullpo_retv(ssd); + + struct PACKET_ZC_ACK_REQNAMEALL packet = {0}; + packet.packet_id = HEADER_ZC_ACK_REQNAMEALL; + packet.gid = ssd->bl.id; + + if (ssd->fakename[0] != '\0') + memcpy(packet.name, ssd->fakename, NAME_LENGTH); + else + memcpy(packet.name, ssd->status.name, NAME_LENGTH); + struct party_data *p = NULL; - struct guild *g = NULL; - struct packet_reqnameall_ack packet = { 0 }; - nullpo_retv(ssd); + if (ssd->status.party_id != 0) + p = party->search(ssd->status.party_id); - if (ssd->fakename[0]) - return; //No need to update as the party/guild was not displayed anyway. + struct guild *g = NULL; + int pos_idx = INDEX_NOT_FOUND; - packet.packet_id = reqNameAllType; - packet.gid = ssd->bl.id; + if (ssd->status.guild_id != 0 && (g = ssd->guild) != NULL) { + int i; + int acc_id = ssd->status.account_id; + int chr_id = ssd->status.char_id; - memcpy(packet.name, ssd->status.name, NAME_LENGTH); + ARR_FIND(0, g->max_member, i, g->member[i].account_id == acc_id && g->member[i].char_id == chr_id); - if (!battle_config.display_party_name) { - if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) - p = party->search(ssd->status.party_id); - } else { - if (ssd->status.party_id > 0) - p = party->search(ssd->status.party_id); + if (i < g->max_member) + pos_idx = g->member[i].position; } - if (ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) { - int i; - ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); - if( i < g->max_member ) ps = g->member[i].position; + if (battle_config.display_party_name == 0 && g == NULL) + p = NULL; // Do not display party name, unless the character is also in a guild. + + if (p != NULL) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_PARTYNAME) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.party_name, p->party.name, NAME_LENGTH); + } } - if (p != NULL) - memcpy(packet.party_name, p->party.name, NAME_LENGTH); + if (g != NULL && pos_idx >= 0 && pos_idx < MAX_GUILDPOSITION) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_GUILDNAME) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.guild_name, g->name,NAME_LENGTH); + } + + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_GUILDPOSITION) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.position_name, g->position[pos_idx].name, NAME_LENGTH); + } + } else if (ssd->status.clan_id != 0) { + struct clan *c = clan->search(ssd->status.clan_id); - 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); + if (c != 0) { + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_CLANPOSITION) != 0) + || ssd->fakename[0] == '\0') { + memcpy(packet.position_name, c->name, NAME_LENGTH); + } + } } -#if PACKETVER >= 20150503 - // Achievement System [Dastgir/Hercules] +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) // Title system. if (ssd->status.title_id > 0) { - packet.title_id = ssd->status.title_id; + if ((ssd->fakename[0] != '\0' && (ssd->fakename_options & FAKENAME_OPTION_SHOW_TITLE) != 0) + || ssd->fakename[0] == '\0') { + packet.title_id = ssd->status.title_id; + } } #endif - // Update nearby clients - clif->send(&packet, sizeof(packet), &ssd->bl, AREA); + clif->send(&packet, sizeof(packet), &ssd->bl, AREA); // Update nearby clients. } /// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP). @@ -10001,55 +10607,123 @@ static void clif_parse_WantToConnection(int fd, struct map_session_data *sd) chrif->authreq(sd,false); } +/** + * Notification from the client, that it has finished map loading and is about to display player's character. (CZ_NOTIFY_ACTORINIT) + * + * @code + * 007d + * @endcode + * + * @param fd The incoming file descriptor. + * @param sd The related character. + * + **/ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -/// Notification from the client, that it has finished map loading and is about to display player's character (CZ_NOTIFY_ACTORINIT). -/// 007d static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) { - bool first_time = false; - - if(sd->bl.prev != NULL) + if (sd->bl.prev != NULL) return; - if (!sd->state.active) { //Character loading is not complete yet! - //Let pc->reg_received reinvoke this when ready. + if (sd->state.active == 0) { // Character loading is not complete yet! Let pc->reg_received reinvoke this when ready. sd->state.connect_new = 0; return; } - if (sd->state.rewarp) { //Rewarp player. + if (sd->state.rewarp != 0) { // Rewarp character. sd->state.rewarp = 0; clif->changemap(sd, sd->bl.m, sd->bl.x, sd->bl.y); return; } sd->state.warping = 0; - sd->state.dialog = 0;/* reset when warping, client dialog will go missing */ + sd->state.dialog = 0; // Reset when warping. Client dialog will go missing. - // Character Looks + // Character looks. #if PACKETVER < 4 clif->changelook(&sd->bl, LOOK_WEAPON, sd->status.look.weapon); clif->changelook(&sd->bl, LOOK_SHIELD, sd->status.look.shield); #else - clif->changelook(&sd->bl,LOOK_WEAPON,0); + clif->changelook(&sd->bl, LOOK_WEAPON, 0); #endif - if(sd->vd.cloth_color) - clif->refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF); + if (sd->vd.cloth_color != 0) + clif->refreshlook(&sd->bl, sd->bl.id, LOOK_CLOTHES_COLOR, sd->vd.cloth_color, SELF); - if (sd->vd.body_style) - clif->refreshlook(&sd->bl,sd->bl.id,LOOK_BODY2,sd->vd.body_style,SELF); + if (sd->vd.body_style != 0) + clif->refreshlook(&sd->bl, sd->bl.id, LOOK_BODY2, sd->vd.body_style, SELF); - // Send character inventory to the client. - // call this before pc->checkitem() so that the client isn't called to delete a non-existent item. + /** + * Send character inventory to the client. + * Call this before pc->checkitem() so that the client isn't called to delete a non-existent items. + * + **/ clif->inventoryList(sd); // Send the cart inventory, counts & weight to the client. - if(pc_iscarton(sd)) { + if (pc_iscarton(sd)) { clif->cartList(sd); clif->updatestatus(sd, SP_CARTINFO); } + /** + * In official servers, an item's unequip script is executed when entering a zone where the item is restricted, + * even if the item won't be unequipped. + * + **/ + if (map->list[sd->bl.m].zone != NULL && map->list[sd->bl.m].zone->disabled_items_count != 0) { + struct map_zone_data *zone = map->list[sd->bl.m].zone; + int dis_items_cnt = zone->disabled_items_count; + int handled_equip = 0x00000000; + + for (int i = 0; i < EQI_MAX; i++) { + if (sd->equip_index[i] == INDEX_NOT_FOUND) + continue; + + int inv_idx = sd->equip_index[i]; + struct item_data *equip_data = sd->inventory_data[inv_idx]; + + if (equip_data == NULL) + continue; + + if ((handled_equip & equip_data->equip) != 0) + continue; // Equipment takes multiple slots and was already handled. + + handled_equip |= equip_data->equip; + + if (equip_data->unequip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == equip_data->nameid); + + if (idx < dis_items_cnt) + script->run_item_unequip_script(sd, equip_data, npc->fake_nd->bl.id); + } + + if (inv_idx != sd->equip_index[i]) + continue; // Unequip script execution corrupted the inventory index. + + struct item *equip = &sd->status.inventory[inv_idx]; + + if (equip != NULL && !itemdb_isspecial(equip->card[0])) { + for (int slot = 0; slot < equip_data->slot; slot++) { + if (equip->card[slot] == 0) + continue; + + struct item_data *card_data = itemdb->exists(equip->card[slot]); + + if (card_data != NULL && card_data->unequip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == card_data->nameid); + + if (idx < dis_items_cnt) + script->run_item_unequip_script(sd, card_data, npc->fake_nd->bl.id); + } + } + } + } + } + // Check for and delete unavailable/disabled items. pc->checkitem(sd); @@ -10057,317 +10731,330 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) clif->updatestatus(sd, SP_WEIGHT); clif->updatestatus(sd, SP_MAXWEIGHT); - // guild - // (needs to go before clif_spawn() to show guild emblems correctly) - if(sd->status.guild_id) - guild->send_memberinfoshort(sd,1); + // Send character's guild info to the client. Call this before clif->spawn() to show guild emblems correctly. + if (sd->status.guild_id != 0) + guild->send_memberinfoshort(sd, 1); - if(battle_config.pc_invincible_time > 0) { - pc->setinvincibletimer(sd,battle_config.pc_invincible_time); - } + if (battle_config.pc_invincible_time > 0) + pc->setinvincibletimer(sd, battle_config.pc_invincible_time); - if( map->list[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs ) + if (map->list[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs != 0) map->spawnmobs(sd->bl.m); - if( map->list[sd->bl.m].instance_id >= 0 ) { + if (map->list[sd->bl.m].instance_id >= 0) { instance->list[map->list[sd->bl.m].instance_id].users++; instance->check_idle(map->list[sd->bl.m].instance_id); } - if( pc_has_permission(sd,PC_PERM_VIEW_HPMETER) ) { + if (pc_has_permission(sd, PC_PERM_VIEW_HPMETER)) { map->list[sd->bl.m].hpmeter_visible++; sd->state.hpmeter_visible = 1; } - if (!pc_isinvisible(sd)) { // increment the number of pvp players on the map + if (!pc_isinvisible(sd)) // Increment the number of pvp players on the map. map->list[sd->bl.m].users_pvp++; - } - sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS] + sd->state.debug_remove_map = 0; // Temporary state to track double calls of unit->remove_map(). [FlavioJS] + sd->state.callshop = 0; // Reset the callshop flag if the character changes map. + map->addblock(&sd->bl); // Add the character to the map. + clif->spawn(&sd->bl); // Spawn character client side. - // reset the callshop flag if the player changes map - sd->state.callshop = 0; - - map->addblock(&sd->bl); - clif->spawn(&sd->bl); - - // Party - // (needs to go after clif_spawn() to show hp bars correctly) - if(sd->status.party_id) { + // Send character's party info to the client. Call this after clif->spawn() to show HP bars correctly. + if (sd->status.party_id != 0) { party->send_movemap(sd); - clif->party_hp(sd); // Show hp after displacement [LuzZza] + clif->party_hp(sd); // Show HP after displacement. [LuzZza] } - if( sd->bg_id ) clif->bg_hp(sd); // BattleGround System + if (sd->bg_id != 0) + clif->bg_hp(sd); // BattleGround system. + + if (map->list[sd->bl.m].flag.pvp != 0 && !pc_isinvisible(sd)) { + if (battle_config.pk_mode == 0) { // Remove PVP stuff for pk_mode. [Valaris] + if (map->list[sd->bl.m].flag.pvp_nocalcrank == 0) + sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0); - if (map->list[sd->bl.m].flag.pvp && !pc_isinvisible(sd)) { - if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris] - if (!map->list[sd->bl.m].flag.pvp_nocalcrank) - sd->pvp_timer = timer->add(timer->gettick()+200, pc->calc_pvprank_timer, sd->bl.id, 0); sd->pvp_rank = 0; sd->pvp_lastusers = 0; sd->pvp_point = 5; sd->pvp_won = 0; sd->pvp_lost = 0; } + clif->map_property(sd, MAPPROPERTY_FREEPVPZONE); - } else - // set flag, if it's a duel [LuzZza] - if(sd->duel_group) + } else if(sd->duel_group != 0) { // Set flag, if it's a duel. [LuzZza] clif->map_property(sd, MAPPROPERTY_FREEPVPZONE); + } - if (map->list[sd->bl.m].flag.gvg_dungeon) + if (map->list[sd->bl.m].flag.gvg_dungeon != 0) clif->map_property(sd, MAPPROPERTY_FREEPVPZONE); //TODO: Figure out the real packet to send here. - if( map_flag_gvg2(sd->bl.m) ) + if (map_flag_gvg2(sd->bl.m)) clif->map_property(sd, MAPPROPERTY_AGITZONE); - // info about nearby objects - // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange) - map->foreachinarea(clif->getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd); + // Info about nearby objects. Must use map->foreachinarea(). (CIRCULAR_AREA interferes with map->foreachinrange().) + map->foreachinarea(clif->getareachar, sd->bl.m, sd->bl.x - AREA_SIZE, sd->bl.y - AREA_SIZE, + sd->bl.x + AREA_SIZE, sd->bl.y + AREA_SIZE, BL_ALL, sd); - // pet - if( sd->pd ) { - if( battle_config.pet_no_gvg && map_flag_gvg2(sd->bl.m) ) { //Return the pet to egg. [Skotlex] - clif->message(sd->fd, msg_sd(sd,866)); // "Pets are not allowed in Guild Wars." - pet->menu(sd, 3); //Option 3 is return to egg. + // Spawn pet. + if (sd->pd != NULL) { + if (battle_config.pet_no_gvg != 0 && map_flag_gvg2(sd->bl.m)) { // Return the pet to egg. [Skotlex] + clif->message(sd->fd, msg_sd(sd, 866)); // "Pets are not allowed in Guild Wars." + pet->menu(sd, 3); // Option 3 is return to egg. } else { map->addblock(&sd->pd->bl); clif->spawn(&sd->pd->bl); - clif->send_petdata(sd,sd->pd,0,0); + clif->send_petdata(sd,sd->pd, 0, 0); clif->send_petstatus(sd); - //skill->unit_move(&sd->pd->bl,timer->gettick(),1); } } - //homunculus [blackhole89] - if( homun_alive(sd->hd) ) { + // Spawn homunculus. [blackhole89] + if (homun_alive(sd->hd)) { map->addblock(&sd->hd->bl); clif->spawn(&sd->hd->bl); - clif->send_homdata(sd,SP_ACK,0); - clif->hominfo(sd,sd->hd,1); - clif->hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice + clif->send_homdata(sd, SP_ACK, 0); + clif->hominfo(sd,sd->hd, 1); + clif->hominfo(sd,sd->hd, 0); // For some reason, at least older clients want this sent twice. clif->homskillinfoblock(sd); - if( battle_config.hom_setting&0x8 ) - status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change - if( !(battle_config.hom_setting&0x2) ) - skill->unit_move(&sd->hd->bl,timer->gettick(),1); // apply land skills immediately + + if ((battle_config.hom_setting & 0x8) != 0) + status_calc_bl(&sd->hd->bl, SCB_SPEED); // Homunculi mimic their master's speed on each map change. + + if ((battle_config.hom_setting & 0x2) == 0) + skill->unit_move(&sd->hd->bl, timer->gettick(), 1); // Apply land skills immediately. } - if( sd->md ) { + // Spawn mercenary. + if (sd->md != NULL) { map->addblock(&sd->md->bl); clif->spawn(&sd->md->bl); clif->mercenary_info(sd); clif->mercenary_skillblock(sd); - status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenary mimic their master's speed on each map change + status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenaries mimic their master's speed on each map change. } - if( sd->ed ) { + // Spawn elemental. + if (sd->ed != NULL) { map->addblock(&sd->ed->bl); clif->spawn(&sd->ed->bl); clif->elemental_info(sd); - clif->elemental_updatestatus(sd,SP_HP); - clif->hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.max_hp); - clif->elemental_updatestatus(sd,SP_SP); - status_calc_bl(&sd->ed->bl, SCB_SPEED); //Elemental mimic their master's speed on each map change + clif->elemental_updatestatus(sd, SP_HP); + clif->hpmeter_single(sd->fd, sd->ed->bl.id, sd->ed->battle_status.hp, sd->ed->battle_status.max_hp); + clif->elemental_updatestatus(sd, SP_SP); + status_calc_bl(&sd->ed->bl, SCB_SPEED); // Elementals mimic their master's speed on each map change. } - if(sd->state.connect_new) { - int lv; + bool first_time = false; + + if (sd->state.connect_new != 0) { first_time = true; sd->state.connect_new = 0; clif->skillinfoblock(sd); - clif->hotkeys(sd); - clif->updatestatus(sd,SP_BASEEXP); - clif->updatestatus(sd,SP_NEXTBASEEXP); - clif->updatestatus(sd,SP_JOBEXP); - clif->updatestatus(sd,SP_NEXTJOBEXP); - clif->updatestatus(sd,SP_SKILLPOINT); + clif->hotkeysAll(sd); + clif->updatestatus(sd, SP_BASEEXP); + clif->updatestatus(sd, SP_NEXTBASEEXP); + clif->updatestatus(sd, SP_JOBEXP); + clif->updatestatus(sd, SP_NEXTJOBEXP); + clif->updatestatus(sd, SP_SKILLPOINT); clif->initialstatus(sd); - if (pc_isfalcon(sd)) - clif->status_change(&sd->bl, SI_FALCON, 1, 0, 0, 0, 0); - if (pc_isridingpeco(sd) || pc_isridingdragon(sd)) - clif->status_change(&sd->bl, SI_RIDING, 1, 0, 0, 0, 0); - else if (pc_isridingwug(sd)) - clif->status_change(&sd->bl, SI_WUGRIDER, 1, 0, 0, 0, 0); + if (pc_isfalcon(sd)) { + int sc_icn = status->get_sc_icon(SC_FALCON); + int sc_typ = status->get_sc_relevant_bl_types(SC_FALCON); + clif->status_change(&sd->bl, sc_icn, sc_typ, 1, 0, 0, 0, 0); + } - if(sd->status.manner < 0) - sc_start(NULL,&sd->bl,SC_NOCHAT,100,0,0); + if (pc_isridingpeco(sd) || pc_isridingdragon(sd)) { + int sc_icn = status->get_sc_icon(SC_RIDING); + int sc_typ = status->get_sc_relevant_bl_types(SC_RIDING); + clif->status_change(&sd->bl, sc_icn, sc_typ, 1, 0, 0, 0, 0); + } else if (pc_isridingwug(sd)) { + int sc_icn = status->get_sc_icon(SC_WUGRIDER); + int sc_typ = status->get_sc_relevant_bl_types(SC_WUGRIDER); + clif->status_change(&sd->bl, sc_icn, sc_typ, 1, 0, 0, 0, 0); + } - //Auron reported that This skill only triggers when you logon on the map o.O [Skotlex] - if ((lv = pc->checkskill(sd,SG_KNOWLEDGE)) > 0) { - int i; - for (i = 0; i < MAX_PC_FEELHATE; i++) { + if (sd->status.manner < 0) + sc_start(NULL, &sd->bl, SC_NOCHAT, 100, 0, 0); + + int lv = pc->checkskill(sd,SG_KNOWLEDGE); + + // Auron reported that this skill only triggers when you logon on the map. [Skotlex] + if (lv > 0) { + for (int i = 0; i < MAX_PC_FEELHATE; i++) { if (sd->bl.m == sd->feel_map[i].m) { - sc_start(NULL,&sd->bl, SC_KNOWLEDGE, 100, lv, skill->get_time(SG_KNOWLEDGE, lv)); + sc_start(NULL, &sd->bl, SC_KNOWLEDGE, 100, lv, skill->get_time(SG_KNOWLEDGE, lv)); break; } } } - if(sd->pd && sd->pd->pet.intimate > 900) - clif->pet_emotion(sd->pd,(sd->pd->pet.class_ - 100)*100 + 50 + pet->hungry_val(sd->pd)); + if (sd->pd != NULL && sd->pd->pet.intimate > PET_INTIMACY_LOYAL) + clif->pet_emotion(sd->pd, (sd->pd->pet.class_ - 100) * 100 + 50 + pet->hungry_val(sd->pd)); - if(homun_alive(sd->hd)) + if (homun_alive(sd->hd)) homun->init_timers(sd->hd); - if (map->night_flag && map->list[sd->bl.m].flag.nightenabled) { + if (map->night_flag != 0 && map->list[sd->bl.m].flag.nightenabled != 0) { + int sc_icn = status->get_sc_icon(SC_SKE); + int sc_typ = status->get_sc_relevant_bl_types(SC_SKE); + sd->state.night = 1; - clif->status_change(&sd->bl, SI_SKE, 1, 0, 0, 0, 0); + clif->status_change(&sd->bl, sc_icn, sc_typ, 1, 0, 0, 0, 0); } - // Notify everyone that this char logged in [Skotlex]. + // Notify everyone that this character logged in. [Skotlex] map->foreachpc(clif->friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1); - //Login Event + // Run OnPCLoginEvent labels. npc->script_event(sd, NPCE_LOGIN); } else { - //For some reason the client "loses" these on warp/map-change. - clif->updatestatus(sd,SP_STR); - clif->updatestatus(sd,SP_AGI); - clif->updatestatus(sd,SP_VIT); - clif->updatestatus(sd,SP_INT); - clif->updatestatus(sd,SP_DEX); - clif->updatestatus(sd,SP_LUK); - - if (sd->state.warp_clean) { - // abort currently running script + // For some reason the client "loses" these on warp/map-change. + clif->updatestatus(sd, SP_STR); + clif->updatestatus(sd, SP_AGI); + clif->updatestatus(sd, SP_VIT); + clif->updatestatus(sd, SP_INT); + clif->updatestatus(sd, SP_DEX); + clif->updatestatus(sd, SP_LUK); + + if (sd->state.warp_clean != 0) { // Abort currently running script. sd->state.using_fake_npc = 0; sd->state.menu_or_input = 0; sd->npc_menu = 0; - if(sd->npc_id) + + if (sd->npc_id != 0) npc->event_dequeue(sd); } else { sd->state.warp_clean = 1; } - if( sd->guild && ( battle_config.guild_notice_changemap == 2 || ( battle_config.guild_notice_changemap == 1 && sd->state.changemap ) ) ) - clif->guild_notice(sd,sd->guild); + + if (sd->guild != NULL && ((battle_config.guild_notice_changemap == 1 && sd->state.changemap != 0) + || battle_config.guild_notice_changemap == 2)) { + clif->guild_notice(sd, sd->guild); + } } - if( sd->state.changemap ) {// restore information that gets lost on map-change + if (sd->state.changemap != 0) { // Restore information that gets lost on map-change. #if PACKETVER >= 20070918 clif->partyinvitationstate(sd); clif->equpcheckbox(sd); #endif #if PACKETVER_MAIN_NUM >= 20171025 || PACKETVER_RE_NUM >= 20170920 - if (sd->hd != NULL) - clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, sd->hd->homunculus.autofeed); - else - clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, false); + if (sd->hd != NULL) + clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, sd->hd->homunculus.autofeed); + else + clif->zc_config(sd, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING, false); #endif - if( (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100) - && (map_flag_gvg2(sd->state.pmap) || map_flag_gvg2(sd->bl.m) - || map->list[sd->state.pmap].flag.battleground || map->list[sd->bl.m].flag.battleground) ) - status_calc_bl(&sd->bl, SCB_FLEE); //Refresh flee penalty - if( map->night_flag && map->list[sd->bl.m].flag.nightenabled ) { - //Display night. - if( !sd->state.night ) { + bool flee_penalty = (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100); + bool is_gvg = (map_flag_gvg2(sd->state.pmap) || map_flag_gvg2(sd->bl.m)); + bool is_bg = (map->list[sd->state.pmap].flag.battleground != 0 || map->list[sd->bl.m].flag.battleground != 0); + + if (flee_penalty && (is_gvg || is_bg)) + status_calc_bl(&sd->bl, SCB_FLEE); // Refresh flee penalty. + + if (map->night_flag != 0 && map->list[sd->bl.m].flag.nightenabled != 0) { + if (sd->state.night == 0) { // Display night. + int sc_icn = status->get_sc_icon(SC_SKE); + int sc_typ = status->get_sc_relevant_bl_types(SC_SKE); + sd->state.night = 1; - clif->status_change(&sd->bl, SI_SKE, 1, 0, 0, 0, 0); + clif->status_change(&sd->bl, sc_icn, sc_typ, 1, 0, 0, 0, 0); } - } else if( sd->state.night ) { //Clear night display. + } else if (sd->state.night != 0) { // Clear night display. sd->state.night = 0; - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_SKE); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SKE)); } - if( map->list[sd->bl.m].flag.battleground ) { - clif->map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground Mode - if( map->list[sd->bl.m].flag.battleground == 2 ) + if (map->list[sd->bl.m].flag.battleground != 0) { + clif->map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground mode. + + if (map->list[sd->bl.m].flag.battleground == 2) clif->bg_updatescore_single(sd); } - if( map->list[sd->bl.m].flag.allowks && !map_flag_ks(sd->bl.m) ) { + if (map->list[sd->bl.m].flag.allowks != 0 && !map_flag_ks(sd->bl.m)) { char output[128]; - sprintf(output, "[ Kill Steal Protection Disabled. KS is allowed in this map ]"); + + sprintf(output, "%s", msg_sd(sd, 893)); // [ Kill Steal Protection Disabled. KS is allowed in this map ] clif->broadcast(&sd->bl, output, (int)strlen(output) + 1, BC_BLUE, SELF); } - map->iwall_get(sd); // Updates Walls Info on this Map to Client - status_calc_pc(sd, SCO_NONE);/* some conditions are map-dependent so we must recalculate */ + map->iwall_get(sd); // Updates walls info on this map to client. + status_calc_pc(sd, SCO_NONE); // Some conditions are map-dependent so we must recalculate. sd->state.changemap = false; - if (channel->config->local && channel->config->local_autojoin) { + if (channel->config->local && channel->config->local_autojoin) channel->map_join(sd); - } - if (channel->config->irc && channel->config->irc_autojoin) { + + if (channel->config->irc && channel->config->irc_autojoin) channel->irc_join(sd); - } } mail->clear(sd); + clif->maptypeproperty2(&sd->bl, SELF); - clif->maptypeproperty2(&sd->bl,SELF); - - /* Guild Aura Init */ - if( sd->state.gmaster_flag ) { - guild->aura_refresh(sd,GD_LEADERSHIP,guild->checkskill(sd->guild,GD_LEADERSHIP)); - guild->aura_refresh(sd,GD_GLORYWOUNDS,guild->checkskill(sd->guild,GD_GLORYWOUNDS)); - guild->aura_refresh(sd,GD_SOULCOLD,guild->checkskill(sd->guild,GD_SOULCOLD)); - guild->aura_refresh(sd,GD_HAWKEYES,guild->checkskill(sd->guild,GD_HAWKEYES)); + // Init guild aura. + if (sd->state.gmaster_flag != 0) { + guild->aura_refresh(sd, GD_LEADERSHIP, guild->checkskill(sd->guild, GD_LEADERSHIP)); + guild->aura_refresh(sd, GD_GLORYWOUNDS, guild->checkskill(sd->guild, GD_GLORYWOUNDS)); + guild->aura_refresh(sd, GD_SOULCOLD, guild->checkskill(sd->guild, GD_SOULCOLD)); + guild->aura_refresh(sd, GD_HAWKEYES, guild->checkskill(sd->guild, GD_HAWKEYES)); } - if( sd->state.vending ) { /* show we have a vending */ - clif->openvending(sd,sd->bl.id,sd->vending); - clif->showvendingboard(&sd->bl,sd->message,0); + if (sd->state.vending != 0) { // Character is vending. + clif->openvending(sd, sd->bl.id, sd->vending); + clif->showvendingboard(&sd->bl, sd->message, 0); } - if(map->list[sd->bl.m].flag.loadevent) // Lance + if (map->list[sd->bl.m].flag.loadevent != 0) // Run OnPCLoadMapEvent labels. [Lance] npc->script_event(sd, NPCE_LOADMAP); - if (pc->checkskill(sd, SG_DEVIL) && !pc->nextjobexp(sd)) //blindness [Komurka] - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_DEVIL1); + if (pc->checkskill(sd, SG_DEVIL) > 0 && pc->nextjobexp(sd) == 0) // Blindness. [Komurka] + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_DEVIL1)); - if (sd->sc.opt2) //Client loses these on warp. + if (sd->sc.opt2 != 0) // Client loses these on warp. clif->changeoption(&sd->bl); - if( sd->sc.data[SC_MONSTER_TRANSFORM] && battle_config.mon_trans_disable_in_gvg && map_flag_gvg2(sd->bl.m) ){ + if (sd->sc.data[SC_MONSTER_TRANSFORM] != NULL && battle_config.mon_trans_disable_in_gvg != 0 + && map_flag_gvg2(sd->bl.m)) { status_change_end(&sd->bl, SC_MONSTER_TRANSFORM, INVALID_TIMER); - clif->message(sd->fd, msg_sd(sd,1488)); // Transforming into monster is not allowed in Guild Wars. + clif->message(sd->fd, msg_sd(sd, 1488)); // Transforming into monster is not allowed in Guild Wars. } clif->weather_check(sd); - // This should be displayed last - if( sd->guild && first_time ) + // This should be displayed last. + if (sd->guild != NULL && first_time) clif->guild_notice(sd, sd->guild); - // For automatic triggering of NPCs after map loading (so you don't need to walk 1 step first) - if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNPC)) - npc->touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y); + // For automatic triggering of NPCs after map loading. (So you don't need to walk 1 step first.) + if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNPC) != 0) + npc->touch_areanpc(sd, sd->bl.m, sd->bl.x, sd->bl.y); else npc->untouch_areanpc(sd, sd->bl.m, sd->bl.x, sd->bl.y); - /* it broke at some point (e.g. during a crash), so we make it visibly dead again. */ - if( !sd->status.hp && !pc_isdead(sd) && status->isdead(&sd->bl) ) + // It broke at some point (e.g. during a crash), so we make it visibly dead again. + if (sd->status.hp == 0 && !pc_isdead(sd) && status->isdead(&sd->bl) != 0) pc_setdead(sd); - // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris] - if(pc_isdead(sd)) + // Send death packet, if character is dead and is spawned (such as @refresh). [Valaris] + if (pc_isdead(sd)) { clif->clearunit_area(&sd->bl, CLR_DEAD); - else { + } else { skill->usave_trigger(sd); + if (battle_config.player_warp_keep_direction == 1) clif->changed_dir(&sd->bl, SELF); // Visually updates player facing direction } - // Trigger skill effects if you appear standing on them - if(!battle_config.pc_invincible_time) - skill->unit_move(&sd->bl,timer->gettick(),1); + // Trigger skill effects if you appear standing on them. + if (battle_config.pc_invincible_time == 0) + skill->unit_move(&sd->bl, timer->gettick(), 1); - // 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); // NPC quest/event icon check. [Kisuka] #endif } @@ -10398,53 +11085,105 @@ static void clif_parse_TickSend(int fd, struct map_session_data *sd) clif->notify_time(sd, timer->gettick()); } +static void clif_hotkeysAll_send(struct map_session_data *sd) +{ +#ifdef HOTKEY_SAVING + clif->hotkeys(sd, 0); +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + // send second tab only if data exists + for (int i = MAX_HOTKEYS; i < MAX_HOTKEYS * 2; i++) { + if (sd->status.hotkeys[i].type != 0 || sd->status.hotkeys[i].id != 0 || sd->status.hotkeys[i].lv != 0) { + clif->hotkeys(sd, 1); + return; + } + } +#endif +#endif +} + /// Sends hotkey bar. /// 02b9 { <is skill>.B <id>.L <count>.W }*27 (ZC_SHORTCUT_KEY_LIST) /// 07d9 { <is skill>.B <id>.L <count>.W }*36 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090603) /// 07d9 { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090617) /// 0a00 <rotate>.B { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V3, PACKETVER >= 20141022) -static void clif_hotkeys_send(struct map_session_data *sd) +static void clif_hotkeys_send(struct map_session_data *sd, int tab) { #ifdef HOTKEY_SAVING - struct packet_hotkey p; - int i; nullpo_retv(sd); - p.PacketType = hotkeyType; -#if PACKETVER >= 20141022 - p.Rotate = sd->status.hotkey_rowshift; + struct PACKET_ZC_SHORTCUT_KEY_LIST p; + p.packetType = HEADER_ZC_SHORTCUT_KEY_LIST; +#if PACKETVER_MAIN_NUM >= 20141022 || PACKETVER_RE_NUM >= 20141015 || defined(PACKETVER_ZERO) + if (tab == 0) + p.rotate = sd->status.hotkey_rowshift; + else + p.rotate = sd->status.hotkey_rowshift2; +#endif +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + p.tab = tab; #endif - for(i = 0; i < ARRAYLENGTH(p.hotkey); i++) { - p.hotkey[i].isSkill = sd->status.hotkeys[i].type; - p.hotkey[i].ID = sd->status.hotkeys[i].id; - p.hotkey[i].count = sd->status.hotkeys[i].lv; + const int offset = tab * MAX_HOTKEYS; + for (int i = 0; i < MAX_HOTKEYS_PACKET; i++) { + p.hotkey[i].isSkill = sd->status.hotkeys[i + offset].type; + p.hotkey[i].id = sd->status.hotkeys[i + offset].id; + p.hotkey[i].count = sd->status.hotkeys[i + offset].lv; } - clif->send(&p, sizeof(p), &sd->bl, SELF); + clif->send(&p, sizeof(struct PACKET_ZC_SHORTCUT_KEY_LIST), &sd->bl, SELF); #endif } -static void clif_parse_HotkeyRowShift(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -static void clif_parse_HotkeyRowShift(int fd, struct map_session_data *sd) +static void clif_parse_HotkeyRowShift1(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_HotkeyRowShift1(int fd, struct map_session_data *sd) { - int cmd = RFIFOW(fd, 0); - sd->status.hotkey_rowshift = RFIFOB(fd, packet_db[cmd].pos[0]); +#if PACKETVER_MAIN_NUM >= 20140129 || PACKETVER_RE_NUM >= 20140129 || defined(PACKETVER_ZERO) + const struct PACKET_CZ_SHORTCUTKEYBAR_ROTATE1 *p = RFIFOP(fd, 0); + sd->status.hotkey_rowshift = p->rowshift; +#endif +} + +static void clif_parse_HotkeyRowShift2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_HotkeyRowShift2(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + const struct PACKET_CZ_SHORTCUTKEYBAR_ROTATE2 *p = RFIFOP(fd, 0); + if (p->tab == 0) + sd->status.hotkey_rowshift = p->rowshift; + else + sd->status.hotkey_rowshift2 = p->rowshift; +#endif } -static void clif_parse_Hotkey(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE). +static void clif_parse_Hotkey1(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE1). /// 02ba <index>.W <is skill>.B <id>.L <count>.W -static void clif_parse_Hotkey(int fd, struct map_session_data *sd) +static void clif_parse_Hotkey1(int fd, struct map_session_data *sd) { #ifdef HOTKEY_SAVING - unsigned short idx; - int cmd; +#if PACKETVER_MAIN_NUM >= 20070618 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_AD_NUM >= 20070618 || PACKETVER_SAK_NUM >= 20070618 + const struct PACKET_CZ_SHORTCUT_KEY_CHANGE1 *p = RFIFOP(fd, 0); + const unsigned short idx = p->index; + Assert_retv(idx < MAX_HOTKEYS); - cmd = RFIFOW(fd, 0); - idx = RFIFOW(fd, packet_db[cmd].pos[0]); - if (idx >= MAX_HOTKEYS) return; + sd->status.hotkeys[idx].type = p->hotkey.isSkill; + sd->status.hotkeys[idx].id = p->hotkey.id; + sd->status.hotkeys[idx].lv = p->hotkey.count; +#endif +#endif +} - sd->status.hotkeys[idx].type = RFIFOB(fd, packet_db[cmd].pos[1]); - sd->status.hotkeys[idx].id = RFIFOL(fd, packet_db[cmd].pos[2]); - sd->status.hotkeys[idx].lv = RFIFOW(fd, packet_db[cmd].pos[3]); +static void clif_parse_Hotkey2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE2). +static void clif_parse_Hotkey2(int fd, struct map_session_data *sd) +{ +#ifdef HOTKEY_SAVING +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + const struct PACKET_CZ_SHORTCUT_KEY_CHANGE2 *p = RFIFOP(fd, 0); + const unsigned short idx = p->index + p->tab * MAX_HOTKEYS; + Assert_retv(idx < MAX_HOTKEYS_DB); + + sd->status.hotkeys[idx].type = p->hotkey.isSkill; + sd->status.hotkeys[idx].id = p->hotkey.id; + sd->status.hotkeys[idx].lv = p->hotkey.count; +#endif #endif } @@ -10507,9 +11246,13 @@ static void clif_parse_progressbar(int fd, struct map_session_data *sd) __attrib /// 02f1 static void clif_parse_progressbar(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int npc_id = sd->progressbar.npc_id; + Assert_retv(npc_id != 0); - if( timer->gettick() < sd->progressbar.timeout && sd->st ) + if (timer->gettick() < sd->progressbar.timeout && sd->st) sd->st->state = END; sd->progressbar.timeout = sd->state.workinprogress = sd->progressbar.npc_id = 0; @@ -10534,7 +11277,7 @@ static void clif_parse_WalkToXY(int fd, struct map_session_data *sd) ; //You CAN walk on this OPT1 value. /*else if( sd->progressbar.npc_id ) clif->progressbar_abort(sd);*/ - else if (pc_cant_act(sd)) + else if (pc_cant_act(sd) || pc_isvending(sd)) return; if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH]) @@ -10547,7 +11290,7 @@ static void clif_parse_WalkToXY(int fd, struct map_session_data *sd) //Set last idle time... [Skotlex] pc->update_idle_time(sd, BCIDLE_WALK); - unit->walktoxy(&sd->bl, x, y, 4); + unit->walk_toxy(&sd->bl, x, y, 4); } /// Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT). @@ -10579,8 +11322,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); } @@ -10612,18 +11357,18 @@ static void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd) sc = status->get_sc(bl); if (sc && sc->option&OPTION_INVISIBLE && !clif->isdisguised(bl) && bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple - pc_get_group_level(sd) < battle_config.hack_info_GM_level + !pc_has_permission(sd, PC_PERM_RECEIVE_HACK_INFO) ) { char gm_msg[256]; sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id); ShowWarning(gm_msg); // information is sent to all online GMs - intif->wis_message_to_gm(map->wisp_server_name, battle_config.hack_info_GM_level, gm_msg); + pc->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, gm_msg); return; } #endif // 0 - clif->charnameack(fd, bl); + clif->blname_ack(fd, bl); } static int clif_undisguise_timer(int tid, int64 tick, int id, intptr_t data) { @@ -10692,7 +11437,10 @@ static void clif_parse_GlobalMessage(int fd, struct map_session_data *sd) timer->settick(sd->fontcolor_tid, td->tick+5000); } - color = channel->config->colors[sd->fontcolor - 1]; + int fontColor = sd->fontcolor - 1; + if (fontColor < 0 || fontColor >= channel->config->colors_count) + fontColor = 0; + color = channel->config->colors[fontColor]; WFIFOHEAD(fd, outlen + 12); WFIFOW(fd,0) = 0x2C1; WFIFOW(fd,2) = outlen + 12; @@ -10760,15 +11508,7 @@ static void clif_parse_MapMove(int fd, struct map_session_data *sd) /// 0 = straight /// 1 = turned CW /// 2 = turned CCW -/// dir: -/// 0 = north -/// 1 = northwest -/// 2 = west -/// 3 = southwest -/// 4 = south -/// 5 = southeast -/// 6 = east -/// 7 = northeast +/// dir: @see enum unit_dir static void clif_changed_dir(struct block_list *bl, enum send_target target) { unsigned char buf[64]; @@ -10893,14 +11633,16 @@ static void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action { struct npc_data *nd = map->id2nd(target_id); if (nd != NULL) { - npc->click(sd, nd); + if (sd->block_action.npc == 0) { // *pcblock script command + npc->click(sd, nd); + } return; } - if( pc_cant_act(sd) || pc_issit(sd) || sd->sc.option&OPTION_HIDE ) + if (pc_cant_act(sd) || pc_issit(sd) || sd->sc.option&OPTION_HIDE || pc_isvending(sd)) return; - if( sd->sc.option&OPTION_COSTUME ) + if (sd->sc.option & OPTION_COSTUME) return; if (!battle_config.sdelay_attack_enable && pc->checkskill(sd, SA_FREECAST) <= 0 && (skill->get_inf2(sd->ud.skill_id) & (INF2_FREE_CAST_REDUCED | INF2_FREE_CAST_NORMAL)) == 0) { @@ -11078,8 +11820,8 @@ static void clif_parse_WisMessage(int fd, struct map_session_data *sd) script->set_var(sd,output,(char *) split_data[i]); } - sprintf(output, "%s::OnWhisperGlobal", nd->exname); - npc->event(sd,output,0); // Calls the NPC label + safesnprintf(output, 255, "%s::OnWhisperGlobal", nd->exname); + npc->event(sd,output, 0); // Calls the NPC label return; } @@ -11103,15 +11845,11 @@ static void clif_parse_WisMessage(int fd, struct map_session_data *sd) } // searching destination character - dstsd = map->nick2sd(target); + dstsd = map->nick2sd(target, false); - if (dstsd == NULL || strcmp(dstsd->status.name, target) != 0) { - // player is not on this map-server - // At this point, don't send wisp/page if it's not exactly the same name, because (example) - // if there are 'Test' player on an other map-server and 'test' player on this map-server, - // and if we ask for 'Test', we must not contact 'test' player - // so, we send information to inter-server, which is the only one which decide (and copy correct name). - intif->wis_message(sd, target, message, (int)strlen(message)); + if (dstsd == NULL) { + // Character not found (or found through partial match). + clif->wis_end(sd->fd, 1); return; } @@ -11127,7 +11865,7 @@ static void clif_parse_WisMessage(int fd, struct map_session_data *sd) // if player is autotrading if (dstsd->state.autotrade) { char output[256]; - sprintf(output, "%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name); + sprintf(output, msg_fd(fd, 894), dstsd->status.name); // %s is in autotrade mode and cannot receive whispered messages. clif->wis_message(fd, map->wisp_server_name, output, (int)strlen(output)); return; } @@ -11158,7 +11896,7 @@ static void clif_parse_Broadcast(int fd, struct map_session_data *sd) char command[sizeof commandname + 2 + CHAT_SIZE_MAX] = ""; // '@' command + ' ' + message + NUL int len = (int)RFIFOW(fd,2) - 4; - if (len < 0) + if (len <= 0) return; sprintf(command, "%c%s ", atcommand->at_symbol, commandname); @@ -11258,6 +11996,9 @@ static void clif_parse_UseItem(int fd, struct map_session_data *sd) { int n; + if (pc_isvending(sd)) + return; + if (pc_isdead(sd)) { clif->clearunit_area(&sd->bl, CLR_DEAD); return; @@ -11270,7 +12011,7 @@ static void clif_parse_UseItem(int fd, struct map_session_data *sd) pc->update_idle_time(sd, BCIDLE_USEITEM); n = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[0])-2; - if (n < 0 || n >= MAX_INVENTORY) + if (n < 0 || n >= sd->status.inventorySize) return; if (!pc->useitem(sd,n)) clif->useitemack(sd,n,0,false); //Send an empty ack packet or the client gets stuck. @@ -11283,15 +12024,17 @@ static void clif_parse_EquipItem(int fd, struct map_session_data *sd) __attribut static void clif_parse_EquipItem(int fd, struct map_session_data *sd) { const struct packet_equip_item *p = RP2PTR(fd); - int index = 0; - if(pc_isdead(sd)) { + if (pc_isvending(sd)) + return; + + if (pc_isdead(sd)) { clif->clearunit_area(&sd->bl,CLR_DEAD); return; } - index = p->index - 2; - if (index >= MAX_INVENTORY) + int index = p->index - 2; + if (index < 0 || index >= sd->status.inventorySize) return; //Out of bounds check. if( sd->npc_id ) { @@ -11302,15 +12045,15 @@ static void clif_parse_EquipItem(int fd, struct map_session_data *sd) else if ( pc_cant_act2(sd) || sd->state.prerefining ) return; - if(!sd->status.inventory[index].identify) { - clif->equipitemack(sd, index, 0, EIA_FAIL);// fail + if (!sd->status.inventory[index].identify) { + clif->equipitemack(sd, index, 0, EIA_FAIL); // fail return; } - if(!sd->inventory_data[index]) + if (!sd->inventory_data[index]) return; - if(sd->inventory_data[index]->type == IT_PETARMOR){ + if (sd->inventory_data[index]->type == IT_PETARMOR) { pet->equipitem(sd, index); return; } @@ -11318,7 +12061,7 @@ static void clif_parse_EquipItem(int fd, struct map_session_data *sd) pc->update_idle_time(sd, BCIDLE_USEITEM); //Client doesn't send the position for ammo. - if(sd->inventory_data[index]->type == IT_AMMO) + if (sd->inventory_data[index]->type == IT_AMMO) pc->equipitem(sd, index, EQP_AMMO); else pc->equipitem(sd, index, p->wearLocation); @@ -11331,6 +12074,9 @@ static void clif_parse_UnequipItem(int fd, struct map_session_data *sd) { int index; + if (pc_isvending(sd)) + return; + if(pc_isdead(sd)) { clif->clearunit_area(&sd->bl,CLR_DEAD); return; @@ -11364,7 +12110,7 @@ static void clif_parse_NpcClicked(int fd, struct map_session_data *sd) clif->clearunit_area(&sd->bl,CLR_DEAD); return; } - if (sd->npc_id || sd->state.workinprogress & 2) { + if (sd->npc_id > 0 || (sd->state.workinprogress & 2) == 2 || sd->block_action.npc == 1) { // *pcblock script command #if PACKETVER >= 20110308 clif->msgtable(sd, MSG_BUSY); #else @@ -11372,7 +12118,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) { @@ -11403,7 +12149,7 @@ static void clif_parse_NpcBuySellSelected(int fd, struct map_session_data *sd) _ /// 1 = sell static void clif_parse_NpcBuySellSelected(int fd, struct map_session_data *sd) { - if (sd->state.trading) + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) return; npc->buysellsel(sd, RFIFOL(fd,2), RFIFOB(fd,6)); } @@ -11415,11 +12161,18 @@ static void clif_parse_NpcBuySellSelected(int fd, struct map_session_data *sd) /// 1 = "You do not have enough zeny." /// 2 = "You are over your Weight Limit." /// 3 = "Out of the maximum capacity, you have too many items." +/// 9 = "Amounts are exceeded the possession of the item is not available for purchase." +/// 10 = "Props open-air store sales will be traded in RODEX" +/// 11 = "The exchange failed." +/// 12 = "The exchange was well done." +/// 13 = "The item is already sold and out of stock." +/// 14 = "There is not enough goods to exchange." static void clif_npc_buy_result(struct map_session_data *sd, unsigned char result) { int fd; nullpo_retv(sd); + pc->update_idle_time(sd, BCIDLE_SCRIPT); fd = sd->fd; WFIFOHEAD(fd,packet_len(0xca)); WFIFOW(fd,0) = 0xca; @@ -11432,9 +12185,12 @@ static void clif_parse_NpcBuyListSend(int fd, struct map_session_data *sd) __att /// 00c8 <packet len>.W { <amount>.W <name id>.W }* static void clif_parse_NpcBuyListSend(int fd, struct map_session_data *sd) { - int n = ((int)RFIFOW(fd, 2) - sizeof(struct PACKET_CZ_PC_PURCHASE_ITEMLIST)) / sizeof(struct PACKET_CZ_PC_PURCHASE_ITEMLIST_sub); - int result; + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_PC_PURCHASE_ITEMLIST *p = RFIFOP(fd, 0); + int n = ((int)p->packetLength - sizeof(struct PACKET_CZ_PC_PURCHASE_ITEMLIST)) / sizeof(struct PACKET_CZ_PC_PURCHASE_ITEMLIST_sub); + int result; Assert_retv(n >= 0); @@ -11473,6 +12229,7 @@ static void clif_npc_sell_result(struct map_session_data *sd, unsigned char resu int fd; nullpo_retv(sd); + pc->update_idle_time(sd, BCIDLE_SCRIPT); fd = sd->fd; WFIFOHEAD(fd,packet_len(0xcb)); WFIFOW(fd,0) = 0xcb; @@ -11491,7 +12248,7 @@ static void clif_parse_NpcSellListSend(int fd, struct map_session_data *sd) Assert_retv(n >= 0); - if (sd->state.trading || !sd->npc_shopid) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd) || !sd->npc_shopid) { fail = 1; } else { struct itemlist item_list = { 0 }; @@ -11526,6 +12283,9 @@ static void clif_parse_CreateChatRoom(int fd, struct map_session_data *sd) __att /// 1 = public static void clif_parse_CreateChatRoom(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = (int)RFIFOW(fd, 2) - 15; int limit; bool pub; @@ -11542,6 +12302,9 @@ static void clif_parse_CreateChatRoom(int fd, struct map_session_data *sd) password = RFIFOP(fd, 7); //not zero-terminated title = RFIFOP(fd, 15); // not zero-terminated + if (limit < 0) + return; + if (pc_ismuted(&sd->sc, MANNER_NOROOM)) return; if(battle_config.basic_skill_check && !pc->check_basicskill(sd, 4)) { @@ -11568,6 +12331,9 @@ static void clif_parse_ChatAddMember(int fd, struct map_session_data *sd) __attr /// 00d9 <chat ID>.L <passwd>.8B static void clif_parse_ChatAddMember(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int chatid = RFIFOL(fd,2); const char *password = RFIFOP(fd,6); // not zero-terminated @@ -11582,6 +12348,9 @@ static void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data *sd) /// 1 = public static void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = (int)RFIFOW(fd, 2) - 15; int limit; bool pub; @@ -11594,6 +12363,8 @@ static void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data *sd) return; limit = RFIFOW(fd, 4); + if (limit < 0) + return; pub = (RFIFOB(fd, 6) != 0); password = RFIFOP(fd, 7); // not zero-terminated title = RFIFOP(fd, 15); // not zero-terminated @@ -11612,6 +12383,9 @@ static void clif_parse_ChangeChatOwner(int fd, struct map_session_data *sd) __at /// 1 = normal static void clif_parse_ChangeChatOwner(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + chat->change_owner(sd, RFIFOP(fd,6)); // non null terminated } @@ -11620,6 +12394,9 @@ static void clif_parse_KickFromChat(int fd, struct map_session_data *sd) __attri /// 00e2 <name>.24B static void clif_parse_KickFromChat(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + chat->kick(sd, RFIFOP(fd,2)); // non null terminated } @@ -11628,6 +12405,9 @@ static void clif_parse_ChatLeave(int fd, struct map_session_data *sd) __attribut /// 00e3 static void clif_parse_ChatLeave(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + chat->leave(sd, false); } @@ -11652,9 +12432,10 @@ static void clif_parse_TradeRequest(int fd, struct map_session_data *sd) __attri /// 00e4 <account id>.L static void clif_parse_TradeRequest(int fd, struct map_session_data *sd) { - struct map_session_data *t_sd; + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; - t_sd = map->id2sd(RFIFOL(fd,2)); + struct map_session_data *t_sd = map->id2sd(RFIFOL(fd, 2)); if (sd->chat_id == 0 && pc_cant_act(sd)) return; //You can trade while in a chatroom. @@ -11681,6 +12462,9 @@ static void clif_parse_TradeAck(int fd, struct map_session_data *sd) __attribute /// 4 = rejected static void clif_parse_TradeAck(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + trade->ack(sd,RFIFOB(fd,2)); } @@ -11689,6 +12473,9 @@ static void clif_parse_TradeAddItem(int fd, struct map_session_data *sd) __attri /// 00e8 <index>.W <amount>.L static void clif_parse_TradeAddItem(int fd, struct map_session_data *sd) { + if (!sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + short index = RFIFOW(fd,2); int amount = RFIFOL(fd,4); @@ -11703,6 +12490,8 @@ static void clif_parse_TradeOk(int fd, struct map_session_data *sd) __attribute_ /// 00eb static void clif_parse_TradeOk(int fd, struct map_session_data *sd) { + if (pc_isdead(sd) || pc_isvending(sd)) + return; trade->ok(sd); } @@ -11711,6 +12500,9 @@ static void clif_parse_TradeCancel(int fd, struct map_session_data *sd) __attrib /// 00ed static void clif_parse_TradeCancel(int fd, struct map_session_data *sd) { + if (pc_isdead(sd) || pc_isvending(sd)) + return; + trade->cancel(sd); } @@ -11719,6 +12511,9 @@ static void clif_parse_TradeCommit(int fd, struct map_session_data *sd) __attrib /// 00ef static void clif_parse_TradeCommit(int fd, struct map_session_data *sd) { + if (pc_isdead(sd) || pc_isvending(sd)) + return; + trade->commit(sd); } @@ -11736,7 +12531,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; @@ -11751,6 +12546,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)); @@ -11774,6 +12571,42 @@ static void clif_parse_RemoveOption(int fd, struct map_session_data *sd) } } +static void clif_parse_reqGearOff(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_reqGearOff(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190703 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190709 + const struct PACKET_CZ_REQ_MOUNTOFF *p = RFIFOP(fd, 0); + switch (p->action) { + case REMOVE_MOUNT_DRAGON: + if (pc_isridingdragon(sd)) + pc->setoption(sd, sd->sc.option &~ OPTION_DRAGON); + break; + case REMOVE_MOUNT_MADO: + if (pc_ismadogear(sd)) + pc->setoption(sd, sd->sc.option &~ OPTION_MADOGEAR); + break; + case REMOVE_MOUNT_PECO: + if (pc_isridingpeco(sd)) + pc->setoption(sd, sd->sc.option &~ OPTION_RIDING); + break; + case REMOVE_MOUNT_FALCON: + if (pc_isfalcon(sd)) + pc->setoption(sd, sd->sc.option &~ OPTION_FALCON); + break; + case REMOVE_MOUNT_CART: + // this packet exists in clients with only new carts [4144] + if (sd->sc.data[SC_PUSH_CART]) + pc->setcart(sd, 0); + break; + case REMOVE_MOUNT_0: + case REMOVE_MOUNT_2: + default: + ShowError("Unknown action in remove mount packet: %d\n", p->action); + break; + } +#endif +} + static void clif_parse_ChangeCart(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); /// Request to change cart's visual look (CZ_REQ_CHANGECART). /// 01af <num>.W @@ -11966,33 +12799,32 @@ static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct unit->skilluse_pos(&md->bl, x, y, skill_id, skill_lv); } -static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -/// Request to use a targeted skill. -/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL) -/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2) -/// There are various variants of this packet, some of them have padding between fields. -static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) +static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id) __attribute__((nonnull (2))); +static void clif_useSkillToIdReal(int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id) { - uint16 skill_id, skill_lv; - int tmp, target_id; int64 tick = timer->gettick(); - skill_lv = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[0]); - skill_id = RFIFOW(fd,packet_db[RFIFOW(fd,0)].pos[1]); - target_id = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[2]); - - if( skill_lv < 1 ) skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex] - - tmp = skill->get_inf(skill_id); - if (tmp&INF_GROUND_SKILL || !tmp) + /** + * According to Skotlex' comment below, the client sometimes passes 0 for the skill level. + * Even though this seems to only affect guild skills, sd->autocast.skill_lv is used + * for the auto-cast data validation if skill_lv is 0. + * + **/ + skill->validate_autocast_data(sd, skill_id, (skill_lv == 0) ? sd->autocast.skill_lv : skill_lv); + + if (skill_lv < 1) + skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex] + + int tmp = skill->get_inf(skill_id); + if (tmp & INF_GROUND_SKILL || !tmp) return; //Using a ground/passive skill on a target? WRONG. - if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) { + if (skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL) { clif->pUseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id); return; } - if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL ) { + if (skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL) { clif->pUseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id); return; } @@ -12009,75 +12841,110 @@ static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) return; } - if( pc_cant_act(sd) - && skill_id != RK_REFRESH - && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) - && (sd->state.storage_flag != STORAGE_FLAG_CLOSED && !(tmp&INF_SELF_SKILL)) // SELF skills can be used with the storage open, issue: 8027 - ) + if (pc_cant_act(sd) + && skill_id != RK_REFRESH + && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) + && (sd->state.storage_flag != STORAGE_FLAG_CLOSED && !(tmp&INF_SELF_SKILL)) // SELF skills can be used with the storage open, issue: 8027 + ) { return; + } - if( pc_issit(sd) ) + if (pc_issit(sd)) return; - if( skill->not_ok(skill_id, sd) ) + if (skill->not_ok(skill_id, sd)) return; - if( sd->bl.id != target_id && tmp&INF_SELF_SKILL ) + if (sd->bl.id != target_id && tmp & INF_SELF_SKILL) target_id = sd->bl.id; // never trust the client - if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris] + if (target_id < 0 && -target_id == sd->bl.id) // for disguises [Valaris] target_id = sd->bl.id; - if( sd->ud.skilltimer != INVALID_TIMER ) { - if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) + if (sd->ud.skilltimer != INVALID_TIMER) { + if (skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST) return; - } else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) { - if( sd->skillitem != skill_id ) { + } else if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) { + if (sd->autocast.type == AUTOCAST_NONE) { clif->skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0, 0); return; } } - if( sd->sc.option&OPTION_COSTUME ) + if (sd->sc.option & OPTION_COSTUME) return; - if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) ) + if (sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id)) return; // On basilica only caster can use Basilica again to stop it. - if( sd->menuskill_id ) { - if( sd->menuskill_id == SA_TAMINGMONSTER ) { + if (sd->menuskill_id) { + if (sd->menuskill_id == SA_TAMINGMONSTER) { clif_menuskill_clear(sd); //Cancel pet capture. - } else if( sd->menuskill_id != SA_AUTOSPELL ) + } else if (sd->menuskill_id != SA_AUTOSPELL) return; //Can't use skills while a menu is open. } - if( sd->skillitem == skill_id ) { - if( skill_lv != sd->skillitemlv ) - skill_lv = sd->skillitemlv; - if( !(tmp&INF_SELF_SKILL) ) + if (sd->autocast.type != AUTOCAST_NONE) { + if (skill_lv != sd->autocast.skill_lv) + skill_lv = sd->autocast.skill_lv; + if (!(tmp&INF_SELF_SKILL)) pc->delinvincibletimer(sd); // Target skills through items cancel invincibility. [Inkfish] unit->skilluse_id(&sd->bl, target_id, skill_id, skill_lv); return; } - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); if (skill_id >= GD_SKILLBASE && skill_id < GD_MAX) { - if( sd->state.gmaster_flag ) + if (sd->state.gmaster_flag) skill_lv = guild->checkskill(sd->guild, skill_id); else skill_lv = 0; } else { tmp = pc->checkskill(sd, skill_id); - if( skill_lv > tmp ) + if (skill_lv > tmp) skill_lv = tmp; } pc->delinvincibletimer(sd); - if( skill_lv ) + if (skill_lv) unit->skilluse_id(&sd->bl, target_id, skill_id, skill_lv); } +static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +/// Request to use a targeted skill. +/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL) +/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2) +/// There are various variants of this packet, some of them have padding between fields. +static void clif_parse_UseSkillToId(int fd, struct map_session_data *sd) +{ + clif->useSkillToIdReal(fd, + sd, + RFIFOW(fd, packet_db[RFIFOW(fd, 0)].pos[1]), + RFIFOW(fd, packet_db[RFIFOW(fd, 0)].pos[0]), + RFIFOL(fd, packet_db[RFIFOW(fd, 0)].pos[2])); +} + +static void clif_parse_startUseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_startUseSkillToId(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181002 || PACKETVER_RE_NUM >= 20181002 || PACKETVER_ZERO_NUM >= 20181010 + const struct PACKET_CZ_START_USE_SKILL *p = RFIFOP(fd, 0); + clif->useSkillToIdReal(fd, sd, p->skillId, p->skillLv, p->targetId); +#endif +} + +static void clif_parse_stopUseSkillToId(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_stopUseSkillToId(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20181002 || PACKETVER_RE_NUM >= 20181002 || PACKETVER_ZERO_NUM >= 20181010 + const struct PACKET_CZ_STOP_USE_SKILL *p = RFIFOP(fd, 0); + if (p->skillId != GC_ROLLINGCUTTER) { + ShowWarning("Packet CZ_STOP_USE_SKILL usage for unknown skill: %d\n", p->skillId); + } +#endif +} + /*========================================== * Client tells server he'd like to use AoE skill id 'skill_id' of level 'skill_lv' on 'x','y' location *------------------------------------------*/ @@ -12086,6 +12953,16 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin int64 tick = timer->gettick(); nullpo_retv(sd); + + /** + * When using clif_item_skill() to initiate the execution of ground skills, + * the client sometimes passes 0 for the skill level in packet 0x0af4. + * In that case sd->autocast.skill_lv is used for the auto-cast data validation, + * since clif_item_skill() is only used for auto-cast skills. + * + **/ + skill->validate_autocast_data(sd, skill_id, (skill_lv == 0) ? sd->autocast.skill_lv : skill_lv); + if( !(skill->get_inf(skill_id)&INF_GROUND_SKILL) ) return; //Using a target skill on the ground? WRONG. @@ -12119,14 +12996,14 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin return; } //You can't use Graffiti/TalkieBox AND have a vending open, so this is safe. - safestrncpy(sd->message, RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE); + safestrncpy(sd->message, RFIFOP(fd, skillmoreinfo), TALKBOX_MESSAGE_SIZE); } if( sd->ud.skilltimer != INVALID_TIMER ) return; if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) { - if( sd->skillitem != skill_id ) { + if (sd->autocast.type == AUTOCAST_NONE) { clif->skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0, 0); return; } @@ -12147,13 +13024,13 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin pc->delinvincibletimer(sd); - if( sd->skillitem == skill_id ) { - if( skill_lv != sd->skillitemlv ) - skill_lv = sd->skillitemlv; + if (sd->autocast.type != AUTOCAST_NONE) { + if (skill_lv != sd->autocast.skill_lv) + skill_lv = sd->autocast.skill_lv; unit->skilluse_pos(&sd->bl, x, y, skill_id, skill_lv); } else { int lv; - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); if( (lv = pc->checkskill(sd, skill_id)) > 0 ) { if( skill_lv > lv ) skill_lv = lv; @@ -12224,8 +13101,15 @@ static void clif_parse_UseSkillMap(int fd, struct map_session_data *sd) return; } + /** + * Since no skill level was passed use 0 to notify skill_validate_autocast_data() of this special case. + * + **/ + skill->validate_autocast_data(sd, skill_id, 0); + pc->delinvincibletimer(sd); skill->castend_map(sd,skill_id,map_name); + pc->autocast_clear(sd); } static void clif_parse_RequestMemo(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -12253,7 +13137,7 @@ static void clif_parse_ProduceMix(int fd, struct map_session_data *sd) default: return; } - if (pc_istrading(sd)) { + if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -12284,7 +13168,7 @@ static void clif_parse_Cooking(int fd, struct map_session_data *sd) if (type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY) return; - if (pc_istrading(sd)) { + if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -12304,7 +13188,7 @@ static void clif_parse_RepairItem(int fd, struct map_session_data *sd) if (sd->menuskill_id != BS_REPAIRWEAPON) return; - if (pc_istrading(sd)) { + if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); @@ -12319,19 +13203,17 @@ static void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) __attri /// 0222 <index>.L static void clif_parse_WeaponRefine(int fd, struct map_session_data *sd) { - int idx; - sd->state.prerefining = 0; if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit? return; - if (pc_istrading(sd)) { + if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); return; } - idx = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[0]); + int idx = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[0]); skill->weaponrefine(sd, idx-2); clif_menuskill_clear(sd); } @@ -12346,6 +13228,9 @@ static void clif_parse_NpcSelectMenu(int fd, struct map_session_data *sd) __attr /// overflows to choice%256. static void clif_parse_NpcSelectMenu(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int npc_id = RFIFOL(fd,2); uint8 select = RFIFOB(fd,6); @@ -12371,6 +13256,9 @@ static void clif_parse_NpcNextClicked(int fd, struct map_session_data *sd) __att /// 00b9 <npc id>.L static void clif_parse_NpcNextClicked(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + npc->scriptcont(sd,RFIFOL(fd,2), false); } @@ -12379,13 +13267,25 @@ static void clif_parse_NpcAmountInput(int fd, struct map_session_data *sd) __att /// 0143 <npc id>.L <value>.L static void clif_parse_NpcAmountInput(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + 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); } @@ -12394,6 +13294,9 @@ static void clif_parse_NpcStringInput(int fd, struct map_session_data *sd) __att /// 01d5 <packet len>.W <npc id>.L <string>.?B static void clif_parse_NpcStringInput(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = RFIFOW(fd, 2); // [4144] can't confirm exact client version. At least >= correct for 20150513 #if PACKETVER >= 20151029 @@ -12421,6 +13324,8 @@ static void clif_parse_NpcCloseClicked(int fd, struct map_session_data *sd) { if (!sd->npc_id) //Avoid parsing anything when the script was done with. [Skotlex] return; + if (sd->state.trading || pc_isvending(sd)) + return; sd->state.dialog = 0; npc->scriptcont(sd, RFIFOL(fd,2), true); } @@ -12432,6 +13337,9 @@ static void clif_parse_ItemIdentify(int fd, struct map_session_data *sd) __attri /// -1 = cancel static void clif_parse_ItemIdentify(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + short idx = RFIFOW(fd,2); if (sd->menuskill_id != MC_IDENTIFY) @@ -12450,14 +13358,17 @@ static void clif_parse_ItemIdentify(int fd, struct map_session_data *sd) /// 0A35 <index>.W static void clif_parse_OneClick_ItemIdentify(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int cmd = RFIFOW(fd,0); short idx = RFIFOW(fd, packet_db[cmd].pos[0]) - 2; int n; - if (idx < 0 || idx >= MAX_INVENTORY || sd->inventory_data[idx] == NULL || sd->status.inventory[idx].nameid <= 0) + if (idx < 0 || idx >= sd->status.inventorySize || sd->inventory_data[idx] == NULL || sd->status.inventory[idx].nameid <= 0) return; - if ((n = pc->have_magnifier(sd) ) != INDEX_NOT_FOUND && + if ((n = pc->have_magnifier(sd)) != INDEX_NOT_FOUND && pc->delitem(sd, n, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME) == 0) skill->identify(sd, idx); } @@ -12468,13 +13379,13 @@ static void clif_parse_SelectArrow(int fd, struct map_session_data *sd) __attrib static void clif_parse_SelectArrow(int fd, struct map_session_data *sd) { int itemId; - if (pc_istrading(sd)) { + if (pc_istrading(sd) || pc_isdead(sd) || pc_isvending(sd)) { //Make it fail to avoid shop exploits where you sell something different than you see. clif->skill_fail(sd, sd->ud.skill_id, USESKILL_FAIL_LEVEL, 0, 0); clif_menuskill_clear(sd); return; } -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 itemId = RFIFOL(fd, 2); #else itemId = RFIFOW(fd, 2); @@ -12505,6 +13416,9 @@ static void clif_parse_AutoSpell(int fd, struct map_session_data *sd) __attribut /// 01ce <skill id>.L static void clif_parse_AutoSpell(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + uint16 skill_id = RFIFOL(fd,2); sd->state.workinprogress = 0; @@ -12524,6 +13438,9 @@ static void clif_parse_UseCard(int fd, struct map_session_data *sd) __attribute_ /// 017a <card index>.W static void clif_parse_UseCard(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + clif->use_card(sd,RFIFOW(fd,2)-2); } @@ -12532,6 +13449,9 @@ static void clif_parse_InsertCard(int fd, struct map_session_data *sd) __attribu /// 017c <card index>.W <equip index>.W static void clif_parse_InsertCard(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + pc->insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2); } @@ -12557,6 +13477,9 @@ static void clif_parse_ResetChar(int fd, struct map_session_data *sd) __attribut /// 1 = skill static void clif_parse_ResetChar(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + char cmd[15]; if( RFIFOW(fd,2) ) @@ -12599,12 +13522,12 @@ 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; item_amount = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[1]); - if (item_index < 0 || item_index >= MAX_INVENTORY || item_amount < 1) + if (item_index < 0 || item_index >= sd->status.inventorySize || item_amount < 1) return; if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) @@ -12620,6 +13543,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; @@ -12636,7 +13562,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; @@ -12652,7 +13578,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; @@ -12739,6 +13665,9 @@ static void clif_parse_CreateParty(int fd, struct map_session_data *sd) __attrib /// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2) static void clif_parse_CreateParty(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + char name[NAME_LENGTH]; safestrncpy(name, RFIFOP(fd,2), NAME_LENGTH); @@ -12759,6 +13688,9 @@ static void clif_parse_CreateParty(int fd, struct map_session_data *sd) static void clif_parse_CreateParty2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_CreateParty2(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + char name[NAME_LENGTH]; int item1 = RFIFOB(fd,26); int item2 = RFIFOB(fd,27); @@ -12784,6 +13716,9 @@ static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) __attrib /// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ) static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + struct map_session_data *t_sd; if(map->list[sd->bl.m].flag.partylock) { @@ -12805,6 +13740,9 @@ static void clif_parse_PartyInvite(int fd, struct map_session_data *sd) static void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + struct map_session_data *t_sd; char name[NAME_LENGTH]; @@ -12816,7 +13754,7 @@ static void clif_parse_PartyInvite2(int fd, struct map_session_data *sd) return; } - t_sd = map->nick2sd(name); + t_sd = map->nick2sd(name, true); if(t_sd && t_sd->state.noask) { // @noask [LuzZza] clif->noask_sub(sd, t_sd, 1); @@ -12835,13 +13773,23 @@ static void clif_parse_ReplyPartyInvite(int fd, struct map_session_data *sd) __a /// 1 = accept static void clif_parse_ReplyPartyInvite(int fd, struct map_session_data *sd) { - party->reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6)); + if (pc_istrading(sd) || pc_isvending(sd)) { + party->reply_invite(sd, RFIFOL(fd, 2), 0); + return; + } + + party->reply_invite(sd, RFIFOL(fd, 2), RFIFOL(fd, 6)); } static void clif_parse_ReplyPartyInvite2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_ReplyPartyInvite2(int fd, struct map_session_data *sd) { - party->reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6)); + if (pc_istrading(sd) || pc_isvending(sd)) { + party->reply_invite(sd, RFIFOL(fd, 2), 0); + return; + } + + party->reply_invite(sd, RFIFOL(fd, 2), RFIFOB(fd, 6)); } static void clif_parse_LeaveParty(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -12849,7 +13797,10 @@ static void clif_parse_LeaveParty(int fd, struct map_session_data *sd) __attribu /// 0100 static void clif_parse_LeaveParty(int fd, struct map_session_data *sd) { - if(map->list[sd->bl.m].flag.partylock) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + if (map->list[sd->bl.m].flag.partylock) { // Party locked. clif->message(fd, msg_fd(fd,227)); // Party modification is disabled in this map. return; @@ -12862,12 +13813,15 @@ static void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) __ /// 0103 <account id>.L <char name>.24B static void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd) { - if(map->list[sd->bl.m].flag.partylock) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + if (map->list[sd->bl.m].flag.partylock) { // Party locked. clif->message(fd, msg_fd(fd,227)); // Party modification is disabled in this map. return; } - party->removemember(sd, RFIFOL(fd,2), RFIFOP(fd,6)); + party->removemember(sd, RFIFOL(fd, 2), RFIFOP(fd, 6)); } static void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -12876,6 +13830,9 @@ static void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) __ /// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2) static void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + struct party_data *p; int i; @@ -12928,6 +13885,9 @@ static void clif_parse_PartyChangeLeader(int fd, struct map_session_data *sd) __ /// 07da <account id>.L static void clif_parse_PartyChangeLeader(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + party->changeleader(sd, map->id2sd(RFIFOL(fd,2))); } @@ -12940,6 +13900,9 @@ static void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data * static void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + short level = RFIFOW(fd,2); short mapid = RFIFOW(fd,4); short job[PARTY_BOOKING_JOBS]; @@ -12982,6 +13945,9 @@ static void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + short level = RFIFOW(fd,2); short mapid = RFIFOW(fd,4); short job = RFIFOW(fd,6); @@ -13033,7 +13999,10 @@ static void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT - if(party->booking_delete(sd)) + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + if (party->booking_delete(sd)) clif->PartyBookingDeleteAck(sd, 0); #else return; @@ -13069,11 +14038,13 @@ static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd) { #ifndef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + short job[PARTY_BOOKING_JOBS]; - int i; - for(i=0; i<PARTY_BOOKING_JOBS; i++) - job[i] = RFIFOW(fd,2+i*2); + for (int i = 0; i < PARTY_BOOKING_JOBS; i++) + job[i] = RFIFOW(fd, 2 + i * 2); party->booking_update(sd, job); #else @@ -13086,8 +14057,11 @@ static void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data *sd static void clif_PartyBookingInsertNotify(struct map_session_data *sd, struct party_booking_ad_info *pb_ad) { #ifndef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + int i; - uint8 buf[38+PARTY_BOOKING_JOBS*2]; + uint8 buf[38 + PARTY_BOOKING_JOBS * 2]; nullpo_retv(sd); if(pb_ad == NULL) return; @@ -13154,7 +14128,10 @@ static void clif_parse_PartyRecruitRegisterReq(int fd, struct map_session_data * static void clif_parse_PartyRecruitRegisterReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - short level = RFIFOW(fd,2); + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + short level = RFIFOW(fd, 2); const char *notice = RFIFOP(fd, 4); party->recruit_register(sd, level, notice); @@ -13225,6 +14202,9 @@ static void clif_parse_PartyRecruitSearchReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitSearchReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + short level = RFIFOW(fd, 2); short mapid = RFIFOW(fd, 4); unsigned long lastindex = RFIFOL(fd, 6); @@ -13242,7 +14222,10 @@ static void clif_parse_PartyRecruitDeleteReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitDeleteReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT - if(party->booking_delete(sd)) + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + if (party->booking_delete(sd)) clif->PartyRecruitDeleteAck(sd, 0); #else return; @@ -13278,6 +14261,9 @@ static void clif_parse_PartyRecruitUpdateReq(int fd, struct map_session_data *sd static void clif_parse_PartyRecruitUpdateReq(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + const char *notice = RFIFOP(fd, 2); party->recruit_update(sd, notice); @@ -13351,6 +14337,9 @@ static void clif_parse_PartyBookingAddFilteringList(int fd, struct map_session_d static void clif_parse_PartyBookingAddFilteringList(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + int index = RFIFOL(fd, 2); clif->PartyBookingAddFilteringList(index, sd); @@ -13365,6 +14354,9 @@ static void clif_parse_PartyBookingSubFilteringList(int fd, struct map_session_d static void clif_parse_PartyBookingSubFilteringList(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + int gid = RFIFOL(fd, 2); clif->PartyBookingSubFilteringList(gid, sd); @@ -13379,6 +14371,9 @@ static void clif_parse_PartyBookingReqVolunteer(int fd, struct map_session_data static void clif_parse_PartyBookingReqVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + int index = RFIFOL(fd, 2); clif->PartyBookingVolunteerInfo(index, sd); @@ -13455,6 +14450,9 @@ static void clif_parse_PartyBookingRefuseVolunteer(int fd, struct map_session_da static void clif_parse_PartyBookingRefuseVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + unsigned int aid = RFIFOL(fd, 2); clif->PartyBookingRefuseVolunteer(aid, sd); @@ -13484,6 +14482,9 @@ static void clif_parse_PartyBookingCancelVolunteer(int fd, struct map_session_da static void clif_parse_PartyBookingCancelVolunteer(int fd, struct map_session_data *sd) { #ifdef PARTY_RECRUIT + if (pc_istrading(sd) || pc_isvending(sd)) + return; + int index = RFIFOL(fd, 2); clif->PartyBookingCancelVolunteer(index, sd); @@ -13559,6 +14560,9 @@ static void clif_parse_CloseVending(int fd, struct map_session_data *sd) __attri /// 012e static void clif_parse_CloseVending(int fd, struct map_session_data *sd) { + if (sd->npc_id || sd->state.buyingstore || sd->state.trading) + return; + vending->close(sd); } @@ -13567,6 +14571,9 @@ static void clif_parse_VendingListReq(int fd, struct map_session_data *sd) __att /// 0130 <account id>.L static void clif_parse_VendingListReq(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isdead(sd)) + return; + if( sd->npc_id ) {// using an NPC return; } @@ -13578,6 +14585,9 @@ static void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) __attrib /// 0134 <packet len>.W <account id>.L { <amount>.W <index>.W }* static void clif_parse_PurchaseReq(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = (int)RFIFOW(fd, 2) - 8; int id; const uint8 *data; @@ -13599,6 +14609,9 @@ static void clif_parse_PurchaseReq2(int fd, struct map_session_data *sd) __attri /// 0801 <packet len>.W <account id>.L <unique id>.L { <amount>.W <index>.W }* static void clif_parse_PurchaseReq2(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = (int)RFIFOW(fd, 2) - 12; int aid; int uid; @@ -13625,25 +14638,25 @@ static void clif_parse_OpenVending(int fd, struct map_session_data *sd) __attrib /// 1 = open static void clif_parse_OpenVending(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isdead(sd) || sd->state.vending || sd->state.buyingstore) + return; + int len = (int)RFIFOW(fd, 2) - 85; - const char *message; - bool flag; - const uint8 *data; if (len < 0) return; - message = RFIFOP(fd,4); - flag = (RFIFOB(fd,84) != 0) ? true : false; - data = RFIFOP(fd,85); + const char *message = RFIFOP(fd, 4); + bool flag = (RFIFOB(fd, 84) != 0) ? true : false; + const uint8 *data = RFIFOP(fd, 85); - if( !flag ) + if (!flag) sd->state.prevend = sd->state.workinprogress = 0; - if(pc_ismuted(&sd->sc, MANNER_NOROOM)) + if (pc_ismuted(&sd->sc, MANNER_NOROOM)) return; - if( map->list[sd->bl.m].flag.novending ) { - clif->message (sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map" + if (map->list[sd->bl.m].flag.novending) { + clif->message (sd->fd, msg_sd(sd, 276)); // "You can't open a shop on this map" return; } if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOVENDING)) { @@ -13662,11 +14675,14 @@ static void clif_parse_CreateGuild(int fd, struct map_session_data *sd) __attrib /// 0165 <char id>.L <guild name>.24B static void clif_parse_CreateGuild(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + char name[NAME_LENGTH]; safestrncpy(name, RFIFOP(fd,6), NAME_LENGTH); - if(map->list[sd->bl.m].flag.guildlock) { - clif->message(fd, msg_fd(fd,228)); // Guild modification is disabled in this map. + if (map->list[sd->bl.m].flag.guildlock) { + clif->message(fd, msg_fd(fd, 228)); // Guild modification is disabled in this map. return; } @@ -13701,6 +14717,7 @@ static void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd) case 0: // Basic Information Guild, hostile alliance information clif->guild_basicinfo(sd); clif->guild_allianceinfo(sd); + clif->guild_castlelist(sd); break; case 1: // Members list, list job title clif->guild_positionnamelist(sd); @@ -13727,6 +14744,9 @@ static void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data * /// 0161 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <name>.24B }* static void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int i; int count = (RFIFOW(fd, 2) - 4) / 40; @@ -13744,6 +14764,9 @@ static void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data /// 0155 <packet len>.W { <account id>.L <char id>.L <position id>.L }* static void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int i; int len = RFIFOW(fd, 2); int count = (len - 4) / 12; @@ -13909,10 +14932,13 @@ static void clif_parse_GuildChangeEmblem(int fd, struct map_session_data *sd) __ /// 0153 <packet len>.W <emblem data>.?B static void clif_parse_GuildChangeEmblem(int fd, struct map_session_data *sd) { - unsigned int emblem_len = RFIFOW(fd,2)-4; - const uint8* emblem = RFIFOP(fd,4); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + unsigned int emblem_len = RFIFOW(fd, 2) - 4; + const uint8* emblem = RFIFOP(fd, 4); - if( !emblem_len || !sd->state.gmaster_flag ) + if (!emblem_len || !sd->state.gmaster_flag) return; if (!clif->validate_emblem(emblem, emblem_len)) { @@ -13929,7 +14955,10 @@ static void clif_parse_GuildChangeNotice(int fd, struct map_session_data *sd) __ /// 016e <guild id>.L <msg1>.60B <msg2>.120B static void clif_parse_GuildChangeNotice(int fd, struct map_session_data *sd) { - int guild_id = RFIFOL(fd,2); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + int guild_id = RFIFOL(fd, 2); char *msg1 = NULL, *msg2 = NULL; if (!sd->state.gmaster_flag) @@ -13981,6 +15010,9 @@ static void clif_parse_GuildInvite(int fd, struct map_session_data *sd) __attrib /// 0168 <account id>.L <inviter account id>.L <inviter char id>.L static void clif_parse_GuildInvite(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *t_sd = map->id2sd(RFIFOL(fd,2)); if (!clif_sub_guild_invite(fd, sd, t_sd)) @@ -13996,7 +15028,7 @@ static void clif_parse_GuildInvite2(int fd, struct map_session_data *sd) struct map_session_data *t_sd = NULL; safestrncpy(nick, RFIFOP(fd, 2), NAME_LENGTH); - t_sd = map->nick2sd(nick); + t_sd = map->nick2sd(nick, true); clif_sub_guild_invite(fd, sd, t_sd); } @@ -14009,7 +15041,7 @@ static void clif_parse_GuildReplyInvite(int fd, struct map_session_data *sd) __a /// 1 = accept static void clif_parse_GuildReplyInvite(int fd, struct map_session_data *sd) { - guild->reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6)); + guild->reply_invite(sd, RFIFOL(fd, 2), RFIFOL(fd, 6)); } static void clif_parse_GuildLeave(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -14017,16 +15049,19 @@ static void clif_parse_GuildLeave(int fd, struct map_session_data *sd) __attribu /// 0159 <guild id>.L <account id>.L <char id>.L <reason>.40B static void clif_parse_GuildLeave(int fd, struct map_session_data *sd) { - if(map->list[sd->bl.m].flag.guildlock) { - clif->message(fd, msg_fd(fd,228)); // Guild modification is disabled in this map. + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + if (map->list[sd->bl.m].flag.guildlock) { + clif->message(fd, msg_fd(fd, 228)); // Guild modification is disabled in this map. return; } - if( sd->bg_id ) { - clif->message(fd, msg_fd(fd,870)); //"You can't leave battleground guilds." + if (sd->bg_id) { + clif->message(fd, msg_fd(fd, 870)); //"You can't leave battleground guilds." return; } - guild->leave(sd,RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOP(fd,14)); + guild->leave(sd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10), RFIFOP(fd, 14)); } static void clif_parse_GuildExpulsion(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -14034,11 +15069,14 @@ static void clif_parse_GuildExpulsion(int fd, struct map_session_data *sd) __att /// 015b <guild id>.L <account id>.L <char id>.L <reason>.40B static void clif_parse_GuildExpulsion(int fd, struct map_session_data *sd) { - if( map->list[sd->bl.m].flag.guildlock || sd->bg_id ) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + if (map->list[sd->bl.m].flag.guildlock || sd->bg_id) { clif->message(fd, msg_fd(fd,228)); // Guild modification is disabled in this map. return; } - guild->expulsion(sd, RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOP(fd,14)); + guild->expulsion(sd, RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10), RFIFOP(fd, 14)); } /** @@ -14071,6 +15109,9 @@ static void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) /// 0170 <account id>.L <inviter account id>.L <inviter char id>.L static void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *t_sd; if(!sd->state.gmaster_flag) @@ -14111,6 +15152,9 @@ static void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) __a /// 1 = Enemy static void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + if(!sd->state.gmaster_flag) return; @@ -14126,6 +15170,9 @@ static void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) __at /// 0180 <account id>.L static void clif_parse_GuildOpposition(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *t_sd; if(!sd->state.gmaster_flag) @@ -14155,6 +15202,9 @@ static void clif_parse_GuildBreak(int fd, struct map_session_data *sd) __attribu /// field name and size is same as the one in CH_DELETE_CHAR. static void clif_parse_GuildBreak(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + char key[40]; if( map->list[sd->bl.m].flag.guildlock ) { clif->message(fd, msg_fd(fd,228)); // Guild modification is disabled in this map. @@ -14178,7 +15228,10 @@ static void clif_parse_PetMenu(int fd, struct map_session_data *sd) __attribute_ /// 4 = unequip accessory static void clif_parse_PetMenu(int fd, struct map_session_data *sd) { - pet->menu(sd,RFIFOB(fd,2)); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + pet->menu(sd, RFIFOB(fd, 2)); } static void clif_parse_CatchPet(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -14186,7 +15239,10 @@ static void clif_parse_CatchPet(int fd, struct map_session_data *sd) __attribute /// 019f <id>.L static void clif_parse_CatchPet(int fd, struct map_session_data *sd) { - pet->catch_process2(sd,RFIFOL(fd,2)); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + pet->catch_process2(sd, RFIFOL(fd, 2)); } static void clif_parse_SelectEgg(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -14194,10 +15250,13 @@ static void clif_parse_SelectEgg(int fd, struct map_session_data *sd) __attribut /// 01a7 <index>.W static void clif_parse_SelectEgg(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + if (sd->menuskill_id != SA_TAMINGMONSTER || sd->menuskill_val != -1) { return; } - pet->select_egg(sd,RFIFOW(fd,2)-2); + pet->select_egg(sd, RFIFOW(fd, 2) - 2); clif_menuskill_clear(sd); } @@ -14237,58 +15296,74 @@ static void clif_parse_ChangePetName(int fd, struct map_session_data *sd) __attr /// 01a5 <name>.24B static void clif_parse_ChangePetName(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + pet->change_name(sd, RFIFOP(fd,2)); } +/** + * Request to evolve the pet. (CZ_PET_EVOLUTION) + * + * @code + * 09fb <Length>.W <EvolvedPetEggID>.W {<index>.W <amount>.W}*items + * @endcode + * + * @param fd The incoming file descriptor. + * @param sd The related character. + * + **/ static void clif_parse_pet_evolution(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -/// Request to Evolve the pet (CZ_PET_EVOLUTION) [Dastgir/Hercules] -/// 09fb <Length>.W <EvolvedPetEggID>.W {<index>.W <amount>.W}*items static void clif_parse_pet_evolution(int fd, struct map_session_data *sd) { - const struct PACKET_CZ_PET_EVOLUTION *p = RP2PTR(fd); - int i = 0, idx, petIndex; - - Assert_retv(p->PacketLength >= (uint16)sizeof(struct PACKET_CZ_PET_EVOLUTION)); + if (sd->state.trading != 0 || pc_isdead(sd) || pc_isvending(sd)) + return; - if (sd->status.pet_id == 0) { + if (sd->pd == NULL || sd->status.pet_id == 0) { // No pet. clif->petEvolutionResult(fd, PET_EVOL_NO_CALLPET); return; } - ARR_FIND(0, MAX_INVENTORY, idx, sd->status.inventory[idx].card[0] == CARD0_PET && - sd->status.pet_id == MakeDWord(sd->status.inventory[idx].card[1], sd->status.inventory[idx].card[2])); + int inv_index; + + ARR_FIND(0, sd->status.inventorySize, inv_index, sd->status.inventory[inv_index].card[0] == CARD0_PET + && sd->status.pet_id == MakeDWord(sd->status.inventory[inv_index].card[1], + sd->status.inventory[inv_index].card[2])); - if (idx == MAX_INVENTORY) { + if (inv_index == sd->status.inventorySize) { // No pet egg. clif->petEvolutionResult(fd, PET_EVOL_NO_PETEGG); return; } - // Not Loyal Yet - if (sd->pd == NULL || sd->pd->pet.intimate < 900) { + if (sd->pd->pet.intimate < PET_INTIMACY_LOYAL) { // Pet isn't loyal. clif->petEvolutionResult(fd, PET_EVOL_RG_FAMILIAR); return; } - ARR_FIND(0, MAX_PET_DB, petIndex, pet->db[petIndex].class_ == sd->pd->pet.class_); + int pet_index; - if (petIndex == MAX_PET_DB) { - // Which error? - clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN); + ARR_FIND(0, MAX_PET_DB, pet_index, pet->db[pet_index].class_ == sd->pd->pet.class_); + + if (pet_index == MAX_PET_DB) { + clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN); // Which error? return; } + const struct PACKET_CZ_PET_EVOLUTION *p = RP2PTR(fd); + + Assert_retv(p->PacketLength >= (uint16)sizeof(struct PACKET_CZ_PET_EVOLUTION)); + // Client side validation is not done as it is insecure. - for (i = 0; i < VECTOR_LENGTH(pet->db[petIndex].evolve_data); i++) { - struct pet_evolve_data *ped = &VECTOR_INDEX(pet->db[petIndex].evolve_data, i); - if (ped->petEggId == p->EvolvedPetEggID) { - int j; - int pet_id; + for (int i = 0; i < VECTOR_LENGTH(pet->db[pet_index].evolve_data); i++) { + struct pet_evolve_data *ped = &VECTOR_INDEX(pet->db[pet_index].evolve_data, i); + if (ped->petEggId == p->EvolvedPetEggID) { if (VECTOR_LENGTH(ped->items) == 0) { clif->petEvolutionResult(fd, PET_EVOL_NO_RECIPE); return; } - for (j = 0; j < VECTOR_LENGTH(ped->items); j++) { + + for (int j = 0; j < VECTOR_LENGTH(ped->items); j++) { struct itemlist_entry *list = &VECTOR_INDEX(ped->items, j); int n = pc->search_inventory(sd, list->id); @@ -14298,7 +15373,7 @@ static void clif_parse_pet_evolution(int fd, struct map_session_data *sd) } } - for (j = 0; j < VECTOR_LENGTH(ped->items); j++) { + for (int j = 0; j < VECTOR_LENGTH(ped->items); j++) { struct itemlist_entry *list = &VECTOR_INDEX(ped->items, j); int n = pc->search_inventory(sd, list->id); @@ -14308,27 +15383,26 @@ static void clif_parse_pet_evolution(int fd, struct map_session_data *sd) } } - // Return to Egg - pet->return_egg(sd, sd->pd); + pet->return_egg(sd, sd->pd); // Return pet to egg. - if (pc->delitem(sd, idx, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG) == 1) { + if (pc->delitem(sd, inv_index, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG) == 1) { clif->petEvolutionResult(fd, PET_EVOL_NO_PETEGG); return; } - pet_id = pet->search_petDB_index(ped->petEggId, PET_EGG); + int pet_id = pet->search_petDB_index(ped->petEggId, PET_EGG); + if (pet_id >= 0) { sd->catch_target_class = pet->db[pet_id].class_; - - intif->create_pet( - sd->status.account_id, sd->status.char_id, - (short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv, - pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate, - 100, 0, 1, pet->db[pet_id].jname); + intif->create_pet(sd->status.account_id, sd->status.char_id, pet->db[pet_id].class_, + mob->db(pet->db[pet_id].class_)->lv, pet->db[pet_id].EggID, + 0, (short)pet->db[pet_id].intimate, PET_HUNGER_STUFFED, + 0, 1, pet->db[pet_id].jname); clif->petEvolutionResult(fd, PET_EVOL_SUCCESS); } else { clif->petEvolutionResult(fd, PET_EVOL_UNKNOWN); } + return; } } @@ -14399,8 +15473,8 @@ static void clif_parse_GMKick(int fd, struct map_session_data *sd) clif->GM_kickack(sd, 0); return; } - npc->unload_duplicates(nd); - npc->unload(nd,true); + npc->unload_duplicates(nd, true); + npc->unload(nd, true, true); npc->read_event_script(); } break; @@ -14433,7 +15507,7 @@ static void clif_parse_GMShift(int fd, struct map_session_data *sd) { // FIXME: remove is supposed to receive account name for clients prior 20100803RE char player_name[NAME_LENGTH]; - char command[NAME_LENGTH+8]; + char command[NAME_LENGTH + 20]; safestrncpy(player_name, RFIFOP(fd,2), NAME_LENGTH); @@ -14452,7 +15526,7 @@ static void clif_parse_GMRemove2(int fd, struct map_session_data *sd) account_id = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[0]); if( (pl_sd = map->id2sd(account_id)) != NULL ) { - char command[NAME_LENGTH+8]; + char command[NAME_LENGTH + 20]; sprintf(command, "%cjumpto %s", atcommand->at_symbol, pl_sd->status.name); atcommand->exec(fd, sd, command, true); } @@ -14489,7 +15563,7 @@ static void clif_parse_GMRecall2(int fd, struct map_session_data *sd) account_id = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[0]); if( (pl_sd = map->id2sd(account_id)) != NULL ) { - char command[NAME_LENGTH+8]; + char command[NAME_LENGTH + 20]; sprintf(command, "%crecall %s", atcommand->at_symbol, pl_sd->status.name); atcommand->exec(fd, sd, command, true); } @@ -14520,7 +15594,7 @@ static void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd) safestrncpy(item_monster_name, p->str, sizeof(item_monster_name)); - if ( (count=itemdb->search_name_array(item_array, 10, item_monster_name, 1)) > 0 ) { + if ( (count=itemdb->search_name_array(item_array, 10, item_monster_name, IT_SEARCH_NAME_EXACT)) > 0 ) { for(i = 0; i < count; i++) { if( !item_array[i] ) continue; @@ -14820,6 +15894,9 @@ static void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) __att /// 01e7 static void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + if (sd->state.doridori) return; switch (sd->job & MAPID_UPPERMASK) { @@ -14846,6 +15923,9 @@ static void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *s /// "Help me out~ Please~ T_T" static void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + /* [Ind/Hercules] */ /* game client is currently broken on this (not sure the packetver range) */ /* it sends the request when the criteria doesn't match (and of course we let it fail) */ @@ -14989,13 +16069,16 @@ static void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd) __att /// 0202 <name>.24B static void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *f_sd; int i; char nick[NAME_LENGTH]; safestrncpy(nick, RFIFOP(fd,2), NAME_LENGTH); - f_sd = map->nick2sd(nick); + f_sd = map->nick2sd(nick, true); // ensure that the request player's friend list is not full ARR_FIND(0, MAX_FRIENDS, i, sd->status.friends[i].char_id == 0); @@ -15110,6 +16193,9 @@ static void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd) __ /// 0203 <account id>.L <char id>.L static void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *f_sd = NULL; int account_id, char_id; int i, j; @@ -15199,60 +16285,98 @@ static void clif_parse_PVPInfo(int fd, struct map_session_data *sd) /// Ranking list /// ranking pointlist { <name>.24B <point>.L }*10 -static void clif_ranklist_sub(unsigned char *buf, enum fame_list_type type) +static void clif_ranklist_sub(struct PACKET_ZC_ACK_RANKING_sub *ranks, enum fame_list_type type) { - const char* name; - struct fame_list* list; - int i; +#if !(PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190724) + nullpo_retv(ranks); - nullpo_retv(buf); - switch( type ) { + struct fame_list* list; + switch (type) { case RANKTYPE_BLACKSMITH: list = pc->smith_fame_list; break; case RANKTYPE_ALCHEMIST: list = pc->chemist_fame_list; break; case RANKTYPE_TAEKWON: list = pc->taekwon_fame_list; break; default: return; // Unsupported } + int i; // Packet size limits this list to 10 elements. [Skotlex] - for( i = 0; i < 10 && i < MAX_FAME_LIST; i++ ) { - if( list[i].id > 0 ) { - if( strcmp(list[i].name, "-") == 0 && (name = map->charid2nick(list[i].id)) != NULL ) { - strncpy(WBUFP(buf, 24 * i), name, NAME_LENGTH); + for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) { + if (list[i].id > 0) { + const char* name; + if (strcmp(list[i].name, "-") == 0 && (name = map->charid2nick(list[i].id)) != NULL) { + strncpy(ranks->names[i].name, name, NAME_LENGTH); } else { - strncpy(WBUFP(buf, 24 * i), list[i].name, NAME_LENGTH); + strncpy(ranks->names[i].name, list[i].name, NAME_LENGTH); } } else { - strncpy(WBUFP(buf, 24 * i), "None", 5); + strncpy(ranks->names[i].name, "None", 5); + } + ranks->points[i].points = list[i].fame; //points + } + for (;i < 10; i++) { // In case the MAX is less than 10. + strncpy(ranks->names[i].name, "Unavailable", 12); + ranks->points[i].points = 0; + } +#endif +} + +static void clif_ranklist_sub2(uint32 *chars, uint32 *points, enum fame_list_type type) +{ +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190724 + nullpo_retv(chars); + nullpo_retv(points); + + struct fame_list* list; + switch (type) { + case RANKTYPE_BLACKSMITH: list = pc->smith_fame_list; break; + case RANKTYPE_ALCHEMIST: list = pc->chemist_fame_list; break; + case RANKTYPE_TAEKWON: list = pc->taekwon_fame_list; break; + default: return; // Unsupported + } + + int i; + // Packet size limits this list to 10 elements. [Skotlex] + for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) { + if (list[i].id > 0) { + chars[i] = list[i].id; + } else { + chars[i] = 0; } - WBUFL(buf, 24 * 10 + i * 4) = list[i].fame; //points + points[i] = list[i].fame; //points } - for( ;i < 10; i++ ) { // In case the MAX is less than 10. - strncpy(WBUFP(buf, 24 * i), "Unavailable", 12); - WBUFL(buf, 24 * 10 + i * 4) = 0; + for (;i < 10; i++) { // In case the MAX is less than 10. + chars[i] = 0; + points[i] = 0; } +#endif } /// 097d <RankingType>.W {<CharName>.24B <point>L}*10 <mypoint>L (ZC_ACK_RANKING) static void clif_ranklist(struct map_session_data *sd, enum fame_list_type type) { -#if PACKETVER >= 20120502 - int fd; - int len = packet_len(0x97d); - +#if PACKETVER_MAIN_NUM >= 20130605 || PACKETVER_RE_NUM >= 20130529 || defined(PACKETVER_ZERO) nullpo_retv(sd); - fd = sd->fd; - WFIFOHEAD(fd, len); - WFIFOW(fd, 0) = 0x97d; - WFIFOW(fd, 2) = type; - clif_ranklist_sub(WFIFOP(fd,4), type); + int fd = sd->fd; + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_ACK_RANKING)); + struct PACKET_ZC_ACK_RANKING *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_ACK_RANKING; + p->rankType = type; +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190724 +PRAGMA_GCC9(GCC diagnostic push) +PRAGMA_GCC9(GCC diagnostic ignored "-Waddress-of-packed-member") + clif->ranklist_sub2(p->chars, p->points, type); +PRAGMA_GCC9(GCC diagnostic pop) +#else + clif->ranklist_sub(&p->ranks, type); +#endif if (pc->famelist_type(sd->job) == type) { - WFIFOL(fd, 284) = sd->status.fame; //mypoint + p->myPoints = sd->status.fame; //mypoint } else { - WFIFOL(fd, 284) = 0; //mypoint + p->myPoints = 0; //mypoint } - WFIFOSET(fd, len); + WFIFOSET(fd, sizeof(struct PACKET_ZC_ACK_RANKING)); #endif } @@ -15302,14 +16426,16 @@ static void clif_update_rankingpoint(struct map_session_data *sd, enum fame_list /// 0219 { <name>.24B }*10 { <point>.L }*10 static void clif_blacksmith(struct map_session_data *sd) { +#if !(PACKETVER_MAIN_NUM >= 20130605 || PACKETVER_RE_NUM >= 20130529 || defined(PACKETVER_ZERO)) int fd; nullpo_retv(sd); fd = sd->fd; WFIFOHEAD(fd,packet_len(0x219)); WFIFOW(fd,0) = 0x219; - clif_ranklist_sub(WFIFOP(fd, 2), RANKTYPE_BLACKSMITH); + clif->ranklist_sub(WFIFOP(fd, 2), RANKTYPE_BLACKSMITH); WFIFOSET(fd, packet_len(0x219)); +#endif } static void clif_parse_Blacksmith(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -15339,14 +16465,16 @@ static void clif_fame_blacksmith(struct map_session_data *sd, int points) /// 021a { <name>.24B }*10 { <point>.L }*10 static void clif_alchemist(struct map_session_data *sd) { +#if !(PACKETVER_MAIN_NUM >= 20130605 || PACKETVER_RE_NUM >= 20130529 || defined(PACKETVER_ZERO)) int fd; nullpo_retv(sd); fd = sd->fd; WFIFOHEAD(fd,packet_len(0x21a)); WFIFOW(fd,0) = 0x21a; - clif_ranklist_sub(WFIFOP(fd,2), RANKTYPE_ALCHEMIST); + clif->ranklist_sub(WFIFOP(fd,2), RANKTYPE_ALCHEMIST); WFIFOSET(fd, packet_len(0x21a)); +#endif } static void clif_parse_Alchemist(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -15376,14 +16504,16 @@ static void clif_fame_alchemist(struct map_session_data *sd, int points) /// 0226 { <name>.24B }*10 { <point>.L }*10 static void clif_taekwon(struct map_session_data *sd) { +#if !(PACKETVER_MAIN_NUM >= 20130605 || PACKETVER_RE_NUM >= 20130529 || defined(PACKETVER_ZERO)) int fd; nullpo_retv(sd); fd = sd->fd; WFIFOHEAD(fd,packet_len(0x226)); WFIFOW(fd,0) = 0x226; - clif_ranklist_sub(WFIFOP(fd,2), RANKTYPE_TAEKWON); + clif->ranklist_sub(WFIFOP(fd,2), RANKTYPE_TAEKWON); WFIFOSET(fd, packet_len(0x226)); +#endif } static void clif_parse_Taekwon(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -15443,6 +16573,9 @@ static void clif_parse_FeelSaveOk(int fd, struct map_session_data *sd) __attribu /// 2 = star static void clif_parse_FeelSaveOk(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int i; if (sd->menuskill_id != SG_FEEL) return; @@ -15467,6 +16600,7 @@ static void clif_parse_FeelSaveOk(int fd, struct map_session_data *sd) /// 0 = sun /// 1 = moon /// 2 = star +/// 10 = Do you agree to cast the magic spell that consumes 1 Black Gemstone and 1,000,000 Zeny? static void clif_feel_req(int fd, struct map_session_data *sd, uint16 skill_lv) { nullpo_retv(sd); @@ -15483,6 +16617,9 @@ static void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) /// 0231 <name>.24B static void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + homun->change_name(sd, RFIFOP(fd,2)); } @@ -15491,6 +16628,9 @@ static void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) __at /// 0234 <id>.L static void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int id = RFIFOL(fd,2); // Mercenary or Homunculus struct block_list *bl = NULL; struct unit_data *ud = NULL; @@ -15504,7 +16644,7 @@ static void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd) unit->calc_pos(bl, sd->bl.x, sd->bl.y, sd->ud.dir); ud = unit->bl2ud(bl); - unit->walktoxy(bl, ud->to_x, ud->to_y, 4); + unit->walk_toxy(bl, ud->to_x, ud->to_y, 4); } static void clif_parse_HomMoveTo(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -15512,6 +16652,9 @@ static void clif_parse_HomMoveTo(int fd, struct map_session_data *sd) __attribut /// 0232 <id>.L <position data>.3B static void clif_parse_HomMoveTo(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int id = RFIFOL(fd,2); // Mercenary or Homunculus struct block_list *bl = NULL; short x, y; @@ -15525,7 +16668,7 @@ static void clif_parse_HomMoveTo(int fd, struct map_session_data *sd) else return; - unit->walktoxy(bl, x, y, 4); + unit->walk_toxy(bl, x, y, 4); } static void clif_parse_HomAttack(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -15535,6 +16678,9 @@ static void clif_parse_HomAttack(int fd, struct map_session_data *sd) __attribut /// always 0 static void clif_parse_HomAttack(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct block_list *bl = NULL; int id = RFIFOL(fd,2), target_id = RFIFOL(fd,6), @@ -15561,6 +16707,9 @@ static void clif_parse_HomMenu(int fd, struct map_session_data *sd) __attribute_ /// 2 = delete static void clif_parse_HomMenu(int fd, struct map_session_data *sd) { //[orn] + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int cmd; cmd = RFIFOW(fd,0); @@ -15576,7 +16725,16 @@ static void clif_parse_AutoRevive(int fd, struct map_session_data *sd) __attribu /// 0292 static void clif_parse_AutoRevive(int fd, struct map_session_data *sd) { - int item_position = pc->search_inventory(sd, ITEMID_TOKEN_OF_SIEGFRIED); + if (pc_istrading(sd) || pc_isvending(sd)) + return; + + if (!pc_isdead(sd)) + return; + + if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER. + return; + + int item_position = pc->have_item_chain(sd, ECC_SIEGFRIED); int hpsp = 100; if (item_position == INDEX_NOT_FOUND) { @@ -15586,18 +16744,15 @@ static void clif_parse_AutoRevive(int fd, struct map_session_data *sd) return; } - if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER. - return; - if (!status->revive(&sd->bl, hpsp, hpsp)) return; if (item_position == INDEX_NOT_FOUND) - status_change_end(&sd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER); + status_change_end(&sd->bl, SC_LIGHT_OF_REGENE, INVALID_TIMER); else pc->delitem(sd, item_position, 1, 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); - clif->skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1); + clif->skill_nodamage(&sd->bl, &sd->bl, ALL_RESURRECTION, 4, 1); } /// Information about character's status values (ZC_ACK_STATUS_GM). @@ -15647,6 +16802,9 @@ static void clif_parse_Check(int fd, struct map_session_data *sd) __attribute__( /// 0213 <char name>.24B static void clif_parse_Check(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isvending(sd)) + return; + char charname[NAME_LENGTH]; struct map_session_data* pl_sd; @@ -15655,7 +16813,7 @@ static void clif_parse_Check(int fd, struct map_session_data *sd) safestrncpy(charname, RFIFOP(fd,packet_db[RFIFOW(fd,0)].pos[0]), sizeof(charname)); - if( ( pl_sd = map->nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) ) { + if ((pl_sd = map->nick2sd(charname, true)) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd)) { return; } @@ -15799,7 +16957,7 @@ static void clif_Mail_refreshinbox(struct map_session_data *sd) if( md->full ) {// TODO: is this official? char output[100]; - sprintf(output, "Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX); + sprintf(output, msg_sd(sd, 511), MAIL_MAX_INBOX); // Inbox is full (Max %d). Delete some mails. clif_disp_onlyself(sd, output); } } @@ -15809,6 +16967,9 @@ static void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd) __ /// 023f static void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct mail_data* md = &sd->mail.inbox; if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) ) @@ -15892,6 +17053,9 @@ static void clif_parse_Mail_read(int fd, struct map_session_data *sd) __attribut /// 0241 <mail id>.L static void clif_parse_Mail_read(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int mail_id = RFIFOL(fd,2); if( mail_id <= 0 ) @@ -15907,6 +17071,9 @@ static void clif_parse_Mail_getattach(int fd, struct map_session_data *sd) __att /// 0244 <mail id>.L static void clif_parse_Mail_getattach(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int mail_id = RFIFOL(fd,2); int i; @@ -15924,7 +17091,7 @@ static void clif_parse_Mail_getattach(int fd, struct map_session_data *sd) if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) ) return; - if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY ) { + if( sd->mail.inbox.msg[i].zeny > MAX_ZENY - sd->status.zeny ) { clif->mail_getattachment(fd, 1); return; } @@ -15975,6 +17142,9 @@ static void clif_parse_Mail_delete(int fd, struct map_session_data *sd) __attrib /// 0243 <mail id>.L static void clif_parse_Mail_delete(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int mail_id = RFIFOL(fd,2); int i; @@ -16006,6 +17176,9 @@ static void clif_parse_Mail_return(int fd, struct map_session_data *sd) __attrib /// 0273 <mail id>.L <receive name>.24B static void clif_parse_Mail_return(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int mail_id = RFIFOL(fd,2); int i; @@ -16030,6 +17203,9 @@ static void clif_parse_Mail_setattach(int fd, struct map_session_data *sd) __att /// 0247 <index>.W <amount>.L static void clif_parse_Mail_setattach(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int idx = RFIFOW(fd,2); int amount = RFIFOL(fd,4); unsigned char flag; @@ -16052,6 +17228,9 @@ static void clif_parse_Mail_winopen(int fd, struct map_session_data *sd) __attri /// 2 = remove zeny static void clif_parse_Mail_winopen(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int flag = RFIFOW(fd,2); if (flag == 0 || flag == 1) @@ -16066,6 +17245,9 @@ static void clif_parse_Mail_send(int fd, struct map_session_data *sd) __attribut static void clif_parse_Mail_send(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct mail_message msg; int body_len; int len = RFIFOW(fd, 2); @@ -16142,7 +17324,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 ) @@ -16222,7 +17404,10 @@ static void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd) __ /// ? = junk, uninitialized value (ex. when switching between list filters) static void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd) { - if( sd->auction.amount > 0 ) + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + if (sd->auction.amount > 0) clif->additem(sd, sd->auction.index, sd->auction.amount, 0); sd->auction.amount = 0; @@ -16233,8 +17418,11 @@ static void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) __at /// 024c <index>.W <count>.L static void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) { - int idx = RFIFOW(fd,2) - 2; - int amount = RFIFOL(fd,4); // Always 1 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + int idx = RFIFOW(fd, 2) - 2; + int amount = RFIFOL(fd, 4); // Always 1 struct item_data *item; if( !battle_config.feature_auction ) @@ -16243,7 +17431,7 @@ static void clif_parse_Auction_setitem(int fd, struct map_session_data *sd) if( sd->auction.amount > 0 ) sd->auction.amount = 0; - if( idx < 0 || idx >= MAX_INVENTORY ) { + if (idx < 0 || idx >= sd->status.inventorySize) { ShowWarning("Character %s trying to set invalid item index in auctions.\n", sd->status.name); return; } @@ -16312,13 +17500,16 @@ static void clif_parse_Auction_register(int fd, struct map_session_data *sd) __a /// 024d <now money>.L <max money>.L <delete hour>.W static void clif_parse_Auction_register(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct auction_data auction; struct item_data *item; if (!battle_config.feature_auction) return; - Assert_retv(sd->auction.index >= 0 && sd->auction.index < MAX_INVENTORY); + Assert_retv(sd->auction.index >= 0 && sd->auction.index < sd->status.inventorySize); memset(&auction, 0, sizeof(auction)); auction.price = RFIFOL(fd,2); @@ -16407,7 +17598,10 @@ static void clif_parse_Auction_cancel(int fd, struct map_session_data *sd) __att /// 024e <auction id>.L static void clif_parse_Auction_cancel(int fd, struct map_session_data *sd) { - unsigned int auction_id = RFIFOL(fd,2); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + unsigned int auction_id = RFIFOL(fd, 2); intif->Auction_cancel(sd->status.char_id, auction_id); } @@ -16417,6 +17611,9 @@ static void clif_parse_Auction_close(int fd, struct map_session_data *sd) __attr /// 025d <auction id>.L static void clif_parse_Auction_close(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + unsigned int auction_id = RFIFOL(fd,2); intif->Auction_close(sd->status.char_id, auction_id); @@ -16427,8 +17624,11 @@ static void clif_parse_Auction_bid(int fd, struct map_session_data *sd) __attrib /// 024f <auction id>.L <money>.L static void clif_parse_Auction_bid(int fd, struct map_session_data *sd) { - unsigned int auction_id = RFIFOL(fd,2); - int bid = RFIFOL(fd,6); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + unsigned int auction_id = RFIFOL(fd, 2); + int bid = RFIFOL(fd, 6); if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish] clif->message(sd->fd, msg_sd(sd,246)); // Your GM level doesn't authorize you to perform this action. @@ -16459,9 +17659,12 @@ static void clif_parse_Auction_search(int fd, struct map_session_data *sd) __att /// 5 = auction id search static void clif_parse_Auction_search(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + char search_text[NAME_LENGTH]; - short type = RFIFOW(fd,2), page = RFIFOW(fd,32); - int price = RFIFOL(fd,4); // FIXME: bug #5071 + short type = RFIFOW(fd, 2), page = RFIFOW(fd, 32); + int price = RFIFOL(fd, 4); // FIXME: bug #5071 if( !battle_config.feature_auction ) return; @@ -16480,7 +17683,10 @@ static void clif_parse_Auction_buysell(int fd, struct map_session_data *sd) __at /// 1 = buy (own bids) static void clif_parse_Auction_buysell(int fd, struct map_session_data *sd) { - short type = RFIFOW(fd,2) + 6; + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + short type = RFIFOW(fd, 2) + 6; if( !battle_config.feature_auction ) return; @@ -16590,6 +17796,9 @@ static void clif_parse_cashshop_buy(int fd, struct map_session_data *sd) __attri /// 0288 <packet len>.W <kafra points>.L <count>.W { <amount>.W <name id>.W }.4B*count (PACKETVER >= 20100803) static void clif_parse_cashshop_buy(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int fail = 0; const struct PACKET_CZ_PC_BUY_CASH_POINT_ITEM *p = RFIFOP(fd, 0); @@ -16682,6 +17891,9 @@ static void clif_parse_Adopt_request(int fd, struct map_session_data *sd) __attr /// 01f9 <account id>.L static void clif_parse_Adopt_request(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct map_session_data *tsd = map->id2sd(RFIFOL(fd,2)), *p_sd = map->charid2sd(sd->status.partner_id); if( pc->can_Adopt(sd, p_sd, tsd) ) { @@ -16698,6 +17910,9 @@ static void clif_parse_Adopt_reply(int fd, struct map_session_data *sd) __attrib /// 1 = accepted static void clif_parse_Adopt_reply(int fd, struct map_session_data *sd) { + if (pc_isdead(sd)) + return; + int p1_id = RFIFOL(fd,2); int p2_id = RFIFOL(fd,6); int result = RFIFOL(fd,10); @@ -16765,6 +17980,9 @@ static void clif_parse_ViewPlayerEquip(int fd, struct map_session_data *sd) __at /// 02d6 <account id>.L static void clif_parse_ViewPlayerEquip(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int charid = RFIFOL(fd, 2); struct map_session_data* tsd = map->id2sd(charid); @@ -16792,6 +18010,9 @@ static void clif_parse_cz_config(int fd, struct map_session_data *sd) __attribut /// 1 = enabled static void clif_parse_cz_config(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + enum CZ_CONFIG type = RFIFOL(fd, 2); int flag = RFIFOL(fd, 6); @@ -16815,6 +18036,9 @@ static void clif_parse_cz_config(int fd, struct map_session_data *sd) hd->homunculus.autofeed = flag; break; } + case CZ_CONFIG_CALL: + sd->status.allow_call = flag; + break; default: ShowWarning("clif_parse_cz_config: Unsupported type has been received (%u).\n", type); return; @@ -16825,13 +18049,20 @@ static void clif_parse_cz_config(int fd, struct map_session_data *sd) static void clif_parse_PartyTick(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); /// Request to change party invitation tick. /// value: -/// 0 = disabled -/// 1 = enabled +/// 0 = enabled +/// 1 = disabled static void clif_parse_PartyTick(int fd, struct map_session_data *sd) { - bool flag = RFIFOB(fd,6)?true:false; - sd->status.allow_party = flag; - clif->partytickack(sd, flag); + const struct PACKET_CZ_PARTY_CONFIG *const p = RFIFOP(fd, 0); + 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); } /// Questlog System [Kevin] [Inkfish] @@ -17097,6 +18328,9 @@ static void clif_parse_questStateAck(int fd, struct map_session_data *sd) __attr /// 02b6 <quest id>.L <active>.B static void clif_parse_questStateAck(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + quest->update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE); } @@ -17316,6 +18550,9 @@ static void clif_parse_mercenary_action(int fd, struct map_session_data *sd) __a /// 2 = delete static void clif_parse_mercenary_action(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int option = RFIFOB(fd,2); if (sd->md == NULL) return; @@ -17784,6 +19021,9 @@ static void clif_parse_ItemListWindowSelected(int fd, struct map_session_data *s /// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b* static void clif_parse_ItemListWindowSelected(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int n = ((int)RFIFOW(fd, 2) - 12) / 4; int type = RFIFOL(fd,4); int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK @@ -17798,8 +19038,8 @@ static void clif_parse_ItemListWindowSelected(int fd, struct map_session_data *s return; // Canceled by player. } - if (n > MAX_INVENTORY) - n = MAX_INVENTORY; // It should be impossible to have more than that. + if (n > sd->status.inventorySize) + n = sd->status.inventorySize; // It should be impossible to have more than that. if (sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL) { clif_menuskill_clear(sd); @@ -17913,6 +19153,9 @@ static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data *sd) _ /// 1 = open static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data *sd) { + if (pc_istrading(sd) || pc_isdead(sd)) + return; + const unsigned int blocksize = sizeof(struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub); const struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub *itemlist; char storename[MESSAGE_SIZE]; @@ -18076,6 +19319,9 @@ static void clif_buyingstore_disappear_entry_single(struct map_session_data *sd, /// 0817 <account id>.L static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int account_id; account_id = RFIFOL(fd,packet_db[RFIFOW(fd,0)].pos[0]); @@ -18120,6 +19366,9 @@ static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data *sd) /// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }* static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const unsigned int blocksize = sizeof(struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub); const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub *itemlist; int account_id; @@ -18258,6 +19507,9 @@ static void clif_parse_SearchStoreInfo(int fd, struct map_session_data *sd) __at /// cannot be searched. static void clif_parse_SearchStoreInfo(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const unsigned int blocksize = sizeof(struct PACKET_CZ_SEARCH_STORE_INFO_item); const struct PACKET_CZ_SEARCH_STORE_INFO_item* itemlist; const struct PACKET_CZ_SEARCH_STORE_INFO_item* cardlist; @@ -18386,6 +19638,7 @@ static void clif_search_store_info_ack(struct map_session_data *sd) /// 2 = "You cannot search anymore." (0x706) /// 3 = "You cannot search yet." (0x708) /// 4 = "No sale (purchase) information available." (0x705) +/// 362 = silent error static void clif_search_store_info_failed(struct map_session_data *sd, unsigned char reason) { #if PACKETVER >= 20100601 @@ -18405,6 +19658,9 @@ static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data * /// 0838 static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + searchstore->next(sd); } @@ -18443,6 +19699,9 @@ static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_d /// 083c <account id>.L <store id>.L <nameid>.W static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_SSILIST_ITEM_CLICK *p = RFIFOP(fd, 0); searchstore->click(sd, p->AID, p->storeId, p->itemId); } @@ -18492,27 +19751,30 @@ static void clif_parse_debug(int fd, struct map_session_data *sd) *------------------------------------------*/ static int clif_elementalconverter_list(struct map_session_data *sd) { - int i,c,view,fd; - nullpo_ret(sd); /// Main client packet processing function - fd=sd->fd; - WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB *2+4); - WFIFOW(fd, 0)=0x1ad; - - for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){ - if( skill->can_produce_mix(sd,skill->dbs->produce_db[i].nameid,23, 1) ){ - if((view = itemdb_viewid(skill->dbs->produce_db[i].nameid)) > 0) - WFIFOW(fd,c*2+ 4)= view; + int fd = sd->fd; + int len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + WFIFOHEAD(fd, len); + struct PACKET_ZC_MAKINGARROW_LIST *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MAKINGARROW_LIST; + + int c = 0; + for (int i = 0; i < MAX_SKILL_PRODUCE_DB; i++) { + if (skill->can_produce_mix(sd,skill->dbs->produce_db[i].nameid,23, 1) ) { + int view = itemdb_viewid(skill->dbs->produce_db[i].nameid); + if (view > 0) + p->items[c].itemId = view; else - WFIFOW(fd,c*2+ 4)= skill->dbs->produce_db[i].nameid; + p->items[c].itemId = skill->dbs->produce_db[i].nameid; c++; } } - WFIFOW(fd,2) = c*2+4; - WFIFOSET(fd, WFIFOW(fd,2)); if (c > 0) { + len = c * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + p->packetLength = len; + WFIFOSET(fd, len); sd->menuskill_id = SA_CREATECON; sd->menuskill_val = c; } @@ -18543,33 +19805,33 @@ static void clif_millenniumshield(struct block_list *bl, short shields) *------------------------------------------*/ static int clif_spellbook_list(struct map_session_data *sd) { - int i, c; - int fd; - nullpo_ret(sd); - fd = sd->fd; - WFIFOHEAD(fd, 8 * 8 + 8); - WFIFOW(fd,0) = 0x1ad; + int fd = sd->fd; + int len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + WFIFOHEAD(fd, len); + struct PACKET_ZC_MAKINGARROW_LIST *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MAKINGARROW_LIST; - for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) + int c = 0; + for (int i = 0; i < sd->status.inventorySize; i ++ ) { - if( itemdb_is_spellbook(sd->status.inventory[i].nameid) ) + if (itemdb_is_spellbook(sd->status.inventory[i].nameid)) { - WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + p->items[c].itemId = sd->status.inventory[i].nameid; c++; } } - if( c > 0 ) + if (c > 0) { - WFIFOW(fd,2) = c * 2 + 4; - WFIFOSET(fd, WFIFOW(fd, 2)); + len = c * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + p->packetLength = len; + WFIFOSET(fd, len); sd->menuskill_id = WL_READING_SB; sd->menuskill_val = c; - } - else{ - status_change_end(&sd->bl,SC_STOP,INVALID_TIMER); + } else { + status_change_end(&sd->bl, SC_STOP, INVALID_TIMER); clif->skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0, 0); } @@ -18584,17 +19846,18 @@ static int clif_spellbook_list(struct map_session_data *sd) static int clif_magicdecoy_list(struct map_session_data *sd, uint16 skill_lv, short x, short y) { int i, c; - int fd; nullpo_ret(sd); - fd = sd->fd; - WFIFOHEAD(fd, 8 * 8 + 8); - WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + int fd = sd->fd; + int len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + WFIFOHEAD(fd, len); + struct PACKET_ZC_MAKINGARROW_LIST *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MAKINGARROW_LIST; - for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { - if( itemdb_is_element(sd->status.inventory[i].nameid) ) { - WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + for (i = 0, c = 0; i < sd->status.inventorySize; i ++) { + if (itemdb_is_element(sd->status.inventory[i].nameid)) { + p->items[c].itemId = sd->status.inventory[i].nameid; c ++; } } @@ -18603,8 +19866,10 @@ static int clif_magicdecoy_list(struct map_session_data *sd, uint16 skill_lv, sh sd->menuskill_val = skill_lv; sd->sc.comet_x = x; sd->sc.comet_y = y; - WFIFOW(fd,2) = c * 2 + 4; - WFIFOSET(fd, WFIFOW(fd, 2)); + + len = c * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + p->packetLength = len; + WFIFOSET(fd, len); } else { clif->skill_fail(sd, NC_MAGICDECOY, USESKILL_FAIL_LEVEL, 0, 0); return 0; @@ -18621,25 +19886,28 @@ static int clif_magicdecoy_list(struct map_session_data *sd, uint16 skill_lv, sh static int clif_poison_list(struct map_session_data *sd, uint16 skill_lv) { int i, c; - int fd; nullpo_ret(sd); - fd = sd->fd; - WFIFOHEAD(fd, 8 * 8 + 8); - WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil] + int fd = sd->fd; + int len = MAX_SKILL_ARROW_DB * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + WFIFOHEAD(fd, len); + struct PACKET_ZC_MAKINGARROW_LIST *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_MAKINGARROW_LIST; - for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) { + for (i = 0, c = 0; i < sd->status.inventorySize; i ++) { if( itemdb_is_poison(sd->status.inventory[i].nameid) ) { - WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid; + p->items[c].itemId = sd->status.inventory[i].nameid; c ++; } } - if( c > 0 ) { + if (c > 0) { sd->menuskill_id = GC_POISONINGWEAPON; sd->menuskill_val = skill_lv; - WFIFOW(fd,2) = c * 2 + 4; - WFIFOSET(fd, WFIFOW(fd, 2)); + + len = c * sizeof(struct PACKET_ZC_MAKINGARROW_LIST_sub) + sizeof(struct PACKET_ZC_MAKINGARROW_LIST); + p->packetLength = len; + WFIFOSET(fd, len); } else { clif->skill_fail(sd, GC_POISONINGWEAPON, USESKILL_FAIL_GUILLONTINE_POISON, 0, 0); return 0; @@ -18717,11 +19985,13 @@ static void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) __at *------------------------------------------*/ static void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; - if( sd->menuskill_id != SC_AUTOSHADOWSPELL ) + 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; @@ -18764,13 +20034,12 @@ static void clif_parse_MoveItem(int fd, struct map_session_data *sd) int index; /* can't move while dead. */ - if(pc_isdead(sd)) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) return; - } index = RFIFOW(fd,2)-2; - if (index < 0 || index >= MAX_INVENTORY) + if (index < 0 || index >= sd->status.inventorySize) return; if ( sd->status.inventory[index].favorite && RFIFOB(fd, 4) == 1 ) @@ -18789,7 +20058,8 @@ static void clif_cashshop_db(void) { struct config_t cashshop_conf; struct config_setting_t *cashshop = NULL, *cats = NULL; - const char *config_filename = "db/cashshop_db.conf"; // FIXME hardcoded name + char config_filename[256]; + libconfig->format_db_path("cashshop_db.conf", config_filename, sizeof(config_filename)); int i, item_count_t = 0; for( i = 0; i < CASHSHOP_TAB_MAX; i++ ) { CREATE(clif->cs.data[i], struct hCSData *, 1); @@ -18914,31 +20184,69 @@ static void clif_parse_dull(int fd, struct map_session_data *sd) return; } -static void clif_parse_CashShopOpen(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -static void clif_parse_CashShopOpen(int fd, struct map_session_data *sd) +static void clif_parse_cashShopOpen1(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopOpen1(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; if (map->list[sd->bl.m].flag.nocashshop) { - clif->messagecolor_self(fd, COLOR_RED, msg_fd(fd,1489)); //Cash Shop is disabled in this map + clif->messagecolor_self(fd, COLOR_RED, msg_fd(fd, 1489)); //Cash Shop is disabled in this map + return; + } + + clif->cashShopOpen(fd, sd, 0); +} + +static void clif_parse_cashShopLimitedReq(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopLimitedReq(int fd, struct map_session_data *sd) +{ +} + +static void clif_parse_cashShopOpen2(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopOpen2(int fd, struct map_session_data *sd) +{ + if (sd->state.trading != 0 || pc_isdead(sd) || pc_isvending(sd)) + return; + + if (map->list[sd->bl.m].flag.nocashshop != 0) { + clif->messagecolor_self(fd, COLOR_RED, msg_fd(fd, 1489)); //Cash Shop is disabled in this map return; } - WFIFOHEAD(fd, 10); - WFIFOW(fd, 0) = 0x845; - WFIFOL(fd, 2) = sd->cashPoints; //[Ryuuzaki] - switched positions to reflect proper values - WFIFOL(fd, 6) = sd->kafraPoints; - WFIFOSET(fd, 10); +#if PACKETVER >= 20191224 + const struct PACKET_CZ_SE_CASHSHOP_OPEN2 *p = RFIFOP(fd, 0); + clif->cashShopOpen(fd, sd, p->tab); +#endif } -static void clif_parse_CashShopClose(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -static void clif_parse_CashShopClose(int fd, struct map_session_data *sd) +static void clif_cashShopOpen(int fd, struct map_session_data *sd, int tab) +{ +#if PACKETVER_MAIN_NUM >= 20101123 || PACKETVER_RE_NUM >= 20120328 || defined(PACKETVER_ZERO) + WFIFOHEAD(fd, sizeof(struct PACKET_ZC_SE_CASHSHOP_OPEN)); + struct PACKET_ZC_SE_CASHSHOP_OPEN *p = WFIFOP(fd, 0); + p->packetType = HEADER_ZC_SE_CASHSHOP_OPEN; + p->cashPoints = sd->cashPoints; //[Ryuuzaki] - switched positions to reflect proper values + p->kafraPoints = sd->kafraPoints; +#if PACKETVER_MAIN_NUM >= 20200129 || PACKETVER_RE_NUM >= 20200205 || PACKETVER_ZERO_NUM >= 20191224 + p->tab = tab; +#endif + WFIFOSET(fd, sizeof(struct PACKET_ZC_SE_CASHSHOP_OPEN)); +#endif +} + +static void clif_parse_cashShopClose(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopClose(int fd, struct map_session_data *sd) { /* TODO apply some state tracking */ } -static void clif_parse_CashShopSchedule(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -static void clif_parse_CashShopSchedule(int fd, struct map_session_data *sd) +static void clif_parse_cashShopSchedule(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopSchedule(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + clif->cashShopSchedule(fd, sd); } @@ -18973,9 +20281,12 @@ void clif_cashShopSchedule(int fd, struct map_session_data *sd) } /// R 0848 <len>.W <limit>.W <kafra pay>.L (<item id>.L <amount>.L <tab>.W)* -static void clif_parse_CashShopBuy(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); -static void clif_parse_CashShopBuy(int fd, struct map_session_data *sd) +static void clif_parse_cashShopBuy(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cashShopBuy(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int len = RFIFOW(fd, 2); unsigned short limit, i, j; unsigned int kafra_pay; @@ -19030,7 +20341,7 @@ static void clif_parse_CashShopBuy(int fd, struct map_session_data *sd) ret = pc->paycash(sd, clif->cs.data[tab][j]->price * qty, kafra_pay);// [Ryuuzaki] //changed Kafrapoints calculation. [Normynator] if (ret < 0) { - ShowError("clif_parse_CashShopBuy: The return from pc->paycash was negative which is not allowed.\n"); + ShowError("clif_parse_cashShopBuy: The return from pc->paycash was negative which is not allowed.\n"); break; //This should never happen. } kafra_pay = ret; @@ -19070,24 +20381,35 @@ 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_parse_CashShopReqTab(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +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) +static void clif_parse_cashShopReqTab(int fd, struct map_session_data *sd) { // [4144] packet exists only in 2011 and was dropped after #if PACKETVER >= 20110222 && PACKETVER < 20120000 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + short tab = RFIFOW(fd, 2); int j; @@ -19156,15 +20478,18 @@ static void clif_status_change2(struct block_list *bl, int tid, enum send_target static void clif_partytickack(struct map_session_data *sd, bool flag) { +#if PACKETVER_MAIN_NUM >= 20070911 || defined(PACKETVER_RE) || PACKETVER_AD_NUM >= 20070911 || PACKETVER_SAK_NUM >= 20070904 || defined(PACKETVER_ZERO) nullpo_retv(sd); - WFIFOHEAD(sd->fd, packet_len(0x2c9)); - WFIFOW(sd->fd, 0) = 0x2c9; - WFIFOB(sd->fd, 2) = flag; - WFIFOSET(sd->fd, packet_len(0x2c9)); + WFIFOHEAD(sd->fd, sizeof(struct PACKET_ZC_PARTY_CONFIG)); + struct PACKET_ZC_PARTY_CONFIG *p = WFIFOP(sd->fd, 0); + p->packetType = HEADER_ZC_PARTY_CONFIG; + p->denyPartyInvites = flag; + WFIFOSET(sd->fd, sizeof(struct PACKET_ZC_PARTY_CONFIG)); +#endif } -static void clif_ShowScript(struct block_list *bl, const char *message) +static void clif_ShowScript(struct block_list *bl, const char *message, enum send_target target) { #if PACKETVER >= 20110111 char buf[256]; @@ -19185,7 +20510,7 @@ static void clif_ShowScript(struct block_list *bl, const char *message) WBUFW(buf,2) = len+8; WBUFL(buf,4) = bl->id; safestrncpy(WBUFP(buf,8),message,len); - clif->send(buf,WBUFW(buf,2),bl,AREA); + clif->send(buf, WBUFW(buf,2), bl, target); #endif } @@ -19242,6 +20567,9 @@ static void clif_bgqueue_notice_delete(struct map_session_data *sd, enum BATTLEG static void clif_parse_bgqueue_register(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_bgqueue_register(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_bgqueue_register *p = RP2PTR(fd); struct bg_arena *arena = NULL; if( !bg->queue_on ) return; /* temp, until feature is complete */ @@ -19282,6 +20610,9 @@ static void clif_bgqueue_update_info(struct map_session_data *sd, unsigned char static void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_bgqueue_checkstate *p = RP2PTR(fd); if (sd->bg_queue.arena && sd->bg_queue.type) { @@ -19294,6 +20625,9 @@ static void clif_parse_bgqueue_checkstate(int fd, struct map_session_data *sd) static void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_bgqueue_revoke_req *p = RP2PTR(fd); if( sd->bg_queue.arena ) @@ -19305,6 +20639,9 @@ static void clif_parse_bgqueue_revoke_req(int fd, struct map_session_data *sd) static void clif_parse_bgqueue_battlebegin_ack(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_bgqueue_battlebegin_ack(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_bgqueue_battlebegin_ack *p = RP2PTR(fd); struct bg_arena *arena; @@ -19369,7 +20706,11 @@ static void clif_package_item_announce(struct map_session_data *sd, int nameid, nullpo_retv(sd); p.PacketType = package_item_announceType; - p.PacketLength = 11 + NAME_LENGTH; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + p.PacketLength = 7 + 4 + 4 + NAME_LENGTH; +#else + p.PacketLength = 7 + 2 + 2 + NAME_LENGTH; +#endif p.type = 0x0; p.ItemID = nameid; p.len = NAME_LENGTH; @@ -19377,7 +20718,7 @@ static void clif_package_item_announce(struct map_session_data *sd, int nameid, p.unknown = 0x2; // some strange byte, IDA shows.. BYTE3(BoxItemIDLength) = 2; p.BoxItemID = containerid; - clif->send(&p,sizeof(p), &sd->bl, ALL_CLIENT); + clif->send(&p, p.PacketLength, &sd->bl, ALL_CLIENT); } /* Made Possible Thanks to Yommy! */ @@ -19395,12 +20736,13 @@ static void clif_item_drop_announce(struct map_session_data *sd, int nameid, cha if (monsterName == NULL) { // message: MSG_BROADCASTING_SPECIAL_ITEM_OBTAIN2 p.type = 0x2; + p.PacketLength -= NAME_LENGTH; } else { // message: MSG_BROADCASTING_SPECIAL_ITEM_OBTAIN p.type = 0x1; safestrncpy(p.monsterName, monsterName, sizeof(p.monsterName)); } - clif->send(&p, sizeof(p), &sd->bl, ALL_CLIENT); + clif->send(&p, p.PacketLength, &sd->bl, ALL_CLIENT); } /* [Ind/Hercules] special thanks to Yommy~! */ @@ -19460,6 +20802,9 @@ static void clif_cart_additem_ack(struct map_session_data *sd, int flag) static void clif_parse_BankDeposit(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_BankDeposit(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_banking_deposit_req *p = RP2PTR(fd); int money; @@ -19476,6 +20821,9 @@ static void clif_parse_BankDeposit(int fd, struct map_session_data *sd) static void clif_parse_BankWithdraw(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_BankWithdraw(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_banking_withdraw_req *p = RP2PTR(fd); int money; @@ -19493,6 +20841,9 @@ static void clif_parse_BankCheck(int fd, struct map_session_data *sd) __attribut static void clif_parse_BankCheck(int fd, struct map_session_data *sd) { #if PACKETVER >= 20130320 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct packet_banking_check p; p.PacketType = banking_checkType; @@ -19559,7 +20910,7 @@ static void clif_show_modifiers(struct map_session_data *sd) if( sd->status.mod_exp != 100 || sd->status.mod_drop != 100 || sd->status.mod_death != 100 ) { char output[128]; - snprintf(output,128,"Base EXP : %d%% | Base Drop: %d%% | Base Death Penalty: %d%%", + snprintf(output,128, msg_sd(sd, 896), // Base EXP : %d%% | Base Drop: %d%% | Base Death Penalty: %d%% sd->status.mod_exp,sd->status.mod_drop,sd->status.mod_death); clif->broadcast2(&sd->bl, output, (int)strlen(output) + 1, 0xffbc90, 0x190, 12, 0, 0, SELF); } @@ -19624,7 +20975,7 @@ static int clif_delay_damage_sub(int tid, int64 tick, int id, intptr_t data) * * @return clif->calc_walkdelay used in further processing **/ -static int clif_delay_damage(int64 tick, struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, unsigned char type) +static int clif_delay_damage(int64 tick, struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, enum battle_dmg_type type) { struct cdelayed_damage *dd; struct status_change *sc; @@ -19695,31 +21046,31 @@ static void clif_parse_NPCShopClosed(int fd, struct map_session_data *sd) /* NPC Market (by Ind after an extensive debugging of the packet, only possible thanks to Yommy <3) */ static void clif_npc_market_open(struct map_session_data *sd, struct npc_data *nd) { -#if PACKETVER >= 20131223 - struct npc_item_list *shop; - unsigned short shop_size, i, c; - +#if PACKETVER_MAIN_NUM >= 20131120 || PACKETVER_RE_NUM >= 20131106 || defined(PACKETVER_ZERO) nullpo_retv(sd); nullpo_retv(nd); - shop = nd->u.scr.shop->item; - shop_size = nd->u.scr.shop->items; - npcmarket_open.PacketType = npcmarketopenType; + struct npc_item_list *shop = nd->u.scr.shop->item; + const int shop_size = nd->u.scr.shop->items; + + int c = 0; + int maxCount = (sizeof(packet_buf) - sizeof(struct PACKET_ZC_NPC_MARKET_OPEN)) / sizeof(struct PACKET_ZC_NPC_MARKET_OPEN_sub); + struct PACKET_ZC_NPC_MARKET_OPEN *packet = (struct PACKET_ZC_NPC_MARKET_OPEN*)&packet_buf[0]; + packet->packetType = HEADER_ZC_NPC_MARKET_OPEN; - for(i = 0, c = 0; i < shop_size; i++) { + for (int i = 0; i < shop_size && c < maxCount; i++) { struct item_data *id = NULL; if (shop[i].nameid && (id = itemdb->exists(shop[i].nameid)) != NULL) { - npcmarket_open.list[c].nameid = shop[i].nameid; - npcmarket_open.list[c].price = shop[i].value; - npcmarket_open.list[c].qty = shop[i].qty; - npcmarket_open.list[c].type = itemtype(id->type); - npcmarket_open.list[c].view = ( id->view_id > 0 ) ? id->view_id : id->nameid; + packet->list[c].nameid = shop[i].nameid; + packet->list[c].price = shop[i].value; + packet->list[c].qty = shop[i].qty; + packet->list[c].type = itemtype(id->type); + packet->list[c].weight = id->weight * 10; c++; } } - npcmarket_open.PacketLength = 4 + ( sizeof(npcmarket_open.list[0]) * c ); - - clif->send(&npcmarket_open,npcmarket_open.PacketLength,&sd->bl,SELF); + packet->packetLength = sizeof(struct PACKET_ZC_NPC_MARKET_OPEN) + sizeof(struct PACKET_ZC_NPC_MARKET_OPEN_sub) * c; + clif->send(packet, packet->packetLength, &sd->bl, SELF); #endif } @@ -19730,40 +21081,49 @@ static void clif_parse_NPCMarketClosed(int fd, struct map_session_data *sd) sd->npc_shopid = 0; } -static void clif_npc_market_purchase_ack(struct map_session_data *sd, const struct itemlist *item_list, unsigned char response) +static void clif_parse_NPCBarterClosed(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_NPCBarterClosed(int fd, struct map_session_data *sd) { -#if PACKETVER >= 20131223 - unsigned short c = 0; + sd->npc_shopid = 0; +} +static void clif_npc_market_purchase_ack(struct map_session_data *sd, const struct itemlist *item_list, enum market_buy_result response) +{ +#if PACKETVER_MAIN_NUM >= 20131120 || PACKETVER_RE_NUM >= 20130911 || defined(PACKETVER_ZERO) nullpo_retv(sd); nullpo_retv(item_list); - npcmarket_result.PacketType = npcmarketresultackType; - npcmarket_result.result = response == 0 ? 1 : 0;/* find other values */ + struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *p = (struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT *)packet_buf; + p->PacketType = HEADER_ZC_NPC_MARKET_PURCHASE_RESULT; + p->result = response; - if (npcmarket_result.result) { + unsigned short c = 0; + if (response == MARKET_BUY_RESULT_SUCCESS) { + int vectorLen = VECTOR_LENGTH(*item_list); + int maxCount = (sizeof(packet_buf) - sizeof(struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT)) / sizeof(struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub); + if (maxCount > vectorLen) + maxCount = vectorLen; struct npc_data *nd = map->id2nd(sd->npc_shopid); struct npc_item_list *shop = nd->u.scr.shop->item; unsigned short shop_size = nd->u.scr.shop->items; - int i; - for (i = 0; i < VECTOR_LENGTH(*item_list); i++) { + for (int i = 0; i < maxCount; i++) { const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); int j; - npcmarket_result.list[i].ITID = entry->id; - npcmarket_result.list[i].qty = entry->amount; + p->list[i].ITID = entry->id; + p->list[i].qty = entry->amount; - ARR_FIND( 0, shop_size, j, entry->id == shop[j].nameid); + ARR_FIND(0, shop_size, j, entry->id == shop[j].nameid); - npcmarket_result.list[i].price = (j != shop_size) ? shop[j].value : 0; + p->list[i].price = (j != shop_size) ? shop[j].value : 0; c++; } } - npcmarket_result.PacketLength = 5 + ( sizeof(npcmarket_result.list[0]) * c );; + p->PacketLength = sizeof(struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT) + (sizeof(struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub) * c); - clif->send(&npcmarket_result,npcmarket_result.PacketLength,&sd->bl,SELF); + clif->send(p, p->PacketLength, &sd->bl, SELF); #endif } @@ -19771,17 +21131,19 @@ static void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) __ static void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) { #if PACKETVER >= 20131223 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct packet_npc_market_purchase *p = RP2PTR(fd); - int response = 0, i; int count = (p->PacketLength - 4) / sizeof p->list[0]; struct itemlist item_list; - Assert_retv(count >= 0 && count <= MAX_INVENTORY); + Assert_retv(count >= 0 && count <= sd->status.inventorySize); VECTOR_INIT(item_list); VECTOR_ENSURE(item_list, count, 1); - for (i = 0; i < count; i++) { + for (int i = 0; i < count; i++) { struct itemlist_entry entry = { 0 }; entry.id = p->list[i].ITID; @@ -19790,7 +21152,7 @@ static void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) VECTOR_PUSH(item_list, entry); } - response = npc->market_buylist(sd, &item_list); + enum market_buy_result response = npc->market_buylist(sd, &item_list); clif->npc_market_purchase_ack(sd, &item_list, response); VECTOR_CLEAR(item_list); @@ -19815,6 +21177,9 @@ static void clif_parse_RouletteOpen(int fd, struct map_session_data *sd) __attri static void clif_parse_RouletteOpen(int fd, struct map_session_data *sd) { #if PACKETVER >= 20140612 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct packet_roulette_open_ack p; if( !battle_config.feature_roulette ) { @@ -19823,7 +21188,7 @@ static void clif_parse_RouletteOpen(int fd, struct map_session_data *sd) } p.PacketType = 0xa1a; - p.Result = 0; + p.Result = OPEN_ROULETTE_SUCCESS; p.Serial = 0; p.Step = sd->roulette.stage - 1; p.Idx = (char)sd->roulette.prizeIdx; @@ -19840,6 +21205,9 @@ static void clif_parse_RouletteInfo(int fd, struct map_session_data *sd) __attri static void clif_parse_RouletteInfo(int fd, struct map_session_data *sd) { #if PACKETVER >= 20140612 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + struct packet_roulette_info_ack p; unsigned short i, j, count = 0; @@ -19885,6 +21253,9 @@ static void clif_parse_RouletteClose(int fd, struct map_session_data *sd) static void clif_parse_RouletteGenerate(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); static void clif_parse_RouletteGenerate(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + unsigned char result = GENERATE_ROULETTE_SUCCESS; short stage = sd->roulette.stage; @@ -19944,6 +21315,8 @@ static void clif_parse_RouletteRecvItem(int fd, struct map_session_data *sd) __a static void clif_parse_RouletteRecvItem(int fd, struct map_session_data *sd) { #if PACKETVER >= 20140612 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; struct packet_roulette_itemrecv_ack p; if( !battle_config.feature_roulette ) { @@ -19993,7 +21366,8 @@ static bool clif_parse_roulette_db(void) { struct config_t roulette_conf; struct config_setting_t *roulette = NULL, *levels = NULL; - const char *config_filename = "db/roulette_db.conf"; // FIXME hardcoded name + char config_filename[256]; + libconfig->format_db_path("roulette_db.conf", config_filename, sizeof(config_filename)); int i, j, item_count_t = 0; for( i = 0; i < MAX_ROULETTE_LEVEL; i++ ) { @@ -20082,7 +21456,7 @@ static bool clif_parse_roulette_db(void) /** * **/ -static void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, int bonusItemID) +static void clif_roulette_generate_ack(struct map_session_data *sd, enum GENERATE_ROULETTE_ACK result, short stage, short prizeIdx, int bonusItemID) { #if PACKETVER >= 20140612 struct packet_roulette_generate_ack p; @@ -20101,20 +21475,34 @@ static void clif_roulette_generate_ack(struct map_session_data *sd, unsigned cha #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 < MAX_INVENTORY; 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) @@ -20125,17 +21513,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++; @@ -20143,12 +21532,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 } @@ -20164,35 +21555,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 >= MAX_INVENTORY || 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); + if (length >= sd->status.inventorySize || length < 2) { + 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 >= MAX_INVENTORY) + 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; @@ -20210,43 +21608,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,MAX_INVENTORY,i,item_data.unique_id == sd->status.inventory[i].unique_id); + 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 } @@ -20358,21 +21742,21 @@ static const char *clif_get_bl_name(const struct block_list *bl) */ static void clif_clan_basicinfo(struct map_session_data *sd) { -#if PACKETVER >= 20120716 +#if PACKETVER_MAIN_NUM >= 20130626 || PACKETVER_RE_NUM >= 20130605 || defined(PACKETVER_ZERO) int len, i, fd; struct clan *c, *ally, *antagonist; struct PACKET_ZC_CLANINFO *packet = NULL; - nullpo_retv(sd); nullpo_retv(c = sd->clan); len = sizeof(struct PACKET_ZC_CLANINFO); fd = sd->fd; - WFIFOHEAD(fd, len); + const int maxEntries = 100; // max entries with clan names + WFIFOHEAD(fd, len + maxEntries * 24); packet = WFIFOP(fd, 0); - packet->PacketType = clanBasicInfo; + packet->PacketType = HEADER_ZC_CLANINFO; packet->ClanID = c->clan_id; safestrncpy(packet->ClanName, c->name, NAME_LENGTH); @@ -20383,24 +21767,27 @@ static void clif_clan_basicinfo(struct map_session_data *sd) packet->AllyCount = VECTOR_LENGTH(c->allies); packet->AntagonistCount = VECTOR_LENGTH(c->antagonists); + int cnt = 0; // All allies and antagonists are assumed as valid entries // since it only gets inside the vector after the validation // on clan->config_read - for (i = 0; i < VECTOR_LENGTH(c->allies); i++) { + for (i = 0; i < VECTOR_LENGTH(c->allies) && cnt < maxEntries; i++) { struct clan_relationship *al = &VECTOR_INDEX(c->allies, i); if ((ally = clan->search(al->clan_id)) != NULL) { safestrncpy(WFIFOP(fd, len), ally->name, NAME_LENGTH); len += NAME_LENGTH; + cnt ++; } } - for (i = 0; i < VECTOR_LENGTH(c->antagonists); i++) { + for (i = 0; i < VECTOR_LENGTH(c->antagonists) && cnt < maxEntries; i++) { struct clan_relationship *an = &VECTOR_INDEX(c->antagonists, i); if ((antagonist = clan->search(an->clan_id)) != NULL) { safestrncpy(WFIFOP(fd, len), antagonist->name, NAME_LENGTH); len += NAME_LENGTH; + cnt ++; } } @@ -20668,6 +22055,9 @@ static void clif_achievement_send_update(int fd, struct map_session_data *sd, co static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int ach_id = RFIFOL(fd, 2); const struct achievement_data *ad = NULL; struct achievement *ach = NULL; @@ -20710,6 +22100,9 @@ static void clif_achievement_reward_ack(int fd, struct map_session_data *sd, con static void clif_parse_change_title(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_change_title(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + int title_id = RFIFOL(fd, 2); if (title_id == sd->status.title_id) { // Same Title @@ -20744,8 +22137,8 @@ static void clif_change_title_ack(int fd, struct map_session_data *sd, int title WFIFOSET(fd, packet_len(0xa2f)); // Update names - clif->charnameack(fd, &sd->bl); - clif->charnameack(0, &sd->bl); + clif->blname_ack(fd, &sd->bl); + clif->blname_ack(0, &sd->bl); #endif } // End of Achievement System @@ -20757,6 +22150,9 @@ static void clif_change_title_ack(int fd, struct map_session_data *sd, int title static void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_OPEN_WRITE_MAIL *rPacket = RFIFOP(fd, 0); int8 result = (rodex->isenabled() == true && sd->npc_id == 0) ? 1 : 0; @@ -20782,6 +22178,9 @@ static void clif_rodex_open_write_mail(int fd, const char *receiver_name, int8 r static void clif_parse_rodex_add_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_add_item(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_ADD_ITEM_TO_MAIL *rPacket = RFIFOP(fd, 0); int16 idx = rPacket->index - 2; @@ -20795,7 +22194,7 @@ static void clif_rodex_add_item_result(struct map_session_data *sd, int16 idx, i int fd, j; nullpo_retv(sd); - if (idx < 0 || idx >= MAX_INVENTORY) + if (idx < 0 || idx >= sd->status.inventorySize) return; fd = sd->fd; @@ -20835,6 +22234,9 @@ static void clif_rodex_add_item_result(struct map_session_data *sd, int16 idx, i static void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_REMOVE_ITEM_MAIL *rPacket = RFIFOP(fd, 0); int16 idx = rPacket->index - 2; @@ -20848,7 +22250,7 @@ static void clif_rodex_remove_item_result(struct map_session_data *sd, int16 idx int fd; nullpo_retv(sd); - Assert_retv(idx >= 0 && idx < MAX_INVENTORY); + Assert_retv(idx >= 0 && idx < sd->status.inventorySize); fd = sd->fd; @@ -20868,7 +22270,7 @@ static void clif_parse_rodex_checkname(int fd, struct map_session_data *sd) { const struct PACKET_CZ_CHECKNAME *rPacket = RFIFOP(fd, 0); int char_id = 0, base_level = 0; - short class = 0; + int class = 0; char name[NAME_LENGTH]; safestrncpy(name, rPacket->Name, NAME_LENGTH); @@ -20876,7 +22278,7 @@ static void clif_parse_rodex_checkname(int fd, struct map_session_data *sd) rodex->check_player(sd, name, &base_level, &char_id, &class); } -static void clif_rodex_checkname_result(struct map_session_data *sd, int char_id, short class_, int base_level, const char *name) +static void clif_rodex_checkname_result(struct map_session_data *sd, int char_id, int class_, int base_level, const char *name) { #if PACKETVER >= 20140521 struct PACKET_ZC_CHECKNAME *sPacket; @@ -20907,6 +22309,9 @@ static void clif_rodex_checkname_result(struct map_session_data *sd, int char_id static void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_SEND_MAIL *rPacket = RFIFOP(fd, 0); int8 result; @@ -21137,6 +22542,9 @@ static void clif_rodex_send_refresh(int fd, struct map_session_data *sd, int8 op static void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_NEXT_MAIL_LIST *packet = RFIFOP(fd, 0); rodex->next_page(sd, packet->opentype, packet->Lower_MailID); @@ -21145,6 +22553,9 @@ static void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) static void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_READ_MAIL *rPacket = RFIFOP(fd, 0); rodex->read_mail(sd, rPacket->MailID); @@ -21214,6 +22625,9 @@ static void clif_rodex_read_mail(struct map_session_data *sd, int8 opentype, str static void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_DELETE_MAIL *rPacket = RFIFOP(fd, 0); rodex->delete_mail(sd, rPacket->MailID); @@ -21241,6 +22655,9 @@ static void clif_rodex_delete_mail(struct map_session_data *sd, int8 opentype, i static void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_ZENY_FROM_MAIL *rPacket = RFIFOP(fd, 0); rodex->get_zeny(sd, rPacket->opentype, rPacket->MailID); @@ -21269,6 +22686,9 @@ static void clif_rodex_request_zeny(struct map_session_data *sd, int8 opentype, static void clif_parse_rodex_request_items(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_request_items(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_ITEM_FROM_MAIL *rPacket = RFIFOP(fd, 0); rodex->get_items(sd, rPacket->opentype, rPacket->MailID); @@ -21308,6 +22728,9 @@ static void clif_rodex_icon(int fd, bool show) static void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_REFRESH_MAIL_LIST *packet = RFIFOP(fd, 0); #if PACKETVER >= 20170419 rodex->refresh(sd, RODEX_OPENTYPE_UNSET, packet->Upper_MailID); @@ -21319,6 +22742,9 @@ static void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *s static void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_OPEN_MAIL *packet = RFIFOP(fd, 0); #if PACKETVER >= 20170419 rodex->open(sd, RODEX_OPENTYPE_UNSET, packet->char_Upper_MailID); @@ -21331,6 +22757,9 @@ static void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) static void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + rodex->clean(sd, 0); intif->rodex_checkhasnew(sd); } @@ -21338,6 +22767,9 @@ static void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) static void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + rodex->clean(sd, 1); } @@ -21371,53 +22803,47 @@ static void clif_skill_scale(struct block_list *bl, int src_id, int x, int y, ui /// 0A3B <Length>.W <AID>.L <Status>.B { <HatEffectId>.W } static void clif_hat_effect(struct block_list *bl, struct block_list *tbl, enum send_target target) { -#if PACKETVER >= 20150422 - unsigned char *buf; - int len, i; - struct map_session_data *sd; - +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) nullpo_retv(bl); - - sd = BL_CAST(BL_PC, bl); - + struct map_session_data *sd = BL_CAST(BL_PC, bl); nullpo_retv(sd); - len = 9 + VECTOR_LENGTH(sd->hatEffectId) * 2; - - buf = (unsigned char*)aMalloc(len); + const int len = sizeof(struct PACKET_ZC_HAT_EFFECT) + VECTOR_LENGTH(sd->hatEffectId) * 2; + struct PACKET_ZC_HAT_EFFECT *p = aMalloc(len); - WBUFW(buf, 0) = 0xa3b; - WBUFW(buf, 2) = len; - WBUFL(buf, 4) = bl->id; - WBUFB(buf, 8) = 1; + p->packetType = HEADER_ZC_HAT_EFFECT; + p->packetLength = len; + p->aid = bl->id; + p->status = 1; - for( i = 0; i < VECTOR_LENGTH(sd->hatEffectId); i++ ){ - WBUFW(buf, 9 + i * 2) = VECTOR_INDEX(sd->hatEffectId, i); + for (int i = 0; i < VECTOR_LENGTH(sd->hatEffectId); i++) { + p->effects[i] = VECTOR_INDEX(sd->hatEffectId, i); } if (tbl != NULL) { - clif->send(buf, len, tbl, target); + clif->send(p, len, tbl, target); } else { - clif->send(buf, len, bl, target); + clif->send(p, len, bl, target); } - - aFree(buf); + aFree(p); #endif } static void clif_hat_effect_single(struct block_list *bl, uint16 effectId, bool enable){ -#if PACKETVER >= 20150422 - unsigned char buf[13]; - +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) nullpo_retv(bl); - WBUFW(buf,0) = 0xa3b; - WBUFW(buf,2) = 13; - WBUFL(buf,4) = bl->id; - WBUFB(buf,8) = enable; - WBUFL(buf,9) = effectId; + const int len = sizeof(struct PACKET_ZC_HAT_EFFECT) + 2; + struct PACKET_ZC_HAT_EFFECT *p = aMalloc(len); - clif_send(buf, 13, bl, AREA); + p->packetType = HEADER_ZC_HAT_EFFECT; + p->packetLength = len; + p->aid = bl->id; + p->status = enable; + p->effects[0] = effectId; + + clif->send(p, len, bl, AREA); + aFree(p); #endif } @@ -21425,7 +22851,8 @@ static bool clif_parse_attendance_db(void) { struct config_t attendance_conf; struct config_setting_t *attendance = NULL, *it = NULL; - const char *config_filename = "db/attendance_db.conf"; // FIXME hardcoded name + char config_filename[256]; + libconfig->format_db_path("attendance_db.conf", config_filename, sizeof(config_filename)); int i = 0; if (!libconfig->load_file(&attendance_conf, config_filename)) @@ -21502,8 +22929,10 @@ static time_t clif_attendance_getendtime(void) static void clif_parse_open_ui_request(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_open_ui_request(int fd, struct map_session_data *sd) { - const struct PACKET_CZ_OPEN_UI *p = RP2PTR(fd); + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_OPEN_UI *p = RP2PTR(fd); clif->open_ui(sd, p->UIType); } @@ -21526,6 +22955,18 @@ static void clif_open_ui(struct map_session_data *sd, enum cz_ui_types uiType) p.data = 0; #endif break; + case CZ_MACRO_REGISTER_UI: + p.UIType = ZC_CAPTCHA_UI; +#if PACKETVER >= 20171122 + p.data = 0; +#endif + break; + case CZ_MACRO_DETECTOR_UI: + p.UIType = ZC_MACRO_UI; +#if PACKETVER >= 20171122 + p.data = 0; +#endif + break; case CZ_ATTENDANCE_UI: { if (clif->attendance_getendtime() < time(NULL)) { @@ -21562,6 +23003,8 @@ static void clif_parse_attendance_reward_request(int fd, struct map_session_data static void clif_parse_attendance_reward_request(int fd, struct map_session_data *sd) { #if PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404 || PACKETVER_ZERO_NUM >= 20180411 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; struct rodex_message msg = { 0 }; struct attendance_entry *entry; @@ -21612,6 +23055,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) @@ -21631,7 +23085,10 @@ static void clif_ui_action(struct map_session_data *sd, int32 UIType, int32 data static void clif_parse_private_airship_request(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_private_airship_request(int fd, struct map_session_data *sd) { -#if PACKETVER_RE_NUM >= 20180321 || PACKETVER_MAIN_NUM >= 20180620 +#if PACKETVER_RE_NUM >= 20180321 || PACKETVER_MAIN_NUM >= 20180620 || defined(PACKETVER_ZERO) + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + char evname[EVENT_NAME_LENGTH]; struct event_data *ev = NULL; const struct PACKET_CZ_PRIVATE_AIRSHIP_REQUEST *p = RP2PTR(fd); @@ -21651,7 +23108,7 @@ static void clif_parse_private_airship_request(int fd, struct map_session_data * static void clif_private_airship_response(struct map_session_data *sd, uint32 flag) { -#if PACKETVER_RE_NUM >= 20180321 || PACKETVER_MAIN_NUM >= 20180620 +#if PACKETVER_RE_NUM >= 20180321 || PACKETVER_MAIN_NUM >= 20180620 || defined(PACKETVER_ZERO) struct PACKET_ZC_PRIVATE_AIRSHIP_RESPONSE p; nullpo_retv(sd); @@ -21670,162 +23127,26 @@ static void clif_private_airship_response(struct map_session_data *sd, uint32 fl #endif } -static void clif_stylist_vector_init(void) -{ - int i; - for (i = 0; i < MAX_STYLIST_TYPE; i++) { - VECTOR_INIT(stylist_data[i]); - } -} - -static void clif_stylist_vector_clear(void) -{ - int i; - for (i = 0; i < MAX_STYLIST_TYPE; i++) { - VECTOR_CLEAR(stylist_data[i]); - } -} - -static bool clif_stylist_read_db_libconfig(void) -{ - struct config_t stylist_conf; - struct config_setting_t *stylist = NULL, *it = NULL; - const char *config_filename = "db/stylist_db.conf"; // FIXME hardcoded name - int i = 0; - - if (!libconfig->load_file(&stylist_conf, config_filename)) - return false; - - if ((stylist = libconfig->setting_get_member(stylist_conf.root, "stylist_db")) == NULL) { - ShowError("can't read %s\n", config_filename); - return false; - } - - clif->stylist_vector_clear(); - - while ((it = libconfig->setting_get_elem(stylist, i++))) { - clif->stylist_read_db_libconfig_sub(it, i - 1, config_filename); - } - - libconfig->destroy(&stylist_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, config_filename); - return true; -} - -static bool clif_stylist_read_db_libconfig_sub(struct config_setting_t *it, int idx, const char *source) -{ - struct stylist_data_entry entry = { 0 }; - int i32 = 0, type = 0; - int64 i64 = 0; - - nullpo_ret(it); - nullpo_ret(source); - - if (!itemdb->lookup_const(it, "Type", &type) || type >= MAX_STYLIST_TYPE || type < 0) { - ShowWarning("clif_stylist_read_db_libconfig_sub: Invalid or missing Type (%d) in \"%s\", entry #%d, skipping.\n", type, source, idx); - return false; - } - if (!itemdb->lookup_const(it, "Id", &i32) || i32 < 0) { - ShowWarning("clif_stylist_read_db_libconfig_sub: Invalid or missing Id (%d) in \"%s\", entry #%d, skipping.\n", i32, source, idx); - return false; - } - entry.id = i32; - - if (libconfig->setting_lookup_int64(it, "Zeny", &i64)) { - if (i64 > MAX_ZENY) { - ShowWarning("clif_stylist_read_db_libconfig_sub: zeny is too big in \"%s\", entry #%d, capping to MAX_ZENY.\n", source, idx); - entry.zeny = MAX_ZENY; - } else { - entry.zeny = (int)i64; - } - } - - if (itemdb->lookup_const(it, "ItemID", &i32)) - entry.itemid = i32; - - if (itemdb->lookup_const(it, "BoxItemID", &i32)) - entry.boxid = i32; - - if (libconfig->setting_lookup_bool(it, "AllowDoram", &i32)) - entry.allow_doram = (i32 == 0) ? false : true; - - VECTOR_ENSURE(stylist_data[type], 1, 1); - VECTOR_PUSH(stylist_data[type], entry); - return true; -} - -static bool clif_style_change_validate_requirements(struct map_session_data *sd, int type, int16 idx) -{ - struct item it; - struct stylist_data_entry *entry; - - nullpo_retr(false, sd); - Assert_retr(false, type >= 0 && type < MAX_STYLIST_TYPE); - Assert_retr(false, idx >= 0 && idx < VECTOR_LENGTH(stylist_data[type])); - - entry = &VECTOR_INDEX(stylist_data[type], idx); - - if (sd->status.class == JOB_SUMMONER && (entry->allow_doram == false)) - return false; - - if (entry->id >= 0) { - if (entry->zeny != 0) { - if (sd->status.zeny < entry->zeny) - return false; - - sd->status.zeny -= entry->zeny; - clif->updatestatus(sd, SP_ZENY); - } else if (entry->itemid != 0) { - it.nameid = entry->itemid; - it.amount = 1; - return script->buildin_delitem_search(sd, &it, false); - } else if (entry->boxid != 0) { - it.nameid = entry->boxid; - it.amount = 1; - return script->buildin_delitem_search(sd, &it, false); - } - return true; - } - return false; -} -static void clif_stylist_send_rodexitem(struct map_session_data *sd, int itemid) -{ - struct rodex_message msg = { 0 }; - - nullpo_retv(sd); - - msg.receiver_id = sd->status.char_id; - msg.items[0].item.nameid = itemid; - msg.items[0].item.amount = 1; - msg.items[0].item.identify = 1; - msg.type = MAIL_TYPE_NPC | MAIL_TYPE_ITEM; - - safestrncpy(msg.sender_name, msg_txt(366), NAME_LENGTH); - safestrncpy(msg.title, msg_txt(367), RODEX_TITLE_LENGTH); - safestrncpy(msg.body, msg_txt(368), MAIL_BODY_LENGTH); - msg.send_date = (int)time(NULL); - msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; - - intif->rodex_sendmail(&msg); -} - static void clif_parse_cz_req_style_change(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_cz_req_style_change(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_STYLE_CHANGE *p = RP2PTR(fd); if (p->HeadStyle > 0) - clif->cz_req_style_change_sub(sd, LOOK_HAIR, p->HeadStyle, false); + stylist->request_style_change(sd, LOOK_HAIR, p->HeadStyle, false); if (p->HeadPalette > 0) - clif->cz_req_style_change_sub(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); + stylist->request_style_change(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); if (p->BodyPalette > 0) - clif->cz_req_style_change_sub(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); + stylist->request_style_change(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); if (p->TopAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_TOP, p->TopAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_TOP, p->TopAccessory, true); if (p->MidAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_MID, p->MidAccessory, true); if (p->BottomAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); return; } @@ -21833,46 +23154,36 @@ static void clif_parse_cz_req_style_change(int fd, struct map_session_data *sd) static void clif_parse_cz_req_style_change2(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_cz_req_style_change2(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_REQ_STYLE_CHANGE2 *p = RP2PTR(fd); if (p->HeadStyle > 0) - clif->cz_req_style_change_sub(sd, LOOK_HAIR, p->HeadStyle, false); + stylist->request_style_change(sd, LOOK_HAIR, p->HeadStyle, false); if (p->HeadPalette > 0) - clif->cz_req_style_change_sub(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); + stylist->request_style_change(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); if (p->BodyPalette > 0) - clif->cz_req_style_change_sub(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); + stylist->request_style_change(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); if (p->TopAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_TOP, p->TopAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_TOP, p->TopAccessory, true); if (p->MidAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_MID, p->MidAccessory, true); if (p->BottomAccessory > 0) - clif->cz_req_style_change_sub(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); + stylist->request_style_change(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); if (p->BodyStyle > 0) { if (pc->has_second_costume(sd)) { - clif->cz_req_style_change_sub(sd, LOOK_BODY2, p->BodyStyle, false); + stylist->request_style_change(sd, LOOK_BODY2, p->BodyStyle, false); } } clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); return; } -static void clif_cz_req_style_change_sub(struct map_session_data *sd, int type, int16 idx, bool isitem) +static void clif_parse_cz_style_close(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_cz_style_close(int fd, struct map_session_data *sd) { - struct stylist_data_entry *entry; - - nullpo_retv(sd); - Assert_retv(idx > 0); - Assert_retv(type >= 0 && type < MAX_STYLIST_TYPE); - - if ((idx - 1) < VECTOR_LENGTH(stylist_data[type])) { - entry = &VECTOR_INDEX(stylist_data[type], idx - 1); - if (clif->style_change_validate_requirements(sd, type, idx - 1)) { - if (isitem == false) - pc->changelook(sd, type, entry->id); - else - clif->stylist_send_rodexitem(sd, entry->id); - } - } + // do nothing } static void clif_style_change_response(struct map_session_data *sd, enum stylist_shop flag) @@ -21908,6 +23219,9 @@ static void clif_parse_changeDress(int fd, struct map_session_data *sd) __attrib /// 0ae8 <packet len>.W static void clif_parse_changeDress(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const char commandname[] = "changedress"; char command[sizeof commandname + 3] = ""; // '@' command + ' ' + NUL @@ -21932,6 +23246,9 @@ static void clif_party_dead_notification(struct map_session_data *sd) static void clif_parse_memorial_dungeon_command(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); static void clif_parse_memorial_dungeon_command(int fd, struct map_session_data *sd) { + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + const struct PACKET_CZ_MEMORIALDUNGEON_COMMAND *p = RP2PTR(fd); switch (p->command) { @@ -21945,7 +23262,7 @@ static void clif_camera_showWindow(struct map_session_data *sd) #if PACKETVER >= 20160525 nullpo_retv(sd); struct PACKET_ZC_CAMERA_INFO p; - p.packetType = 0xa78; + p.packetType = HEADER_ZC_CAMERA_INFO; p.action = 1; p.range = 0; p.rotation = 0; @@ -21959,7 +23276,7 @@ static void clif_camera_change(struct map_session_data *sd, float range, float r #if PACKETVER >= 20160525 nullpo_retv(sd); struct PACKET_ZC_CAMERA_INFO p; - p.packetType = 0xa78; + p.packetType = HEADER_ZC_CAMERA_INFO; p.action = 0; p.range = range; p.rotation = rotation; @@ -21968,12 +23285,30 @@ static void clif_camera_change(struct map_session_data *sd, float range, float r #endif } +static void clif_parse_cameraInfo(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_cameraInfo(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20160525 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + const struct PACKET_CZ_CAMERA_INFO *const p = RFIFOP(fd, 0); + char command[100]; + if (p->action == 1) { + sprintf(command, "%ccamerainfo", atcommand->at_symbol); + } else { + sprintf(command, "%ccamerainfo %15f %15f %15f", atcommand->at_symbol, p->range, p->rotation, p->latitude); + } + atcommand->exec(fd, sd, command, true); +#endif +} + // show item preview in already opened preview window static void clif_item_preview(struct map_session_data *sd, int n) { #if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) nullpo_retv(sd); - Assert_retv(n >= 0 && n < MAX_INVENTORY); + Assert_retv(n >= 0 && n < sd->status.inventorySize); struct PACKET_ZC_ITEM_PREVIEW p; p.packetType = HEADER_ZC_ITEM_PREVIEW; @@ -21988,6 +23323,598 @@ static void clif_item_preview(struct map_session_data *sd, int n) #endif } +// insert cardId into equipped item in pos equipment slot into slot cardSlot. +static bool clif_enchant_equipment(struct map_session_data *sd, enum equip_pos pos, int cardSlot, int cardId) +{ +#if PACKETVER_MAIN_NUM >= 20160831 || PACKETVER_RE_NUM >= 20151118 || defined(PACKETVER_ZERO) + nullpo_ret(sd); + Assert_ret(cardSlot >= 0 && cardSlot < MAX_SLOTS); + struct PACKET_ZC_ENCHANT_EQUIPMENT p; + p.packetType = HEADER_ZC_ENCHANT_EQUIPMENT; + p.wearState = pos; + p.cardSlot = cardSlot; + p.itemId = cardId; + clif->send(&p, sizeof(p), &sd->bl, SELF); + return true; +#else + return false; +#endif +} + +static void clif_npc_barter_open(struct map_session_data *sd, struct npc_data *nd) +{ +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 + nullpo_retv(sd); + nullpo_retv(nd); + struct npc_item_list *shop = nd->u.scr.shop->item; + const int shop_size = nd->u.scr.shop->items; + + int c = 0; + int maxCount = (sizeof(packet_buf) - sizeof(struct PACKET_ZC_NPC_BARTER_OPEN)) / sizeof(struct PACKET_ZC_NPC_BARTER_OPEN_sub); + struct PACKET_ZC_NPC_BARTER_OPEN *packet = (struct PACKET_ZC_NPC_BARTER_OPEN*)&packet_buf[0]; + packet->packetType = HEADER_ZC_NPC_BARTER_OPEN; + + for (int i = 0; i < shop_size && c < maxCount; i++) { + if (shop[i].nameid) { + struct item_data *id = itemdb->exists(shop[i].nameid); + if (id == NULL) + continue; + + packet->list[c].nameid = shop[i].nameid; + packet->list[c].type = itemtype(id->type); + packet->list[c].amount = shop[i].qty; + packet->list[c].currencyNameid = shop[i].value; + packet->list[c].currencyAmount = shop[i].value2; + packet->list[c].weight = id->weight * 10; + packet->list[c].index = i; + c++; + } + } + + packet->packetLength = sizeof(struct PACKET_ZC_NPC_BARTER_OPEN) + sizeof(struct PACKET_ZC_NPC_BARTER_OPEN_sub) * c; + clif->send(packet, packet->packetLength, &sd->bl, SELF); +#endif +} + +static void clif_parse_NPCBarterPurchase(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_NPCBarterPurchase(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + const struct PACKET_CZ_NPC_BARTER_PURCHASE *p = RP2PTR(fd); + int count = (p->packetLength - sizeof(struct PACKET_CZ_NPC_BARTER_PURCHASE)) / sizeof p->list[0]; + struct barteritemlist item_list; + + Assert_retv(count >= 0 && count <= sd->status.inventorySize); + + VECTOR_INIT(item_list); + VECTOR_ENSURE(item_list, count, 1); + + for (int i = 0; i < count; i++) { + struct barter_itemlist_entry entry = { 0 }; + entry.addId = p->list[i].itemId; + entry.addAmount = p->list[i].amount; + entry.removeIndex = p->list[i].invIndex - 2; + entry.shopIndex = p->list[i].shopIndex; + + VECTOR_PUSH(item_list, entry); + } + + int response = npc->barter_buylist(sd, &item_list); + clif->npc_buy_result(sd, response); + + VECTOR_CLEAR(item_list); +#endif +} + +static void clif_parse_npc_expanded_barter_closed(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_npc_expanded_barter_closed(int fd, struct map_session_data *sd) +{ +} + +#if PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 || PACKETVER_ZERO_NUM >= 20191127 +#define NEXT_EXPANDED_BARTER_ITEM(var, count) \ + var = (struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub *)((char*)item + \ + sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub) - \ + sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2) + \ + count * sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2)) +#endif + +static void clif_npc_expanded_barter_open(struct map_session_data *sd, struct npc_data *nd) +{ +#if PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 || PACKETVER_ZERO_NUM >= 20191127 + nullpo_retv(sd); + nullpo_retv(nd); + struct npc_item_list *shop = nd->u.scr.shop->item; + const int shop_size = nd->u.scr.shop->items; + + int items_count = 0; + int currencies_count = 0; + struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN *packet = (struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN*)&packet_buf[0]; + STATIC_ASSERT(sizeof(packet_buf) > sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN), "packet_buf size too small"); + int buf_left = sizeof(packet_buf) - sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN); + packet->packetType = HEADER_ZC_NPC_EXPANDED_BARTER_OPEN; + struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub *item = &packet->items[0]; + + // Workaround for fix Visual Studio bug (error C2233) + // Here should be sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub) + const int ptr_size = sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub) - + sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2); + for (int i = 0; i < shop_size && buf_left >= ptr_size; i++) { + if (shop[i].nameid) { + struct item_data *id = itemdb->exists(shop[i].nameid); + if (id == NULL) + continue; + + item->nameid = shop[i].nameid; + item->type = itemtype(id->type); + item->amount = shop[i].qty; + item->weight = id->weight * 10; + item->index = i; + item->zeny = shop[i].value; + item->currency_count = 0; + buf_left -= ptr_size; + items_count ++; + int count = shop[i].value2; + if (buf_left < sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2) * count) { + NEXT_EXPANDED_BARTER_ITEM(item, 0); + break; + } + for (int j = 0; j < count; j ++) { + struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2 *packet_currency = &item->currencies[j]; + struct npc_barter_currency *currency = &shop[i].currency[j]; + struct item_data *id2 = itemdb->exists(currency->nameid); + if (id2 == NULL) + continue; + packet_currency->nameid = currency->nameid; + if (currency->refine == -1) + packet_currency->refine_level = 0; + else + packet_currency->refine_level = currency->refine; + packet_currency->amount = currency->amount; + packet_currency->type = itemtype(id2->type); + currencies_count ++; + item->currency_count ++; + } + NEXT_EXPANDED_BARTER_ITEM(item, item->currency_count); + } + } + + packet->items_count = items_count; + packet->packetLength = sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN) + + ptr_size * items_count + + sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2) * currencies_count; + clif->send(packet, packet->packetLength, &sd->bl, SELF); +#endif +} + +#undef NEXT_EXPANDED_BARTER_ITEM + +static void clif_parse_npc_expanded_barter_purchase(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_npc_expanded_barter_purchase(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190904 || PACKETVER_RE_NUM >= 20190904 || PACKETVER_ZERO_NUM >= 20190828 + if (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + const struct PACKET_CZ_NPC_EXPANDED_BARTER_PURCHASE *const p = RP2PTR(fd); + int count = (p->packetLength - sizeof(struct PACKET_CZ_NPC_EXPANDED_BARTER_PURCHASE)) / sizeof p->list[0]; + struct barteritemlist item_list; + + Assert_retv(count >= 0 && count <= sd->status.inventorySize); + + VECTOR_INIT(item_list); + VECTOR_ENSURE(item_list, count, 1); + + for (int i = 0; i < count; i++) { + struct barter_itemlist_entry entry = { 0 }; + entry.addId = p->list[i].itemId; + entry.addAmount = p->list[i].amount; + entry.removeIndex = -1; + entry.shopIndex = p->list[i].shopIndex; + VECTOR_PUSH(item_list, entry); + } + + int response = npc->expanded_barter_buylist(sd, &item_list); + clif->npc_buy_result(sd, response); + + VECTOR_CLEAR(item_list); +#endif +} + +static void clif_parse_clientVersion(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_clientVersion(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20090406 || PACKETVER_RE_NUM >= 20090408 || PACKETVER_SAK_NUM >= 20090408 || defined(PACKETVER_ZERO) + // TODO: show or store client version +#endif +} + +static void clif_parse_ping(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_ping(int fd, struct map_session_data *sd) +{ + // do nothing, any packet update client tick +} + +static void clif_ping(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220 + nullpo_retv(sd); + struct PACKET_ZC_PING p; + p.packetType = HEADER_ZC_PING; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + +static int clif_pingTimer(int tid, int64 tick, int id, intptr_t data) +{ + map->foreachpc(clif->pingTimerSub, time(NULL)); + return 0; +} + +static int clif_pingTimerSub(struct map_session_data *sd, va_list ap) +{ + nullpo_ret(sd); + const int fd = sd->fd; + + if (!sockt->session_is_active(fd)) + { + return 0; + } + + time_t tick = va_arg(ap, time_t); + + if (sockt->session[fd]->wdata_tick + battle_config.ping_time < tick) + { + clif->ping(sd); + } + return 0; +} + +static void clif_parse_ResetCooldown(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_ResetCooldown(int fd, struct map_session_data *sd) +{ + char cmd[15]; + sprintf(cmd,"%ccddebug reset", atcommand->at_symbol); + 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 (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + 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 (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + 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 (sd->state.trading || pc_isdead(sd) || pc_isvending(sd)) + return; + + 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 +} + +static void clif_parse_GuildCastleTeleportRequest(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_GuildCastleTeleportRequest(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190522 || PACKETVER_ZERO_NUM >= 20190515 + const struct PACKET_CZ_CASTLE_TELEPORT_REQUEST *p = RFIFO2PTR(fd); + struct guild *g = sd->guild; + + if (g == NULL) + return; + + struct guild_castle *gc = guild->castle_search(p->castle_id); + if (gc == NULL) + return; + if (gc->enable_client_warp == false) + return; + if (gc->guild_id != g->guild_id) + return; + + if (map->list[sd->bl.m].flag.gvg_castle == 1) + return; + + int zeny = gc->client_warp.zeny; + if (gc->siege_type == SIEGE_TYPE_FE && map->agit_flag == 1) { + zeny = gc->client_warp.zeny_siege; + } else if (gc->siege_type == SIEGE_TYPE_SE && map->agit2_flag == 1) { + zeny = gc->client_warp.zeny_siege; + } else if (gc->siege_type == SIEGE_TYPE_TE) { + clif->guild_castleteleport_res(sd, SIEGE_TP_INVALID_MODE); + return; + } + + if (sd->status.zeny < zeny) { + clif->guild_castleteleport_res(sd, SIEGE_TP_NOT_ENOUGH_ZENY); + return; + } + sd->status.zeny -= zeny; + clif->updatestatus(sd, SP_ZENY); + pc->setpos(sd, gc->mapindex, gc->client_warp.x, gc->client_warp.y, CLR_OUTSIGHT); +#endif +} + +static void clif_guild_castleteleport_res(struct map_session_data *sd, enum siege_teleport_result result) +{ +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 + + nullpo_retv(sd); + + struct PACKET_ZC_CASTLE_TELEPORT_RESPONSE p = { 0 }; + p.packetType = HEADER_ZC_CASTLE_TELEPORT_RESPONSE; + p.result = (int16)result; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} +static void clif_parse_GuildCastleInfoRequest(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_GuildCastleInfoRequest(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190522 || PACKETVER_ZERO_NUM >= 20190515 + const struct PACKET_CZ_CASTLE_INFO_REQUEST *p = RFIFO2PTR(fd); + struct guild *g = sd->guild; + + if (g == NULL) + return; + + struct guild_castle *gc = guild->castle_search(p->castle_id); + if (gc == NULL) + return; + if (gc->guild_id != g->guild_id) + return; + clif->guild_castleinfo(sd, gc); +#endif +} + +static bool clif_lapineDdukDdak_open(struct map_session_data *sd, int item_id) +{ +#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) + nullpo_retr(false, sd); + nullpo_retr(false, itemdb->exists(item_id)); + struct PACKET_ZC_LAPINEDDUKDDAK_OPEN p; + + p.packetType = HEADER_ZC_LAPINEDDUKDDAK_OPEN; + p.itemId = item_id; + clif->send(&p, sizeof(p), &sd->bl, SELF); + + sd->state.lapine_ui = 1; + return true; +#else + return false; +#endif // PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) +} + +static bool clif_lapineDdukDdak_result(struct map_session_data *sd, enum lapineddukddak_result result) +{ +#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) + nullpo_retr(false, sd); + struct PACKET_ZC_LAPINEDDUKDDAK_RESULT p; + + p.packetType = HEADER_ZC_LAPINEDDUKDDAK_RESULT; + p.result = result; + clif->send(&p, sizeof(p), &sd->bl, SELF); + return true; +#else + return false; +#endif // PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) +} + +static void clif_parse_lapineDdukDdak_ack(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_lapineDdukDdak_ack(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20160302 + if (sd->state.lapine_ui == 0) + return; + + const struct PACKET_CZ_LAPINEDDUKDDAK_ACK *p = RP2PTR(fd); + struct item_data *it = itemdb->exists(p->itemId); + + if (it == NULL || it->lapineddukddak == NULL) + return; + if (pc_cant_act_except_lapine(sd)) + return; + if (pc->search_inventory(sd, it->nameid) == INDEX_NOT_FOUND) + return; + + if (((p->packetLength - sizeof(struct PACKET_CZ_LAPINEDDUKDDAK_ACK)) / sizeof(struct PACKET_CZ_LAPINEDDUKDDAK_ACK_sub)) != it->lapineddukddak->NeedCount) + return; + + for (int i = 0; i < it->lapineddukddak->NeedCount; ++i) { + int16 idx = p->items[i].index - 2; + Assert_retv(idx >= 0 && idx < sd->status.inventorySize); + + struct item itr = sd->status.inventory[idx]; + int j = 0; + for (j = 0; j < VECTOR_LENGTH(it->lapineddukddak->SourceItems); ++j) { + if (itr.nameid == VECTOR_INDEX(it->lapineddukddak->SourceItems, j).id) { + // Validate that the amount sent in the packet is matching the database + if (p->items[i].count != VECTOR_INDEX(it->lapineddukddak->SourceItems, j).amount) { + clif->lapineDdukDdak_result(sd, LAPINEDDKUKDDAK_INSUFFICIENT_AMOUNT); + return; + } + + // Validate that the player have enough of the item + if (itr.amount < VECTOR_INDEX(it->lapineddukddak->SourceItems, j).amount) { + clif->lapineDdukDdak_result(sd, LAPINEDDKUKDDAK_INSUFFICIENT_AMOUNT); + return; + } + + // Validate refine rate requirement + if ((itemdb_type(itr.nameid) == IT_ARMOR || itemdb_type(itr.nameid) == IT_WEAPON) + && (itr.refine < it->lapineddukddak->NeedRefineMin || itr.refine > it->lapineddukddak->NeedRefineMax)) + return; + + // All requirements are met, move to the next one + break; + } + } + // The item is not in sources list + if (j == VECTOR_LENGTH(it->lapineddukddak->SourceItems)) { + clif->lapineDdukDdak_result(sd, LAPINEDDKUKDDAK_INVALID_ITEM); + return; + } + } + + for (int i = 0; i < it->lapineddukddak->NeedCount; ++i) + pc->delitem(sd, p->items[i].index - 2, p->items[i].count, 0, DELITEM_NORMAL, LOG_TYPE_SCRIPT); + if (it->lapineddukddak->script != NULL) + script->run_item_lapineddukddak_script(sd, it, npc->fake_nd->bl.id); + clif->lapineDdukDdak_result(sd, LAPINEDDKUKDDAK_SUCCESS); + return; +#endif // PACKETVER >= 20160302 +} + +static void clif_parse_lapineDdukDdak_close(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_lapineDdukDdak_close(int fd, struct map_session_data *sd) +{ +#if PACKETVER >= 20160504 + sd->state.lapine_ui = 0; +#endif // PACKETVER >= 20160504 +} + +static bool clif_lapineUpgrade_open(struct map_session_data *sd, int item_id) +{ +#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) + nullpo_retr(false, sd); + nullpo_retr(false, itemdb->exists(item_id)); + struct PACKET_ZC_LAPINEUPGRADE_OPEN p; + + p.packetType = HEADER_ZC_LAPINEUPGRADE_OPEN; + p.itemId = item_id; + clif->send(&p, sizeof(p), &sd->bl, SELF); + + return true; +#else + return false; +#endif // PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) +} + +static void clif_parse_lapineUpgrade_close(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_lapineUpgrade_close(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) +#endif // PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) +} + +static void clif_parse_lapineUpgrade_makeItem(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); +static void clif_parse_lapineUpgrade_makeItem(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) + ShowError("Lapin upgrade not implimented yet"); + clif->lapineUpgrade_result(sd, LAPINE_UPGRADE_FAILED); +#endif // PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) +} + +static bool clif_lapineUpgrade_result(struct map_session_data *sd, enum lapineUpgrade_result result) +{ +#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) + nullpo_retr(false, sd); + struct PACKET_ZC_LAPINEUPGRADE_RESULT p; + + p.packetType = HEADER_ZC_LAPINEUPGRADE_RESULT; + p.result = result; + clif->send(&p, sizeof(p), &sd->bl, SELF); + + return true; +#else + return false; +#endif // PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -22229,9 +24156,9 @@ static void packetdb_loaddb(void) static void clif_bc_ready(void) { if( battle_config.display_status_timers ) - clif->status_change = clif_status_change; + clif->status_change_sub = clif_status_change_sub; else - clif->status_change = clif_status_change_notick; + clif->status_change_sub = clif_status_change_notick; switch( battle_config.packet_obfuscation ) { case 0: @@ -22269,6 +24196,12 @@ static int do_init_clif(bool minimal) clif->delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR); clif->delayed_damage_ers = ers_new(sizeof(struct cdelayed_damage),"clif.c::delayed_damage_ers",ERS_OPT_CLEAR); +#if PACKETVER_MAIN_NUM >= 20190403 || PACKETVER_RE_NUM >= 20190320 + timer->add_func_list(clif->pingTimer, "clif_pingTimer"); + if (battle_config.ping_timer_interval != 0) + timer->add_interval(timer->gettick() + battle_config.ping_timer_interval * 1000, clif->pingTimer, 0, 0, battle_config.ping_timer_interval * 1000); +#endif + return 0; } @@ -22425,9 +24358,12 @@ void clif_defaults(void) clif->fame_alchemist = clif_fame_alchemist; clif->fame_taekwon = clif_fame_taekwon; clif->ranklist = clif_ranklist; + clif->ranklist_sub = clif_ranklist_sub; + clif->ranklist_sub2 = clif_ranklist_sub2; clif->pRanklist = clif_parse_ranklist; clif->update_rankingpoint = clif_update_rankingpoint; clif->hotkeys = clif_hotkeys_send; + clif->hotkeysAll = clif_hotkeysAll_send; clif->insight = clif_insight; clif->outsight = clif_outsight; clif->skillcastcancel = clif_skillcastcancel; @@ -22440,6 +24376,7 @@ void clif_defaults(void) clif->autospell = clif_autospell; clif->combo_delay = clif_combo_delay; clif->status_change = clif_status_change; + clif->status_change_sub = clif_status_change_sub; clif->insert_card = clif_insert_card; clif->inventoryList = clif_inventoryList; clif->inventoryItems = clif_inventoryItems; @@ -22447,6 +24384,12 @@ void clif_defaults(void) clif->equipItems = clif_equipItems; clif->cartList = clif_cartList; clif->cartItems = clif_cartItems; + clif->inventoryExpansionInfo = clif_inventoryExpansionInfo; + clif->inventoryExpandAck = clif_inventoryExpandAck; + clif->inventoryExpandResult = clif_inventoryExpandResult; + clif->pInventoryExpansion = clif_parse_inventoryExpansion; + clif->pInventoryExpansionConfirmed = clif_parse_inventoryExpansionConfirmed; + clif->pInventoryExpansionRejected = clif_parse_inventoryExpansionRejected; clif->favorite_item = clif_favorite_item; clif->clearcart = clif_clearcart; clif->item_identify_list = clif_item_identify_list; @@ -22460,7 +24403,21 @@ void clif_defaults(void) clif->mvp_exp = clif_mvp_exp; clif->mvp_noitem = clif_mvp_noitem; clif->changed_dir = clif_changed_dir; - clif->charnameack = clif_charnameack; + 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->skillname_ack = clif_skillname_ack; + clif->itemname_ack = clif_itemname_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; @@ -22514,6 +24471,7 @@ void clif_defaults(void) /* visual effects client-side */ clif->misceffect = clif_misceffect; clif->changeoption = clif_changeoption; + clif->changeoption_target = clif_changeoption_target; clif->changeoption2 = clif_changeoption2; clif->emotion = clif_emotion; clif->talkiebox = clif_talkiebox; @@ -22539,6 +24497,7 @@ void clif_defaults(void) clif->specialeffect = clif_specialeffect; clif->specialeffect_single = clif_specialeffect_single; clif->specialeffect_value = clif_specialeffect_value; + clif->specialeffect_value_single = clif_specialeffect_value_single; clif->removeSpecialEffect = clif_removeSpecialEffect; clif->removeSpecialEffect_single = clif_removeSpecialEffect_single; clif->millenniumshield = clif_millenniumshield; @@ -22558,6 +24517,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; @@ -22568,6 +24528,7 @@ void clif_defaults(void) clif->broadcast2 = clif_broadcast2; clif->messagecolor_self = clif_messagecolor_self; clif->messagecolor = clif_messagecolor; + clif->serviceMessageColor = clif_serviceMessageColor; clif->disp_overhead = clif_disp_overhead; clif->notify_playerchat = clif_notify_playerchat; clif->msgtable_skill = clif_msgtable_skill; @@ -22619,6 +24580,7 @@ void clif_defaults(void) clif->skillinfo = clif_skillinfo; clif->addskill = clif_addskill; clif->deleteskill = clif_deleteskill; + clif->playerSkillToPacket = clif_playerSkillToPacket; /* party-specific */ clif->party_created = clif_party_created; clif->party_member_info = clif_party_member_info; @@ -22642,6 +24604,8 @@ void clif_defaults(void) clif->guild_masterormember = clif_guild_masterormember; clif->guild_basicinfo = clif_guild_basicinfo; clif->guild_allianceinfo = clif_guild_allianceinfo; + clif->guild_castlelist = clif_guild_castlelist; + clif->guild_castleinfo = clif_guild_castleinfo; clif->guild_memberlist = clif_guild_memberlist; clif->guild_skillinfo = clif_guild_skillinfo; clif->guild_send_onlineinfo = clif_guild_send_onlineinfo; @@ -22819,11 +24783,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; /* */ @@ -22845,7 +24811,8 @@ void clif_defaults(void) clif->pWantToConnection = clif_parse_WantToConnection; clif->pLoadEndAck = clif_parse_LoadEndAck; clif->pTickSend = clif_parse_TickSend; - clif->pHotkey = clif_parse_Hotkey; + clif->pHotkey1 = clif_parse_Hotkey1; + clif->pHotkey2 = clif_parse_Hotkey2; clif->pProgressbar = clif_parse_progressbar; clif->pWalkToXY = clif_parse_WalkToXY; clif->pQuitGame = clif_parse_QuitGame; @@ -22892,6 +24859,9 @@ void clif_defaults(void) clif->pUseSkillToId = clif_parse_UseSkillToId; clif->pUseSkillToId_homun = clif_parse_UseSkillToId_homun; clif->pUseSkillToId_mercenary = clif_parse_UseSkillToId_mercenary; + clif->pStartUseSkillToId = clif_parse_startUseSkillToId; + clif->pStopUseSkillToId = clif_parse_stopUseSkillToId; + clif->useSkillToIdReal = clif_useSkillToIdReal; clif->pUseSkillToPos = clif_parse_UseSkillToPos; clif->pUseSkillToPosSub = clif_parse_UseSkillToPosSub; clif->pUseSkillToPos_homun = clif_parse_UseSkillToPos_homun; @@ -23051,11 +25021,15 @@ void clif_defaults(void) clif->pBGQueueRevokeReq = clif_parse_bgqueue_revoke_req; clif->pBGQueueBattleBeginAck = clif_parse_bgqueue_battlebegin_ack; /* RagExe Cash Shop [Ind/Hercules] */ - clif->pCashShopOpen = clif_parse_CashShopOpen; - clif->pCashShopClose = clif_parse_CashShopClose; - clif->pCashShopReqTab = clif_parse_CashShopReqTab; - clif->pCashShopSchedule = clif_parse_CashShopSchedule; - clif->pCashShopBuy = clif_parse_CashShopBuy; + clif->pCashShopOpen1 = clif_parse_cashShopOpen1; + clif->pCashShopOpen2 = clif_parse_cashShopOpen2; + clif->pCashShopLimitedReq = clif_parse_cashShopLimitedReq; + clif->pCashShopClose = clif_parse_cashShopClose; + clif->pCashShopReqTab = clif_parse_cashShopReqTab; + clif->pCashShopSchedule = clif_parse_cashShopSchedule; + clif->pCashShopBuy = clif_parse_cashShopBuy; + clif->cashShopBuyAck = clif_cashShopBuyAck; + clif->cashShopOpen = clif_cashShopOpen; /* */ clif->pPartyTick = clif_parse_PartyTick; clif->pGuildInvite2 = clif_parse_GuildInvite2; @@ -23084,7 +25058,8 @@ void clif_defaults(void) clif->pNPCMarketPurchase = clif_parse_NPCMarketPurchase; /* */ clif->add_item_options = clif_add_item_options; - clif->pHotkeyRowShift = clif_parse_HotkeyRowShift; + clif->pHotkeyRowShift1 = clif_parse_HotkeyRowShift1; + clif->pHotkeyRowShift2 = clif_parse_HotkeyRowShift2; clif->dressroom_open = clif_dressroom_open; clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; /* Achievements [Smokexyz/Hercules] */ @@ -23140,24 +25115,54 @@ void clif_defaults(void) clif->pPrivateAirshipRequest = clif_parse_private_airship_request; clif->PrivateAirshipResponse = clif_private_airship_response; - clif->stylist_vector_init = clif_stylist_vector_init; - clif->stylist_vector_clear = clif_stylist_vector_clear; - clif->stylist_read_db_libconfig = clif_stylist_read_db_libconfig; - clif->stylist_read_db_libconfig_sub = clif_stylist_read_db_libconfig_sub; - clif->style_change_validate_requirements = clif_style_change_validate_requirements; - clif->stylist_send_rodexitem = clif_stylist_send_rodexitem; clif->pReqStyleChange = clif_parse_cz_req_style_change; clif->pReqStyleChange2 = clif_parse_cz_req_style_change2; - clif->cz_req_style_change_sub = clif_cz_req_style_change_sub; + clif->pStyleClose = clif_parse_cz_style_close; clif->style_change_response = clif_style_change_response; clif->camera_showWindow = clif_camera_showWindow; clif->camera_change = clif_camera_change; + clif->pCameraInfo = clif_parse_cameraInfo; clif->item_preview = clif_item_preview; + clif->enchant_equipment = clif_enchant_equipment; // -- Pet Evolution clif->pPetEvolution = clif_parse_pet_evolution; clif->petEvolutionResult = clif_pet_evolution_result; clif->pMemorialDungeonCommand = clif_parse_memorial_dungeon_command; + clif->pReqRemainTime = clif_parse_reqRemainTime; + + clif->npc_barter_open = clif_npc_barter_open; + clif->pNPCBarterClosed = clif_parse_NPCBarterClosed; + clif->pNPCBarterPurchase = clif_parse_NPCBarterPurchase; + clif->npc_expanded_barter_open = clif_npc_expanded_barter_open; + clif->pNPCExpandedBarterPurchase = clif_parse_npc_expanded_barter_purchase; + clif->pNPCExpandedBarterClosed = clif_parse_npc_expanded_barter_closed; + clif->pClientVersion = clif_parse_clientVersion; + clif->pPing = clif_parse_ping; + clif->ping = clif_ping; + 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; + clif->pGuildCastleTeleportRequest = clif_parse_GuildCastleTeleportRequest; + clif->pGuildCastleInfoRequest = clif_parse_GuildCastleInfoRequest; + clif->guild_castleteleport_res = clif_guild_castleteleport_res; + clif->lapineDdukDdak_open = clif_lapineDdukDdak_open; + clif->lapineDdukDdak_result = clif_lapineDdukDdak_result; + clif->plapineDdukDdak_ack = clif_parse_lapineDdukDdak_ack; + clif->plapineDdukDdak_close = clif_parse_lapineDdukDdak_close; + clif->lapineUpgrade_open = clif_lapineUpgrade_open; + clif->lapineUpgrade_result = clif_lapineUpgrade_result; + clif->pLapineUpgrade_close = clif_parse_lapineUpgrade_close; + clif->pLapineUpgrade_makeItem = clif_parse_lapineUpgrade_makeItem; + clif->pReqGearOff = clif_parse_reqGearOff; } diff --git a/src/map/clif.h b/src/map/clif.h index 593dc15c1..fdaaf85e3 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,7 +55,11 @@ struct skill_unit; struct unit_data; struct view_data; struct achievement_data; // map/achievement.h +struct s_refine_requirement; +struct PACKET_ZC_ACK_RANKING_sub; +struct SKILLDATA; +enum battle_dmg_type; enum clif_messages; enum rodex_add_item; enum rodex_get_zeny; @@ -71,10 +75,6 @@ enum rodex_get_items; #define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/ #define RGB2BGR(c) (((c) & 0x0000FF) << 16 | ((c) & 0x00FF00) | ((c) & 0xFF0000) >> 16) -#ifndef MAX_STYLIST_TYPE -#define MAX_STYLIST_TYPE LOOK_MAX -#endif - #define COLOR_CYAN 0x00ffffU #define COLOR_RED 0xff0000U #define COLOR_GREEN 0x00ff00U @@ -82,6 +82,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 **/ @@ -237,13 +242,13 @@ typedef enum emotion_type { E_MAX } emotion_type; -typedef enum clr_type { +enum clr_type { CLR_OUTSIGHT = 0, CLR_DEAD, CLR_RESPAWN, CLR_TELEPORT, CLR_TRICKDEAD, -} clr_type; +}; enum map_property { // clif_map_property MAPPROPERTY_NOTHING = 0, @@ -409,23 +414,26 @@ enum CASH_SHOP_BUY_RESULT { CSBR_RUNE_OVERCOUNT = 0x9, CSBR_EACHITEM_OVERCOUNT = 0xa, CSBR_UNKNOWN = 0xb, + CSBR_BUSY = 0xc, }; enum BATTLEGROUNDS_QUEUE_ACK { - BGQA_SUCCESS = 1, - BGQA_FAIL_QUEUING_FINISHED, - BGQA_FAIL_BGNAME_INVALID, - BGQA_FAIL_TYPE_INVALID, - BGQA_FAIL_PPL_OVERAMOUNT, - BGQA_FAIL_LEVEL_INCORRECT, - BGQA_DUPLICATE_REQUEST, - BGQA_PLEASE_RELOGIN, - BGQA_NOT_PARTY_GUILD_LEADER, - BGQA_FAIL_CLASS_INVALID, + BGQA_SUCCESS = 1, + BGQA_FAIL_QUEUING_FINISHED = 2, + BGQA_FAIL_BGNAME_INVALID = 3, + BGQA_FAIL_TYPE_INVALID = 4, + BGQA_FAIL_PPL_OVERAMOUNT = 5, + BGQA_FAIL_LEVEL_INCORRECT = 6, + BGQA_DUPLICATE_REQUEST = 7, + BGQA_PLEASE_RELOGIN = 8, + BGQA_NOT_PARTY_GUILD_LEADER = 9, + BGQA_FAIL_CLASS_INVALID = 10, /* not official way to respond (gotta find packet?) */ - BGQA_FAIL_DESERTER, - BGQA_FAIL_COOLDOWN, - BGQA_FAIL_TEAM_COUNT, + BGQA_FAIL_DESERTER = 11, + BGQA_FAIL_COOLDOWN = 12, + BGQA_FAIL_TEAM_COUNT = 13, + // official continue + BGQA_FAIL_TEAM_IN_BG_ALREADY = 15 }; enum BATTLEGROUNDS_QUEUE_NOTICE_DELETED { @@ -493,13 +501,17 @@ enum RECV_ROULETTE_ITEM_ACK { }; enum GENERATE_ROULETTE_ACK { - GENERATE_ROULETTE_SUCCESS = 0x0, - GENERATE_ROULETTE_FAILED = 0x1, - GENERATE_ROULETTE_NO_ENOUGH_POINT = 0x2, - GENERATE_ROULETTE_LOSING = 0x3, + GENERATE_ROULETTE_SUCCESS = 0x0, + GENERATE_ROULETTE_FAILED = 0x1, + GENERATE_ROULETTE_NO_ENOUGH_POINT = 0x2, + GENERATE_ROULETTE_LOSING = 0x3, + GENERATE_ROULETTE_NO_ENOUGH_INVENTORY_SPACE = 0x4, +#if PACKETVER >= 20141001 + GENERATE_ROULETTE_CANT_PLAY = 0x5, +#endif }; -enum OPEN_ROULETTE_ACK{ +enum OPEN_ROULETTE_ACK { OPEN_ROULETTE_SUCCESS = 0x0, OPEN_ROULETTE_FAILED = 0x1, }; @@ -554,7 +566,7 @@ enum clif_unittype { **/ enum CZ_CONFIG { CZ_CONFIG_OPEN_EQUIPMENT_WINDOW = 0, - // Unknown = 1, + CZ_CONFIG_CALL = 1, CZ_CONFIG_PET_AUTOFEEDING = 2, CZ_CONFIG_HOMUNCULUS_AUTOFEEDING = 3, }; @@ -583,8 +595,8 @@ enum zc_ui_types { **/ enum cz_ui_types { CZ_STYLIST_UI = 1, - //unknown = 2, - //unknown = 3, + CZ_MACRO_REGISTER_UI = 2, + CZ_MACRO_DETECTOR_UI = 3, CZ_ATTENDANCE_UI = 5 }; @@ -621,6 +633,13 @@ enum inventory_type { INVTYPE_GUILD_STORAGE = 3, }; +/** Guild Teleport Results */ +enum siege_teleport_result { + SIEGE_TP_SUCCESS = 0x0, + SIEGE_TP_NOT_ENOUGH_ZENY = 0x1, + SIEGE_TP_INVALID_MODE = 0x2 +}; + /** * Structures **/ @@ -651,15 +670,14 @@ struct attendance_entry { int qty; }; -/* Stylist data [Asheraf/Hercules]*/ -struct stylist_data_entry { - int16 id; - int32 zeny; - int itemid; - int boxid; - bool allow_doram; +struct barter_itemlist_entry { + int addId; + int addAmount; + int removeIndex; + int shopIndex; }; -VECTOR_DECL(struct stylist_data_entry) stylist_data[MAX_STYLIST_TYPE]; + +VECTOR_STRUCT_DECL(barteritemlist, struct barter_itemlist_entry); /** * Stylist Shop Responds @@ -673,6 +691,63 @@ enum memorial_dungeon_command { COMMAND_MEMORIALDUNGEON_DESTROY_FORCE = 0x3, }; +enum expand_inventory { + EXPAND_INVENTORY_ASK_CONFIRMATION = 0, + EXPAND_INVENTORY_FAILED = 1, + EXPAND_INVENTORY_OTHER_WORK = 2, + EXPAND_INVENTORY_MISSING_ITEM = 3, + EXPAND_INVENTORY_MAX_SIZE = 4 +}; + +enum expand_inventory_result { + EXPAND_INVENTORY_RESULT_SUCCESS = 0, + EXPAND_INVENTORY_RESULT_FAILED = 1, + EXPAND_INVENTORY_RESULT_OTHER_WORK = 2, + EXPAND_INVENTORY_RESULT_MISSING_ITEM = 3, + EXPAND_INVENTORY_RESULT_MAX_SIZE = 4 +}; + +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814 +enum market_buy_result { + MARKET_BUY_RESULT_ERROR = 0xffff, // -1 + MARKET_BUY_RESULT_SUCCESS = 0, + MARKET_BUY_RESULT_NO_ZENY = 1, + MARKET_BUY_RESULT_OVER_WEIGHT = 2, + MARKET_BUY_RESULT_OUT_OF_SPACE = 3, + MARKET_BUY_RESULT_AMOUNT_TOO_BIG = 9 +}; +#else +enum market_buy_result { + MARKET_BUY_RESULT_ERROR = 0, + MARKET_BUY_RESULT_SUCCESS = 1, + MARKET_BUY_RESULT_NO_ZENY = 0, + MARKET_BUY_RESULT_OVER_WEIGHT = 0, + MARKET_BUY_RESULT_OUT_OF_SPACE = 0, + MARKET_BUY_RESULT_AMOUNT_TOO_BIG = 0 +}; +#endif + +enum lapineddukddak_result { + LAPINEDDKUKDDAK_SUCCESS = 0, + LAPINEDDKUKDDAK_INSUFFICIENT_AMOUNT = 5, + LAPINEDDKUKDDAK_INVALID_ITEM = 7, +}; + +enum lapineUpgrade_result { + LAPINE_UPGRADE_SUCCESS = 0, + LAPINE_UPGRADE_FAILED = 1 +}; + +enum removeGear_flag { + REMOVE_MOUNT_0 = 0, // unused + REMOVE_MOUNT_DRAGON = 1, + REMOVE_MOUNT_2 = 2, // unused + REMOVE_MOUNT_MADO = 3, + REMOVE_MOUNT_PECO = 4, + REMOVE_MOUNT_FALCON = 5, + REMOVE_MOUNT_CART = 6, +}; + /** * Clif.c Interface **/ @@ -752,9 +827,9 @@ struct clif_interface { void (*package_announce) (struct map_session_data *sd, int nameid, int containerid); void (*item_drop_announce) (struct map_session_data *sd, int nameid, char *monsterName); /* unit-related */ - void (*clearunit_single) (int id, clr_type type, int fd); - void (*clearunit_area) (struct block_list* bl, clr_type type); - void (*clearunit_delayed) (struct block_list* bl, clr_type type, int64 tick); + void (*clearunit_single) (int id, enum clr_type type, int fd); + void (*clearunit_area) (struct block_list* bl, enum clr_type type); + void (*clearunit_delayed) (struct block_list* bl, enum clr_type type, int64 tick); void (*walkok) (struct map_session_data *sd); void (*move) (struct unit_data *ud); void (*move2) (struct block_list *bl, struct view_data *vd, struct unit_data *ud); @@ -815,7 +890,7 @@ struct clif_interface { void (*scriptclear) (struct map_session_data *sd, int npcid); /* client-user-interface-related */ void (*viewpoint) (struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color); - int (*damage) (struct block_list* src, struct block_list* dst, int sdelay, int ddelay, int64 damage, short div, unsigned char type, int64 damage2); + int (*damage) (struct block_list* src, struct block_list* dst, int sdelay, int ddelay, int64 damage, short div, enum battle_dmg_type type, int64 damage2); void (*sitting) (struct block_list* bl); void (*standing) (struct block_list* bl); void (*arrow_create_list) (struct map_session_data *sd); @@ -825,9 +900,12 @@ struct clif_interface { void (*fame_alchemist) (struct map_session_data *sd, int points); void (*fame_taekwon) (struct map_session_data *sd, int points); void (*ranklist) (struct map_session_data *sd, enum fame_list_type type); + void (*ranklist_sub) (struct PACKET_ZC_ACK_RANKING_sub *ranks, enum fame_list_type type); + void (*ranklist_sub2) (uint32 *chars, uint32 *points, enum fame_list_type type); void (*update_rankingpoint) (struct map_session_data *sd, enum fame_list_type type, int points); void (*pRanklist) (int fd, struct map_session_data *sd); - void (*hotkeys) (struct map_session_data *sd); + void (*hotkeys) (struct map_session_data *sd, int tab); + void (*hotkeysAll) (struct map_session_data *sd); int (*insight) (struct block_list *bl,va_list ap); int (*outsight) (struct block_list *bl,va_list ap); void (*skillcastcancel) (struct block_list* bl); @@ -839,7 +917,8 @@ struct clif_interface { void (*cooking_list) (struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type); void (*autospell) (struct map_session_data *sd,uint16 skill_lv); void (*combo_delay) (struct block_list *bl,int wait); - void (*status_change) (struct block_list *bl,int type,int flag,int tick,int val1, int val2, int val3); + void (*status_change) (struct block_list *bl, int relevant_bl, int type, int flag, int total_tick, int val1, int val2, int val3); + void (*status_change_sub) (struct block_list *bl, int type, int relevant_bl, int flag, int tick, int total_tick, int val1, int val2, int val3); void (*insert_card) (struct map_session_data *sd,int idx_equip,int idx_card,int flag); void (*inventoryList) (struct map_session_data *sd); void (*inventoryItems) (struct map_session_data *sd, enum inventory_type type); @@ -847,6 +926,12 @@ struct clif_interface { void (*equipItems) (struct map_session_data *sd, enum inventory_type type); void (*cartList) (struct map_session_data *sd); void (*cartItems) (struct map_session_data *sd, enum inventory_type type); + void (*inventoryExpansionInfo) (struct map_session_data *sd); + void (*inventoryExpandAck) (struct map_session_data *sd, enum expand_inventory result, int itemId); + void (*inventoryExpandResult) (struct map_session_data *sd, enum expand_inventory_result result); + void (*pInventoryExpansion) (int fd, struct map_session_data *sd); + void (*pInventoryExpansionConfirmed) (int fd, struct map_session_data *sd); + void (*pInventoryExpansionRejected) (int fd, struct map_session_data *sd); void (*favorite_item) (struct map_session_data* sd, unsigned short index); void (*clearcart) (int fd); void (*item_identify_list) (struct map_session_data *sd); @@ -860,7 +945,21 @@ struct clif_interface { void (*mvp_exp) (struct map_session_data *sd, unsigned int exp); void (*mvp_noitem) (struct map_session_data* sd); void (*changed_dir) (struct block_list *bl, enum send_target target); - void (*charnameack) (int fd, struct block_list *bl); + 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 (*skillname_ack) (int fd, struct block_list *bl); + void (*itemname_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); @@ -914,13 +1013,14 @@ struct clif_interface { /* visual effects client-side */ void (*misceffect) (struct block_list* bl,int type); void (*changeoption) (struct block_list* bl); + void (*changeoption_target) (struct block_list *bl, struct block_list *target_bl, enum send_target target); void (*changeoption2) (struct block_list* bl); void (*emotion) (struct block_list *bl,int type); void (*talkiebox) (struct block_list* bl, const char* talkie); void (*wedding_effect) (struct block_list *bl); void (*divorced) (struct map_session_data* sd, const char* name); void (*callpartner) (struct map_session_data *sd); - int (*skill_damage) (struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int64 damage, int div, uint16 skill_id, uint16 skill_lv, int type); + int (*skill_damage) (struct block_list *src, struct block_list *dst, int64 tick, int sdelay, int ddelay, int64 damage, int div, uint16 skill_id, uint16 skill_lv, enum battle_dmg_type type); int (*skill_nodamage) (struct block_list *src,struct block_list *dst,uint16 skill_id,int heal,int fail); void (*skill_poseffect) (struct block_list *src, uint16 skill_id, int val, int x, int y, int64 tick); void (*skill_estimation) (struct map_session_data *sd,struct block_list *dst); @@ -938,7 +1038,8 @@ struct clif_interface { void (*weather) (int16 m); void (*specialeffect) (struct block_list* bl, int type, enum send_target target); void (*specialeffect_single) (struct block_list* bl, int type, int fd); - void (*specialeffect_value) (struct block_list* bl, int effect_id, int num, send_target target); + void (*specialeffect_value) (struct block_list* bl, int effect_id, uint64 num, send_target target); + void (*specialeffect_value_single) (struct block_list *bl, int effect_id, uint64 num, int fd); void (*removeSpecialEffect) (struct block_list *bl, int effectId, enum send_target target); void (*removeSpecialEffect_single) (struct block_list *bl, int effectId, struct block_list *targetBl); void (*millenniumshield) (struct block_list *bl, short shields ); @@ -958,6 +1059,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); @@ -968,6 +1070,7 @@ struct clif_interface { void (*broadcast2) (struct block_list *bl, const char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target); void (*messagecolor_self) (int fd, uint32 color, const char *msg); void (*messagecolor) (struct block_list* bl, uint32 color, const char* msg); + void (*serviceMessageColor) (struct map_session_data *sd, uint32 color, const char *msg); void (*disp_overhead) (struct block_list *bl, const char *mes, enum send_target target, struct block_list *target_bl); void (*notify_playerchat) (struct block_list *bl, const char *mes); void (*msgtable) (struct map_session_data* sd, enum clif_messages msg_id); @@ -985,7 +1088,7 @@ struct clif_interface { void (*wisexin) (struct map_session_data *sd,int type,int flag); void (*wisall) (struct map_session_data *sd,int type,int flag); void (*PMIgnoreList) (struct map_session_data* sd); - void (*ShowScript) (struct block_list* bl, const char* message); + void (*ShowScript) (struct block_list* bl, const char* message, enum send_target target); /* trade handling */ void (*traderequest) (struct map_session_data* sd, const char* name); void (*tradestart) (struct map_session_data* sd, uint8 type); @@ -1020,6 +1123,7 @@ struct clif_interface { void (*skillinfo) (struct map_session_data *sd,int skill_id, int inf); void (*addskill) (struct map_session_data *sd, int id); void (*deleteskill) (struct map_session_data *sd, int id); + void (*playerSkillToPacket) (struct map_session_data *sd, struct SKILLDATA *skillData, int skillId, int idx, bool newSkill); /* party-specific */ void (*party_created) (struct map_session_data *sd,int result); void (*party_member_info) (struct party_data *p, struct map_session_data *sd); @@ -1043,6 +1147,8 @@ struct clif_interface { void (*guild_masterormember) (struct map_session_data *sd); void (*guild_basicinfo) (struct map_session_data *sd); void (*guild_allianceinfo) (struct map_session_data *sd); + void (*guild_castlelist) (struct map_session_data *sd); + void (*guild_castleinfo) (struct map_session_data *sd, struct guild_castle *gc); void (*guild_memberlist) (struct map_session_data *sd); void (*guild_skillinfo) (struct map_session_data* sd); void (*guild_send_onlineinfo) (struct map_session_data *sd); //[LuzZza] @@ -1212,19 +1318,21 @@ struct clif_interface { /* */ void (*notify_bounditem) (struct map_session_data *sd, unsigned short index); /* */ - int (*delay_damage) (int64 tick, struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, unsigned char type); + int (*delay_damage) (int64 tick, struct block_list *src, struct block_list *dst, int sdelay, int ddelay, int64 in_damage, short div, enum battle_dmg_type type); int (*delay_damage_sub) (int tid, int64 tick, int id, intptr_t data); /* NPC Market */ void (*npc_market_open) (struct map_session_data *sd, struct npc_data *nd); - void (*npc_market_purchase_ack) (struct map_session_data *sd, const struct itemlist *item_list, unsigned char response); + void (*npc_market_purchase_ack) (struct map_session_data *sd, const struct itemlist *item_list, enum market_buy_result response); /* */ bool (*parse_roulette_db) (void); - void (*roulette_generate_ack) (struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, int bonusItemID); + 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); @@ -1242,7 +1350,8 @@ struct clif_interface { void (*pWantToConnection) (int fd, struct map_session_data *sd); void (*pLoadEndAck) (int fd,struct map_session_data *sd); void (*pTickSend) (int fd, struct map_session_data *sd); - void (*pHotkey) (int fd, struct map_session_data *sd); + void (*pHotkey1) (int fd, struct map_session_data *sd); + void (*pHotkey2) (int fd, struct map_session_data *sd); void (*pProgressbar) (int fd, struct map_session_data * sd); void (*pWalkToXY) (int fd, struct map_session_data *sd); void (*pQuitGame) (int fd, struct map_session_data *sd); @@ -1285,7 +1394,10 @@ struct clif_interface { void (*pChangeCart) (int fd,struct map_session_data *sd); void (*pStatusUp) (int fd,struct map_session_data *sd); void (*pSkillUp) (int fd,struct map_session_data *sd); + void (*useSkillToIdReal) (int fd, struct map_session_data *sd, int skill_id, int skill_lv, int target_id); void (*pUseSkillToId) (int fd, struct map_session_data *sd); + void (*pStartUseSkillToId) (int fd, struct map_session_data *sd); + void (*pStopUseSkillToId) (int fd, struct map_session_data *sd); void (*pUseSkillToId_homun) (struct homun_data *hd, struct map_session_data *sd, int64 tick, uint16 skill_id, uint16 skill_lv, int target_id); void (*pUseSkillToId_mercenary) (struct mercenary_data *md, struct map_session_data *sd, int64 tick, uint16 skill_id, uint16 skill_lv, int target_id); void (*pUseSkillToPos) (int fd, struct map_session_data *sd); @@ -1444,13 +1556,17 @@ struct clif_interface { void (*pBGQueueRevokeReq) (int fd, struct map_session_data *sd); void (*pBGQueueBattleBeginAck) (int fd, struct map_session_data *sd); /* RagExe Cash Shop [Ind/Hercules] */ - void (*pCashShopOpen) (int fd, struct map_session_data *sd); + void (*pCashShopOpen1) (int fd, struct map_session_data *sd); + void (*pCashShopOpen2) (int fd, struct map_session_data *sd); + void (*pCashShopLimitedReq) (int fd, struct map_session_data *sd); void (*pCashShopClose) (int fd, struct map_session_data *sd); void (*pCashShopReqTab) (int fd, struct map_session_data *sd); void (*pCashShopSchedule) (int fd, struct map_session_data *sd); 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); + void (*cashShopOpen) (int fd, struct map_session_data *sd, int tab); /* Group Search System Update */ void (*pPartyBookingAddFilter) (int fd, struct map_session_data *sd); void (*pPartyBookingSubFilter) (int fd, struct map_session_data *sd); @@ -1476,7 +1592,8 @@ struct clif_interface { void (*pNPCMarketPurchase) (int fd, struct map_session_data *sd); /* */ int (*add_item_options) (struct ItemOptions *buf, const struct item *it); - void (*pHotkeyRowShift) (int fd, struct map_session_data *sd); + void (*pHotkeyRowShift1) (int fd, struct map_session_data *sd); + void (*pHotkeyRowShift2) (int fd, struct map_session_data *sd); void (*dressroom_open) (struct map_session_data *sd, int view); void (*pOneClick_ItemIdentify) (int fd,struct map_session_data *sd); /* Cart Deco */ @@ -1504,7 +1621,7 @@ struct clif_interface { void (*pRodexCancelWriteMail) (int fd, struct map_session_data *sd); void (*pRodexOpenMailbox) (int fd, struct map_session_data *sd); void (*pRodexCheckName) (int fd, struct map_session_data *sd); - void (*rodex_checkname_result) (struct map_session_data *sd, int char_id, short class_, int base_level, const char *name); + void (*rodex_checkname_result) (struct map_session_data *sd, int char_id, int class_, int base_level, const char *name); void (*pRodexDeleteMail) (int fd, struct map_session_data *sd); void (*rodex_delete_mail) (struct map_session_data *sd, int8 opentype, int64 mail_id); void (*pRodexRefreshMaillist) (int fd, struct map_session_data *sd); @@ -1537,15 +1654,9 @@ struct clif_interface { void (*pPrivateAirshipRequest) (int fd, struct map_session_data *sd); void (*PrivateAirshipResponse) (struct map_session_data *sd, uint32 flag); - void (*stylist_vector_init) (void); - void (*stylist_vector_clear) (void); - bool (*stylist_read_db_libconfig) (void); - bool (*stylist_read_db_libconfig_sub) (struct config_setting_t *it, int idx, const char *source); - bool (*style_change_validate_requirements) (struct map_session_data *sd, int type, int16 idx); - void (*stylist_send_rodexitem) (struct map_session_data *sd, int itemid); void (*pReqStyleChange) (int fd, struct map_session_data *sd); void (*pReqStyleChange2) (int fd, struct map_session_data *sd); - void (*cz_req_style_change_sub) (struct map_session_data *sd, int type, int16 idx, bool isitem); + void (*pStyleClose) (int fd, struct map_session_data *sd); void (*style_change_response) (struct map_session_data *sd, enum stylist_shop flag); void (*pPetEvolution) (int fd, struct map_session_data *sd); void (*petEvolutionResult) (int fd, enum pet_evolution_result result); @@ -1553,7 +1664,42 @@ struct clif_interface { void (*pMemorialDungeonCommand) (int fd, struct map_session_data *sd); void (*camera_showWindow) (struct map_session_data *sd); void (*camera_change) (struct map_session_data *sd, float range, float rotation, float latitude, enum send_target target); + void (*pCameraInfo) (int fd, struct map_session_data *sd); void (*item_preview) (struct map_session_data *sd, int n); + bool (*enchant_equipment) (struct map_session_data *sd, enum equip_pos pos, int cardSlot, int cardId); + void (*pReqRemainTime) (int fd, struct map_session_data *sd); + void (*npc_barter_open) (struct map_session_data *sd, struct npc_data *nd); + void (*pNPCBarterClosed) (int fd, struct map_session_data *sd); + void (*pNPCBarterPurchase) (int fd, struct map_session_data *sd); + void (*pNPCExpandedBarterClosed) (int fd, struct map_session_data *sd); + void (*pNPCExpandedBarterPurchase) (int fd, struct map_session_data *sd); + void (*npc_expanded_barter_open) (struct map_session_data *sd, struct npc_data *nd); + void (*pClientVersion) (int fd, struct map_session_data *sd); + void (*pPing) (int fd, struct map_session_data *sd); + void (*ping) (struct map_session_data *sd); + 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); + void (*pGuildCastleTeleportRequest) (int fd, struct map_session_data *sd); + void (*pGuildCastleInfoRequest) (int fd, struct map_session_data *sd); + void (*guild_castleteleport_res) (struct map_session_data *sd, enum siege_teleport_result result); + bool (*lapineDdukDdak_open) (struct map_session_data *sd, int item_id); + bool (*lapineDdukDdak_result) (struct map_session_data *sd, enum lapineddukddak_result result); + void (*plapineDdukDdak_ack) (int fd, struct map_session_data *sd); + void (*plapineDdukDdak_close) (int fd, struct map_session_data *sd); + bool (*lapineUpgrade_open) (struct map_session_data *sd, int item_id); + bool (*lapineUpgrade_result) (struct map_session_data *sd, enum lapineUpgrade_result result); + void (*pLapineUpgrade_close) (int fd, struct map_session_data *sd); + void (*pLapineUpgrade_makeItem) (int fd, struct map_session_data *sd); + void (*pReqGearOff) (int fd, struct map_session_data *sd); }; #ifdef HERCULES_CORE diff --git a/src/map/constants.inc b/src/map/constants.inc deleted file mode 100644 index 355e2a825..000000000 --- a/src/map/constants.inc +++ /dev/null @@ -1,1029 +0,0 @@ - script->constdb_comment("Status Icons"); - - script->set_constant("SI_BLANK", SI_BLANK, false, false); - - script->set_constant("SI_PROVOKE", SI_PROVOKE, false, false); - script->set_constant("SI_ENDURE", SI_ENDURE, false, false); - script->set_constant("SI_TWOHANDQUICKEN", SI_TWOHANDQUICKEN, false, false); - script->set_constant("SI_CONCENTRATION", SI_CONCENTRATION, false, false); - script->set_constant("SI_HIDING", SI_HIDING, false, false); - script->set_constant("SI_CLOAKING", SI_CLOAKING, false, false); - script->set_constant("SI_ENCHANTPOISON", SI_ENCHANTPOISON, false, false); - script->set_constant("SI_POISONREACT", SI_POISONREACT, false, false); - script->set_constant("SI_QUAGMIRE", SI_QUAGMIRE, false, false); - script->set_constant("SI_ANGELUS", SI_ANGELUS, false, false); - script->set_constant("SI_BLESSING", SI_BLESSING, false, false); - script->set_constant("SI_CRUCIS", SI_CRUCIS, false, false); - script->set_constant("SI_INC_AGI", SI_INC_AGI, false, false); - script->set_constant("SI_DEC_AGI", SI_DEC_AGI, false, false); - script->set_constant("SI_SLOWPOISON", SI_SLOWPOISON, false, false); - script->set_constant("SI_IMPOSITIO", SI_IMPOSITIO, false, false); - script->set_constant("SI_SUFFRAGIUM", SI_SUFFRAGIUM, false, false); - script->set_constant("SI_ASPERSIO", SI_ASPERSIO, false, false); - script->set_constant("SI_BENEDICTIO", SI_BENEDICTIO, false, false); - script->set_constant("SI_KYRIE", SI_KYRIE, false, false); - script->set_constant("SI_MAGNIFICAT", SI_MAGNIFICAT, false, false); - script->set_constant("SI_GLORIA", SI_GLORIA, false, false); - script->set_constant("SI_LEXAETERNA", SI_LEXAETERNA, false, false); - script->set_constant("SI_ADRENALINE", SI_ADRENALINE, false, false); - script->set_constant("SI_WEAPONPERFECT", SI_WEAPONPERFECT, false, false); - script->set_constant("SI_OVERTHRUST", SI_OVERTHRUST, false, false); - script->set_constant("SI_MAXIMIZE", SI_MAXIMIZE, false, false); - script->set_constant("SI_RIDING", SI_RIDING, false, false); - script->set_constant("SI_FALCON", SI_FALCON, false, false); - script->set_constant("SI_TRICKDEAD", SI_TRICKDEAD, false, false); - script->set_constant("SI_SHOUT", SI_SHOUT, false, false); - script->set_constant("SI_ENERGYCOAT", SI_ENERGYCOAT, false, false); - script->set_constant("SI_BROKENARMOR", SI_BROKENARMOR, false, false); - script->set_constant("SI_BROKENWEAPON", SI_BROKENWEAPON, false, false); - script->set_constant("SI_ILLUSION", SI_ILLUSION, false, false); - script->set_constant("SI_WEIGHTOVER50", SI_WEIGHTOVER50, false, false); - script->set_constant("SI_WEIGHTOVER90", SI_WEIGHTOVER90, false, false); - script->set_constant("SI_ATTHASTE_POTION1", SI_ATTHASTE_POTION1, false, false); - script->set_constant("SI_ATTHASTE_POTION2", SI_ATTHASTE_POTION2, false, false); - script->set_constant("SI_ATTHASTE_POTION3", SI_ATTHASTE_POTION3, false, false); - script->set_constant("SI_ATTHASTE_INFINITY", SI_ATTHASTE_INFINITY, false, false); - script->set_constant("SI_MOVHASTE_POTION", SI_MOVHASTE_POTION, false, false); - script->set_constant("SI_MOVHASTE_INFINITY", SI_MOVHASTE_INFINITY, false, false); - //script->set_constant("SI_AUTOCOUNTER", SI_AUTOCOUNTER, false, false); - //script->set_constant("SI_SPLASHER", SI_SPLASHER, false, false); - script->set_constant("SI_ANKLESNARE", SI_ANKLESNARE, false, false); - script->set_constant("SI_POSTDELAY", SI_POSTDELAY, false, false); - //script->set_constant("SI_NOACTION", SI_NOACTION, false, false); - //script->set_constant("SI_IMPOSSIBLEPICKUP", SI_IMPOSSIBLEPICKUP, false, false); - //script->set_constant("SI_BARRIER", SI_BARRIER, false, false); - - script->set_constant("SI_NOEQUIPWEAPON", SI_NOEQUIPWEAPON, false, false); - script->set_constant("SI_NOEQUIPSHIELD", SI_NOEQUIPSHIELD, false, false); - script->set_constant("SI_NOEQUIPARMOR", SI_NOEQUIPARMOR, false, false); - script->set_constant("SI_NOEQUIPHELM", SI_NOEQUIPHELM, false, false); - script->set_constant("SI_PROTECTWEAPON", SI_PROTECTWEAPON, false, false); - script->set_constant("SI_PROTECTSHIELD", SI_PROTECTSHIELD, false, false); - script->set_constant("SI_PROTECTARMOR", SI_PROTECTARMOR, false, false); - script->set_constant("SI_PROTECTHELM", SI_PROTECTHELM, false, false); - script->set_constant("SI_AUTOGUARD", SI_AUTOGUARD, false, false); - script->set_constant("SI_REFLECTSHIELD", SI_REFLECTSHIELD, false, false); - //script->set_constant("SI_DEVOTION", SI_DEVOTION, false, false); - script->set_constant("SI_PROVIDENCE", SI_PROVIDENCE, false, false); - script->set_constant("SI_DEFENDER", SI_DEFENDER, false, false); - //script->set_constant("SI_MAGICROD", SI_MAGICROD, false, false); - //script->set_constant("SI_WEAPONPROPERTY", SI_WEAPONPROPERTY, false, false); - script->set_constant("SI_AUTOSPELL", SI_AUTOSPELL, false, false); - //script->set_constant("SI_SPECIALZONE", SI_SPECIALZONE, false, false); - //script->set_constant("SI_MASK", SI_MASK, false, false); - script->set_constant("SI_SPEARQUICKEN", SI_SPEARQUICKEN, false, false); - //script->set_constant("SI_BDPLAYING", SI_BDPLAYING, false, false); - //script->set_constant("SI_WHISTLE", SI_WHISTLE, false, false); - //script->set_constant("SI_ASSASSINCROSS", SI_ASSASSINCROSS, false, false); - //script->set_constant("SI_POEMBRAGI", SI_POEMBRAGI, false, false); - //script->set_constant("SI_APPLEIDUN", SI_APPLEIDUN, false, false); - //script->set_constant("SI_HUMMING", SI_HUMMING, false, false); - //script->set_constant("SI_DONTFORGETME", SI_DONTFORGETME, false, false); - //script->set_constant("SI_FORTUNEKISS", SI_FORTUNEKISS, false, false); - //script->set_constant("SI_SERVICEFORYOU", SI_SERVICEFORYOU, false, false); - //script->set_constant("SI_RICHMANKIM", SI_RICHMANKIM, false, false); - //script->set_constant("SI_ETERNALCHAOS", SI_ETERNALCHAOS, false, false); - //script->set_constant("SI_DRUMBATTLEFIELD", SI_DRUMBATTLEFIELD, false, false); - //script->set_constant("SI_RINGNIBELUNGEN", SI_RINGNIBELUNGEN, false, false); - //script->set_constant("SI_ROKISWEIL", SI_ROKISWEIL, false, false); - //script->set_constant("SI_INTOABYSS", SI_INTOABYSS, false, false); - //script->set_constant("SI_SIEGFRIED", SI_SIEGFRIED, false, false); - //script->set_constant("SI_BLADESTOP", SI_BLADESTOP, false, false); - script->set_constant("SI_EXPLOSIONSPIRITS", SI_EXPLOSIONSPIRITS, false, false); - script->set_constant("SI_STEELBODY", SI_STEELBODY, false, false); - script->set_constant("SI_EXTREMITYFIST", SI_EXTREMITYFIST, false, false); - //script->set_constant("SI_COMBOATTACK", SI_COMBOATTACK, false, false); - script->set_constant("SI_PROPERTYFIRE", SI_PROPERTYFIRE, false, false); - script->set_constant("SI_PROPERTYWATER", SI_PROPERTYWATER, false, false); - script->set_constant("SI_PROPERTYWIND", SI_PROPERTYWIND, false, false); - script->set_constant("SI_PROPERTYGROUND", SI_PROPERTYGROUND, false, false); - //script->set_constant("SI_MAGICATTACK", SI_MAGICATTACK, false, false); - script->set_constant("SI_STOP", SI_STOP, false, false); - //script->set_constant("SI_WEAPONBRAKER", SI_WEAPONBRAKER, false, false); - script->set_constant("SI_PROPERTYUNDEAD", SI_PROPERTYUNDEAD, false, false); - //script->set_constant("SI_POWERUP", SI_POWERUP, false, false); - //script->set_constant("SI_AGIUP", SI_AGIUP, false, false); - - //script->set_constant("SI_SIEGEMODE", SI_SIEGEMODE, false, false); - //script->set_constant("SI_INVISIBLE", SI_INVISIBLE, false, false); - //script->set_constant("SI_STATUSONE", SI_STATUSONE, false, false); - script->set_constant("SI_AURABLADE", SI_AURABLADE, false, false); - script->set_constant("SI_PARRYING", SI_PARRYING, false, false); - script->set_constant("SI_LKCONCENTRATION", SI_LKCONCENTRATION, false, false); - script->set_constant("SI_TENSIONRELAX", SI_TENSIONRELAX, false, false); - script->set_constant("SI_BERSERK", SI_BERSERK, false, false); - //script->set_constant("SI_SACRIFICE", SI_SACRIFICE, false, false); - //script->set_constant("SI_GOSPEL", SI_GOSPEL, false, false); - script->set_constant("SI_ASSUMPTIO", SI_ASSUMPTIO, false, false); - //script->set_constant("SI_BASILICA", SI_BASILICA, false, false); - script->set_constant("SI_GROUNDMAGIC", SI_GROUNDMAGIC, false, false); - script->set_constant("SI_MAGICPOWER", SI_MAGICPOWER, false, false); - script->set_constant("SI_EDP", SI_EDP, false, false); - script->set_constant("SI_TRUESIGHT", SI_TRUESIGHT, false, false); - script->set_constant("SI_WINDWALK", SI_WINDWALK, false, false); - script->set_constant("SI_MELTDOWN", SI_MELTDOWN, false, false); - script->set_constant("SI_CARTBOOST", SI_CARTBOOST, false, false); - //script->set_constant("SI_CHASEWALK", SI_CHASEWALK, false, false); - script->set_constant("SI_SWORDREJECT", SI_SWORDREJECT, false, false); - script->set_constant("SI_MARIONETTE_MASTER", SI_MARIONETTE_MASTER, false, false); - script->set_constant("SI_MARIONETTE", SI_MARIONETTE, false, false); - script->set_constant("SI_MOON", SI_MOON, false, false); - script->set_constant("SI_BLOODING", SI_BLOODING, false, false); - script->set_constant("SI_JOINTBEAT", SI_JOINTBEAT, false, false); - //script->set_constant("SI_MINDBREAKER", SI_MINDBREAKER, false, false); - //script->set_constant("SI_MEMORIZE", SI_MEMORIZE, false, false); - //script->set_constant("SI_FOGWALL", SI_FOGWALL, false, false); - //script->set_constant("SI_SPIDERWEB", SI_SPIDERWEB, false, false); - script->set_constant("SI_PROTECTEXP", SI_PROTECTEXP, false, false); - //script->set_constant("SI_SUB_WEAPONPROPERTY", SI_SUB_WEAPONPROPERTY, false, false); - script->set_constant("SI_AUTOBERSERK", SI_AUTOBERSERK, false, false); - script->set_constant("SI_RUN", SI_RUN, false, false); - script->set_constant("SI_TING", SI_TING, false, false); - script->set_constant("SI_STORMKICK_ON", SI_STORMKICK_ON, false, false); - script->set_constant("SI_STORMKICK_READY", SI_STORMKICK_READY, false, false); - script->set_constant("SI_DOWNKICK_ON", SI_DOWNKICK_ON, false, false); - script->set_constant("SI_DOWNKICK_READY", SI_DOWNKICK_READY, false, false); - script->set_constant("SI_TURNKICK_ON", SI_TURNKICK_ON, false, false); - script->set_constant("SI_TURNKICK_READY", SI_TURNKICK_READY, false, false); - script->set_constant("SI_COUNTER_ON", SI_COUNTER_ON, false, false); - script->set_constant("SI_COUNTER_READY", SI_COUNTER_READY, false, false); - script->set_constant("SI_DODGE_ON", SI_DODGE_ON, false, false); - script->set_constant("SI_DODGE_READY", SI_DODGE_READY, false, false); - script->set_constant("SI_STRUP", SI_STRUP, false, false); - script->set_constant("SI_PROPERTYDARK", SI_PROPERTYDARK, false, false); - script->set_constant("SI_ADRENALINE2", SI_ADRENALINE2, false, false); - script->set_constant("SI_PROPERTYTELEKINESIS", SI_PROPERTYTELEKINESIS, false, false); - script->set_constant("SI_SOULLINK", SI_SOULLINK, false, false); - - script->set_constant("SI_PLUSATTACKPOWER", SI_PLUSATTACKPOWER, false, false); - script->set_constant("SI_PLUSMAGICPOWER", SI_PLUSMAGICPOWER, false, false); - script->set_constant("SI_DEVIL1", SI_DEVIL1, false, false); - script->set_constant("SI_KAITE", SI_KAITE, false, false); - //script->set_constant("SI_SWOO", SI_SWOO, false, false); - //script->set_constant("SI_STAR2", SI_STAR2, false, false); - script->set_constant("SI_KAIZEL", SI_KAIZEL, false, false); - script->set_constant("SI_KAAHI", SI_KAAHI, false, false); - script->set_constant("SI_KAUPE", SI_KAUPE, false, false); - script->set_constant("SI_SMA_READY", SI_SMA_READY, false, false); - script->set_constant("SI_SKE", SI_SKE, false, false); - script->set_constant("SI_ONEHANDQUICKEN", SI_ONEHANDQUICKEN, false, false); - //script->set_constant("SI_FRIEND", SI_FRIEND, false, false); - //script->set_constant("SI_FRIENDUP", SI_FRIENDUP, false, false); - //script->set_constant("SI_SG_WARM", SI_SG_WARM, false, false); - script->set_constant("SI_SG_SUN_WARM", SI_SG_SUN_WARM, false, false); - //script->set_constant("SI_SG_MOON_WARM", SI_SG_MOON_WARM, false, false); - //script->set_constant("SI_SG_STAR_WARM", SI_SG_STAR_WARM, false, false); - //script->set_constant("SI_EMOTION", SI_EMOTION, false, false); - script->set_constant("SI_SUN_COMFORT", SI_SUN_COMFORT, false, false); - script->set_constant("SI_MOON_COMFORT", SI_MOON_COMFORT, false, false); - script->set_constant("SI_STAR_COMFORT", SI_STAR_COMFORT, false, false); - //script->set_constant("SI_EXPUP", SI_EXPUP, false, false); - //script->set_constant("SI_GDSKILL_BATTLEORDER", SI_GDSKILL_BATTLEORDER, false, false); - //script->set_constant("SI_GDSKILL_REGENERATION", SI_GDSKILL_REGENERATION, false, false); - //script->set_constant("SI_GDSKILL_POSTDELAY", SI_GDSKILL_POSTDELAY, false, false); - //script->set_constant("SI_RESISTHANDICAP", SI_RESISTHANDICAP, false, false); - //script->set_constant("SI_MAXHPPERCENT", SI_MAXHPPERCENT, false, false); - //script->set_constant("SI_MAXSPPERCENT", SI_MAXSPPERCENT, false, false); - //script->set_constant("SI_DEFENCE", SI_DEFENCE, false, false); - //script->set_constant("SI_SLOWDOWN", SI_SLOWDOWN, false, false); - script->set_constant("SI_PRESERVE", SI_PRESERVE, false, false); - script->set_constant("SI_INCSTR", SI_INCSTR, false, false); - //script->set_constant("SI_NOT_EXTREMITYFIST", SI_NOT_EXTREMITYFIST, false, false); - script->set_constant("SI_CLAIRVOYANCE", SI_CLAIRVOYANCE, false, false); - script->set_constant("SI_MOVESLOW_POTION", SI_MOVESLOW_POTION, false, false); - script->set_constant("SI_DOUBLECASTING", SI_DOUBLECASTING, false, false); - //script->set_constant("SI_GRAVITATION", SI_GRAVITATION, false, false); - script->set_constant("SI_OVERTHRUSTMAX", SI_OVERTHRUSTMAX, false, false); - //script->set_constant("SI_LONGING", SI_LONGING, false, false); - //script->set_constant("SI_HERMODE", SI_HERMODE, false, false); - script->set_constant("SI_TAROTCARD", SI_TAROTCARD, false, false); - //script->set_constant("SI_HLIF_AVOID", SI_HLIF_AVOID, false, false); - //script->set_constant("SI_HFLI_FLEET", SI_HFLI_FLEET, false, false); - //script->set_constant("SI_HFLI_SPEED", SI_HFLI_SPEED, false, false); - //script->set_constant("SI_HLIF_CHANGE", SI_HLIF_CHANGE, false, false); - //script->set_constant("SI_HAMI_BLOODLUST", SI_HAMI_BLOODLUST, false, false); - script->set_constant("SI_CR_SHRINK", SI_CR_SHRINK, false, false); - script->set_constant("SI_WZ_SIGHTBLASTER", SI_WZ_SIGHTBLASTER, false, false); - script->set_constant("SI_DC_WINKCHARM", SI_DC_WINKCHARM, false, false); - - script->set_constant("SI_RG_CCONFINE_M", SI_RG_CCONFINE_M, false, false); - script->set_constant("SI_RG_CCONFINE_S", SI_RG_CCONFINE_S, false, false); - //script->set_constant("SI_DISABLEMOVE", SI_DISABLEMOVE, false, false); - script->set_constant("SI_GS_MADNESSCANCEL", SI_GS_MADNESSCANCEL, false, false); - script->set_constant("SI_GS_GATLINGFEVER", SI_GS_GATLINGFEVER, false, false); - script->set_constant("SI_EARTHSCROLL", SI_EARTHSCROLL, false, false); - script->set_constant("SI_NJ_UTSUSEMI", SI_NJ_UTSUSEMI, false, false); - script->set_constant("SI_NJ_BUNSINJYUTSU", SI_NJ_BUNSINJYUTSU, false, false); - script->set_constant("SI_NJ_NEN", SI_NJ_NEN, false, false); - script->set_constant("SI_GS_ADJUSTMENT", SI_GS_ADJUSTMENT, false, false); - script->set_constant("SI_GS_ACCURACY", SI_GS_ACCURACY, false, false); - script->set_constant("SI_NJ_SUITON", SI_NJ_SUITON, false, false); - //script->set_constant("SI_PET", SI_PET, false, false); - //script->set_constant("SI_MENTAL", SI_MENTAL, false, false); - //script->set_constant("SI_EXPMEMORY", SI_EXPMEMORY, false, false); - //script->set_constant("SI_PERFORMANCE", SI_PERFORMANCE, false, false); - //script->set_constant("SI_GAIN", SI_GAIN, false, false); - //script->set_constant("SI_GRIFFON", SI_GRIFFON, false, false); - //script->set_constant("SI_DRIFT", SI_DRIFT, false, false); - //script->set_constant("SI_WALLSHIFT", SI_WALLSHIFT, false, false); - //script->set_constant("SI_REINCARNATION", SI_REINCARNATION, false, false); - //script->set_constant("SI_PATTACK", SI_PATTACK, false, false); - //script->set_constant("SI_PSPEED", SI_PSPEED, false, false); - //script->set_constant("SI_PDEFENSE", SI_PDEFENSE, false, false); - //script->set_constant("SI_PCRITICAL", SI_PCRITICAL, false, false); - //script->set_constant("SI_RANKING", SI_RANKING, false, false); - //script->set_constant("SI_PTRIPLE", SI_PTRIPLE, false, false); - //script->set_constant("SI_DENERGY", SI_DENERGY, false, false); - //script->set_constant("SI_WAVE1", SI_WAVE1, false, false); - //script->set_constant("SI_WAVE2", SI_WAVE2, false, false); - //script->set_constant("SI_WAVE3", SI_WAVE3, false, false); - //script->set_constant("SI_WAVE4", SI_WAVE4, false, false); - //script->set_constant("SI_DAURA", SI_DAURA, false, false); - //script->set_constant("SI_DFREEZER", SI_DFREEZER, false, false); - //script->set_constant("SI_DPUNISH", SI_DPUNISH, false, false); - //script->set_constant("SI_DBARRIER", SI_DBARRIER, false, false); - //script->set_constant("SI_DWARNING", SI_DWARNING, false, false); - //script->set_constant("SI_MOUSEWHEEL", SI_MOUSEWHEEL, false, false); - //script->set_constant("SI_DGAUGE", SI_DGAUGE, false, false); - //script->set_constant("SI_DACCEL", SI_DACCEL, false, false); - //script->set_constant("SI_DBLOCK", SI_DBLOCK, false, false); - script->set_constant("SI_FOOD_STR", SI_FOOD_STR, false, false); - script->set_constant("SI_FOOD_AGI", SI_FOOD_AGI, false, false); - script->set_constant("SI_FOOD_VIT", SI_FOOD_VIT, false, false); - script->set_constant("SI_FOOD_DEX", SI_FOOD_DEX, false, false); - script->set_constant("SI_FOOD_INT", SI_FOOD_INT, false, false); - script->set_constant("SI_FOOD_LUK", SI_FOOD_LUK, false, false); - script->set_constant("SI_FOOD_BASICAVOIDANCE", SI_FOOD_BASICAVOIDANCE, false, false); - script->set_constant("SI_FOOD_BASICHIT", SI_FOOD_BASICHIT, false, false); - script->set_constant("SI_FOOD_CRITICALSUCCESSVALUE", SI_FOOD_CRITICALSUCCESSVALUE, false, false); - - script->set_constant("SI_CASH_PLUSEXP", SI_CASH_PLUSEXP, false, false); - script->set_constant("SI_CASH_DEATHPENALTY", SI_CASH_DEATHPENALTY, false, false); - script->set_constant("SI_CASH_RECEIVEITEM", SI_CASH_RECEIVEITEM, false, false); - script->set_constant("SI_CASH_BOSS_ALARM", SI_CASH_BOSS_ALARM, false, false); - //script->set_constant("SI_DA_ENERGY", SI_DA_ENERGY, false, false); - //script->set_constant("SI_DA_FIRSTSLOT", SI_DA_FIRSTSLOT, false, false); - //script->set_constant("SI_DA_HEADDEF", SI_DA_HEADDEF, false, false); - //script->set_constant("SI_DA_SPACE", SI_DA_SPACE, false, false); - //script->set_constant("SI_DA_TRANSFORM", SI_DA_TRANSFORM, false, false); - //script->set_constant("SI_DA_ITEMREBUILD", SI_DA_ITEMREBUILD, false, false); - //script->set_constant("SI_DA_ILLUSION", SI_DA_ILLUSION, false, false); - //script->set_constant("SI_DA_DARKPOWER", SI_DA_DARKPOWER, false, false); - //script->set_constant("SI_DA_EARPLUG", SI_DA_EARPLUG, false, false); - //script->set_constant("SI_DA_CONTRACT", SI_DA_CONTRACT, false, false); - //script->set_constant("SI_DA_BLACK", SI_DA_BLACK, false, false); - //script->set_constant("SI_DA_MAGICCART", SI_DA_MAGICCART, false, false); - //script->set_constant("SI_CRYSTAL", SI_CRYSTAL, false, false); - //script->set_constant("SI_DA_REBUILD", SI_DA_REBUILD, false, false); - //script->set_constant("SI_DA_EDARKNESS", SI_DA_EDARKNESS, false, false); - //script->set_constant("SI_DA_EGUARDIAN", SI_DA_EGUARDIAN, false, false); - //script->set_constant("SI_DA_TIMEOUT", SI_DA_TIMEOUT, false, false); - script->set_constant("SI_FOOD_STR_CASH", SI_FOOD_STR_CASH, false, false); - script->set_constant("SI_FOOD_AGI_CASH", SI_FOOD_AGI_CASH, false, false); - script->set_constant("SI_FOOD_VIT_CASH", SI_FOOD_VIT_CASH, false, false); - script->set_constant("SI_FOOD_DEX_CASH", SI_FOOD_DEX_CASH, false, false); - script->set_constant("SI_FOOD_INT_CASH", SI_FOOD_INT_CASH, false, false); - script->set_constant("SI_FOOD_LUK_CASH", SI_FOOD_LUK_CASH, false, false); - script->set_constant("SI_MER_FLEE", SI_MER_FLEE, false, false); - script->set_constant("SI_MER_ATK", SI_MER_ATK, false, false); - script->set_constant("SI_MER_HP", SI_MER_HP, false, false); - script->set_constant("SI_MER_SP", SI_MER_SP, false, false); - script->set_constant("SI_MER_HIT", SI_MER_HIT, false, false); - script->set_constant("SI_SLOWCAST", SI_SLOWCAST, false, false); - //script->set_constant("SI_MAGICMIRROR", SI_MAGICMIRROR, false, false); - //script->set_constant("SI_STONESKIN", SI_STONESKIN, false, false); - //script->set_constant("SI_ANTIMAGIC", SI_ANTIMAGIC, false, false); - script->set_constant("SI_CRITICALWOUND", SI_CRITICALWOUND, false, false); - //script->set_constant("SI_NPC_DEFENDER", SI_NPC_DEFENDER, false, false); - //script->set_constant("SI_NOACTION_WAIT", SI_NOACTION_WAIT, false, false); - script->set_constant("SI_MOVHASTE_HORSE", SI_MOVHASTE_HORSE, false, false); - script->set_constant("SI_PROTECT_DEF", SI_PROTECT_DEF, false, false); - script->set_constant("SI_PROTECT_MDEF", SI_PROTECT_MDEF, false, false); - script->set_constant("SI_HEALPLUS", SI_HEALPLUS, false, false); - script->set_constant("SI_S_LIFEPOTION", SI_S_LIFEPOTION, false, false); - script->set_constant("SI_L_LIFEPOTION", SI_L_LIFEPOTION, false, false); - script->set_constant("SI_CRITICALPERCENT", SI_CRITICALPERCENT, false, false); - script->set_constant("SI_PLUSAVOIDVALUE", SI_PLUSAVOIDVALUE, false, false); - script->set_constant("SI_ATKER_ASPD", SI_ATKER_ASPD, false, false); - script->set_constant("SI_TARGET_ASPD", SI_TARGET_ASPD, false, false); - script->set_constant("SI_ATKER_MOVESPEED", SI_ATKER_MOVESPEED, false, false); - - script->set_constant("SI_ATKER_BLOOD", SI_ATKER_BLOOD, false, false); - script->set_constant("SI_TARGET_BLOOD", SI_TARGET_BLOOD, false, false); - script->set_constant("SI_ARMOR_PROPERTY", SI_ARMOR_PROPERTY, false, false); - //script->set_constant("SI_REUSE_LIMIT_A", SI_REUSE_LIMIT_A, false, false); - script->set_constant("SI_HELLPOWER", SI_HELLPOWER, false, false); - script->set_constant("SI_STEAMPACK", SI_STEAMPACK, false, false); - //script->set_constant("SI_REUSE_LIMIT_B", SI_REUSE_LIMIT_B, false, false); - //script->set_constant("SI_REUSE_LIMIT_C", SI_REUSE_LIMIT_C, false, false); - //script->set_constant("SI_REUSE_LIMIT_D", SI_REUSE_LIMIT_D, false, false); - //script->set_constant("SI_REUSE_LIMIT_E", SI_REUSE_LIMIT_E, false, false); - //script->set_constant("SI_REUSE_LIMIT_F", SI_REUSE_LIMIT_F, false, false); - script->set_constant("SI_INVINCIBLE", SI_INVINCIBLE, false, false); - script->set_constant("SI_CASH_PLUSONLYJOBEXP", SI_CASH_PLUSONLYJOBEXP, false, false); - script->set_constant("SI_PARTYFLEE", SI_PARTYFLEE, false, false); - script->set_constant("SI_ANGEL_PROTECT", SI_ANGEL_PROTECT, false, false); - //script->set_constant("SI_ENDURE_MDEF", SI_ENDURE_MDEF, false, false); - script->set_constant("SI_ENCHANTBLADE", SI_ENCHANTBLADE, false, false); - script->set_constant("SI_DEATHBOUND", SI_DEATHBOUND, false, false); - script->set_constant("SI_REFRESH", SI_REFRESH, false, false); - script->set_constant("SI_GIANTGROWTH", SI_GIANTGROWTH, false, false); - script->set_constant("SI_STONEHARDSKIN", SI_STONEHARDSKIN, false, false); - script->set_constant("SI_VITALITYACTIVATION", SI_VITALITYACTIVATION, false, false); - script->set_constant("SI_FIGHTINGSPIRIT", SI_FIGHTINGSPIRIT, false, false); - script->set_constant("SI_ABUNDANCE", SI_ABUNDANCE, false, false); - script->set_constant("SI_REUSE_MILLENNIUMSHIELD", SI_REUSE_MILLENNIUMSHIELD, false, false); - script->set_constant("SI_REUSE_CRUSHSTRIKE", SI_REUSE_CRUSHSTRIKE, false, false); - script->set_constant("SI_REUSE_REFRESH", SI_REUSE_REFRESH, false, false); - script->set_constant("SI_REUSE_STORMBLAST", SI_REUSE_STORMBLAST, false, false); - script->set_constant("SI_VENOMIMPRESS", SI_VENOMIMPRESS, false, false); - script->set_constant("SI_EPICLESIS", SI_EPICLESIS, false, false); - script->set_constant("SI_ORATIO", SI_ORATIO, false, false); - script->set_constant("SI_LAUDAAGNUS", SI_LAUDAAGNUS, false, false); - script->set_constant("SI_LAUDARAMUS", SI_LAUDARAMUS, false, false); - script->set_constant("SI_CLOAKINGEXCEED", SI_CLOAKINGEXCEED, false, false); - script->set_constant("SI_HALLUCINATIONWALK", SI_HALLUCINATIONWALK, false, false); - script->set_constant("SI_HALLUCINATIONWALK_POSTDELAY", SI_HALLUCINATIONWALK_POSTDELAY, false, false); - script->set_constant("SI_RENOVATIO", SI_RENOVATIO, false, false); - script->set_constant("SI_WEAPONBLOCKING", SI_WEAPONBLOCKING, false, false); - script->set_constant("SI_WEAPONBLOCKING_POSTDELAY", SI_WEAPONBLOCKING_POSTDELAY, false, false); - script->set_constant("SI_ROLLINGCUTTER", SI_ROLLINGCUTTER, false, false); - script->set_constant("SI_EXPIATIO", SI_EXPIATIO, false, false); - script->set_constant("SI_POISONINGWEAPON", SI_POISONINGWEAPON, false, false); - script->set_constant("SI_TOXIN", SI_TOXIN, false, false); - script->set_constant("SI_PARALYSE", SI_PARALYSE, false, false); - script->set_constant("SI_VENOMBLEED", SI_VENOMBLEED, false, false); - script->set_constant("SI_MAGICMUSHROOM", SI_MAGICMUSHROOM, false, false); - script->set_constant("SI_DEATHHURT", SI_DEATHHURT, false, false); - script->set_constant("SI_PYREXIA", SI_PYREXIA, false, false); - script->set_constant("SI_OBLIVIONCURSE", SI_OBLIVIONCURSE, false, false); - script->set_constant("SI_LEECHESEND", SI_LEECHESEND, false, false); - - script->set_constant("SI_DUPLELIGHT", SI_DUPLELIGHT, false, false); - script->set_constant("SI_FROSTMISTY", SI_FROSTMISTY, false, false); - script->set_constant("SI_FEARBREEZE", SI_FEARBREEZE, false, false); - script->set_constant("SI_ELECTRICSHOCKER", SI_ELECTRICSHOCKER, false, false); - script->set_constant("SI_MARSHOFABYSS", SI_MARSHOFABYSS, false, false); - script->set_constant("SI_RECOGNIZEDSPELL", SI_RECOGNIZEDSPELL, false, false); - script->set_constant("SI_STASIS", SI_STASIS, false, false); - script->set_constant("SI_WUGRIDER", SI_WUGRIDER, false, false); - script->set_constant("SI_WUGDASH", SI_WUGDASH, false, false); - script->set_constant("SI_WUGBITE", SI_WUGBITE, false, false); - script->set_constant("SI_CAMOUFLAGE", SI_CAMOUFLAGE, false, false); - script->set_constant("SI_ACCELERATION", SI_ACCELERATION, false, false); - script->set_constant("SI_HOVERING", SI_HOVERING, false, false); - script->set_constant("SI_SPHERE_1", SI_SPHERE_1, false, false); - script->set_constant("SI_SPHERE_2", SI_SPHERE_2, false, false); - script->set_constant("SI_SPHERE_3", SI_SPHERE_3, false, false); - script->set_constant("SI_SPHERE_4", SI_SPHERE_4, false, false); - script->set_constant("SI_SPHERE_5", SI_SPHERE_5, false, false); - script->set_constant("SI_MVPCARD_TAOGUNKA", SI_MVPCARD_TAOGUNKA, false, false); - script->set_constant("SI_MVPCARD_MISTRESS", SI_MVPCARD_MISTRESS, false, false); - script->set_constant("SI_MVPCARD_ORCHERO", SI_MVPCARD_ORCHERO, false, false); - script->set_constant("SI_MVPCARD_ORCLORD", SI_MVPCARD_ORCLORD, false, false); - script->set_constant("SI_OVERHEAT_LIMITPOINT", SI_OVERHEAT_LIMITPOINT, false, false); - script->set_constant("SI_OVERHEAT", SI_OVERHEAT, false, false); - script->set_constant("SI_SHAPESHIFT", SI_SHAPESHIFT, false, false); - script->set_constant("SI_INFRAREDSCAN", SI_INFRAREDSCAN, false, false); - script->set_constant("SI_MAGNETICFIELD", SI_MAGNETICFIELD, false, false); - script->set_constant("SI_NEUTRALBARRIER", SI_NEUTRALBARRIER, false, false); - script->set_constant("SI_NEUTRALBARRIER_MASTER", SI_NEUTRALBARRIER_MASTER, false, false); - script->set_constant("SI_STEALTHFIELD", SI_STEALTHFIELD, false, false); - script->set_constant("SI_STEALTHFIELD_MASTER", SI_STEALTHFIELD_MASTER, false, false); - script->set_constant("SI_MANU_ATK", SI_MANU_ATK, false, false); - script->set_constant("SI_MANU_DEF", SI_MANU_DEF, false, false); - script->set_constant("SI_SPL_ATK", SI_SPL_ATK, false, false); - script->set_constant("SI_SPL_DEF", SI_SPL_DEF, false, false); - script->set_constant("SI_REPRODUCE", SI_REPRODUCE, false, false); - script->set_constant("SI_MANU_MATK", SI_MANU_MATK, false, false); - script->set_constant("SI_SPL_MATK", SI_SPL_MATK, false, false); - script->set_constant("SI_STR_SCROLL", SI_STR_SCROLL, false, false); - script->set_constant("SI_INT_SCROLL", SI_INT_SCROLL, false, false); - script->set_constant("SI_LG_REFLECTDAMAGE", SI_LG_REFLECTDAMAGE, false, false); - script->set_constant("SI_FORCEOFVANGUARD", SI_FORCEOFVANGUARD, false, false); - script->set_constant("SI_BUCHEDENOEL", SI_BUCHEDENOEL, false, false); - script->set_constant("SI_AUTOSHADOWSPELL", SI_AUTOSHADOWSPELL, false, false); - script->set_constant("SI_SHADOWFORM", SI_SHADOWFORM, false, false); - script->set_constant("SI_RAID", SI_RAID, false, false); - script->set_constant("SI_SHIELDSPELL_DEF", SI_SHIELDSPELL_DEF, false, false); - script->set_constant("SI_SHIELDSPELL_MDEF", SI_SHIELDSPELL_MDEF, false, false); - script->set_constant("SI_SHIELDSPELL_REF", SI_SHIELDSPELL_REF, false, false); - script->set_constant("SI_BODYPAINT", SI_BODYPAINT, false, false); - - script->set_constant("SI_EXEEDBREAK", SI_EXEEDBREAK, false, false); - script->set_constant("SI_ADORAMUS", SI_ADORAMUS, false, false); - script->set_constant("SI_PRESTIGE", SI_PRESTIGE, false, false); - script->set_constant("SI_INVISIBILITY", SI_INVISIBILITY, false, false); - script->set_constant("SI_DEADLYINFECT", SI_DEADLYINFECT, false, false); - script->set_constant("SI_BANDING", SI_BANDING, false, false); - script->set_constant("SI_EARTHDRIVE", SI_EARTHDRIVE, false, false); - script->set_constant("SI_INSPIRATION", SI_INSPIRATION, false, false); - script->set_constant("SI_ENERVATION", SI_ENERVATION, false, false); - script->set_constant("SI_GROOMY", SI_GROOMY, false, false); - script->set_constant("SI_RAISINGDRAGON", SI_RAISINGDRAGON, false, false); - script->set_constant("SI_IGNORANCE", SI_IGNORANCE, false, false); - script->set_constant("SI_LAZINESS", SI_LAZINESS, false, false); - script->set_constant("SI_LIGHTNINGWALK", SI_LIGHTNINGWALK, false, false); - script->set_constant("SI_ACARAJE", SI_ACARAJE, false, false); - script->set_constant("SI_UNLUCKY", SI_UNLUCKY, false, false); - script->set_constant("SI_CURSEDCIRCLE_ATKER", SI_CURSEDCIRCLE_ATKER, false, false); - script->set_constant("SI_CURSEDCIRCLE_TARGET", SI_CURSEDCIRCLE_TARGET, false, false); - script->set_constant("SI_WEAKNESS", SI_WEAKNESS, false, false); - script->set_constant("SI_CRESCENTELBOW", SI_CRESCENTELBOW, false, false); - script->set_constant("SI_NOEQUIPACCESSARY", SI_NOEQUIPACCESSARY, false, false); - script->set_constant("SI_STRIPACCESSARY", SI_STRIPACCESSARY, false, false); - script->set_constant("SI_MANHOLE", SI_MANHOLE, false, false); - script->set_constant("SI_POPECOOKIE", SI_POPECOOKIE, false, false); - script->set_constant("SI_FALLENEMPIRE", SI_FALLENEMPIRE, false, false); - script->set_constant("SI_GENTLETOUCH_ENERGYGAIN", SI_GENTLETOUCH_ENERGYGAIN, false, false); - script->set_constant("SI_GENTLETOUCH_CHANGE", SI_GENTLETOUCH_CHANGE, false, false); - script->set_constant("SI_GENTLETOUCH_REVITALIZE", SI_GENTLETOUCH_REVITALIZE, false, false); - script->set_constant("SI_BLOODYLUST", SI_BLOODYLUST, false, false); - script->set_constant("SI_SWINGDANCE", SI_SWINGDANCE, false, false); - script->set_constant("SI_SYMPHONYOFLOVERS", SI_SYMPHONYOFLOVERS, false, false); - script->set_constant("SI_PROPERTYWALK", SI_PROPERTYWALK, false, false); - script->set_constant("SI_SPELLFIST", SI_SPELLFIST, false, false); - script->set_constant("SI_NETHERWORLD", SI_NETHERWORLD, false, false); - script->set_constant("SI_SIREN", SI_SIREN, false, false); - script->set_constant("SI_DEEPSLEEP", SI_DEEPSLEEP, false, false); - script->set_constant("SI_SIRCLEOFNATURE", SI_SIRCLEOFNATURE, false, false); - script->set_constant("SI_COLD", SI_COLD, false, false); - script->set_constant("SI_GLOOMYDAY", SI_GLOOMYDAY, false, false); - script->set_constant("SI_SONG_OF_MANA", SI_SONG_OF_MANA, false, false); - script->set_constant("SI_CLOUDKILL", SI_CLOUDKILL, false, false); - script->set_constant("SI_DANCEWITHWUG", SI_DANCEWITHWUG, false, false); - script->set_constant("SI_RUSHWINDMILL", SI_RUSHWINDMILL, false, false); - script->set_constant("SI_ECHOSONG", SI_ECHOSONG, false, false); - script->set_constant("SI_HARMONIZE", SI_HARMONIZE, false, false); - script->set_constant("SI_STRIKING", SI_STRIKING, false, false); - //script->set_constant("SI_WARMER", SI_WARMER, false, false); - script->set_constant("SI_MOONLITSERENADE", SI_MOONLITSERENADE, false, false); - script->set_constant("SI_SATURDAYNIGHTFEVER", SI_SATURDAYNIGHTFEVER, false, false); - script->set_constant("SI_SITDOWN_FORCE", SI_SITDOWN_FORCE, false, false); - - script->set_constant("SI_ANALYZE", SI_ANALYZE, false, false); - script->set_constant("SI_LERADSDEW", SI_LERADSDEW, false, false); - script->set_constant("SI_MELODYOFSINK", SI_MELODYOFSINK, false, false); - script->set_constant("SI_WARCRYOFBEYOND", SI_WARCRYOFBEYOND, false, false); - script->set_constant("SI_UNLIMITEDHUMMINGVOICE", SI_UNLIMITEDHUMMINGVOICE, false, false); - script->set_constant("SI_SPELLBOOK1", SI_SPELLBOOK1, false, false); - script->set_constant("SI_SPELLBOOK2", SI_SPELLBOOK2, false, false); - script->set_constant("SI_SPELLBOOK3", SI_SPELLBOOK3, false, false); - script->set_constant("SI_FREEZE_SP", SI_FREEZE_SP, false, false); - script->set_constant("SI_GN_TRAINING_SWORD", SI_GN_TRAINING_SWORD, false, false); - script->set_constant("SI_GN_REMODELING_CART", SI_GN_REMODELING_CART, false, false); - script->set_constant("SI_CARTSBOOST", SI_CARTSBOOST, false, false); - script->set_constant("SI_FIXEDCASTINGTM_REDUCE", SI_FIXEDCASTINGTM_REDUCE, false, false); - script->set_constant("SI_THORNTRAP", SI_THORNTRAP, false, false); - script->set_constant("SI_BLOODSUCKER", SI_BLOODSUCKER, false, false); - script->set_constant("SI_SPORE_EXPLOSION", SI_SPORE_EXPLOSION, false, false); - script->set_constant("SI_DEMONIC_FIRE", SI_DEMONIC_FIRE, false, false); - script->set_constant("SI_FIRE_EXPANSION_SMOKE_POWDER", SI_FIRE_EXPANSION_SMOKE_POWDER, false, false); - script->set_constant("SI_FIRE_EXPANSION_TEAR_GAS", SI_FIRE_EXPANSION_TEAR_GAS, false, false); - script->set_constant("SI_BLOCKING_PLAY", SI_BLOCKING_PLAY, false, false); - script->set_constant("SI_MANDRAGORA", SI_MANDRAGORA, false, false); - script->set_constant("SI_ACTIVATE", SI_ACTIVATE, false, false); - script->set_constant("SI_SECRAMENT", SI_SECRAMENT, false, false); - script->set_constant("SI_ASSUMPTIO2", SI_ASSUMPTIO2, false, false); - script->set_constant("SI_TK_SEVENWIND", SI_TK_SEVENWIND, false, false); - script->set_constant("SI_LIMIT_ODINS_RECALL", SI_LIMIT_ODINS_RECALL, false, false); - script->set_constant("SI_STOMACHACHE", SI_STOMACHACHE, false, false); - script->set_constant("SI_MYSTERIOUS_POWDER", SI_MYSTERIOUS_POWDER, false, false); - script->set_constant("SI_MELON_BOMB", SI_MELON_BOMB, false, false); - script->set_constant("SI_BANANA_BOMB_SITDOWN_POSTDELAY", SI_BANANA_BOMB_SITDOWN_POSTDELAY, false, false); - script->set_constant("SI_PROMOTE_HEALTH_RESERCH", SI_PROMOTE_HEALTH_RESERCH, false, false); - script->set_constant("SI_ENERGY_DRINK_RESERCH", SI_ENERGY_DRINK_RESERCH, false, false); - script->set_constant("SI_EXTRACT_WHITE_POTION_Z", SI_EXTRACT_WHITE_POTION_Z, false, false); - script->set_constant("SI_VITATA_500", SI_VITATA_500, false, false); - script->set_constant("SI_EXTRACT_SALAMINE_JUICE", SI_EXTRACT_SALAMINE_JUICE, false, false); - script->set_constant("SI_BOOST500", SI_BOOST500, false, false); - script->set_constant("SI_FULL_SWING_K", SI_FULL_SWING_K, false, false); - script->set_constant("SI_MANA_PLUS", SI_MANA_PLUS, false, false); - script->set_constant("SI_MUSTLE_M", SI_MUSTLE_M, false, false); - script->set_constant("SI_LIFE_FORCE_F", SI_LIFE_FORCE_F, false, false); - script->set_constant("SI_VACUUM_EXTREME", SI_VACUUM_EXTREME, false, false); - script->set_constant("SI_SAVAGE_STEAK", SI_SAVAGE_STEAK, false, false); - script->set_constant("SI_COCKTAIL_WARG_BLOOD", SI_COCKTAIL_WARG_BLOOD, false, false); - script->set_constant("SI_MINOR_BBQ", SI_MINOR_BBQ, false, false); - script->set_constant("SI_SIROMA_ICE_TEA", SI_SIROMA_ICE_TEA, false, false); - script->set_constant("SI_DROCERA_HERB_STEAMED", SI_DROCERA_HERB_STEAMED, false, false); - script->set_constant("SI_PUTTI_TAILS_NOODLES", SI_PUTTI_TAILS_NOODLES, false, false); - script->set_constant("SI_BANANA_BOMB", SI_BANANA_BOMB, false, false); - script->set_constant("SI_SUMMON_AGNI", SI_SUMMON_AGNI, false, false); - script->set_constant("SI_SPELLBOOK4", SI_SPELLBOOK4, false, false); - - script->set_constant("SI_SPELLBOOK5", SI_SPELLBOOK5, false, false); - script->set_constant("SI_SPELLBOOK6", SI_SPELLBOOK6, false, false); - script->set_constant("SI_SPELLBOOK7", SI_SPELLBOOK7, false, false); - script->set_constant("SI_ELEMENTAL_AGGRESSIVE", SI_ELEMENTAL_AGGRESSIVE, false, false); - script->set_constant("SI_RETURN_TO_ELDICASTES", SI_RETURN_TO_ELDICASTES, false, false); - script->set_constant("SI_BANDING_DEFENCE", SI_BANDING_DEFENCE, false, false); - script->set_constant("SI_SKELSCROLL", SI_SKELSCROLL, false, false); - script->set_constant("SI_DISTRUCTIONSCROLL", SI_DISTRUCTIONSCROLL, false, false); - script->set_constant("SI_ROYALSCROLL", SI_ROYALSCROLL, false, false); - script->set_constant("SI_IMMUNITYSCROLL", SI_IMMUNITYSCROLL, false, false); - script->set_constant("SI_MYSTICSCROLL", SI_MYSTICSCROLL, false, false); - script->set_constant("SI_BATTLESCROLL", SI_BATTLESCROLL, false, false); - script->set_constant("SI_ARMORSCROLL", SI_ARMORSCROLL, false, false); - script->set_constant("SI_FREYJASCROLL", SI_FREYJASCROLL, false, false); - script->set_constant("SI_SOULSCROLL", SI_SOULSCROLL, false, false); - script->set_constant("SI_CIRCLE_OF_FIRE", SI_CIRCLE_OF_FIRE, false, false); - script->set_constant("SI_CIRCLE_OF_FIRE_OPTION", SI_CIRCLE_OF_FIRE_OPTION, false, false); - script->set_constant("SI_FIRE_CLOAK", SI_FIRE_CLOAK, false, false); - script->set_constant("SI_FIRE_CLOAK_OPTION", SI_FIRE_CLOAK_OPTION, false, false); - script->set_constant("SI_WATER_SCREEN", SI_WATER_SCREEN, false, false); - script->set_constant("SI_WATER_SCREEN_OPTION", SI_WATER_SCREEN_OPTION, false, false); - script->set_constant("SI_WATER_DROP", SI_WATER_DROP, false, false); - script->set_constant("SI_WATER_DROP_OPTION", SI_WATER_DROP_OPTION, false, false); - script->set_constant("SI_WIND_STEP", SI_WIND_STEP, false, false); - script->set_constant("SI_WIND_STEP_OPTION", SI_WIND_STEP_OPTION, false, false); - script->set_constant("SI_WIND_CURTAIN", SI_WIND_CURTAIN, false, false); - script->set_constant("SI_WIND_CURTAIN_OPTION", SI_WIND_CURTAIN_OPTION, false, false); - script->set_constant("SI_WATER_BARRIER", SI_WATER_BARRIER, false, false); - script->set_constant("SI_ZEPHYR", SI_ZEPHYR, false, false); - script->set_constant("SI_SOLID_SKIN", SI_SOLID_SKIN, false, false); - script->set_constant("SI_SOLID_SKIN_OPTION", SI_SOLID_SKIN_OPTION, false, false); - script->set_constant("SI_STONE_SHIELD", SI_STONE_SHIELD, false, false); - script->set_constant("SI_STONE_SHIELD_OPTION", SI_STONE_SHIELD_OPTION, false, false); - script->set_constant("SI_POWER_OF_GAIA", SI_POWER_OF_GAIA, false, false); - //script->set_constant("SI_EL_WAIT", SI_EL_WAIT, false, false); - //script->set_constant("SI_EL_PASSIVE", SI_EL_PASSIVE, false, false); - //script->set_constant("SI_EL_DEFENSIVE", SI_EL_DEFENSIVE, false, false); - //script->set_constant("SI_EL_OFFENSIVE", SI_EL_OFFENSIVE, false, false); - //script->set_constant("SI_EL_COST", SI_EL_COST, false, false); - script->set_constant("SI_PYROTECHNIC", SI_PYROTECHNIC, false, false); - script->set_constant("SI_PYROTECHNIC_OPTION", SI_PYROTECHNIC_OPTION, false, false); - script->set_constant("SI_HEATER", SI_HEATER, false, false); - script->set_constant("SI_HEATER_OPTION", SI_HEATER_OPTION, false, false); - script->set_constant("SI_TROPIC", SI_TROPIC, false, false); - script->set_constant("SI_TROPIC_OPTION", SI_TROPIC_OPTION, false, false); - script->set_constant("SI_AQUAPLAY", SI_AQUAPLAY, false, false); - script->set_constant("SI_AQUAPLAY_OPTION", SI_AQUAPLAY_OPTION, false, false); - script->set_constant("SI_COOLER", SI_COOLER, false, false); - script->set_constant("SI_COOLER_OPTION", SI_COOLER_OPTION, false, false); - script->set_constant("SI_CHILLY_AIR", SI_CHILLY_AIR, false, false); - - script->set_constant("SI_CHILLY_AIR_OPTION", SI_CHILLY_AIR_OPTION, false, false); - script->set_constant("SI_GUST", SI_GUST, false, false); - script->set_constant("SI_GUST_OPTION", SI_GUST_OPTION, false, false); - script->set_constant("SI_BLAST", SI_BLAST, false, false); - script->set_constant("SI_BLAST_OPTION", SI_BLAST_OPTION, false, false); - script->set_constant("SI_WILD_STORM", SI_WILD_STORM, false, false); - script->set_constant("SI_WILD_STORM_OPTION", SI_WILD_STORM_OPTION, false, false); - script->set_constant("SI_PETROLOGY", SI_PETROLOGY, false, false); - script->set_constant("SI_PETROLOGY_OPTION", SI_PETROLOGY_OPTION, false, false); - script->set_constant("SI_CURSED_SOIL", SI_CURSED_SOIL, false, false); - script->set_constant("SI_CURSED_SOIL_OPTION", SI_CURSED_SOIL_OPTION, false, false); - script->set_constant("SI_UPHEAVAL", SI_UPHEAVAL, false, false); - script->set_constant("SI_UPHEAVAL_OPTION", SI_UPHEAVAL_OPTION, false, false); - script->set_constant("SI_TIDAL_WEAPON", SI_TIDAL_WEAPON, false, false); - script->set_constant("SI_TIDAL_WEAPON_OPTION", SI_TIDAL_WEAPON_OPTION, false, false); - script->set_constant("SI_ROCK_CRUSHER", SI_ROCK_CRUSHER, false, false); - script->set_constant("SI_ROCK_CRUSHER_ATK", SI_ROCK_CRUSHER_ATK, false, false); - script->set_constant("SI_FIRE_INSIGNIA", SI_FIRE_INSIGNIA, false, false); - script->set_constant("SI_WATER_INSIGNIA", SI_WATER_INSIGNIA, false, false); - script->set_constant("SI_WIND_INSIGNIA", SI_WIND_INSIGNIA, false, false); - script->set_constant("SI_EARTH_INSIGNIA", SI_EARTH_INSIGNIA, false, false); - script->set_constant("SI_EQUIPED_FLOOR", SI_EQUIPED_FLOOR, false, false); - script->set_constant("SI_GUARDIAN_RECALL", SI_GUARDIAN_RECALL, false, false); - script->set_constant("SI_MORA_BUFF", SI_MORA_BUFF, false, false); - script->set_constant("SI_REUSE_LIMIT_G", SI_REUSE_LIMIT_G, false, false); - script->set_constant("SI_REUSE_LIMIT_H", SI_REUSE_LIMIT_H, false, false); - script->set_constant("SI_NEEDLE_OF_PARALYZE", SI_NEEDLE_OF_PARALYZE, false, false); - script->set_constant("SI_PAIN_KILLER", SI_PAIN_KILLER, false, false); - script->set_constant("SI_G_LIFEPOTION", SI_G_LIFEPOTION, false, false); - script->set_constant("SI_VITALIZE_POTION", SI_VITALIZE_POTION, false, false); - script->set_constant("SI_LIGHT_OF_REGENE", SI_LIGHT_OF_REGENE, false, false); - script->set_constant("SI_OVERED_BOOST", SI_OVERED_BOOST, false, false); - script->set_constant("SI_SILENT_BREEZE", SI_SILENT_BREEZE, false, false); - script->set_constant("SI_ODINS_POWER", SI_ODINS_POWER, false, false); - script->set_constant("SI_STYLE_CHANGE", SI_STYLE_CHANGE, false, false); - script->set_constant("SI_SONIC_CLAW_POSTDELAY", SI_SONIC_CLAW_POSTDELAY, false, false); - // 586 - // 587 - // 588 - // 589 - // 590 - // 591 - // 592 - // 593 - // 594 - // 595 - script->set_constant("SI_SILVERVEIN_RUSH_POSTDELAY", SI_SILVERVEIN_RUSH_POSTDELAY, false, false); - script->set_constant("SI_MIDNIGHT_FRENZY_POSTDELAY", SI_MIDNIGHT_FRENZY_POSTDELAY, false, false); - script->set_constant("SI_GOLDENE_FERSE", SI_GOLDENE_FERSE, false, false); - script->set_constant("SI_ANGRIFFS_MODUS", SI_ANGRIFFS_MODUS, false, false); - - script->set_constant("SI_TINDER_BREAKER", SI_TINDER_BREAKER, false, false); - script->set_constant("SI_TINDER_BREAKER_POSTDELAY", SI_TINDER_BREAKER_POSTDELAY, false, false); - script->set_constant("SI_CBC", SI_CBC, false, false); - script->set_constant("SI_CBC_POSTDELAY", SI_CBC_POSTDELAY, false, false); - script->set_constant("SI_EQC", SI_EQC, false, false); - script->set_constant("SI_MAGMA_FLOW", SI_MAGMA_FLOW, false, false); - script->set_constant("SI_GRANITIC_ARMOR", SI_GRANITIC_ARMOR, false, false); - script->set_constant("SI_PYROCLASTIC", SI_PYROCLASTIC, false, false); - script->set_constant("SI_VOLCANIC_ASH", SI_VOLCANIC_ASH, false, false); - script->set_constant("SI_SPIRITS_SAVEINFO1", SI_SPIRITS_SAVEINFO1, false, false); - script->set_constant("SI_SPIRITS_SAVEINFO2", SI_SPIRITS_SAVEINFO2, false, false); - script->set_constant("SI_MAGIC_CANDY", SI_MAGIC_CANDY, false, false); - script->set_constant("SI_SEARCH_STORE_INFO", SI_SEARCH_STORE_INFO, false, false); - script->set_constant("SI_ALL_RIDING", SI_ALL_RIDING, false, false); - script->set_constant("SI_ALL_RIDING_REUSE_LIMIT", SI_ALL_RIDING_REUSE_LIMIT, false, false); - script->set_constant("SI_MACRO", SI_MACRO, false, false); - script->set_constant("SI_MACRO_POSTDELAY", SI_MACRO_POSTDELAY, false, false); - script->set_constant("SI_BEER_BOTTLE_CAP", SI_BEER_BOTTLE_CAP, false, false); - script->set_constant("SI_OVERLAPEXPUP", SI_OVERLAPEXPUP, false, false); - script->set_constant("SI_PC_IZ_DUN05", SI_PC_IZ_DUN05, false, false); - script->set_constant("SI_CRUSHSTRIKE", SI_CRUSHSTRIKE, false, false); - script->set_constant("SI_MONSTER_TRANSFORM", SI_MONSTER_TRANSFORM, false, false); - script->set_constant("SI_SIT", SI_SIT, false, false); - script->set_constant("SI_ONAIR", SI_ONAIR, false, false); - script->set_constant("SI_MTF_ASPD", SI_MTF_ASPD, false, false); - script->set_constant("SI_MTF_RANGEATK", SI_MTF_RANGEATK, false, false); - script->set_constant("SI_MTF_MATK", SI_MTF_MATK, false, false); - script->set_constant("SI_MTF_MLEATKED", SI_MTF_MLEATKED, false, false); - script->set_constant("SI_MTF_CRIDAMAGE", SI_MTF_CRIDAMAGE, false, false); - script->set_constant("SI_REUSE_LIMIT_MTF", SI_REUSE_LIMIT_MTF, false, false); - script->set_constant("SI_MACRO_PERMIT", SI_MACRO_PERMIT, false, false); - script->set_constant("SI_MACRO_PLAY", SI_MACRO_PLAY, false, false); - script->set_constant("SI_SKF_CAST", SI_SKF_CAST, false, false); - script->set_constant("SI_SKF_ASPD", SI_SKF_ASPD, false, false); - script->set_constant("SI_SKF_ATK", SI_SKF_ATK, false, false); - script->set_constant("SI_SKF_MATK", SI_SKF_MATK, false, false); - script->set_constant("SI_REWARD_PLUSONLYJOBEXP", SI_REWARD_PLUSONLYJOBEXP, false, false); - script->set_constant("SI_HANDICAPSTATE_NORECOVER", SI_HANDICAPSTATE_NORECOVER, false, false); - script->set_constant("SI_SET_NUM_DEF", SI_SET_NUM_DEF, false, false); - script->set_constant("SI_SET_NUM_MDEF", SI_SET_NUM_MDEF, false, false); - script->set_constant("SI_SET_PER_DEF", SI_SET_PER_DEF, false, false); - script->set_constant("SI_SET_PER_MDEF", SI_SET_PER_MDEF, false, false); - script->set_constant("SI_PARTYBOOKING_SEARCH_DEALY", SI_PARTYBOOKING_SEARCH_DEALY, false, false); - script->set_constant("SI_PARTYBOOKING_REGISTER_DEALY", SI_PARTYBOOKING_REGISTER_DEALY, false, false); - script->set_constant("SI_PERIOD_TIME_CHECK_DETECT_SKILL", SI_PERIOD_TIME_CHECK_DETECT_SKILL, false, false); - script->set_constant("SI_KO_JYUMONJIKIRI", SI_KO_JYUMONJIKIRI, false, false); - script->set_constant("SI_MEIKYOUSISUI", SI_MEIKYOUSISUI, false, false); - script->set_constant("SI_ATTHASTE_CASH", SI_ATTHASTE_CASH, false, false); - script->set_constant("SI_EQUIPPED_DIVINE_ARMOR", SI_EQUIPPED_DIVINE_ARMOR, false, false); - script->set_constant("SI_EQUIPPED_HOLY_ARMOR", SI_EQUIPPED_HOLY_ARMOR, false, false); - - script->set_constant("SI_2011RWC", SI_2011RWC, false, false); - script->set_constant("SI_KYOUGAKU", SI_KYOUGAKU, false, false); - script->set_constant("SI_IZAYOI", SI_IZAYOI, false, false); - script->set_constant("SI_ZENKAI", SI_ZENKAI, false, false); - script->set_constant("SI_KG_KAGEHUMI", SI_KG_KAGEHUMI, false, false); - script->set_constant("SI_KYOMU", SI_KYOMU, false, false); - script->set_constant("SI_KAGEMUSYA", SI_KAGEMUSYA, false, false); - script->set_constant("SI_ZANGETSU", SI_ZANGETSU, false, false); - script->set_constant("SI_PHI_DEMON", SI_PHI_DEMON, false, false); - script->set_constant("SI_GENSOU", SI_GENSOU, false, false); - script->set_constant("SI_AKAITSUKI", SI_AKAITSUKI, false, false); - script->set_constant("SI_TETANY", SI_TETANY, false, false); - script->set_constant("SI_GM_BATTLE", SI_GM_BATTLE, false, false); - script->set_constant("SI_GM_BATTLE2", SI_GM_BATTLE2, false, false); - script->set_constant("SI_2011RWC_SCROLL", SI_2011RWC_SCROLL, false, false); - script->set_constant("SI_ACTIVE_MONSTER_TRANSFORM", SI_ACTIVE_MONSTER_TRANSFORM, false, false); - script->set_constant("SI_MYSTICPOWDER", SI_MYSTICPOWDER, false, false); - script->set_constant("SI_ECLAGE_RECALL", SI_ECLAGE_RECALL, false, false); - script->set_constant("SI_ENTRY_QUEUE_APPLY_DELAY", SI_ENTRY_QUEUE_APPLY_DELAY, false, false); - script->set_constant("SI_REUSE_LIMIT_ECL", SI_REUSE_LIMIT_ECL, false, false); - script->set_constant("SI_M_LIFEPOTION", SI_M_LIFEPOTION, false, false); - script->set_constant("SI_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT", SI_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT, false, false); - script->set_constant("SI_UNKNOWN_NAME", SI_UNKNOWN_NAME, false, false); - script->set_constant("SI_ON_PUSH_CART", SI_ON_PUSH_CART, false, false); - script->set_constant("SI_HAT_EFFECT", SI_HAT_EFFECT, false, false); - script->set_constant("SI_FLOWER_LEAF", SI_FLOWER_LEAF, false, false); - script->set_constant("SI_RAY_OF_PROTECTION", SI_RAY_OF_PROTECTION, false, false); - script->set_constant("SI_GLASTHEIM_ATK", SI_GLASTHEIM_ATK, false, false); - script->set_constant("SI_GLASTHEIM_DEF", SI_GLASTHEIM_DEF, false, false); - script->set_constant("SI_GLASTHEIM_HEAL", SI_GLASTHEIM_HEAL, false, false); - script->set_constant("SI_GLASTHEIM_HIDDEN", SI_GLASTHEIM_HIDDEN, false, false); - script->set_constant("SI_GLASTHEIM_STATE", SI_GLASTHEIM_STATE, false, false); - script->set_constant("SI_GLASTHEIM_ITEMDEF", SI_GLASTHEIM_ITEMDEF, false, false); - script->set_constant("SI_GLASTHEIM_HPSP", SI_GLASTHEIM_HPSP, false, false); - script->set_constant("SI_HOMUN_SKILL_POSTDELAY", SI_HOMUN_SKILL_POSTDELAY, false, false); - script->set_constant("SI_ALMIGHTY", SI_ALMIGHTY, false, false); - script->set_constant("SI_GVG_GIANT", SI_GVG_GIANT, false, false); - script->set_constant("SI_GVG_GOLEM", SI_GVG_GOLEM, false, false); - script->set_constant("SI_GVG_STUN", SI_GVG_STUN, false, false); - script->set_constant("SI_GVG_STONE", SI_GVG_STONE, false, false); - script->set_constant("SI_GVG_FREEZ", SI_GVG_FREEZ, false, false); - script->set_constant("SI_GVG_SLEEP", SI_GVG_SLEEP, false, false); - script->set_constant("SI_GVG_CURSE", SI_GVG_CURSE, false, false); - script->set_constant("SI_GVG_SILENCE", SI_GVG_SILENCE, false, false); - script->set_constant("SI_GVG_BLIND", SI_GVG_BLIND, false, false); - script->set_constant("SI_CLIENT_ONLY_EQUIP_ARROW", SI_CLIENT_ONLY_EQUIP_ARROW, false, false); - script->set_constant("SI_CLAN_INFO", SI_CLAN_INFO, false, false); - script->set_constant("SI_JP_EVENT01", SI_JP_EVENT01, false, false); - script->set_constant("SI_JP_EVENT02", SI_JP_EVENT02, false, false); - script->set_constant("SI_JP_EVENT03", SI_JP_EVENT03, false, false); - - script->set_constant("SI_JP_EVENT04", SI_JP_EVENT04, false, false); - script->set_constant("SI_TELEPORT_FIXEDCASTINGDELAY", SI_TELEPORT_FIXEDCASTINGDELAY, false, false); - script->set_constant("SI_GEFFEN_MAGIC1", SI_GEFFEN_MAGIC1, false, false); - script->set_constant("SI_GEFFEN_MAGIC2", SI_GEFFEN_MAGIC2, false, false); - script->set_constant("SI_GEFFEN_MAGIC3", SI_GEFFEN_MAGIC3, false, false); - script->set_constant("SI_QUEST_BUFF1", SI_QUEST_BUFF1, false, false); - script->set_constant("SI_QUEST_BUFF2", SI_QUEST_BUFF2, false, false); - script->set_constant("SI_QUEST_BUFF3", SI_QUEST_BUFF3, false, false); - script->set_constant("SI_REUSE_LIMIT_RECALL", SI_REUSE_LIMIT_RECALL, false, false); - script->set_constant("SI_SAVEPOSITION", SI_SAVEPOSITION, false, false); - script->set_constant("SI_HANDICAPSTATE_ICEEXPLO", SI_HANDICAPSTATE_ICEEXPLO, false, false); - script->set_constant("SI_FENRIR_CARD", SI_FENRIR_CARD, false, false); - script->set_constant("SI_REUSE_LIMIT_ASPD_POTION", SI_REUSE_LIMIT_ASPD_POTION, false, false); - script->set_constant("SI_MAXPAIN", SI_MAXPAIN, false, false); - script->set_constant("SI_PC_STOP", SI_PC_STOP, false, false); - script->set_constant("SI_FRIGG_SONG", SI_FRIGG_SONG, false, false); - script->set_constant("SI_OFFERTORIUM", SI_OFFERTORIUM, false, false); - script->set_constant("SI_TELEKINESIS_INTENSE", SI_TELEKINESIS_INTENSE, false, false); - script->set_constant("SI_MOONSTAR", SI_MOONSTAR, false, false); - script->set_constant("SI_STRANGELIGHTS", SI_STRANGELIGHTS, false, false); - script->set_constant("SI_FULL_THROTTLE", SI_FULL_THROTTLE, false, false); - script->set_constant("SI_REBOUND", SI_REBOUND, false, false); - script->set_constant("SI_UNLIMIT", SI_UNLIMIT, false, false); - script->set_constant("SI_KINGS_GRACE", SI_KINGS_GRACE, false, false); - script->set_constant("SI_ITEM_ATKMAX", SI_ITEM_ATKMAX, false, false); - script->set_constant("SI_ITEM_ATKMIN", SI_ITEM_ATKMIN, false, false); - script->set_constant("SI_ITEM_MATKMAX", SI_ITEM_MATKMAX, false, false); - script->set_constant("SI_ITEM_MATKMIN", SI_ITEM_MATKMIN, false, false); - script->set_constant("SI_SUPER_STAR", SI_SUPER_STAR, false, false); - script->set_constant("SI_HIGH_RANKER", SI_HIGH_RANKER, false, false); - script->set_constant("SI_DARKCROW", SI_DARKCROW, false, false); - script->set_constant("SI_2013_VALENTINE1", SI_2013_VALENTINE1, false, false); - script->set_constant("SI_2013_VALENTINE2", SI_2013_VALENTINE2, false, false); - script->set_constant("SI_2013_VALENTINE3", SI_2013_VALENTINE3, false, false); - script->set_constant("SI_ILLUSIONDOPING", SI_ILLUSIONDOPING, false, false); - //script->set_constant("SI_WIDEWEB", SI_WIDEWEB, false, false); - script->set_constant("SI_CHILL", SI_CHILL, false, false); - script->set_constant("SI_BURNT", SI_BURNT, false, false); - //script->set_constant("SI_PCCAFE_PLAY_TIME", SI_PCCAFE_PLAY_TIME, false, false); - //script->set_constant("SI_TWISTED_TIME", SI_TWISTED_TIME, false, false); - script->set_constant("SI_FLASHCOMBO", SI_FLASHCOMBO, false, false); - //script->set_constant("SI_JITTER_BUFF1", SI_JITTER_BUFF1, false, false); - //script->set_constant("SI_JITTER_BUFF2", SI_JITTER_BUFF2, false, false); - //script->set_constant("SI_JITTER_BUFF3", SI_JITTER_BUFF3, false, false); - //script->set_constant("SI_JITTER_BUFF4", SI_JITTER_BUFF4, false, false); - //script->set_constant("SI_JITTER_BUFF5", SI_JITTER_BUFF5, false, false); - //script->set_constant("SI_JITTER_BUFF6", SI_JITTER_BUFF6, false, false); - //script->set_constant("SI_JITTER_BUFF7", SI_JITTER_BUFF7, false, false); - //script->set_constant("SI_JITTER_BUFF8", SI_JITTER_BUFF8, false, false); - //script->set_constant("SI_JITTER_BUFF9", SI_JITTER_BUFF9, false, false); - - //script->set_constant("SI_JITTER_BUFF10", SI_JITTER_BUFF10, false, false); - script->set_constant("SI_CUP_OF_BOZA", SI_CUP_OF_BOZA, false, false); - script->set_constant("SI_B_TRAP", SI_B_TRAP, false, false); - script->set_constant("SI_E_CHAIN", SI_E_CHAIN, false, false); - script->set_constant("SI_E_QD_SHOT_READY", SI_E_QD_SHOT_READY, false, false); - script->set_constant("SI_C_MARKER", SI_C_MARKER, false, false); - script->set_constant("SI_H_MINE", SI_H_MINE, false, false); - script->set_constant("SI_H_MINE_SPLASH", SI_H_MINE_SPLASH, false, false); - script->set_constant("SI_P_ALTER", SI_P_ALTER, false, false); - script->set_constant("SI_HEAT_BARREL", SI_HEAT_BARREL, false, false); - script->set_constant("SI_ANTI_M_BLAST", SI_ANTI_M_BLAST, false, false); - script->set_constant("SI_SLUGSHOT", SI_SLUGSHOT, false, false); - script->set_constant("SI_SWORDCLAN", SI_SWORDCLAN, false, false); - script->set_constant("SI_ARCWANDCLAN", SI_ARCWANDCLAN, false, false); - script->set_constant("SI_GOLDENMACECLAN", SI_GOLDENMACECLAN, false, false); - script->set_constant("SI_CROSSBOWCLAN", SI_CROSSBOWCLAN, false, false); - script->set_constant("SI_PACKING_ENVELOPE1", SI_PACKING_ENVELOPE1, false, false); - script->set_constant("SI_PACKING_ENVELOPE2", SI_PACKING_ENVELOPE2, false, false); - script->set_constant("SI_PACKING_ENVELOPE3", SI_PACKING_ENVELOPE3, false, false); - script->set_constant("SI_PACKING_ENVELOPE4", SI_PACKING_ENVELOPE4, false, false); - script->set_constant("SI_PACKING_ENVELOPE5", SI_PACKING_ENVELOPE5, false, false); - script->set_constant("SI_PACKING_ENVELOPE6", SI_PACKING_ENVELOPE6, false, false); - script->set_constant("SI_PACKING_ENVELOPE7", SI_PACKING_ENVELOPE7, false, false); - script->set_constant("SI_PACKING_ENVELOPE8", SI_PACKING_ENVELOPE8, false, false); - script->set_constant("SI_PACKING_ENVELOPE9", SI_PACKING_ENVELOPE9, false, false); - script->set_constant("SI_PACKING_ENVELOPE10", SI_PACKING_ENVELOPE10, false, false); - script->set_constant("SI_GLASTHEIM_TRANS", SI_GLASTHEIM_TRANS, false, false); - //script->set_constant("SI_ZONGZI_POUCH_TRANS", SI_ZONGZI_POUCH_TRANS, false, false); - script->set_constant("SI_HEAT_BARREL_AFTER", SI_HEAT_BARREL_AFTER, false, false); - script->set_constant("SI_DECORATION_OF_MUSIC", SI_DECORATION_OF_MUSIC, false, false); - //script->set_constant("SI_OVERSEAEXPUP", SI_OVERSEAEXPUP, false, false); - //script->set_constant("SI_CLOWN_N_GYPSY_CARD", SI_CLOWN_N_GYPSY_CARD, false, false); - //script->set_constant("SI_OPEN_NPC_MARKET", SI_OPEN_NPC_MARKET, false, false); - //script->set_constant("SI_BEEF_RIB_STEW", SI_BEEF_RIB_STEW, false, false); - //script->set_constant("SI_PORK_RIB_STEW", SI_PORK_RIB_STEW, false, false); - //script->set_constant("SI_CHUSEOK_MONDAY", SI_CHUSEOK_MONDAY, false, false); - //script->set_constant("SI_CHUSEOK_TUESDAY", SI_CHUSEOK_TUESDAY, false, false); - //script->set_constant("SI_CHUSEOK_WEDNESDAY", SI_CHUSEOK_WEDNESDAY, false, false); - //script->set_constant("SI_CHUSEOK_THURSDAY", SI_CHUSEOK_THURSDAY, false, false); - //script->set_constant("SI_CHUSEOK_FRIDAY", SI_CHUSEOK_FRIDAY, false, false); - //script->set_constant("SI_CHUSEOK_WEEKEND", SI_CHUSEOK_WEEKEND, false, false); - //script->set_constant("SI_ALL_LIGHTGUARD", SI_ALL_LIGHTGUARD, false, false); - //script->set_constant("SI_ALL_LIGHTGUARD_COOL_TIME", SI_ALL_LIGHTGUARD_COOL_TIME, false, false); - script->set_constant("SI_MTF_MHP", SI_MTF_MHP, false, false); - script->set_constant("SI_MTF_MSP", SI_MTF_MSP, false, false); - script->set_constant("SI_MTF_PUMPKIN", SI_MTF_PUMPKIN, false, false); - script->set_constant("SI_MTF_HITFLEE", SI_MTF_HITFLEE, false, false); - //script->set_constant("SI_MTF_CRIDAMAGE2", SI_MTF_CRIDAMAGE2, false, false); - //script->set_constant("SI_MTF_SPDRAIN", SI_MTF_SPDRAIN, false, false); - //script->set_constant("SI_ACUO_MINT_GUM", SI_ACUO_MINT_GUM, false, false); - - //script->set_constant("SI_S_HEALPOTION", SI_S_HEALPOTION, false, false); - //script->set_constant("SI_REUSE_LIMIT_S_HEAL_POTION", SI_REUSE_LIMIT_S_HEAL_POTION, false, false); - //script->set_constant("SI_PLAYTIME_STATISTICS", SI_PLAYTIME_STATISTICS, false, false); - //script->set_constant("SI_GN_CHANGEMATERIAL_OPERATOR", SI_GN_CHANGEMATERIAL_OPERATOR, false, false); - //script->set_constant("SI_GN_MIX_COOKING_OPERATOR", SI_GN_MIX_COOKING_OPERATOR, false, false); - //script->set_constant("SI_GN_MAKEBOMB_OPERATOR", SI_GN_MAKEBOMB_OPERATOR, false, false); - //script->set_constant("SI_GN_S_PHARMACY_OPERATOR", SI_GN_S_PHARMACY_OPERATOR, false, false); - //script->set_constant("SI_SO_EL_ANALYSIS_DISASSEMBLY_OPERATOR", SI_SO_EL_ANALYSIS_DISASSEMBLY_OPERATOR, false, false); - //script->set_constant("SI_SO_EL_ANALYSIS_COMBINATION_OPERATOR", SI_SO_EL_ANALYSIS_COMBINATION_OPERATOR, false, false); - //script->set_constant("SI_NC_MAGICDECOY_OPERATOR", SI_NC_MAGICDECOY_OPERATOR, false, false); - //script->set_constant("SI_GUILD_STORAGE", SI_GUILD_STORAGE, false, false); - //script->set_constant("SI_GC_POISONINGWEAPON_OPERATOR", SI_GC_POISONINGWEAPON_OPERATOR, false, false); - //script->set_constant("SI_WS_WEAPONREFINE_OPERATOR", SI_WS_WEAPONREFINE_OPERATOR, false, false); - //script->set_constant("SI_BS_REPAIRWEAPON_OPERATOR", SI_BS_REPAIRWEAPON_OPERATOR, false, false); - //script->set_constant("SI_GET_MAILBOX", SI_GET_MAILBOX, false, false); - //script->set_constant("SI_JUMPINGCLAN", SI_JUMPINGCLAN, false, false); - //script->set_constant("SI_JP_OTP", SI_JP_OTP, false, false); - //script->set_constant("SI_HANDICAPTOLERANCE_LEVELGAP", SI_HANDICAPTOLERANCE_LEVELGAP, false, false); - //script->set_constant("SI_MTF_RANGEATK2", SI_MTF_RANGEATK2, false, false); - //script->set_constant("SI_MTF_ASPD2", SI_MTF_ASPD2, false, false); - //script->set_constant("SI_MTF_MATK2", SI_MTF_MATK2, false, false); - //script->set_constant("SI_SHOW_NPCHPBAR", SI_SHOW_NPCHPBAR, false, false); - script->set_constant("SI_FLOWERSMOKE", SI_FLOWERSMOKE, false, false); - script->set_constant("SI_FSTONE", SI_FSTONE, false, false); - //script->set_constant("SI_DAILYSENDMAILCNT", SI_DAILYSENDMAILCNT, false, false); - //script->set_constant("SI_QSCARABA", SI_QSCARABA, false, false); - script->set_constant("SI_LJOSALFAR", SI_LJOSALFAR, false, false); - //script->set_constant("SI_PAD_READER_KNIGHT", SI_PAD_READER_KNIGHT, false, false); - //script->set_constant("SI_PAD_READER_CRUSADER", SI_PAD_READER_CRUSADER, false, false); - //script->set_constant("SI_PAD_READER_BLACKSMITH", SI_PAD_READER_BLACKSMITH, false, false); - //script->set_constant("SI_PAD_READER_ALCHEMIST", SI_PAD_READER_ALCHEMIST, false, false); - //script->set_constant("SI_PAD_READER_ASSASSIN", SI_PAD_READER_ASSASSIN, false, false); - //script->set_constant("SI_PAD_READER_ROGUE", SI_PAD_READER_ROGUE, false, false); - //script->set_constant("SI_PAD_READER_WIZARD", SI_PAD_READER_WIZARD, false, false); - //script->set_constant("SI_PAD_READER_SAGE", SI_PAD_READER_SAGE, false, false); - //script->set_constant("SI_PAD_READER_PRIEST", SI_PAD_READER_PRIEST, false, false); - //script->set_constant("SI_PAD_READER_MONK", SI_PAD_READER_MONK, false, false); - //script->set_constant("SI_PAD_READER_HUNTER", SI_PAD_READER_HUNTER, false, false); - //script->set_constant("SI_PAD_READER_BARD", SI_PAD_READER_BARD, false, false); - //script->set_constant("SI_PAD_READER_DANCER", SI_PAD_READER_DANCER, false, false); - //script->set_constant("SI_PAD_READER_TAEKWON", SI_PAD_READER_TAEKWON, false, false); - //script->set_constant("SI_PAD_READER_NINJA", SI_PAD_READER_NINJA, false, false); - //script->set_constant("SI_PAD_READER_GUNSLINGER", SI_PAD_READER_GUNSLINGER, false, false); - //script->set_constant("SI_PAD_READER_SUPERNOVICE", SI_PAD_READER_SUPERNOVICE, false, false); - //script->set_constant("SI_ESSENCE_OF_TIME", SI_ESSENCE_OF_TIME, false, false); - //script->set_constant("SI_MINIGAME_ROULETTE", SI_MINIGAME_ROULETTE, false, false); - //script->set_constant("SI_MINIGAME_GOLD_POINT", SI_MINIGAME_GOLD_POINT, false, false); - //script->set_constant("SI_MINIGAME_SILVER_POINT", SI_MINIGAME_SILVER_POINT, false, false); - //script->set_constant("SI_MINIGAME_BRONZE_POINT", SI_MINIGAME_BRONZE_POINT, false, false); - script->set_constant("SI_HAPPINESS_STAR", SI_HAPPINESS_STAR, false, false); - - //script->set_constant("SI_SUMMEREVENT01", SI_SUMMEREVENT01, false, false); - //script->set_constant("SI_SUMMEREVENT02", SI_SUMMEREVENT02, false, false); - //script->set_constant("SI_SUMMEREVENT03", SI_SUMMEREVENT03, false, false); - //script->set_constant("SI_SUMMEREVENT04", SI_SUMMEREVENT04, false, false); - //script->set_constant("SI_SUMMEREVENT05", SI_SUMMEREVENT05, false, false); - //script->set_constant("SI_MINIGAME_ROULETTE_BONUS_ITEM", SI_MINIGAME_ROULETTE_BONUS_ITEM, false, false); - //script->set_constant("SI_DRESS_UP", SI_DRESS_UP, false, false); - script->set_constant("SI_MAPLE_FALLS", SI_MAPLE_FALLS, false, false); - //script->set_constant("SI_ALL_NIFLHEIM_RECALL", SI_ALL_NIFLHEIM_RECALL, false, false); - // 859 - //script->set_constant("SI_MTF_MARIONETTE", SI_MTF_MARIONETTE, false, false); - //script->set_constant("SI_MTF_LUDE", SI_MTF_LUDE, false, false); - //script->set_constant("SI_MTF_CRUISER", SI_MTF_CRUISER, false, false); - script->set_constant("SI_MERMAID_LONGING", SI_MERMAID_LONGING, false, false); - script->set_constant("SI_MAGICAL_FEATHER", SI_MAGICAL_FEATHER, false, false); - //script->set_constant("SI_DRACULA_CARD", SI_DRACULA_CARD, false, false); - // 866 - //script->set_constant("SI_LIMIT_POWER_BOOSTER", SI_LIMIT_POWER_BOOSTER, false, false); - // 868 - // 869 - // 870 - // 871 - script->set_constant("SI_TIME_ACCESSORY", SI_TIME_ACCESSORY, false, false); - //script->set_constant("SI_EP16_DEF", SI_EP16_DEF, false, false); - //script->set_constant("SI_NORMAL_ATKED_SP", SI_NORMAL_ATKED_SP, false, false); - //script->set_constant("SI_BODYSTATE_STONECURSE", SI_BODYSTATE_STONECURSE, false, false); - //script->set_constant("SI_BODYSTATE_FREEZING", SI_BODYSTATE_FREEZING, false, false); - //script->set_constant("SI_BODYSTATE_STUN", SI_BODYSTATE_STUN, false, false); - //script->set_constant("SI_BODYSTATE_SLEEP", SI_BODYSTATE_SLEEP, false, false); - //script->set_constant("SI_BODYSTATE_UNDEAD", SI_BODYSTATE_UNDEAD, false, false); - //script->set_constant("SI_BODYSTATE_STONECURSE_ING", SI_BODYSTATE_STONECURSE_ING, false, false); - //script->set_constant("SI_BODYSTATE_BURNNING", SI_BODYSTATE_BURNNING, false, false); - //script->set_constant("SI_BODYSTATE_IMPRISON", SI_BODYSTATE_IMPRISON, false, false); - //script->set_constant("SI_HEALTHSTATE_POISON", SI_HEALTHSTATE_POISON, false, false); - //script->set_constant("SI_HEALTHSTATE_CURSE", SI_HEALTHSTATE_CURSE, false, false); - //script->set_constant("SI_HEALTHSTATE_SILENCE", SI_HEALTHSTATE_SILENCE, false, false); - //script->set_constant("SI_HEALTHSTATE_CONFUSION", SI_HEALTHSTATE_CONFUSION, false, false); - //script->set_constant("SI_HEALTHSTATE_BLIND", SI_HEALTHSTATE_BLIND, false, false); - //script->set_constant("SI_HEALTHSTATE_ANGELUS", SI_HEALTHSTATE_ANGELUS, false, false); - //script->set_constant("SI_HEALTHSTATE_BLOODING", SI_HEALTHSTATE_BLOODING, false, false); - //script->set_constant("SI_HEALTHSTATE_HEAVYPOISON", SI_HEALTHSTATE_HEAVYPOISON, false, false); - //script->set_constant("SI_HEALTHSTATE_FEAR", SI_HEALTHSTATE_FEAR, false, false); - //script->set_constant("SI_CHERRY_BLOSSOM_CAKE", SI_CHERRY_BLOSSOM_CAKE, false, false); - script->set_constant("SI_SU_STOOP", SI_SU_STOOP, false, false); - script->set_constant("SI_CATNIPPOWDER", SI_CATNIPPOWDER, false, false); - script->set_constant("SI_BLOSSOM_FLUTTERING", SI_BLOSSOM_FLUTTERING, false, false); - script->set_constant("SI_SV_ROOTTWIST", SI_SV_ROOTTWIST, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_NOTHING", SI_ATTACK_PROPERTY_NOTHING, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_WATER", SI_ATTACK_PROPERTY_WATER, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_GROUND", SI_ATTACK_PROPERTY_GROUND, false, false); - - //script->set_constant("SI_ATTACK_PROPERTY_FIRE", SI_ATTACK_PROPERTY_FIRE, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_WIND", SI_ATTACK_PROPERTY_WIND, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_POISON", SI_ATTACK_PROPERTY_POISON, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_SAINT", SI_ATTACK_PROPERTY_SAINT, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_DARKNESS", SI_ATTACK_PROPERTY_DARKNESS, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_TELEKINESIS", SI_ATTACK_PROPERTY_TELEKINESIS, false, false); - //script->set_constant("SI_ATTACK_PROPERTY_UNDEAD", SI_ATTACK_PROPERTY_UNDEAD, false, false); - //script->set_constant("SI_RESIST_PROPERTY_NOTHING", SI_RESIST_PROPERTY_NOTHING, false, false); - //script->set_constant("SI_RESIST_PROPERTY_WATER", SI_RESIST_PROPERTY_WATER, false, false); - //script->set_constant("SI_RESIST_PROPERTY_GROUND", SI_RESIST_PROPERTY_GROUND, false, false); - //script->set_constant("SI_RESIST_PROPERTY_FIRE", SI_RESIST_PROPERTY_FIRE, false, false); - //script->set_constant("SI_RESIST_PROPERTY_WIND", SI_RESIST_PROPERTY_WIND, false, false); - //script->set_constant("SI_RESIST_PROPERTY_POISON", SI_RESIST_PROPERTY_POISON, false, false); - //script->set_constant("SI_RESIST_PROPERTY_SAINT", SI_RESIST_PROPERTY_SAINT, false, false); - //script->set_constant("SI_RESIST_PROPERTY_DARKNESS", SI_RESIST_PROPERTY_DARKNESS, false, false); - //script->set_constant("SI_RESIST_PROPERTY_TELEKINESIS", SI_RESIST_PROPERTY_TELEKINESIS, false, false); - //script->set_constant("SI_RESIST_PROPERTY_UNDEAD", SI_RESIST_PROPERTY_UNDEAD, false, false); - script->set_constant("SI_BITESCAR", SI_BITESCAR, false, false); - script->set_constant("SI_ARCLOUSEDASH", SI_ARCLOUSEDASH, false, false); - script->set_constant("SI_TUNAPARTY", SI_TUNAPARTY, false, false); - script->set_constant("SI_SHRIMP", SI_SHRIMP, false, false); - script->set_constant("SI_FRESHSHRIMP", SI_FRESHSHRIMP, false, false); - //script->set_constant("SI_PERIOD_RECEIVEITEM", SI_PERIOD_RECEIVEITEM, false, false); - //script->set_constant("SI_PERIOD_PLUSEXP", SI_PERIOD_PLUSEXP, false, false); - //script->set_constant("SI_PERIOD_PLUSJOBEXP", SI_PERIOD_PLUSJOBEXP, false, false); - //script->set_constant("SI_RUNEHELM", SI_RUNEHELM, false, false); - //script->set_constant("SI_HELM_VERKANA", SI_HELM_VERKANA, false, false); - //script->set_constant("SI_HELM_RHYDO", SI_HELM_RHYDO, false, false); - //script->set_constant("SI_HELM_TURISUS", SI_HELM_TURISUS, false, false); - //script->set_constant("SI_HELM_HAGALAS", SI_HELM_HAGALAS, false, false); - //script->set_constant("SI_HELM_ISIA", SI_HELM_ISIA, false, false); - //script->set_constant("SI_HELM_ASIR", SI_HELM_ASIR, false, false); - //script->set_constant("SI_HELM_URJ", SI_HELM_URJ, false, false); - script->set_constant("SI_SUHIDE", SI_SUHIDE, false, false); - // 934 - //script->set_constant("SI_DORAM_BUF_01", SI_DORAM_BUF_01, false, false); - //script->set_constant("SI_DORAM_BUF_02", SI_DORAM_BUF_02, false, false); - script->set_constant("SI_SPRITEMABLE", SI_SPRITEMABLE, false, false); - //script->set_constant("SI_AID_PERIOD_RECEIVEITEM", SI_AID_PERIOD_RECEIVEITEM, false, false); - //script->set_constant("SI_AID_PERIOD_PLUSEXP", SI_AID_PERIOD_PLUSEXP, false, false); - //script->set_constant("SI_AID_PERIOD_PLUSJOBEXP", SI_AID_PERIOD_PLUSJOBEXP, false, false); - //script->set_constant("SI_AID_PERIOD_DEADPENALTY", SI_AID_PERIOD_DEADPENALTY, false, false); - //script->set_constant("SI_AID_PERIOD_ADDSTOREITEMCOUNT", SI_AID_PERIOD_ADDSTOREITEMCOUNT, false, false); - // 943 - // 944 - // 945 - // 946 - // 947 - // 948 - // 949 - //script->set_constant("SI_HISS", SI_HISS, false, false); - // 951 - //script->set_constant("SI_NYANGGRASS", SI_NYANGGRASS, false, false); - //script->set_constant("SI_CHATTERING", SI_CHATTERING, false, false); - // 954 - // 955 - // 956 - // 957 - // 958 - // 959 - // 960 - //script->set_constant("SI_GROOMING", SI_GROOMING, false, false); - //script->set_constant("SI_PROTECTIONOFSHRIMP", SI_PROTECTIONOFSHRIMP, false, false); - //script->set_constant("SI_EP16_2_BUFF_SS", SI_EP16_2_BUFF_SS, false, false); - //script->set_constant("SI_EP16_2_BUFF_SC", SI_EP16_2_BUFF_SC, false, false); - //script->set_constant("SI_EP16_2_BUFF_AC", SI_EP16_2_BUFF_AC, false, false); - //script->set_constant("SI_GS_MAGICAL_BULLET", SI_GS_MAGICAL_BULLET, false, false); - // 967 - // 968 - // 969 - // 970 - // 971 - // 972 - // 973 - // 974 - // 975 - //script->set_constant("SI_FALLEN_ANGEL", SI_FALLEN_ANGEL, false, false); - // 977 - // 978 - //script->set_constant("SI_BLAZE_BEAD", SI_BLAZE_BEAD, false, false); - //script->set_constant("SI_FROZEN_BEAD", SI_FROZEN_BEAD, false, false); - //script->set_constant("SI_BREEZE_BEAD", SI_BREEZE_BEAD, false, false); - // 982 - //script->set_constant("SI_AID_PERIOD_RECEIVEITEM_2ND", SI_AID_PERIOD_RECEIVEITEM_2ND, false, false); - //script->set_constant("SI_AID_PERIOD_PLUSEXP_2ND", SI_AID_PERIOD_PLUSEXP_2ND, false, false); - //script->set_constant("SI_AID_PERIOD_PLUSJOBEXP_2ND", SI_AID_PERIOD_PLUSJOBEXP_2ND, false, false); - //script->set_constant("SI_PRONTERA_JP", SI_PRONTERA_JP, false, false); - // 987 - //script->set_constant("SI_GLOOM_CARD", SI_GLOOM_CARD, false, false); - //script->set_constant("SI_PHARAOH_CARD", SI_PHARAOH_CARD, false, false); - //script->set_constant("SI_KIEL_CARD", SI_KIEL_CARD, false, false); - // 991 - //script->set_constant("SI_CHEERUP", SI_CHEERUP, false, false); - // 993 - // 994 - //script->set_constant("SI_S_MANAPOTION", SI_S_MANAPOTION, false, false); - //script->set_constant("SI_M_DEFSCROLL", SI_M_DEFSCROLL, false, false); - // 997 - // 998 - // 999 - //script->set_constant("SI_AS_RAGGED_GOLEM_CARD", SI_AS_RAGGED_GOLEM_CARD, false, false); - //script->set_constant("SI_LHZ_DUN_N1", SI_LHZ_DUN_N1, false, false); - //script->set_constant("SI_LHZ_DUN_N2", SI_LHZ_DUN_N2, false, false); - //script->set_constant("SI_LHZ_DUN_N3", SI_LHZ_DUN_N3, false, false); - //script->set_constant("SI_LHZ_DUN_N4", SI_LHZ_DUN_N4, false, false); - - script->set_constant("SI_MAX", SI_MAX, false, false); diff --git a/src/map/date.c b/src/map/date.c index 8060db458..9db7155ab 100644 --- a/src/map/date.c +++ b/src/map/date.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/date.h b/src/map/date.h index c5fe56d5f..1673b8c6d 100644 --- a/src/map/date.h +++ b/src/map/date.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/duel.c b/src/map/duel.c index c2c24e37b..c66fd6fc2 100644 --- a/src/map/duel.c +++ b/src/map/duel.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,27 +41,12 @@ struct duel_interface *duel; *------------------------------------------*/ static void duel_savetime(struct map_session_data *sd) { - time_t clock; - struct tm *t; - - time(&clock); - t = localtime(&clock); - - pc_setglobalreg(sd, script->add_variable("PC_LAST_DUEL_TIME"), t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min); + pc_setglobalreg(sd, script->add_variable("PC_LAST_DUEL_TIME"), (int)time(NULL)); } -static int duel_checktime(struct map_session_data *sd) +static int64 duel_difftime(struct map_session_data *sd) { - int diff; - time_t clock; - struct tm *t; - - time(&clock); - t = localtime(&clock); - - diff = t->tm_mday*24*60 + t->tm_hour*60 + t->tm_min - pc_readglobalreg(sd, script->add_variable("PC_LAST_DUEL_TIME") ); - - return !(diff >= 0 && diff < battle_config.duel_time_interval); + return (pc_readglobalreg(sd, script->add_variable("PC_LAST_DUEL_TIME")) + battle_config.duel_time_interval - (int)time(NULL)); } static int duel_showinfo_sub(struct map_session_data *sd, va_list va) @@ -233,7 +218,7 @@ void duel_defaults(void) duel->reject = duel_reject; duel->leave = duel_leave; duel->showinfo = duel_showinfo; - duel->checktime = duel_checktime; + duel->difftime = duel_difftime; duel->init = do_init_duel; duel->final = do_final_duel; diff --git a/src/map/duel.h b/src/map/duel.h index facb09ff9..1620ca891 100644 --- a/src/map/duel.h +++ b/src/map/duel.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,7 +52,7 @@ struct duel_interface { void (*reject) (const unsigned int did, struct map_session_data* sd); void (*leave) (const unsigned int did, struct map_session_data* sd); void (*showinfo) (const unsigned int did, struct map_session_data* sd); - int (*checktime) (struct map_session_data* sd); + int64 (*difftime) (struct map_session_data* sd); void (*init) (bool minimal); void (*final) (void); diff --git a/src/map/elemental.c b/src/map/elemental.c index b7bd5c090..f176bb9e2 100644 --- a/src/map/elemental.c +++ b/src/map/elemental.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -788,8 +788,8 @@ static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_ return 0; //Already walking to him if( DIFF_TICK(tick, ed->ud.canmove_tick) < 0 ) return 0; //Can't move yet. - if( map->search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1) - && unit->walktoxy(&ed->bl, x, y, 0) ) + if (map->search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1) != 0 + && unit->walk_toxy(&ed->bl, x, y, 0) == 0) return 0; } diff --git a/src/map/elemental.h b/src/map/elemental.h index 0c6b4ec27..71c991268 100644 --- a/src/map/elemental.h +++ b/src/map/elemental.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/guild.c b/src/map/guild.c index 17bf7fa6b..f344878e1 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,6 +38,7 @@ #include "map/storage.h" #include "common/HPM.h" #include "common/cbasetypes.h" +#include "common/conf.h" #include "common/ers.h" #include "common/memmgr.h" #include "common/mapindex.h" @@ -147,26 +148,126 @@ static int guild_check_skill_require(struct guild *g, int id) return 1; } -static bool guild_read_castledb(char *str[], int columns, int current) -{// <castle id>,<map name>,<castle name>,<castle event>[,<reserved/unused switch flag>] - struct guild_castle *gc; - int index; +static bool guild_read_castledb_libconfig(void) +{ + struct config_t castle_conf; + struct config_setting_t *castle_db = NULL, *it = NULL; + char config_filename[256]; + libconfig->format_db_path("castle_db.conf", config_filename, sizeof(config_filename)); + int i = 0; + + if (libconfig->load_file(&castle_conf, config_filename) == 0) + return false; - nullpo_retr(false, str); - index = mapindex->name2id(str[1]); - if (map->mapindex2mapid(index) < 0) // Map not found or on another map-server + if ((castle_db = libconfig->setting_get_member(castle_conf.root, "castle_db")) == NULL) { + libconfig->destroy(&castle_conf); + ShowError("guild_read_castledb_libconfig: can't read %s\n", config_filename); return false; + } + + while ((it = libconfig->setting_get_elem(castle_db, i++)) != NULL) { + guild->read_castledb_libconfig_sub(it, i - 1, config_filename); + } + + libconfig->destroy(&castle_conf); + ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", db_size(guild->castle_db), config_filename); + return true; +} + +static bool guild_read_castledb_libconfig_sub(struct config_setting_t *it, int idx, const char *source) +{ + nullpo_ret(it); + nullpo_ret(source); + struct guild_castle *gc = NULL; + const char *name = NULL; + int i32 = 0; CREATE(gc, struct guild_castle, 1); - gc->castle_id = atoi(str[0]); + + if (libconfig->setting_lookup_int(it, "CastleID", &i32) == CONFIG_FALSE) { + aFree(gc); + ShowWarning("guild_read_castledb_libconfig_sub: Invalid or missing CastleID (%d) in \"%s\", entry #%d, skipping.\n", i32, source, idx); + return false; + } + gc->castle_id = i32; + + if (libconfig->setting_lookup_string(it, "MapName", &name) == CONFIG_FALSE) { + aFree(gc); + ShowWarning("guild_read_castledb_libconfig_sub: Invalid or missing MapName in \"%s\", entry #%d, skipping.\n", source, idx); + return false; + } + int index = mapindex->name2id(name); + if (map->mapindex2mapid(index) < 0) { + aFree(gc); + ShowWarning("guild_read_castledb_libconfig_sub: Invalid MapName (%s) in \"%s\", entry #%d, skipping.\n", name, source, idx); + return false; + } gc->mapindex = index; - safestrncpy(gc->castle_name, str[2], sizeof(gc->castle_name)); - safestrncpy(gc->castle_event, str[3], sizeof(gc->castle_event)); - idb_put(guild->castle_db,gc->castle_id,gc); + if (libconfig->setting_lookup_string(it, "CastleName", &name) == CONFIG_FALSE) { + aFree(gc); + ShowWarning("guild_read_castledb_libconfig_sub: Invalid CastleName in \"%s\", entry #%d, skipping.\n", source, idx); + return false; + } + safestrncpy(gc->castle_name, name, sizeof(gc->castle_name)); - //intif->guild_castle_info(gc->castle_id); + if (libconfig->setting_lookup_string(it, "OnGuildBreakEventName", &name) == CONFIG_FALSE){ + aFree(gc); + ShowWarning("guild_read_castledb_libconfig_sub: Invalid OnGuildBreakEventName in \"%s\", entry #%d, skipping.\n", source, idx); + return false; + } + safestrncpy(gc->castle_event, name, sizeof(gc->castle_event)); + if (itemdb->lookup_const(it, "SiegeType", &i32) && (i32 >= SIEGE_TYPE_MAX || i32 < 0)) { + ShowWarning("guild_read_castledb_libconfig_sub: Invalid SiegeType in \"%s\", entry #%d, defaulting to SIEGE_TYPE_FE.\n", source, idx); + gc->siege_type = SIEGE_TYPE_FE; + } else { + gc->siege_type = i32; + } + + libconfig->setting_lookup_bool_real(it, "EnableClientWarp", &gc->enable_client_warp); + if (gc->enable_client_warp == true) { + struct config_setting_t *wd = libconfig->setting_get_member(it, "ClientWarp"); + guild->read_castledb_libconfig_sub_warp(wd, source, gc); + } + idb_put(guild->castle_db, gc->castle_id, gc); + return true; +} + +static bool guild_read_castledb_libconfig_sub_warp(struct config_setting_t *wd, const char *source, struct guild_castle *gc) +{ + nullpo_retr(false, wd); + nullpo_retr(false, gc); + nullpo_retr(false, source); + + int64 i64 = 0; + struct config_setting_t *it = libconfig->setting_get_member(wd, "Position"); + if (config_setting_is_list(it)) { + int m = map->mapindex2mapid(gc->mapindex); + + gc->client_warp.x = libconfig->setting_get_int_elem(it, 0); + gc->client_warp.y = libconfig->setting_get_int_elem(it, 1); + if (gc->client_warp.x < 0 || gc->client_warp.x >= map->list[m].xs || gc->client_warp.y < 0 || gc->client_warp.y >= map->list[m].ys) { + ShowWarning("guild_read_castledb_libconfig_sub_warp: Invalid Position in \"%s\", for castle (%d).\n", source, gc->castle_id); + return false; + } + } else { + ShowWarning("guild_read_castledb_libconfig_sub_warp: Invalid format for Position in \"%s\", for castle (%d).\n", source, gc->castle_id); + return false; + } + + if (libconfig->setting_lookup_int64(wd, "ZenyCost", &i64)) { + if (i64 > MAX_ZENY) { + ShowWarning("guild_read_castledb_libconfig_sub_warp: ZenyCost is too big in \"%s\", for castle (%d), capping to MAX_ZENY.\n", source, gc->castle_id); + } + gc->client_warp.zeny = cap_value((int)i64, 0, MAX_ZENY); + } + if (libconfig->setting_lookup_int64(wd, "ZenyCostSiegeTime", &i64)) { + if (i64 > MAX_ZENY) { + ShowWarning("guild_read_castledb_libconfig_sub_warp: ZenyCostSiegeTime is too big in \"%s\", for castle (%d), capping to MAX_ZENY.\n", source, gc->castle_id); + } + gc->client_warp.zeny_siege = cap_value((int)i64, 0, MAX_ZENY); + } return true; } @@ -177,7 +278,7 @@ static struct guild *guild_search(int guild_id) } /// lookup: guild name -> guild* -static struct guild *guild_searchname(char *str) +static struct guild *guild_searchname(const char *str) { struct guild* g; struct DBIterator *iter = db_iterator(guild->db); @@ -465,6 +566,7 @@ static int guild_check_member(const struct guild *g) if (i == INDEX_NOT_FOUND) { sd->status.guild_id=0; sd->guild_emblem_id=0; + sd->guild = NULL; ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name); } } @@ -481,8 +583,11 @@ static int guild_recv_noinfo(int guild_id) iter = mapit_getallusers(); for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if( sd->status.guild_id == guild_id ) + if (sd->status.guild_id == guild_id) { sd->status.guild_id = 0; // erase guild + sd->guild_emblem_id = 0; + sd->guild = NULL; + } } mapit->free(iter); @@ -554,7 +659,7 @@ static int guild_recv_info(const struct guild *sg) before=*sg; //Perform the check on the user because the first load guild->check_member(sg); - if ((sd = map->nick2sd(sg->master)) != NULL) { + if ((sd = map->nick2sd(sg->master, false)) != NULL) { //If the guild master is online the first time the guild_info is received, //that means he was the first to join, so apply guild skill blocking here. if( battle_config.guild_skill_relog_delay == 1) @@ -772,6 +877,8 @@ static void guild_member_joined(struct map_session_data *sd) i = guild->getindex(g, sd->status.account_id, sd->status.char_id); if (i == INDEX_NOT_FOUND) { sd->status.guild_id = 0; + sd->guild_emblem_id = 0; + sd->guild = NULL; } else { g->member[i].sd = sd; sd->guild = g; @@ -1018,7 +1125,7 @@ static int guild_send_memberinfoshort(struct map_session_data *sd, int online) } // cleaned up [LuzZza] -static int guild_recv_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int16 class, uint32 last_login) +static int guild_recv_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class, uint32 last_login) { int i, alv, c, idx = INDEX_NOT_FOUND, om = 0, oldonline = -1; struct guild *g = guild->search(guild_id); @@ -1086,13 +1193,14 @@ static int guild_recv_memberinfoshort(int guild_id, int account_id, int char_id, *---------------------------------------------------*/ static int guild_send_message(struct map_session_data *sd, const char *mes) { - int len = (int)strlen(mes); nullpo_ret(sd); - if (sd->status.guild_id == 0) + if (sd->status.guild_id == 0 || sd->guild == NULL) return 0; - intif->guild_message(sd->status.guild_id, sd->status.account_id, mes, len); - guild->recv_message(sd->status.guild_id, sd->status.account_id, mes, len); + + int len = (int)strlen(mes); + + clif->guild_message(sd->guild, sd->status.account_id, mes, len); // Chat logging type 'G' / Guild Chat logs->chat(LOG_CHAT_GUILD, sd->status.guild_id, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); @@ -1101,18 +1209,6 @@ static int guild_send_message(struct map_session_data *sd, const char *mes) } /*==================================================== - * Guild receive a message, will be displayed to whole member - *---------------------------------------------------*/ -static int guild_recv_message(int guild_id, int account_id, const char *mes, int len) -{ - struct guild *g; - if( (g=guild->search(guild_id))==NULL) - return 0; - clif->guild_message(g,account_id,mes,len); - return 0; -} - -/*==================================================== * Member changing position in guild *---------------------------------------------------*/ static int guild_change_memberposition(int guild_id, int account_id, int char_id, short idx) @@ -1899,7 +1995,7 @@ static int guild_gm_changed(int guild_id, int account_id, int char_id) if (g->member[pos].sd && g->member[pos].sd->fd) { clif->message(g->member[pos].sd->fd, msg_sd(g->member[pos].sd,878)); //"You no longer are the Guild Master." g->member[pos].sd->state.gmaster_flag = 0; - clif->charnameack(0, &g->member[pos].sd->bl); + clif->blname_ack(0, &g->member[pos].sd->bl); } if (g->member[0].sd && g->member[0].sd->fd) { @@ -1907,7 +2003,7 @@ static int guild_gm_changed(int guild_id, int account_id, int char_id) g->member[0].sd->state.gmaster_flag = 1; //Block his skills for 5 minutes to prevent abuse. guild->block_skill(g->member[0].sd, 300000); - clif->charnameack(0, &g->member[pos].sd->bl); + clif->blname_ack(0, &g->member[pos].sd->bl); } // announce the change to all guild members @@ -2321,8 +2417,7 @@ static void do_init_guild(bool minimal) guild->infoevent_db = idb_alloc(DB_OPT_BASE); guild->expcache_ers = ers_new(sizeof(struct guild_expcache),"guild.c::expcache_ers",ERS_OPT_NONE); - sv->readdb(map->db_path, "castle_db.txt", ',', 4, 5, -1, guild->read_castledb); - + guild->read_castledb_libconfig(); sv->readdb(map->db_path, "guild_skill_tree.txt", ',', 2+MAX_GUILD_SKILL_REQUIRE*2, 2+MAX_GUILD_SKILL_REQUIRE*2, -1, guild->read_guildskill_tree_db); //guild skill tree [Komurka] timer->add_func_list(guild->payexp_timer,"guild_payexp_timer"); @@ -2430,7 +2525,6 @@ void guild_defaults(void) guild->change_emblem = guild_change_emblem; guild->emblem_changed = guild_emblem_changed; guild->send_message = guild_send_message; - guild->recv_message = guild_recv_message; guild->send_dot_remove = guild_send_dot_remove; guild->skillupack = guild_skillupack; guild->dobreak = guild_break; @@ -2457,7 +2551,9 @@ void guild_defaults(void) guild->payexp_timer = guild_payexp_timer; guild->sd_check = guild_sd_check; guild->read_guildskill_tree_db = guild_read_guildskill_tree_db; - guild->read_castledb = guild_read_castledb; + guild->read_castledb_libconfig = guild_read_castledb_libconfig; + guild->read_castledb_libconfig_sub = guild_read_castledb_libconfig_sub; + guild->read_castledb_libconfig_sub_warp = guild_read_castledb_libconfig_sub_warp; guild->payexp_timer_sub = guild_payexp_timer_sub; guild->send_xy_timer_sub = guild_send_xy_timer_sub; guild->send_xy_timer = guild_send_xy_timer; diff --git a/src/map/guild.h b/src/map/guild.h index 3df86a3ca..82582a864 100644 --- a/src/map/guild.h +++ b/src/map/guild.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -91,7 +91,7 @@ struct guild_interface { bool (*isallied) (int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex] /* */ struct guild *(*search) (int guild_id); - struct guild *(*searchname) (char *str); + struct guild *(*searchname) (const char *str); struct guild_castle *(*castle_search) (int gcid); /* */ struct guild_castle *(*mapname2gc) (const char* mapname); @@ -126,7 +126,7 @@ struct guild_interface { int (*check_alliance) (int guild_id1, int guild_id2, int flag); /* */ int (*send_memberinfoshort) (struct map_session_data *sd,int online); - int (*recv_memberinfoshort) (int guild_id, int account_id, int char_id, int online, int lv, int16 class, uint32 last_login); + int (*recv_memberinfoshort) (int guild_id, int account_id, int char_id, int online, int lv, int class, uint32 last_login); int (*change_memberposition) (int guild_id,int account_id,int char_id,short idx); int (*memberposition_changed) (struct guild *g,int idx,int pos); int (*change_position) (int guild_id,int idx,int mode,int exp_mode,const char *name); @@ -136,7 +136,6 @@ struct guild_interface { int (*change_emblem) (struct map_session_data *sd,int len,const char *data); int (*emblem_changed) (int len,int guild_id,int emblem_id,const char *data); int (*send_message) (struct map_session_data *sd, const char *mes); - int (*recv_message) (int guild_id,int account_id,const char *mes,int len); int (*send_dot_remove) (struct map_session_data *sd); int (*skillupack) (int guild_id,uint16 skill_id,int account_id); int (*dobreak) (struct map_session_data *sd, const char *name); @@ -165,7 +164,9 @@ struct guild_interface { int (*payexp_timer) (int tid, int64 tick, int id, intptr_t data); struct map_session_data *(*sd_check) (int guild_id, int account_id, int char_id); bool (*read_guildskill_tree_db) (char* split[], int columns, int current); - bool (*read_castledb) (char* str[], int columns, int current); + bool (*read_castledb_libconfig) (void); + bool (*read_castledb_libconfig_sub) (struct config_setting_t *it, int idx, const char *source); + bool (*read_castledb_libconfig_sub_warp) (struct config_setting_t *wd, const char *source, struct guild_castle *gc); int (*payexp_timer_sub) (union DBKey key, struct DBData *data, va_list ap); int (*send_xy_timer_sub) (union DBKey key, struct DBData *data, va_list ap); int (*send_xy_timer) (int tid, int64 tick, int id, intptr_t data); diff --git a/src/map/homunculus.c b/src/map/homunculus.c index 6df272243..65c457283 100644 --- a/src/map/homunculus.c +++ b/src/map/homunculus.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -172,26 +172,26 @@ static int homunculus_dead(struct homun_data *hd) } //Vaporize a character's homun. If flag, HP needs to be 80% or above. -static int homunculus_vaporize(struct map_session_data *sd, enum homun_state flag) +static int homunculus_vaporize(struct map_session_data *sd, enum homun_state state, bool force) { struct homun_data *hd; nullpo_ret(sd); hd = sd->hd; - if (!hd || hd->homunculus.vaporize != HOM_ST_ACTIVE) + if (hd == NULL || hd->bl.prev == NULL || hd->homunculus.vaporize != HOM_ST_ACTIVE) return 0; if (status->isdead(&hd->bl)) return 0; //Can't vaporize a dead homun. - if (flag == HOM_ST_REST && get_percentage(hd->battle_status.hp, hd->battle_status.max_hp) < 80) + if (!force && get_percentage(hd->battle_status.hp, hd->battle_status.max_hp) < 80) return 0; hd->regen.state.block = 3; //Block regen while vaporized. //Delete timers when vaporized. homun->hunger_timer_delete(hd); - hd->homunculus.vaporize = flag; + hd->homunculus.vaporize = state; if(battle_config.hom_setting&0x40) memset(hd->blockskill, 0, sizeof(hd->blockskill)); clif->hominfo(sd, sd->hd, 0); @@ -258,7 +258,7 @@ static int homunculus_calc_skilltree(struct homun_data *hd, int flag_evolve) for( i = 0; i < MAX_SKILL_TREE && ( id = homun->dbs->skill_tree[c][i].id ) > 0; i++ ) { if( hd->homunculus.hskill[ id - HM_SKILLBASE ].id ) continue; //Skill already known. - j = ( flag_evolve ) ? 10 : hd->homunculus.intimacy; + j = ( flag_evolve ) ? 1000 : hd->homunculus.intimacy; if( j < homun->dbs->skill_tree[c][i].intimacylv ) continue; if(!battle_config.skillfree) { @@ -401,7 +401,7 @@ static bool homunculus_levelup(struct homun_data *hd) if ( battle_config.homunculus_show_growth ) { char output[256] ; sprintf(output, - "Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", + msg_sd(hd->master, 892), // Growth: hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) growth_max_hp, growth_max_sp, growth_str/10.0, growth_agi/10.0, growth_vit/10.0, growth_int/10.0, growth_dex/10.0, growth_luk/10.0); @@ -411,7 +411,7 @@ static bool homunculus_levelup(struct homun_data *hd) return true; } -static int homunculus_change_class(struct homun_data *hd, short class_) +static int homunculus_change_class(struct homun_data *hd, int class_) { int i = homun->db_search(class_,HOMUNCULUS_CLASS); nullpo_retr(0, hd); @@ -525,6 +525,21 @@ static bool homunculus_mutate(struct homun_data *hd, int homun_id) return true; } +static int homunculus_gainexp_real(struct homun_data *hd, unsigned int exp) +{ + nullpo_ret(hd); + nullpo_ret(hd->master); + + hd->homunculus.exp += exp; + + if (hd->master->state.showexp && hd->exp_next > 0) { + char output[256]; + sprintf(output, msg_fd(hd->master->fd, 449), exp, ((float)exp / (float)hd->exp_next * (float)100)); + clif_disp_onlyself(hd->master, output); + } + return 1; +} + static int homunculus_gainexp(struct homun_data *hd, unsigned int exp) { enum homun_type htype; @@ -550,10 +565,10 @@ static int homunculus_gainexp(struct homun_data *hd, unsigned int exp) break; } - hd->homunculus.exp += exp; + homun->gainexp_real(hd, exp); - if(hd->homunculus.exp < hd->exp_next) { - clif->hominfo(hd->master,hd,0); + if (hd->homunculus.exp < hd->exp_next) { + clif->hominfo(hd->master, hd, 0); return 0; } @@ -779,7 +794,7 @@ static bool homunculus_change_name_ack(struct map_session_data *sd, const char * } safestrncpy(hd->homunculus.name, newname, NAME_LENGTH); aFree(newname); - clif->charnameack (0,&hd->bl); + clif->blname_ack(0,&hd->bl); hd->homunculus.rename_flag = 1; clif->hominfo(sd,hd,0); return true; @@ -820,7 +835,7 @@ static int homunculus_db_search(int key, int type) * @param hom The homunculus source data. * @retval false in case of errors. */ -static bool homunculus_create(struct map_session_data *sd, const struct s_homunculus *hom) +static bool homunculus_create(struct map_session_data *sd, const struct s_homunculus *hom, bool is_new) { struct homun_data *hd; int i = 0; @@ -864,7 +879,9 @@ static bool homunculus_create(struct map_session_data *sd, const struct s_homunc map->addiddb(&hd->bl); status_calc_homunculus(hd,SCO_FIRST); - status_percent_heal(&hd->bl, 100, 100); + if (is_new) { + status_percent_heal(&hd->bl, 100, 100); + } hd->hungry_timer = INVALID_TIMER; return true; @@ -921,6 +938,7 @@ static bool homunculus_recv_data(int account_id, const struct s_homunculus *sh, { struct map_session_data *sd; struct homun_data *hd; + bool is_new = false; nullpo_retr(false, sh); @@ -936,15 +954,17 @@ static bool homunculus_recv_data(int account_id, const struct s_homunculus *sh, if (sd->status.char_id != sh->char_id && sd->status.hom_id != sh->hom_id) return false; - if (sd->status.hom_id == 0) //Hom just created. + if (sd->status.hom_id == 0) { // Hom just created. sd->status.hom_id = sh->hom_id; + is_new = true; + } if (sd->hd != NULL) { //uh? Overwrite the data. memcpy(&sd->hd->homunculus, sh, sizeof sd->hd->homunculus); sd->hd->homunculus.char_id = sd->status.char_id; // Correct char id if necessary. } else { - homun->create(sd, sh); + homun->create(sd, sh, is_new); } hd = sd->hd; @@ -1306,7 +1326,7 @@ static bool homunculus_read_skill_db_sub(char *split[], int columns, int current homun->dbs->skill_tree[classid][j].need[k].lv = atoi(split[3+k*2+minJobLevelPresent+1]); } - homun->dbs->skill_tree[classid][j].intimacylv = atoi(split[13+minJobLevelPresent]); + homun->dbs->skill_tree[classid][j].intimacylv = atoi(split[13+minJobLevelPresent]) * 100; return true; } @@ -1369,7 +1389,7 @@ static void homunculus_exp_db_read(void) homun->dbs->exptable[MAX_LEVEL - 1] = 0; } fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s/%s"CL_RESET"'.\n", j, map->db_path, filename[i]); } } @@ -1433,6 +1453,7 @@ void homunculus_defaults(void) homun->evolve = homunculus_evolve; homun->mutate = homunculus_mutate; homun->gainexp = homunculus_gainexp; + homun->gainexp_real = homunculus_gainexp_real; homun->add_intimacy = homunculus_add_intimacy; homun->consume_intimacy = homunculus_consume_intimacy; homun->healed = homunculus_healed; diff --git a/src/map/homunculus.h b/src/map/homunculus.h index f0a156fd7..c12629f46 100644 --- a/src/map/homunculus.h +++ b/src/map/homunculus.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -133,7 +133,7 @@ struct homun_skill_tree_entry { short id; unsigned char max; unsigned char joblv; - short intimacylv; + int intimacylv; struct { short id; unsigned char lv; @@ -167,17 +167,18 @@ struct homunculus_interface { enum homun_type (*class2type) (int class_); void (*damaged) (struct homun_data *hd); int (*dead) (struct homun_data *hd); - int (*vaporize) (struct map_session_data *sd, enum homun_state flag); + int (*vaporize) (struct map_session_data *sd, enum homun_state state, bool force); int (*delete) (struct homun_data *hd, int emote); int (*checkskill) (struct homun_data *hd, uint16 skill_id); int (*calc_skilltree) (struct homun_data *hd, int flag_evolve); int (*skill_tree_get_max) (int id, int b_class); void (*skillup) (struct homun_data *hd, uint16 skill_id); bool (*levelup) (struct homun_data *hd); - int (*change_class) (struct homun_data *hd, short class_); + int (*change_class) (struct homun_data *hd, int class_); bool (*evolve) (struct homun_data *hd); bool (*mutate) (struct homun_data *hd, int homun_id); int (*gainexp) (struct homun_data *hd, unsigned int exp); + int (*gainexp_real) (struct homun_data *hd, unsigned int exp); unsigned int (*add_intimacy) (struct homun_data * hd, unsigned int value); unsigned int (*consume_intimacy) (struct homun_data *hd, unsigned int value); void (*healed) (struct homun_data *hd); @@ -189,7 +190,7 @@ struct homunculus_interface { int (*change_name) (struct map_session_data *sd, const char *name); bool (*change_name_ack) (struct map_session_data *sd, const char *name, int flag); int (*db_search) (int key,int type); - bool (*create) (struct map_session_data *sd, const struct s_homunculus *hom); + bool (*create) (struct map_session_data *sd, const struct s_homunculus *hom, bool is_new); void (*init_timers) (struct homun_data * hd); bool (*call) (struct map_session_data *sd); bool (*recv_data) (int account_id, const struct s_homunculus *sh, int flag); diff --git a/src/map/instance.c b/src/map/instance.c index 1e83b0b76..1104b7e88 100644 --- a/src/map/instance.c +++ b/src/map/instance.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; @@ -453,7 +446,7 @@ static int instance_cleanup_sub(struct block_list *bl, va_list ap) map->quit(BL_UCAST(BL_PC, bl)); break; case BL_NPC: - npc->unload(BL_UCAST(BL_NPC, bl), true); + npc->unload(BL_UCAST(BL_NPC, bl), true, true); break; case BL_MOB: unit->free(bl,CLR_OUTSIGHT); @@ -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/instance.h b/src/map/instance.h index 91928bf40..8206902c1 100644 --- a/src/map/instance.h +++ b/src/map/instance.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/intif.c b/src/map/intif.c index ed4c0e2d2..a420b7204 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -71,26 +71,26 @@ static int CheckForCharServer(void) } // pet -static int intif_create_pet(int account_id, int char_id, short pet_class, short pet_lv, int pet_egg_id, +static int intif_create_pet(int account_id, int char_id, int pet_class, int pet_lv, int pet_egg_id, int pet_equip, short intimate, short hungry, char rename_flag, char incubate, char *pet_name) { if (intif->CheckForCharServer()) return 0; nullpo_ret(pet_name); - WFIFOHEAD(inter_fd, 28 + NAME_LENGTH); + WFIFOHEAD(inter_fd, 32 + NAME_LENGTH); WFIFOW(inter_fd, 0) = 0x3080; WFIFOL(inter_fd, 2) = account_id; WFIFOL(inter_fd, 6) = char_id; - WFIFOW(inter_fd, 10) = pet_class; - WFIFOW(inter_fd, 12) = pet_lv; - WFIFOL(inter_fd, 14) = pet_egg_id; - WFIFOL(inter_fd, 18) = pet_equip; - WFIFOW(inter_fd, 22) = intimate; - WFIFOW(inter_fd, 24) = hungry; - WFIFOB(inter_fd, 26) = rename_flag; - WFIFOB(inter_fd, 27) = incubate; - memcpy(WFIFOP(inter_fd, 28), pet_name, NAME_LENGTH); - WFIFOSET(inter_fd, 28 + NAME_LENGTH); + WFIFOL(inter_fd, 10) = pet_class; + WFIFOL(inter_fd, 14) = pet_lv; + WFIFOL(inter_fd, 18) = pet_egg_id; + WFIFOL(inter_fd, 22) = pet_equip; + WFIFOW(inter_fd, 26) = intimate; + WFIFOW(inter_fd, 28) = hungry; + WFIFOB(inter_fd, 30) = rename_flag; + WFIFOB(inter_fd, 31) = incubate; + memcpy(WFIFOP(inter_fd, 32), pet_name, NAME_LENGTH); + WFIFOSET(inter_fd, 32 + NAME_LENGTH); return 0; } @@ -153,158 +153,6 @@ static int intif_rename(struct map_session_data *sd, int type, const char *name) return 0; } -// GM Send a message -static int intif_broadcast(const char *mes, int len, int type) -{ - int lp = (type&BC_COLOR_MASK) ? 4 : 0; - - nullpo_ret(mes); - Assert_ret(len < 32000); - // Send to the local players - clif->broadcast(NULL, mes, len, type, ALL_CLIENT); - - if (intif->CheckForCharServer()) - return 0; - - if (chrif->other_mapserver_count < 1) - return 0; //No need to send. - - WFIFOHEAD(inter_fd, 16 + lp + len); - WFIFOW(inter_fd,0) = 0x3000; - WFIFOW(inter_fd,2) = 16 + lp + len; - WFIFOL(inter_fd,4) = 0xFF000000; // 0xFF000000 color signals standard broadcast - WFIFOW(inter_fd,8) = 0; // fontType not used with standard broadcast - WFIFOW(inter_fd,10) = 0; // fontSize not used with standard broadcast - WFIFOW(inter_fd,12) = 0; // fontAlign not used with standard broadcast - WFIFOW(inter_fd,14) = 0; // fontY not used with standard broadcast - if (type&BC_BLUE) - WFIFOL(inter_fd,16) = 0x65756c62; //If there's "blue" at the beginning of the message, game client will display it in blue instead of yellow. - else if (type&BC_WOE) - WFIFOL(inter_fd,16) = 0x73737373; //If there's "ssss", game client will recognize message as 'WoE broadcast'. - memcpy(WFIFOP(inter_fd,16 + lp), mes, len); - WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); - return 0; -} - -static int intif_broadcast2(const char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY) -{ - nullpo_ret(mes); - Assert_ret(len < 32000); - // Send to the local players - clif->broadcast2(NULL, mes, len, fontColor, fontType, fontSize, fontAlign, fontY, ALL_CLIENT); - - if (intif->CheckForCharServer()) - return 0; - - if (chrif->other_mapserver_count < 1) - return 0; //No need to send. - - WFIFOHEAD(inter_fd, 16 + len); - WFIFOW(inter_fd,0) = 0x3000; - WFIFOW(inter_fd,2) = 16 + len; - WFIFOL(inter_fd,4) = fontColor; - WFIFOW(inter_fd,8) = fontType; - WFIFOW(inter_fd,10) = fontSize; - WFIFOW(inter_fd,12) = fontAlign; - WFIFOW(inter_fd,14) = fontY; - memcpy(WFIFOP(inter_fd,16), mes, len); - WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); - return 0; -} - -/// send a message using the main chat system -/// <sd> the source of message -/// <message> the message that was sent -static int intif_main_message(struct map_session_data *sd, const char *message) -{ - char output[256]; - - nullpo_ret(sd); - nullpo_ret(message); - - // format the message for main broadcasting - snprintf( output, sizeof(output), msg_txt(386), sd->status.name, message ); - - // send the message using the inter-server broadcast service - intif->broadcast2(output, (int)strlen(output) + 1, 0xFE000000, 0, 0, 0, 0); - - // log the chat message - logs->chat( LOG_CHAT_MAINCHAT, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message ); - - return 0; -} - -// The transmission of Wisp/Page to inter-server (player not found on this server) -static int intif_wis_message(struct map_session_data *sd, const char *nick, const char *mes, int mes_len) -{ - if (intif->CheckForCharServer()) - return 0; - nullpo_ret(sd); - nullpo_ret(nick); - nullpo_ret(mes); - - if (chrif->other_mapserver_count < 1) { - //Character not found. - clif->wis_end(sd->fd, 1); - return 0; - } - - WFIFOHEAD(inter_fd,mes_len + 52); - WFIFOW(inter_fd,0) = 0x3001; - WFIFOW(inter_fd,2) = mes_len + 52; - memcpy(WFIFOP(inter_fd,4), sd->status.name, NAME_LENGTH); - memcpy(WFIFOP(inter_fd,4+NAME_LENGTH), nick, NAME_LENGTH); - memcpy(WFIFOP(inter_fd,4+2*NAME_LENGTH), mes, mes_len); - WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); - - if (battle_config.etc_log) - ShowInfo("intif_wis_message from %s to %s (message: '%s')\n", sd->status.name, nick, mes); - - return 0; -} - -// The reply of Wisp/page -static int intif_wis_replay(int id, int flag) -{ - if (intif->CheckForCharServer()) - return 0; - WFIFOHEAD(inter_fd,7); - WFIFOW(inter_fd,0) = 0x3002; - WFIFOL(inter_fd,2) = id; - WFIFOB(inter_fd,6) = flag; // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target - WFIFOSET(inter_fd,7); - - if (battle_config.etc_log) - ShowInfo("intif_wis_replay: id: %d, flag:%d\n", id, flag); - - return 0; -} - -// The transmission of GM only Wisp/Page from server to inter-server -static int intif_wis_message_to_gm(char *wisp_name, int permission, char *mes) -{ - int mes_len; - if (intif->CheckForCharServer()) - return 0; - nullpo_ret(wisp_name); - nullpo_ret(mes); - mes_len = (int)strlen(mes) + 1; // + null - Assert_ret(mes_len > 0 && mes_len <= INT16_MAX - 32); - - WFIFOHEAD(inter_fd, mes_len + 32); - WFIFOW(inter_fd,0) = 0x3003; - WFIFOW(inter_fd,2) = mes_len + 32; - memcpy(WFIFOP(inter_fd,4), wisp_name, NAME_LENGTH); - WFIFOL(inter_fd,4+NAME_LENGTH) = permission; - memcpy(WFIFOP(inter_fd,8+NAME_LENGTH), mes, mes_len); - WFIFOSET(inter_fd, WFIFOW(inter_fd,2)); - - if (battle_config.etc_log) - ShowNotice("intif_wis_message_to_gm: from: '%s', required permission: %d, message: '%s'.\n", wisp_name, permission, mes); - - return 0; -} - //Request for saving registry values. static int intif_saveregistry(struct map_session_data *sd) { @@ -714,27 +562,6 @@ static int intif_break_party(int party_id) return 0; } -// Sending party chat -static int intif_party_message(int party_id, int account_id, const char *mes, int len) -{ - if (intif->CheckForCharServer()) - return 0; - - if (chrif->other_mapserver_count < 1) - return 0; //No need to send. - - nullpo_ret(mes); - Assert_ret(len > 0 && len < 32000); - WFIFOHEAD(inter_fd,len + 12); - WFIFOW(inter_fd,0)=0x3027; - WFIFOW(inter_fd,2)=len+12; - WFIFOL(inter_fd,4)=party_id; - WFIFOL(inter_fd,8)=account_id; - memcpy(WFIFOP(inter_fd,12),mes,len); - WFIFOSET(inter_fd,len+12); - return 0; -} - // Request a new leader for party static int intif_party_leaderchange(int party_id, int account_id, int char_id) { @@ -903,19 +730,19 @@ static int intif_guild_leave(int guild_id, int account_id, int char_id, int flag } //Update request / Lv online status of the guild members -static int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int16 class) +static int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class) { if (intif->CheckForCharServer()) return 0; - WFIFOHEAD(inter_fd, 19); + WFIFOHEAD(inter_fd, 23); WFIFOW(inter_fd, 0) = 0x3035; WFIFOL(inter_fd, 2) = guild_id; WFIFOL(inter_fd, 6) = account_id; WFIFOL(inter_fd,10) = char_id; WFIFOB(inter_fd,14) = online; - WFIFOW(inter_fd,15) = lv; - WFIFOW(inter_fd,17) = class; - WFIFOSET(inter_fd,19); + WFIFOL(inter_fd,15) = lv; + WFIFOL(inter_fd,19) = class; + WFIFOSET(inter_fd,23); return 0; } @@ -931,28 +758,6 @@ static int intif_guild_break(int guild_id) return 0; } -// Send a guild message -static int intif_guild_message(int guild_id, int account_id, const char *mes, int len) -{ - if (intif->CheckForCharServer()) - return 0; - - if (chrif->other_mapserver_count < 1) - return 0; //No need to send. - - nullpo_ret(mes); - Assert_ret(len > 0 && len < 32000); - WFIFOHEAD(inter_fd, len + 12); - WFIFOW(inter_fd,0)=0x3037; - WFIFOW(inter_fd,2)=len+12; - WFIFOL(inter_fd,4)=guild_id; - WFIFOL(inter_fd,8)=account_id; - memcpy(WFIFOP(inter_fd,12),mes,len); - WFIFOSET(inter_fd,len+12); - - return 0; -} - /** * Requests to change a basic guild information, it is parsed via mapif_parse_GuildBasicInfoChange * To see the information types that can be changed see mmo.h::guild_basic_info @@ -1166,98 +971,6 @@ static int intif_homunculus_requestdelete(int homun_id) //----------------------------------------------------------------- // Packets receive from inter server -// Wisp/Page reception // rewritten by [Yor] -static void intif_parse_WisMessage(int fd) -{ - struct map_session_data* sd; - const char *wisp_source; - char name[NAME_LENGTH]; - int id, i; - - id=RFIFOL(fd,4); - - safestrncpy(name, RFIFOP(fd,32), NAME_LENGTH); - sd = map->nick2sd(name); - if(sd == NULL || strcmp(sd->status.name, name) != 0) { - //Not found - intif_wis_replay(id,1); - return; - } - if(sd->state.ignoreAll) { - intif_wis_replay(id, 2); - return; - } - wisp_source = RFIFOP(fd,8); // speed up [Yor] - for(i=0; i < MAX_IGNORE_LIST && - sd->ignore[i].name[0] != '\0' && - strcmp(sd->ignore[i].name, wisp_source) != 0 - ; i++); - - if (i < MAX_IGNORE_LIST && sd->ignore[i].name[0] != '\0') { - //Ignored - intif_wis_replay(id, 2); - return; - } - //Success to send whisper. - clif->wis_message(sd->fd, wisp_source, RFIFOP(fd,56),RFIFOW(fd,2)-57); - intif_wis_replay(id,0); // success -} - -// Wisp/page transmission result reception -static void intif_parse_WisEnd(int fd) -{ - struct map_session_data* sd; - const char *playername = RFIFOP(fd, 2); - - if (battle_config.etc_log) - ShowInfo("intif_parse_wisend: player: %s, flag: %d\n", playername, RFIFOB(fd,26)); // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target - sd = map->nick2sd(playername); - if (sd != NULL) - clif->wis_end(sd->fd, RFIFOB(fd,26)); - - return; -} - -static int intif_parse_WisToGM_sub(struct map_session_data *sd, va_list va) -{ - int permission = va_arg(va, int); - char *wisp_name; - char *message; - int len; - - nullpo_ret(sd); - if (!pc_has_permission(sd, permission)) - return 0; - wisp_name = va_arg(va, char*); - message = va_arg(va, char*); - len = va_arg(va, int); - clif->wis_message(sd->fd, wisp_name, message, len); - return 1; -} - -// Received wisp message from map-server via char-server for ALL gm -// 0x3003/0x3803 <packet_len>.w <wispname>.24B <permission>.l <message>.?B -static void intif_parse_WisToGM(int fd) -{ - int permission, mes_len; - char Wisp_name[NAME_LENGTH]; - char mbuf[255] = { 0 }; - char *message; - - mes_len = RFIFOW(fd,2) - 33; // Length not including the NUL terminator - Assert_retv(mes_len > 0 && mes_len < 32000); - message = (mes_len >= 255 ? aMalloc(mes_len + 1) : mbuf); - - permission = RFIFOL(fd,28); - safestrncpy(Wisp_name, RFIFOP(fd,4), NAME_LENGTH); - safestrncpy(message, RFIFOP(fd,32), mes_len + 1); - // information is sent to all online GM - map->foreachpc(intif->pWisToGM_sub, permission, Wisp_name, message, mes_len); - - if (message != mbuf) - aFree(message); -} - // Request player registre static void intif_parse_Registers(int fd) { @@ -1464,12 +1177,6 @@ static void intif_parse_PartyMove(int fd) party->recv_movemap(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOW(fd,14),RFIFOB(fd,16),RFIFOW(fd,17)); } -// ACK party messages -static void intif_parse_PartyMessage(int fd) -{ - party->recv_message(RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); -} - // ACK guild creation static void intif_parse_GuildCreated(int fd) { @@ -1507,7 +1214,7 @@ static void intif_parse_GuildMemberWithdraw(int fd) // ACK guild member basic info static void intif_parse_GuildMemberInfoShort(int fd) { - guild->recv_memberinfoshort(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOW(fd,17),RFIFOL(fd,19)); + guild->recv_memberinfoshort(RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),RFIFOB(fd,14),RFIFOW(fd,15),RFIFOL(fd,17),RFIFOL(fd,21)); } // ACK guild break @@ -1619,12 +1326,6 @@ static void intif_parse_GuildEmblem(int fd) guild->emblem_changed(RFIFOW(fd,2)-12, RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12)); } -// ACK guild message -static void intif_parse_GuildMessage(int fd) -{ - guild->recv_message(RFIFOL(fd,4), RFIFOL(fd,8), RFIFOP(fd,12), RFIFOW(fd,2)-12); -} - // Reply guild castle data request static void intif_parse_GuildCastleDataLoad(int fd) { @@ -1640,7 +1341,7 @@ static void intif_parse_GuildMasterChanged(int fd) // Request pet creation static void intif_parse_CreatePet(int fd) { - pet->get_egg(RFIFOL(fd,2), RFIFOW(fd,6), RFIFOL(fd,8)); + pet->get_egg(RFIFOL(fd, 2), RFIFOL(fd, 6), RFIFOL(fd, 10)); } // ACK pet data @@ -2754,16 +2455,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; } @@ -2830,11 +2536,11 @@ static void intif_parse_RodexCheckName(int fd) struct map_session_data *sd = NULL; int reqchar_id = RFIFOL(fd, 2); int target_char_id = RFIFOL(fd, 6); - short target_class = RFIFOW(fd, 10); - int target_level = RFIFOL(fd, 12); + int target_class = RFIFOL(fd, 10); + int target_level = RFIFOL(fd, 14); char name[NAME_LENGTH]; - safestrncpy(name, RFIFOP(inter_fd, 16), NAME_LENGTH); + safestrncpy(name, RFIFOP(inter_fd, 18), NAME_LENGTH); if (reqchar_id <= 0) return; @@ -2855,6 +2561,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. @@ -2881,15 +2616,6 @@ static int intif_parse(int fd) } // Processing branch switch(cmd){ - case 0x3800: - if (RFIFOL(fd,4) == 0xFF000000) //Normal announce. - clif->broadcast(NULL, RFIFOP(fd,16), packet_len-16, BC_DEFAULT, ALL_CLIENT); - else //Color announce. - clif->broadcast2(NULL, RFIFOP(fd,16), packet_len-16, RFIFOL(fd,4), RFIFOW(fd,8), RFIFOW(fd,10), RFIFOW(fd,12), RFIFOW(fd,14), ALL_CLIENT); - break; - case 0x3801: intif->pWisMessage(fd); break; - case 0x3802: intif->pWisEnd(fd); break; - case 0x3803: intif->pWisToGM(fd); break; case 0x3804: intif->pRegisters(fd); break; case 0x3805: intif->pAccountStorage(fd); break; case 0x3806: intif->pChangeNameOk(fd); break; @@ -2905,14 +2631,12 @@ static int intif_parse(int fd) case 0x3824: intif->pPartyMemberWithdraw(fd); break; case 0x3825: intif->pPartyMove(fd); break; case 0x3826: intif->pPartyBroken(fd); break; - case 0x3827: intif->pPartyMessage(fd); break; case 0x3830: intif->pGuildCreated(fd); break; case 0x3831: intif->pGuildInfo(fd); break; case 0x3832: intif->pGuildMemberAdded(fd); break; case 0x3834: intif->pGuildMemberWithdraw(fd); break; case 0x3835: intif->pGuildMemberInfoShort(fd); break; case 0x3836: intif->pGuildBroken(fd); break; - case 0x3837: intif->pGuildMessage(fd); break; case 0x3839: intif->pGuildBasicInfoChanged(fd); break; case 0x383a: intif->pGuildMemberInfoChanged(fd); break; case 0x383b: intif->pGuildPosition(fd); break; @@ -2972,6 +2696,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; @@ -2992,16 +2718,16 @@ static int intif_parse(int fd) void intif_defaults(void) { const int packet_len_table [INTIF_PACKET_LEN_TABLE_SIZE] = { - -1,-1,27,-1, -1,-1,37,-1, 7, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f + 0, 0, 0, 0, -1,-1,37,-1, 7, 0, 0, 0, 0, 0, 0, 0, //0x3800-0x380f -1, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810 Achievements [Smokexyz/Hercules] - 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 - 10,-1,15, 0, 79,23, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 + 39,-1,15,15, 14,19, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 + 10,-1,15, 0, 79,25, 7, 0, 0,-1,-1,-1, 14,67,186,-1, //0x3830 -1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 -1,-1, 7, 7, 7,11, 8, 0, 10, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari] Clan System[Murilo BiO] -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] - 12,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 - -1,-1, 7, 3, 0,-1, 7, 15,16 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] / RoDEX [KirieZ] + 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, 23, 16 + sizeof(struct rodex_item) * RODEX_MAX_ITEM, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] / RoDEX [KirieZ] }; intif = &intif_s; @@ -3012,11 +2738,6 @@ void intif_defaults(void) /* funcs */ intif->parse = intif_parse; intif->create_pet = intif_create_pet; - intif->broadcast = intif_broadcast; - intif->broadcast2 = intif_broadcast2; - intif->main_message = intif_main_message; - intif->wis_message = intif_wis_message; - intif->wis_message_to_gm = intif_wis_message_to_gm; intif->saveregistry = intif_saveregistry; intif->request_registry = intif_request_registry; intif->request_account_storage = intif_request_account_storage; @@ -3030,7 +2751,6 @@ void intif_defaults(void) intif->party_leave = intif_party_leave; intif->party_changemap = intif_party_changemap; intif->break_party = intif_break_party; - intif->party_message = intif_party_message; intif->party_leaderchange = intif_party_leaderchange; intif->guild_create = intif_guild_create; intif->guild_request_info = intif_guild_request_info; @@ -3038,7 +2758,6 @@ void intif_defaults(void) intif->guild_leave = intif_guild_leave; intif->guild_memberinfoshort = intif_guild_memberinfoshort; intif->guild_break = intif_guild_break; - intif->guild_message = intif_guild_message; intif->guild_change_gm = intif_guild_change_gm; intif->guild_change_basicinfo = intif_guild_change_basicinfo; intif->guild_change_memberinfo = intif_guild_change_memberinfo; @@ -3102,10 +2821,6 @@ void intif_defaults(void) intif->achievements_request = intif_achievements_request; intif->achievements_save = intif_achievements_save; /* parse functions */ - intif->pWisMessage = intif_parse_WisMessage; - intif->pWisEnd = intif_parse_WisEnd; - intif->pWisToGM_sub = intif_parse_WisToGM_sub; - intif->pWisToGM = intif_parse_WisToGM; intif->pRegisters = intif_parse_Registers; intif->pChangeNameOk = intif_parse_ChangeNameOk; intif->pMessageToFD = intif_parse_MessageToFD; @@ -3120,14 +2835,12 @@ void intif_defaults(void) intif->pPartyMemberWithdraw = intif_parse_PartyMemberWithdraw; intif->pPartyMove = intif_parse_PartyMove; intif->pPartyBroken = intif_parse_PartyBroken; - intif->pPartyMessage = intif_parse_PartyMessage; intif->pGuildCreated = intif_parse_GuildCreated; intif->pGuildInfo = intif_parse_GuildInfo; intif->pGuildMemberAdded = intif_parse_GuildMemberAdded; intif->pGuildMemberWithdraw = intif_parse_GuildMemberWithdraw; intif->pGuildMemberInfoShort = intif_parse_GuildMemberInfoShort; intif->pGuildBroken = intif_parse_GuildBroken; - intif->pGuildMessage = intif_parse_GuildMessage; intif->pGuildBasicInfoChanged = intif_parse_GuildBasicInfoChanged; intif->pGuildMemberInfoChanged = intif_parse_GuildMemberInfoChanged; intif->pGuildPosition = intif_parse_GuildPosition; @@ -3171,6 +2884,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 21f7a494c..c397eda7d 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,13 +58,8 @@ struct intif_interface { int packet_len_table[INTIF_PACKET_LEN_TABLE_SIZE]; /* funcs */ int (*parse) (int fd); - int (*create_pet)(int account_id, int char_id, short pet_type, short pet_lv, int pet_egg_id, + int (*create_pet)(int account_id, int char_id, int pet_type, int pet_lv, int pet_egg_id, int pet_equip, short intimate, short hungry, char rename_flag, char incubate, char *pet_name); - int (*broadcast) (const char *mes, int len, int type); - int (*broadcast2) (const char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY); - int (*main_message) (struct map_session_data* sd, const char* message); - int (*wis_message) (struct map_session_data *sd, const char *nick, const char *mes, int mes_len); - int (*wis_message_to_gm) (char *Wisp_name, int permission, char *mes); int (*saveregistry) (struct map_session_data *sd); int (*request_registry) (struct map_session_data *sd, int flag); void (*request_account_storage) (const struct map_session_data *sd); @@ -78,15 +73,13 @@ struct intif_interface { int (*party_leave) (int party_id,int account_id, int char_id); int (*party_changemap) (struct map_session_data *sd, int online); int (*break_party) (int party_id); - int (*party_message) (int party_id, int account_id, const char *mes,int len); int (*party_leaderchange) (int party_id,int account_id,int char_id); int (*guild_create) (const char *name, const struct guild_member *master); int (*guild_request_info) (int guild_id); int (*guild_addmember) (int guild_id, struct guild_member *m); int (*guild_leave) (int guild_id, int account_id, int char_id, int flag, const char *mes); - int (*guild_memberinfoshort) (int guild_id, int account_id, int char_id, int online, int lv, int16 class); + int (*guild_memberinfoshort) (int guild_id, int account_id, int char_id, int online, int lv, int class); int (*guild_break) (int guild_id); - int (*guild_message) (int guild_id, int account_id, const char *mes, int len); int (*guild_change_gm) (int guild_id, const char *name, int len); int (*guild_change_basicinfo) (int guild_id, int type, const void *data, int len); int (*guild_change_memberinfo) (int guild_id, int account_id, int char_id, int type, const void *data, int len); @@ -135,9 +128,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); @@ -149,10 +144,6 @@ struct intif_interface { void(*achievements_request) (struct map_session_data *sd); void(*achievements_save) (struct map_session_data *sd); /* */ - void (*pWisMessage) (int fd); - void (*pWisEnd) (int fd); - int (*pWisToGM_sub) (struct map_session_data* sd,va_list va); - void (*pWisToGM) (int fd); void (*pRegisters) (int fd); void (*pAccountStorage) (int fd); void (*pChangeNameOk) (int fd); @@ -167,14 +158,12 @@ struct intif_interface { void (*pPartyMemberWithdraw) (int fd); void (*pPartyMove) (int fd); void (*pPartyBroken) (int fd); - void (*pPartyMessage) (int fd); void (*pGuildCreated) (int fd); void (*pGuildInfo) (int fd); void (*pGuildMemberAdded) (int fd); void (*pGuildMemberWithdraw) (int fd); void (*pGuildMemberInfoShort) (int fd); void (*pGuildBroken) (int fd); - void (*pGuildMessage) (int fd); void (*pGuildBasicInfoChanged) (int fd); void (*pGuildMemberInfoChanged) (int fd); void (*pGuildPosition) (int fd); diff --git a/src/map/irc-bot.c b/src/map/irc-bot.c index 996107fea..bc9cb6ad1 100644 --- a/src/map/irc-bot.c +++ b/src/map/irc-bot.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 @@ -396,7 +396,7 @@ static void irc_userjoin(int fd, char *cmd, char *source, char *target, char *ms ircbot->parse_source(source,source_nick,source_ident,source_host); if( ircbot->channel ) { - snprintf(send_string, 150, "[ #%s ] User IRC.%s joined the channel.",ircbot->channel->name,source_nick); + snprintf(send_string, 150, msg_txt(468), ircbot->channel->name, source_nick); // [ #%s ] User IRC.%s joined the channel. clif->channel_msg2(ircbot->channel,send_string); } } @@ -414,9 +414,9 @@ static void irc_userleave(int fd, char *cmd, char *source, char *target, char *m if( ircbot->channel ) { if (!strcmpi(cmd, "QUIT")) - snprintf(send_string, 150, "[ #%s ] User IRC.%s left the channel. [Quit: %s]",ircbot->channel->name,source_nick,msg); + snprintf(send_string, 150, msg_txt(465), ircbot->channel->name, source_nick, msg); // [ #%s ] User IRC.%s left the channel. [Quit: %s] else - snprintf(send_string, 150, "[ #%s ] User IRC.%s left the channel. [%s]",ircbot->channel->name,source_nick,msg); + snprintf(send_string, 150, msg_txt(466), ircbot->channel->name, source_nick, msg); // [ #%s ] User IRC.%s left the channel. [%s] clif->channel_msg2(ircbot->channel,send_string); } } @@ -433,7 +433,7 @@ static void irc_usernick(int fd, char *cmd, char *source, char *target, char *ms ircbot->parse_source(source,source_nick,source_ident,source_host); if( ircbot->channel ) { - snprintf(send_string, 150, "[ #%s ] User IRC.%s is now known as IRC.%s",ircbot->channel->name,source_nick,msg); + snprintf(send_string, 150, msg_txt(467), ircbot->channel->name, source_nick, msg); // [ #%s ] User IRC.%s is now known as IRC.%s clif->channel_msg2(ircbot->channel,send_string); } } diff --git a/src/map/irc-bot.h b/src/map/irc-bot.h index 54f462e35..66880f427 100644 --- a/src/map/irc-bot.h +++ b/src/map/irc-bot.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 diff --git a/src/map/itemdb.c b/src/map/itemdb.c index 5e447d4c0..2b8200c06 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -116,73 +117,105 @@ static struct item_data *itemdb_name2id(const char *str) */ static int itemdb_searchname_array_sub(union DBKey key, struct DBData data, va_list ap) { - struct item_data *item = DB->data2ptr(&data); - char *str; - str=va_arg(ap,char *); + struct item_data *itd = DB->data2ptr(&data); + const char *str = va_arg(ap, const char *); + enum item_name_search_flag flag = va_arg(ap, enum item_name_search_flag); + nullpo_ret(str); - if (item == &itemdb->dummy) + + if (itd == &itemdb->dummy) return 1; //Invalid item. - if(stristr(item->jname,str)) - return 0; - if(battle_config.case_sensitive_aegisnames && strstr(item->name,str)) - return 0; - if(!battle_config.case_sensitive_aegisnames && stristr(item->name,str)) + + if ( + (flag == IT_SEARCH_NAME_PARTIAL + && (stristr(itd->jname, str) != NULL + || (battle_config.case_sensitive_aegisnames && strstr(itd->name, str)) + || (!battle_config.case_sensitive_aegisnames && stristr(itd->name, str)) + )) + || (flag == IT_SEARCH_NAME_EXACT + && (strcmp(itd->jname, str) == 0 + || (battle_config.case_sensitive_aegisnames && strcmp(itd->name, str) == 0) + || (!battle_config.case_sensitive_aegisnames && strcasecmp(itd->name, str) == 0) + )) + ) { + return 0; - return strcmpi(item->jname,str); + } else { + return 1; + } } -/*========================================== - * Founds up to N matches. Returns number of matches [Skotlex] - * search flag : - * 0 - approximate match - * 1 - exact match - *------------------------------------------*/ -static int itemdb_searchname_array(struct item_data **data, int size, const char *str, int flag) +/** + * Finds up to passed size matches + * @param data array of struct item_data for returning the results in + * @param size size of the array + * @param str string used in this search + * @param flag search mode refer to enum item_name_search_flag for possible values + * @return returns all found matches in the database which could be bigger than size + **/ +static int itemdb_searchname_array(struct item_data **data, const int size, const char *str, enum item_name_search_flag flag) { - struct item_data* item; - int i; - int count=0; - nullpo_ret(data); nullpo_ret(str); - // Search in the array - for( i = 0; i < ARRAYLENGTH(itemdb->array); ++i ) - { - item = itemdb->array[i]; - if( item == NULL ) + Assert_ret(flag >= IT_SEARCH_NAME_PARTIAL && flag < IT_SEARCH_NAME_MAX); + Assert_ret(size > 0); + + int + results_count = 0, + length = 0; + + // Search in array + for (int i = 0; i < ARRAYLENGTH(itemdb->array); ++i) { + struct item_data *itd = itemdb->array[i]; + + if (itd == NULL) continue; - if( - (!flag - && (stristr(item->jname,str) - || (battle_config.case_sensitive_aegisnames && strstr(item->name,str)) - || (!battle_config.case_sensitive_aegisnames && stristr(item->name,str)) - )) - || (flag - && (strcmp(item->jname,str) == 0 - || (battle_config.case_sensitive_aegisnames && strcmp(item->name,str) == 0) - || (!battle_config.case_sensitive_aegisnames && strcasecmp(item->name,str) == 0) - )) - ) { - if( count < size ) - data[count] = item; - ++count; + if ( + (flag == IT_SEARCH_NAME_PARTIAL + && (stristr(itd->jname, str) != NULL + || (battle_config.case_sensitive_aegisnames && strstr(itd->name, str)) + || (!battle_config.case_sensitive_aegisnames && stristr(itd->name, str)) + )) + || (flag == IT_SEARCH_NAME_EXACT + && (strcmp(itd->jname, str) == 0 + || (battle_config.case_sensitive_aegisnames && strcmp(itd->name, str) == 0) + || (!battle_config.case_sensitive_aegisnames && strcasecmp(itd->name, str) == 0) + )) + ) { + if (length < size) { + data[length] = itd; + ++length; + } + + ++results_count; } } - // search in the db - if( count < size ) - { - struct DBData *db_data[MAX_SEARCH]; - int db_count = 0; - size -= count; - db_count = itemdb->other->getall(itemdb->other, (struct DBData**)&db_data, size, itemdb->searchname_array_sub, str); - for (i = 0; i < db_count; i++) - data[count++] = DB->data2ptr(db_data[i]); - count += db_count; + // Search in dbmap + int dbmap_size = size - length; + if (dbmap_size > 0) { + struct DBData **dbmap_data = NULL; + int dbmap_count = 0; + CREATE(dbmap_data, struct DBData *, dbmap_size); + + dbmap_count = itemdb->other->getall(itemdb->other, dbmap_data, dbmap_size, itemdb->searchname_array_sub, str, flag); + dbmap_size = min(dbmap_count, dbmap_size); + + for (int i = 0; i < dbmap_size; ++i) { + data[length] = DB->data2ptr(dbmap_data[i]); + ++length; + } + + results_count += dbmap_count; + aFree(dbmap_data); + } else { // We got all matches we can return, so we only need to count now. + results_count += itemdb->other->getall(itemdb->other, NULL, 0, itemdb->searchname_array_sub, str, flag); } - return count; + + return results_count; } + /* [Ind/Hercules] */ static int itemdb_chain_item(unsigned short chain_id, int *rate) { @@ -813,11 +846,8 @@ static void itemdb_read_groups(void) { struct config_t item_group_conf; struct config_setting_t *itg = NULL, *it = NULL; -#ifdef RENEWAL - const char *config_filename = "db/re/item_group.conf"; // FIXME hardcoded name -#else - const char *config_filename = "db/pre-re/item_group.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_group.conf", config_filename, sizeof(config_filename)); const char *itname; int i = 0, count = 0, c; unsigned int *gsize = NULL; @@ -1111,11 +1141,8 @@ static void itemdb_read_packages(void) { struct config_t item_packages_conf; struct config_setting_t *itg = NULL, *it = NULL, *t = NULL; -#ifdef RENEWAL - const char *config_filename = "db/re/item_packages.conf"; // FIXME hardcoded name -#else - const char *config_filename = "db/pre-re/item_packages.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_packages.conf", config_filename, sizeof(config_filename)); const char *itname; int i = 0, count = 0, c = 0, highest_gcount = 0; unsigned int *must = NULL, *random = NULL, *rgroup = NULL, **rgroups = NULL; @@ -1360,7 +1387,8 @@ static void itemdb_read_options(void) struct config_t item_options_db; struct config_setting_t *ito = NULL, *conf = NULL; int index = 0, count = 0; - const char *filepath = "db/item_options.conf"; + char filepath[256]; + libconfig->format_db_path("item_options.conf", filepath, sizeof(filepath)); VECTOR_DECL(int) duplicate_id; if (!libconfig->load_file(&item_options_db, filepath)) @@ -1461,11 +1489,8 @@ static void itemdb_read_chains(void) { struct config_t item_chain_conf; struct config_setting_t *itc = NULL; -#ifdef RENEWAL - const char *config_filename = "db/re/item_chain.conf"; // FIXME hardcoded name -#else - const char *config_filename = "db/pre-re/item_chain.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"item_chain.conf", config_filename, sizeof(config_filename)); int i = 0, count = 0; if (!libconfig->load_file(&item_chain_conf, config_filename)) @@ -1522,141 +1547,112 @@ static void itemdb_read_chains(void) else itemdb->chain_cache[ECC_ORE] = i; + if (!script->get_constant("ITMCHAIN_SIEGFRIED", &i)) + ShowWarning("itemdb_read_chains: failed to find 'ITMCHAIN_SIEGFRIED' chain to link to cache!\n"); + else + itemdb->chain_cache[ECC_SIEGFRIED] = i; + + if (!script->get_constant("ITMCHAIN_NEO_INSURANCE", &i)) + ShowWarning("itemdb_read_chains: failed to find 'ITMCHAIN_NEO_INSURANCE' chain to link to cache!\n"); + else + itemdb->chain_cache[ECC_NEO_INSURANCE] = i; + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, config_filename); } -/** - * @return: amount of retrieved entries. - **/ -static int itemdb_combo_split_atoi(char *str, int *val) +static bool itemdb_read_combodb_libconfig(void) { - int i; - - nullpo_ret(val); + struct config_t combo_conf; + char filepath[256]; + safesnprintf(filepath, sizeof(filepath), "%s/%s/%s", map->db_path, DBPATH, "item_combo_db.conf"); - for (i=0; i<MAX_ITEMS_PER_COMBO; i++) { - if (!str) break; + if (libconfig->load_file(&combo_conf, filepath) == CONFIG_FALSE) { + ShowError("itemdb_read_combodb_libconfig: can't read %s\n", filepath); + return false; + } - val[i] = atoi(str); - str = strchr(str,':'); - if (str) - *str++=0; + struct config_setting_t *combo_db = NULL; + if ((combo_db = libconfig->setting_get_member(combo_conf.root, "combo_db")) == NULL) { + ShowError("itemdb_read_combodb_libconfig: can't read %s\n", filepath); + return false; } - if( i == 0 ) //No data found. - return 0; + int i = 0; + int count = 0; + struct config_setting_t *it = NULL; - return i; + while ((it = libconfig->setting_get_elem(combo_db, i++)) != NULL) { + if (itemdb->read_combodb_libconfig_sub(it, i - 1, filepath)) + ++count; + } + + libconfig->destroy(&combo_conf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + return true; } -/** - * <combo{:combo{:combo:{..}}}>,<{ script }> - **/ -static void itemdb_read_combos(void) + +static bool itemdb_read_combodb_libconfig_sub(struct config_setting_t *it, int idx, const char *source) { - uint32 lines = 0, count = 0; - char line[1024]; - char filepath[256]; - FILE* fp; + nullpo_retr(false, it); + nullpo_retr(false, source); - safesnprintf(filepath, 256, "%s/%s", map->db_path, DBPATH"item_combo_db.txt"); + struct config_setting_t *t = NULL; - if ((fp = fopen(filepath, "r")) == NULL) { - ShowError("itemdb_read_combos: File not found \"%s\".\n", filepath); - return; + if ((t = libconfig->setting_get_member(it, "Items")) == NULL) { + ShowWarning("itemdb_read_combodb_libconfig_sub: invalid item list for combo (%d), in (%s), skipping..\n", idx, source); + return false; } - // process rows one by one - while(fgets(line, sizeof(line), fp)) { - char *str[2], *p; + if (!config_setting_is_array(t)) { + ShowWarning("itemdb_read_combodb_libconfig_sub: the combo (%d) item list must be an array, in (%s), skipping..\n", idx, source); + return false; + } - lines++; + int len = libconfig->setting_length(t); + if (len > MAX_ITEMS_PER_COMBO) { + ShowWarning("itemdb_read_combodb_libconfig_sub: the size of combo (%d) item list is too big (%d, max = %d), in (%s), skipping..\n", idx, len, MAX_ITEMS_PER_COMBO, source); + return false; + } - if (line[0] == '/' && line[1] == '/') - continue; + struct item_combo *combo = NULL; + RECREATE(itemdb->combos, struct item_combo *, ++itemdb->combo_count); + CREATE(combo, struct item_combo, 1); - memset(str, 0, sizeof(str)); + combo->id = itemdb->combo_count - 1; + combo->count = len; - p = line; - p = trim(p); - if (*p == '\0') - continue;// empty line + for (int i = 0; i < len; i++) { + struct item_data *item = NULL; + const char *name = libconfig->setting_get_string_elem(t, i); - if (!strchr(p,',')) { - /* is there even a single column? */ - ShowError("itemdb_read_combos: Insufficient columns in line %u of \"%s\", skipping.\n", lines, filepath); - continue; + if ((item = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_combodb_libconfig_sub: unknown item '%s', in (%s), skipping..\n", name, source); + --itemdb->combo_count; + aFree(combo); + return false; } + combo->nameid[i] = item->nameid; + } - str[0] = p; - p = strchr(p,','); - *p = '\0'; - p++; - - str[1] = p; - p = strchr(p,','); - p++; - - if (str[1][0] != '{') { - ShowError("itemdb_read_combos(#1): Invalid format (Script column) in line %u of \"%s\", skipping.\n", lines, filepath); - continue; - } - - /* no ending key anywhere (missing \}\) */ - if ( str[1][strlen(str[1])-1] != '}' ) { - ShowError("itemdb_read_combos(#2): Invalid format (Script column) in line %u of \"%s\", skipping.\n", lines, filepath); - continue; - } else { - int items[MAX_ITEMS_PER_COMBO]; - int v = 0, retcount = 0; - struct item_combo *combo = NULL; - - if((retcount = itemdb->combo_split_atoi(str[0], items)) < 2) { - ShowError("itemdb_read_combos: line %u of \"%s\" doesn't have enough items to make for a combo (min:2), skipping.\n", lines, filepath); - continue; - } - - /* validate */ - for(v = 0; v < retcount; v++) { - if( !itemdb->exists(items[v]) ) { - ShowError("itemdb_read_combos: line %u of \"%s\" contains unknown item ID %d, skipping.\n", lines, filepath, items[v]); - break; - } - } - /* failed at some item */ - if( v < retcount ) - continue; - - RECREATE(itemdb->combos, struct item_combo*, ++itemdb->combo_count); - - CREATE(combo, struct item_combo, 1); - - combo->count = retcount; - combo->script = script->parse(str[1], filepath, lines, 0, NULL); - combo->id = itemdb->combo_count - 1; - /* populate ->nameid field */ - for( v = 0; v < retcount; v++ ) { - combo->nameid[v] = items[v]; - } - - itemdb->combos[itemdb->combo_count - 1] = combo; + const char *str = NULL; + if (libconfig->setting_lookup_string(it, "Script", &str) == CONFIG_TRUE) { + combo->script = *str ? script->parse(str, source, -idx, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + } else { + ShowWarning("itemdb_read_combodb_libconfig_sub: invalid script for combo (%d) in (%s), skipping..\n", idx, source); + --itemdb->combo_count; + aFree(combo); + return false; + } - /* populate the items to refer to this combo */ - for( v = 0; v < retcount; v++ ) { - struct item_data * it; - int index; + itemdb->combos[combo->id] = combo; - it = itemdb->exists(items[v]); - index = it->combos_count; - RECREATE(it->combos, struct item_combo*, ++it->combos_count); - it->combos[index] = combo; - } - } - count++; + /* populate the items to refer to this combo */ + for (int i = 0; i < len; i++) { + struct item_data *item = itemdb->exists(combo->nameid[i]); + RECREATE(item->combos, struct item_combo *, ++item->combos_count); + item->combos[item->combos_count - 1] = combo; } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"item_combo_db"CL_RESET"'.\n", count); - - return; + return true; } /*====================================== @@ -1700,7 +1696,12 @@ static int itemdb_validate_entry(struct item_data *entry, int n, const char *sou nullpo_ret(entry); nullpo_ret(source); +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 if (entry->nameid <= 0 || entry->nameid > MAX_ITEM_ID) { +#else + if (entry->nameid <= 0) { +#endif + // item id wrong for any packet versions ShowWarning("itemdb_validate_entry: Invalid item ID %d in entry %d of '%s', allowed values 0 < ID < %d (MAX_ITEM_ID), skipping.\n", entry->nameid, n, source, MAX_ITEM_ID); if (entry->script) { @@ -1715,8 +1716,23 @@ static int itemdb_validate_entry(struct item_data *entry, int n, const char *sou script->free_code(entry->unequip_script); entry->unequip_script = NULL; } + if (entry->rental_start_script != NULL) { + script->free_code(entry->rental_start_script); + entry->rental_start_script = NULL; + } + if (entry->rental_end_script != NULL) { + script->free_code(entry->rental_end_script); + entry->rental_end_script = NULL; + } return 0; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 } +#else + } else if (entry->nameid > MAX_ITEM_ID) { + // item id too big for packet version before item id in 4 bytes + entry->view_id = UNKNOWN_ITEM_ID; + } +#endif { const char *c = entry->name; @@ -1738,6 +1754,14 @@ static int itemdb_validate_entry(struct item_data *entry, int n, const char *sou script->free_code(entry->unequip_script); entry->unequip_script = NULL; } + if (entry->rental_start_script != NULL) { + script->free_code(entry->rental_start_script); + entry->rental_start_script = NULL; + } + if (entry->rental_end_script != NULL) { + script->free_code(entry->rental_end_script); + entry->rental_end_script = NULL; + } return 0; } } @@ -1865,7 +1889,14 @@ static int itemdb_validate_entry(struct item_data *entry, int n, const char *sou script->free_code(item->unequip_script); item->unequip_script = NULL; } - + if (item->rental_start_script != NULL && item->rental_start_script != entry->rental_start_script) { // Don't free if it's inheriting the same script + script->free_code(item->rental_start_script); + item->rental_start_script = NULL; + } + if (item->rental_end_script != NULL && item->rental_end_script != entry->rental_end_script) { // Don't free if it's inheriting the same script + script->free_code(item->rental_end_script); + item->rental_end_script = NULL; + } *item = *entry; return item->nameid; } @@ -1981,6 +2012,8 @@ static int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const * "> * OnEquipScript: <" OnEquip Script "> * OnUnequipScript: <" OnUnequip Script "> + * OnRentalStartScript: <" on renting script "> + * OnRentalEndScript: <" on renting end script "> * Inherit: inherit or override */ if( !itemdb->lookup_const(it, "Id", &i32) ) { @@ -2258,6 +2291,12 @@ static int itemdb_readdb_libconfig_sub(struct config_setting_t *it, int n, const if( libconfig->setting_lookup_string(it, "OnUnequipScript", &str) ) id.unequip_script = *str ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + if (libconfig->setting_lookup_string(it, "OnRentalStartScript", &str) != CONFIG_FALSE) + id.rental_start_script = (*str != '\0') ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + + if (libconfig->setting_lookup_string(it, "OnRentalEndScript", &str) != CONFIG_FALSE) + id.rental_end_script = (*str != '\0') ? script->parse(str, source, -id.nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + return itemdb->validate_entry(&id, n, source); } @@ -2394,7 +2433,7 @@ static int itemdb_readdb_libconfig(const char *filename) } db_destroy(duplicate_db); libconfig->destroy(&item_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); return count; } @@ -2409,6 +2448,98 @@ static uint64 itemdb_unique_id(struct map_session_data *sd) return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++; } +static bool itemdb_read_libconfig_lapineddukddak(void) +{ + struct config_t item_lapineddukddak; + struct config_setting_t *it = NULL; + char filepath[256]; + + int i = 0; + int count = 0; + + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, DBPATH"item_lapineddukddak.conf"); + if (libconfig->load_file(&item_lapineddukddak, filepath) == CONFIG_FALSE) + return false; + + while ((it = libconfig->setting_get_elem(item_lapineddukddak.root, i++)) != NULL) { + if (itemdb->read_libconfig_lapineddukddak_sub(it, filepath)) + ++count; + } + + libconfig->destroy(&item_lapineddukddak); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + return true; +} + +static bool itemdb_read_libconfig_lapineddukddak_sub(struct config_setting_t *it, const char *source) +{ + nullpo_retr(false, it); + nullpo_retr(false, source); + + struct item_data *data = NULL; + const char *name = config_setting_name(it); + const char *str = NULL; + int i32 = 0; + + if ((data = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: unknown item '%s', skipping..\n", name); + return false; + } + + data->lapineddukddak = aCalloc(1, sizeof(struct item_lapineddukddak)); + if (libconfig->setting_lookup_int(it, "NeedCount", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedCount = (int16)i32; + + if (libconfig->setting_lookup_int(it, "NeedRefineMin", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedRefineMin = (int8)i32; + + if (libconfig->setting_lookup_int(it, "NeedRefineMax", &i32) == CONFIG_TRUE) + data->lapineddukddak->NeedRefineMax = (int8)i32; + + struct config_setting_t *sources = libconfig->setting_get_member(it, "SourceItems"); + itemdb->read_libconfig_lapineddukddak_sub_sources(sources, data); + + if (libconfig->setting_lookup_string(it, "Script", &str) == CONFIG_TRUE) + data->lapineddukddak->script = *str ? script->parse(str, source, -data->nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + return true; +} + +static bool itemdb_read_libconfig_lapineddukddak_sub_sources(struct config_setting_t *sources, struct item_data *data) +{ + nullpo_retr(false, data); + nullpo_retr(false, data->lapineddukddak); + + int i = 0; + struct config_setting_t *entry = NULL; + + if (sources == NULL || !config_setting_is_group(sources)) + return false; + + VECTOR_INIT(data->lapineddukddak->SourceItems); + while ((entry = libconfig->setting_get_elem(sources, i++)) != NULL) { + struct item_data *edata = NULL; + struct itemlist_entry item = { 0 }; + const char *name = config_setting_name(entry); + int i32 = 0; + + if ((edata = itemdb->name2id(name)) == NULL) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: unknown item '%s', skipping..\n", name); + continue; + } + item.id = edata->nameid; + + if ((i32 = libconfig->setting_get_int(entry)) == CONFIG_TRUE && (i32 <= 0 || i32 > MAX_AMOUNT)) { + ShowWarning("itemdb_read_libconfig_lapineddukddak_sub: invalid amount (%d) for source item '%s', skipping..\n", i32, name); + continue; + } + item.amount = i32; + + VECTOR_ENSURE(data->lapineddukddak->SourceItems, 1, 1); + VECTOR_PUSH(data->lapineddukddak->SourceItems, item); + } + return true; +} + /** * Reads all item-related databases. */ @@ -2434,17 +2565,37 @@ static void itemdb_read(bool minimal) } } + itemdb->other->foreach(itemdb->other, itemdb->addname_sub); + + itemdb->read_options(); + if (minimal) return; itemdb->name_constants(); - itemdb->read_combos(); + itemdb->read_combodb_libconfig(); itemdb->read_groups(); itemdb->read_chains(); itemdb->read_packages(); - itemdb->read_options(); - clif->stylist_read_db_libconfig(); + itemdb->read_libconfig_lapineddukddak(); +} + +/** + * Add item name with high id into map + * @see DBApply + */ +static int itemdb_addname_sub(union DBKey key, struct DBData *data, va_list ap) +{ + struct item_data *item = DB->data2ptr(data); + struct DBData prev; + + if (itemdb->names->put(itemdb->names, DB->str2key(item->name), DB->ptr2data(item), &prev)) { + struct item_data *oldItem = DB->data2ptr(&prev); + ShowError("itemdb_read: duplicate AegisName '%s' in item ID %d and %d\n", item->name, item->nameid, oldItem->nameid); + } + + return 0; } /** @@ -2482,8 +2633,18 @@ static void destroy_item_data(struct item_data *self, int free_self) script->free_code(self->equip_script); if( self->unequip_script ) script->free_code(self->unequip_script); + if (self->rental_start_script != NULL) + script->free_code(self->rental_start_script); + if (self->rental_end_script != NULL) + script->free_code(self->rental_end_script); if( self->combos ) aFree(self->combos); + if (self->lapineddukddak != NULL) { + if (self->lapineddukddak->script != NULL) + script->free_code(self->lapineddukddak->script); + VECTOR_CLEAR(self->lapineddukddak->SourceItems); + aFree(self->lapineddukddak); + } HPM->data_store_destroy(&self->hdata); #if defined(DEBUG) // trash item @@ -2676,7 +2837,6 @@ static void do_final_itemdb(void) itemdb->destroy_item_data(&itemdb->dummy, 0); db_destroy(itemdb->names); VECTOR_CLEAR(clif->attendance_data); - clif->stylist_vector_clear(); } static void do_init_itemdb(bool minimal) @@ -2686,7 +2846,6 @@ static void do_init_itemdb(bool minimal) itemdb->options = idb_alloc(DB_OPT_RELEASE_DATA); itemdb->names = strdb_alloc(DB_OPT_BASE,ITEM_NAME_LENGTH); itemdb->create_dummy_data(); //Dummy data item. - clif->stylist_vector_init(); itemdb->read(minimal); if (minimal) @@ -2770,8 +2929,8 @@ void itemdb_defaults(void) itemdb->isrestricted = itemdb_isrestricted; itemdb->isidentified = itemdb_isidentified; itemdb->isidentified2 = itemdb_isidentified2; - itemdb->combo_split_atoi = itemdb_combo_split_atoi; - itemdb->read_combos = itemdb_read_combos; + itemdb->read_combodb_libconfig = itemdb_read_combodb_libconfig; + itemdb->read_combodb_libconfig_sub = itemdb_read_combodb_libconfig_sub; itemdb->gendercheck = itemdb_gendercheck; itemdb->validate_entry = itemdb_validate_entry; itemdb->readdb_options_additional_fields = itemdb_readdb_options_additional_fields; @@ -2789,4 +2948,8 @@ void itemdb_defaults(void) itemdb->is_item_usable = itemdb_is_item_usable; itemdb->lookup_const = itemdb_lookup_const; itemdb->lookup_const_mask = itemdb_lookup_const_mask; + itemdb->addname_sub = itemdb_addname_sub; + itemdb->read_libconfig_lapineddukddak = itemdb_read_libconfig_lapineddukddak; + itemdb->read_libconfig_lapineddukddak_sub = itemdb_read_libconfig_lapineddukddak_sub; + itemdb->read_libconfig_lapineddukddak_sub_sources = itemdb_read_libconfig_lapineddukddak_sub_sources; } diff --git a/src/map/itemdb.h b/src/map/itemdb.h index d92940445..5f0790b10 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,8 +38,8 @@ struct hplugin_data_store; #endif #ifndef MAX_ITEM_ID -#if PACKETVER_RE_NUM >= 20180704 -#define MAX_ITEM_ID 0x20000 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 +#define MAX_ITEM_ID INT32_MAX #else #define MAX_ITEM_ID 0xFFFF #endif @@ -72,8 +72,8 @@ struct hplugin_data_store; #if MAX_ITEM_ID < MAX_ITEMDB #error "MAX_ITEM_ID must be bigger or same with MAX_ITEMDB" #endif -#if MAX_ITEM_ID > 0xFFFF && PACKETVER_RE_NUM < 20180704 -#error "For clients before 20180704 RE, MAX_ITEM_ID must be smaller than 0x10000" +#if MAX_ITEM_ID > 0xFFFF && PACKETVER_MAIN_NUM < 20181121 && PACKETVER_RE_NUM < 20180704 && PACKETVER_ZERO_NUM < 20181114 +#error "For clients before 20181121 Main and 20180704 RE and 20181114 zero, MAX_ITEM_ID must be smaller than 0x10000" #endif enum item_itemid { @@ -95,6 +95,8 @@ enum item_itemid { ITEMID_ALOEBERA = 606, ITEMID_SPECTACLES = 611, ITEMID_POISON_BOTTLE = 678, + ITEMID_EARTH_SCROLL_1_3 = 686, + ITEMID_EARTH_SCROLL_1_5 = 687, ITEMID_EMPTY_BOTTLE = 713, ITEMID_EMPERIUM = 714, ITEMID_YELLOW_GEMSTONE = 715, @@ -130,6 +132,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, @@ -138,7 +141,6 @@ enum item_itemid { ITEMID_COATING_BOTTLE = 7139, ITEMID_FRAGMENT_OF_CRYSTAL = 7321, ITEMID_SKULL_ = 7420, - ITEMID_TOKEN_OF_SIEGFRIED = 7621, ITEMID_SPECIAL_ALLOY_TRAP = 7940, ITEMID_CATNIP_FRUIT = 11602, ITEMID_RED_POUCH_OF_SURPRISE = 12024, @@ -358,6 +360,8 @@ enum geneticist_item_list { // enum e_chain_cache { ECC_ORE, + ECC_SIEGFRIED, + ECC_NEO_INSURANCE, /* */ ECC_MAX, }; @@ -411,6 +415,16 @@ enum ItemOptionTypes { IT_OPT_MAX }; +/** + * Item name search flags + **/ + +enum item_name_search_flag { + IT_SEARCH_NAME_PARTIAL, + IT_SEARCH_NAME_EXACT, + IT_SEARCH_NAME_MAX, +}; + /** Convenience item list (entry) used in various functions */ struct itemlist_entry { int id; ///< Item ID or (inventory) index @@ -481,6 +495,14 @@ struct itemdb_option { struct script_code *script; }; +struct item_lapineddukddak { + int16 NeedCount; + int8 NeedRefineMin; + int8 NeedRefineMax; + VECTOR_DECL(struct itemlist_entry) SourceItems; + struct script_code *script; +}; + struct item_data { int nameid; char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH]; @@ -515,12 +537,15 @@ struct item_data { struct script_code *script; ///< Default script for everything. struct script_code *equip_script; ///< Script executed once when equipping. struct script_code *unequip_script; ///< Script executed once when unequipping. + struct script_code *rental_start_script; ///< Script executed once this item get rented + struct script_code *rental_end_script; ///< Script executed once this item rent ends struct { unsigned available : 1; unsigned no_refine : 1; // [celest] unsigned delay_consume : 1; ///< Signifies items that are not consumed immediately upon double-click [Skotlex] unsigned trade_restriction : 9; ///< Item trade restrictions mask (@see enum ItemTradeRestrictions) - unsigned autoequip: 1; + unsigned autoequip : 1; + unsigned auto_favorite : 1; unsigned buyingstore : 1; unsigned bindonequip : 1; unsigned keepafteruse : 1; @@ -547,6 +572,7 @@ struct item_data { /* TODO add a pointer to some sort of (struct extra) and gather all the not-common vals into it to save memory */ struct item_group *group; struct item_package *package; + struct item_lapineddukddak *lapineddukddak; struct hplugin_data_store *hdata; ///< HPM Plugin Data Store }; @@ -631,7 +657,7 @@ struct itemdb_interface { /* */ struct item_data* (*name2id) (const char *str); struct item_data* (*search_name) (const char *name); - int (*search_name_array) (struct item_data** data, int size, const char *str, int flag); + int (*search_name_array) (struct item_data **data, const int size, const char *str, enum item_name_search_flag flag); struct item_data* (*load)(int nameid); struct item_data* (*search)(int nameid); struct item_data* (*exists) (int nameid); @@ -664,8 +690,8 @@ struct itemdb_interface { int (*isrestricted) (struct item *item, int gmlv, int gmlv2, int(*func)(struct item_data *, int, int)); int (*isidentified) (int nameid); int (*isidentified2) (struct item_data *data); - int (*combo_split_atoi) (char *str, int *val); - void (*read_combos) (void); + bool (*read_combodb_libconfig) (void); + bool (*read_combodb_libconfig_sub) (struct config_setting_t *it, int idx, const char *source); int (*gendercheck) (struct item_data *id); int (*validate_entry) (struct item_data *entry, int n, const char *source); void (*readdb_options_additional_fields) (struct itemdb_option *ito, struct config_setting_t *t, const char *source); @@ -683,6 +709,10 @@ struct itemdb_interface { bool (*is_item_usable) (struct item_data *item); bool (*lookup_const) (const struct config_setting_t *it, const char *name, int *value); bool (*lookup_const_mask) (const struct config_setting_t *it, const char *name, int *value); + int (*addname_sub) (union DBKey key, struct DBData *data, va_list ap); + bool (*read_libconfig_lapineddukddak) (void); + bool (*read_libconfig_lapineddukddak_sub) (struct config_setting_t *it, const char *source); + bool (*read_libconfig_lapineddukddak_sub_sources) (struct config_setting_t *sources, struct item_data *data); }; #ifdef HERCULES_CORE diff --git a/src/map/log.c b/src/map/log.c index efb7fefbc..61679089b 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ static char log_picktype2char(e_log_pick_type type) switch( type ) { case LOG_TYPE_TRADE: return 'T'; // (T)rade case LOG_TYPE_VENDING: return 'V'; // (V)ending - case LOG_TYPE_PICKDROP_PLAYER: return 'P'; // (P)player + case LOG_TYPE_PICKDROP_PLAYER: return 'P'; // (P)layer case LOG_TYPE_PICKDROP_MONSTER: return 'M'; // (M)onster case LOG_TYPE_NPC: return 'S'; // NPC (S)hop case LOG_TYPE_SCRIPT: return 'N'; // (N)PC Script @@ -75,6 +75,7 @@ static char log_picktype2char(e_log_pick_type type) case LOG_TYPE_SKILL: return '1'; // Skill case LOG_TYPE_REFINE: return '2'; // Refine case LOG_TYPE_OTHER: return 'X'; // Other + case LOG_TYPE_ACHIEVEMENT: return '3'; // Achievement } // should not get here, fallback @@ -106,7 +107,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 +124,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; @@ -511,7 +512,7 @@ static void log_sql_final(void) /** * Initializes logs->config variables */ -void log_set_defaults(void) +static void log_set_defaults(void) { memset(&logs->config, 0, sizeof(logs->config)); @@ -540,7 +541,7 @@ void log_set_defaults(void) * * @retval false in case of error. */ -bool log_config_read_database(const char *filename, struct config_t *config, bool imported) +static bool log_config_read_database(const char *filename, struct config_t *config, bool imported) { struct config_setting_t *setting = NULL; @@ -596,7 +597,7 @@ bool log_config_read_database(const char *filename, struct config_t *config, boo * * @retval false in case of error. */ -bool log_config_read_filter_item(const char *filename, struct config_t *config, bool imported) +static bool log_config_read_filter_item(const char *filename, struct config_t *config, bool imported) { struct config_setting_t *setting = NULL; @@ -625,7 +626,7 @@ bool log_config_read_filter_item(const char *filename, struct config_t *config, * * @retval false in case of error. */ -bool log_config_read_filter_chat(const char *filename, struct config_t *config, bool imported) +static bool log_config_read_filter_chat(const char *filename, struct config_t *config, bool imported) { struct config_setting_t *setting = NULL; @@ -651,7 +652,7 @@ bool log_config_read_filter_chat(const char *filename, struct config_t *config, * * @retval false in case of error. */ -bool log_config_read_filter(const char *filename, struct config_t *config, bool imported) +static bool log_config_read_filter(const char *filename, struct config_t *config, bool imported) { bool retval = true; @@ -674,7 +675,7 @@ bool log_config_read_filter(const char *filename, struct config_t *config, bool * * @retval false in case of error. */ -bool log_config_read(const char *filename, bool imported) +static bool log_config_read(const char *filename, bool imported) { struct config_t config; struct config_setting_t *setting = NULL; @@ -755,7 +756,7 @@ bool log_config_read(const char *filename, bool imported) return retval; } -void log_config_complete(void) +static void log_config_complete(void) { if( logs->config.sql_logs ) { logs->pick_sub = log_pick_sub_sql; diff --git a/src/map/log.h b/src/map/log.h index db802575d..3f9a80e36 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -87,9 +87,9 @@ typedef enum e_log_pick_type { LOG_TYPE_QUEST = 0x02000000, LOG_TYPE_SKILL = 0x04000000, LOG_TYPE_REFINE = 0x08000000, + LOG_TYPE_LOOT = 0x10000000, + LOG_TYPE_ACHIEVEMENT = 0x20000000, - // combinations - LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME, // all LOG_TYPE_ALL = 0xFFFFFFFF, } e_log_pick_type; @@ -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 d67e658fd..a1176e8fc 100644 --- a/src/map/mail.c +++ b/src/map/mail.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,7 +100,7 @@ static unsigned char mail_setitem(struct map_session_data *sd, int idx, int amou idx -= 2; mail->removeitem(sd, 0); - if( idx < 0 || idx >= MAX_INVENTORY ) + if (idx < 0 || idx >= sd->status.inventorySize) return 1; if( amount <= 0 || amount > sd->status.inventory[idx].amount ) return 1; @@ -128,7 +128,7 @@ static bool mail_setattachment(struct map_session_data *sd, struct mail_message return false; n = sd->mail.index; - Assert_retr(false, n >= 0 && n < MAX_INVENTORY); + Assert_retr(false, n >= 0 && n < sd->status.inventorySize); if( sd->mail.amount ) { if( sd->status.inventory[n].nameid != sd->mail.nameid ) @@ -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/mail.h b/src/map/mail.h index 9aeb46120..6281890c0 100644 --- a/src/map/mail.h +++ b/src/map/mail.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/map.c b/src/map/map.c index 93e86f80b..f66f40dfc 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -45,7 +45,6 @@ #include "map/mapreg.h" #include "map/mercenary.h" #include "map/mob.h" -#include "map/npc.h" #include "map/npc.h" // npc_setcells(), npc_unsetcells() #include "map/party.h" #include "map/path.h" @@ -56,7 +55,9 @@ #include "map/skill.h" #include "map/status.h" #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" @@ -530,10 +531,12 @@ static struct skill_unit *map_find_skill_unit_oncell(struct block_list *target, return NULL; } -/** @name Functions for block_list search and manipulation +/** + * @name Functions for block_list search and manipulation + * + * @{ */ -/* @{ */ /** * Applies func to every block_list in bl_list starting with bl_list[blockcount]. * Sets bl_list_count back to blockcount. @@ -637,9 +640,12 @@ static int map_foreachinmap(int (*func)(struct block_list*, va_list), int16 m, i static int map_forcountinmap(int (*func)(struct block_list*, va_list), int16 m, int count, int type, ...) { - int returnCount; + int returnCount = 0; va_list ap; + if (m < 0) + return returnCount; + va_start(ap, type); returnCount = map->vforcountinarea(func, m, 0, 0, map->list[m].xs, map->list[m].ys, count, type, ap); va_end(ap); @@ -1664,7 +1670,7 @@ static int map_search_freecell(struct block_list *src, int16 m, int16 *x, int16 *------------------------------------------*/ static bool map_closest_freecell(int16 m, const struct block_list *bl, int16 *x, int16 *y, int type, int flag) { - uint8 dir = 6; + enum unit_dir dir = UNIT_DIR_EAST; int16 tx; int16 ty; int costrange = 10; @@ -1683,7 +1689,7 @@ static bool map_closest_freecell(int16 m, const struct block_list *bl, int16 *x, short dy = diry[dir]; //Linear search - if(dir%2 == 0 && costrange%MOVE_COST == 0) { + if (!unit_is_diagonal_dir(dir) && (costrange % MOVE_COST) == 0) { tx = *x+dx*(costrange/MOVE_COST); ty = *y+dy*(costrange/MOVE_COST); if (!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m, bl, tx, ty, CELL_CHKPASS)) { @@ -1693,7 +1699,7 @@ static bool map_closest_freecell(int16 m, const struct block_list *bl, int16 *x, } } //Full diagonal search - else if(dir%2 == 1 && costrange%MOVE_DIAGONAL_COST == 0) { + else if (unit_is_diagonal_dir(dir) && (costrange % MOVE_DIAGONAL_COST) == 0) { tx = *x+dx*(costrange/MOVE_DIAGONAL_COST); ty = *y+dy*(costrange/MOVE_DIAGONAL_COST); if (!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m, bl, tx, ty, CELL_CHKPASS)) { @@ -1703,16 +1709,24 @@ static bool map_closest_freecell(int16 m, const struct block_list *bl, int16 *x, } } //One cell diagonal, rest linear (TODO: Find a better algorithm for this) - else if(dir%2 == 1 && costrange%MOVE_COST == 4) { - tx = *x+dx*((dir%4==3)?(costrange/MOVE_COST):1); - ty = *y+dy*((dir%4==1)?(costrange/MOVE_COST):1); + else if (unit_is_diagonal_dir(dir) && (costrange % MOVE_COST) == 4) { + tx = *x + dx; + ty = *y + dy; + if (unit_is_dir_or_opposite(dir, UNIT_DIR_SOUTHWEST)) + tx = tx * costrange / MOVE_COST; + if (unit_is_dir_or_opposite(dir, UNIT_DIR_NORTHWEST)) + ty = ty * costrange / MOVE_COST; if (!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m, bl, tx, ty, CELL_CHKPASS)) { *x = tx; *y = ty; return true; } - tx = *x+dx*((dir%4==1)?(costrange/MOVE_COST):1); - ty = *y+dy*((dir%4==3)?(costrange/MOVE_COST):1); + tx = *x + dx; + ty = *y + dy; + if (unit_is_dir_or_opposite(dir, UNIT_DIR_NORTHWEST)) + tx = tx * costrange / MOVE_COST; + if (unit_is_dir_or_opposite(dir, UNIT_DIR_SOUTHWEST)) + ty = ty * costrange / MOVE_COST; if (!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m, bl, tx, ty, CELL_CHKPASS)) { *x = tx; *y = ty; @@ -1721,17 +1735,17 @@ static bool map_closest_freecell(int16 m, const struct block_list *bl, int16 *x, } //Get next direction - if (dir == 5) { + if (dir == UNIT_DIR_SOUTHEAST) { //Diagonal search complete, repeat with higher cost range if(costrange == 14) costrange += 6; else if(costrange == 28 || costrange >= 38) costrange += 2; else costrange += 4; - dir = 6; - } else if (dir == 4) { + dir = UNIT_DIR_EAST; + } else if (dir == UNIT_DIR_SOUTH) { //Linear search complete, switch to diagonal directions - dir = 7; + dir = UNIT_DIR_NORTHEAST; } else { - dir = (dir+2)%8; + dir = unit_get_ccw90_dir(dir); } } @@ -2266,30 +2280,25 @@ static struct map_session_data *map_charid2sd(int charid) * (without sensitive case if necessary) * return map_session_data pointer or NULL *------------------------------------------*/ -static struct map_session_data *map_nick2sd(const char *nick) +static struct map_session_data *map_nick2sd(const char *nick, bool allow_partial) { - struct map_session_data* sd; - struct map_session_data* found_sd; - struct s_mapiterator* iter; - size_t nicklen; - int qty = 0; - - if( nick == NULL ) + if (nick == NULL) return NULL; - nicklen = strlen(nick); - iter = mapit_getallusers(); + struct s_mapiterator *iter = mapit_getallusers(); + struct map_session_data *found_sd = NULL; - found_sd = NULL; - for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if( battle_config.partial_name_scan ) - {// partial name search - if( strnicmp(sd->status.name, nick, nicklen) == 0 ) - { + if (battle_config.partial_name_scan && allow_partial) { + int nicklen = (int)strlen(nick); + int qty = 0; + + // partial name search + for (struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { + if (strnicmp(sd->status.name, nick, nicklen) == 0) { found_sd = sd; - if( strcmp(sd->status.name, nick) == 0 ) - {// Perfect Match + if (strcmp(sd->status.name, nick) == 0) { + // Perfect Match qty = 1; break; } @@ -2297,17 +2306,20 @@ static struct map_session_data *map_nick2sd(const char *nick) qty++; } } - else if( strcasecmp(sd->status.name, nick) == 0 ) - {// exact search only - found_sd = sd; - break; + + if (qty != 1) + found_sd = NULL; + } else { + // exact search only + for (struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { + if (strcasecmp(sd->status.name, nick) == 0) { + found_sd = sd; + break; + } } } mapit->free(iter); - if( battle_config.partial_name_scan && qty != 1 ) - found_sd = NULL; - return found_sd; } @@ -2841,63 +2853,70 @@ static int map_mapname2ipport(unsigned short name, uint32 *ip, uint16 *port) return 0; } -/*========================================== +/** * Checks if both dirs point in the same direction. - *------------------------------------------*/ -static int map_check_dir(int s_dir, int t_dir) + * @param s_dir: direction source is facing + * @param t_dir: direction target is facing + * @return 0: success(both face the same direction), 1: failure + **/ +static int map_check_dir(enum unit_dir s_dir, enum unit_dir t_dir) { - if(s_dir == t_dir) + if (s_dir == t_dir || ((t_dir + UNIT_DIR_MAX - 1) % UNIT_DIR_MAX) == s_dir + || ((t_dir + UNIT_DIR_MAX + 1) % UNIT_DIR_MAX) == s_dir) return 0; - switch(s_dir) { - case 0: if(t_dir == 7 || t_dir == 1 || t_dir == 0) return 0; break; - case 1: if(t_dir == 0 || t_dir == 2 || t_dir == 1) return 0; break; - case 2: if(t_dir == 1 || t_dir == 3 || t_dir == 2) return 0; break; - case 3: if(t_dir == 2 || t_dir == 4 || t_dir == 3) return 0; break; - case 4: if(t_dir == 3 || t_dir == 5 || t_dir == 4) return 0; break; - case 5: if(t_dir == 4 || t_dir == 6 || t_dir == 5) return 0; break; - case 6: if(t_dir == 5 || t_dir == 7 || t_dir == 6) return 0; break; - case 7: if(t_dir == 6 || t_dir == 0 || t_dir == 7) return 0; break; - } return 1; } -/*========================================== +/** * Returns the direction of the given cell, relative to 'src' - *------------------------------------------*/ -static uint8 map_calc_dir(struct block_list *src, int16 x, int16 y) + * @param src: object to put in relation between coordinates + * @param x: x-coordinate of cell + * @param y: y-coordinate of cell + * @return the direction of the given cell, relative to 'src' + **/ +static enum unit_dir map_calc_dir(const struct block_list *src, int16 x, int16 y) { - uint8 dir = 0; - int dx, dy; - - nullpo_ret(src); + nullpo_retr(UNIT_DIR_NORTH, src); + enum unit_dir dir = UNIT_DIR_NORTH; - dx = x-src->x; - dy = y-src->y; + int dx = x - src->x; + int dy = y - src->y; if (dx == 0 && dy == 0) { // both are standing on the same spot. // aegis-style, makes knockback default to the left. // athena-style, makes knockback default to behind 'src'. - dir = (battle_config.knockback_left ? 6 : unit->getdir(src)); - } else if (dx >= 0 && dy >=0) { - // upper-right - if( dx*2 < dy || dx == 0 ) dir = 0; // up - else if( dx > dy*2+1 || dy == 0 ) dir = 6; // right - else dir = 7; // up-right + if (battle_config.knockback_left != 0) + dir = UNIT_DIR_EAST; + else + dir = unit->getdir(src); + } else if (dx >= 0 && dy >= 0) { + if (dx * 2 < dy || dx == 0) + dir = UNIT_DIR_NORTH; + else if (dx > dy * 2 + 1 || dy == 0) + dir = UNIT_DIR_EAST; + else + dir = UNIT_DIR_NORTHEAST; } else if (dx >= 0 && dy <= 0) { - // lower-right - if( dx*2 < -dy || dx == 0 ) dir = 4; // down - else if( dx > -dy*2+1 || dy == 0 ) dir = 6; // right - else dir = 5; // down-right + if (dx * 2 < -dy || dx == 0) + dir = UNIT_DIR_SOUTH; + else if (dx > -dy * 2 + 1 || dy == 0) + dir = UNIT_DIR_EAST; + else + dir = UNIT_DIR_SOUTHEAST; } else if (dx <= 0 && dy <= 0) { - // lower-left - if( dx*2 > dy || dx == 0 ) dir = 4; // down - else if( dx < dy*2-1 || dy == 0 ) dir = 2; // left - else dir = 3; // down-left + if (dx * 2 > dy || dx == 0 ) + dir = UNIT_DIR_SOUTH; + else if (dx < dy * 2 + 1 || dy == 0) + dir = UNIT_DIR_WEST; + else + dir = UNIT_DIR_SOUTHWEST; } else { - // upper-left - if( -dx*2 < dy || dx == 0 ) dir = 0; // up - else if( -dx > dy*2+1 || dy == 0) dir = 2; // left - else dir = 1; // up-left + if (-dx * 2 < dy || dx == 0 ) + dir = UNIT_DIR_NORTH; + else if (-dx > dy * 2 + 1 || dy == 0) + dir = UNIT_DIR_WEST; + else + dir = UNIT_DIR_NORTHWEST; } return dir; } @@ -2925,11 +2944,11 @@ static int map_random_dir(struct block_list *bl, int16 *x, int16 *y) if (dist < 1) dist =1; do { - int j = 1 + 2*(rnd()%4); //Pick a random diagonal direction + enum unit_dir dir = unit_get_rnd_diagonal_dir(); short segment = 1+(rnd()%dist); //Pick a random interval from the whole vector in that direction - xi = bl->x + segment*dirx[j]; + xi = bl->x + segment * dirx[dir]; segment = (short)sqrt((float)(dist2 - segment*segment)); //The complement of the previously picked segment - yi = bl->y + segment*diry[j]; + yi = bl->y + segment * diry[dir]; } while ((map->getcell(bl->m, bl, xi, yi, CELL_CHKNOPASS) || !path->search(NULL, bl, bl->m, bl->x, bl->y, xi, yi, 1, CELL_CHKNOREACH)) && (++i)<100); @@ -3058,6 +3077,8 @@ static int map_getcellp(struct map_data *m, const struct block_list *bl, int16 x return (cell.icewall); case CELL_CHKNOICEWALL: return (cell.noicewall); + case CELL_CHKNOSKILL: + return (cell.noskill); // special checks case CELL_CHKPASS: @@ -3122,6 +3143,7 @@ static void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) case CELL_NOCHAT: map->list[m].cell[j].nochat = flag; break; case CELL_ICEWALL: map->list[m].cell[j].icewall = flag; break; case CELL_NOICEWALL: map->list[m].cell[j].noicewall = flag; break; + case CELL_NOSKILL: map->list[m].cell[j].noskill = flag; break; default: ShowWarning("map_setcell: invalid cell type '%d'\n", (int)cell); @@ -3584,23 +3606,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); @@ -3609,98 +3635,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) @@ -3791,7 +3763,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); } } @@ -3819,8 +3792,29 @@ static int map_waterheight(char *mapname) // read & convert fn rsw = grfio_read(fn); if (rsw) { + if (memcmp(rsw, "GRSW", 4) != 0) { + ShowWarning("Failed to find water level for %s (%s)\n", mapname, fn); + aFree(rsw); + return NO_WATER; + } + int major_version = rsw[4]; + int minor_version = rsw[5]; + if (major_version > 2 || (major_version == 2 && minor_version > 2)) { + ShowWarning("Failed to find water level for %s (%s)\n", mapname, fn); + aFree(rsw); + return NO_WATER; + } + if (major_version < 1 || (major_version == 1 && minor_version <= 4)) { + ShowWarning("Failed to find water level for %s (%s)\n", mapname, fn); + aFree(rsw); + return NO_WATER; + } + int offset = 166; + if (major_version == 2 && minor_version >= 2) { + offset = 167; + } //Load water height from file - int wh = (int) *(float*)(rsw+166); + int wh = (int)*(float*)(rsw + offset); aFree(rsw); return wh; } @@ -4104,6 +4098,7 @@ static bool map_config_read_database(const char *filename, struct config_t *conf return false; } libconfig->setting_lookup_mutable_string(setting, "db_path", map->db_path, sizeof(map->db_path)); + libconfig->set_db_path(map->db_path); libconfig->setting_lookup_int(setting, "save_settings", &map->save_settings); if (libconfig->setting_lookup_int(setting, "autosave_time", &map->autosave_interval) == CONFIG_TRUE) { @@ -4479,6 +4474,8 @@ static bool inter_config_read_database_names(const char *filename, const struct libconfig->setting_lookup_mutable_string(setting, "autotrade_merchants_db", map->autotrade_merchants_db, sizeof(map->autotrade_merchants_db)); libconfig->setting_lookup_mutable_string(setting, "autotrade_data_db", map->autotrade_data_db, sizeof(map->autotrade_data_db)); libconfig->setting_lookup_mutable_string(setting, "npc_market_data_db", map->npc_market_data_db, sizeof(map->npc_market_data_db)); + libconfig->setting_lookup_mutable_string(setting, "npc_barter_data_db", map->npc_barter_data_db, sizeof(map->npc_barter_data_db)); + libconfig->setting_lookup_mutable_string(setting, "npc_expanded_barter_data_db", map->npc_expanded_barter_data_db, sizeof(map->npc_expanded_barter_data_db)); if (!mapreg->config_read(filename, setting, imported)) retval = false; @@ -5425,6 +5422,32 @@ static bool map_zone_mf_cache(int m, char *flag, char *params) else if( map->list[m].flag.nocashshop ) map_zone_mf_cache_add(m,"nocashshop"); } + } else if (strcmpi(flag, "nostorage") == 0) { + if (!state) { + if (map->list[m].flag.nostorage != 0) { + sprintf(rflag, "nostorage\t%d", map->list[m].flag.nostorage); + map_zone_mf_cache_add(m, rflag); + } + } + if (sscanf(params, "%d", &state) == 1) { + if (state != map->list[m].flag.nostorage) { + sprintf(rflag, "nostorage\t%d", state); + map_zone_mf_cache_add(m, rflag); + } + } + } else if (strcmpi(flag, "nogstorage") == 0) { + if (!state) { + if (map->list[m].flag.nogstorage != 0) { + sprintf(rflag, "nogstorage\t%d", map->list[m].flag.nogstorage); + map_zone_mf_cache_add(m, rflag); + } + } + if (sscanf(params, "%d", &state) == 1) { + if (state != map->list[m].flag.nogstorage) { + sprintf(rflag, "nogstorage\t%d", state); + map_zone_mf_cache_add(m, rflag); + } + } } return false; @@ -5596,12 +5619,8 @@ static void read_map_zone_db(void) { struct config_t map_zone_db; struct config_setting_t *zones = NULL; - /* TODO: #ifndef required for re/pre-re */ -#ifdef RENEWAL - const char *config_filename = "db/re/map_zone_db.conf"; // FIXME hardcoded name -#else - const char *config_filename = "db/pre-re/map_zone_db.conf"; // FIXME hardcoded name -#endif + char config_filename[256]; + libconfig->format_db_path(DBPATH"map_zone_db.conf", config_filename, sizeof(config_filename)); if (!libconfig->load_file(&map_zone_db, config_filename)) return; @@ -6026,28 +6045,34 @@ 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); + + 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)) { + return false; + } - VECTOR_ENSURE(map->list[m].qi_data, 1, 1); - VECTOR_PUSH(map->list[m].qi_data, *qi); + 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; } @@ -6094,7 +6119,7 @@ static int cleanup_sub(struct block_list *bl, va_list ap) map->quit(BL_UCAST(BL_PC, bl)); break; case BL_NPC: - npc->unload(BL_UCAST(BL_NPC, bl), false); + npc->unload(BL_UCAST(BL_NPC, bl), false, true); break; case BL_MOB: unit->free(bl,CLR_OUTSIGHT); @@ -6188,6 +6213,7 @@ int do_final(void) atcommand->final_msg(); skill->final(); status->final(); + refine->final(); unit->final(); bg->final(); duel->final(); @@ -6196,6 +6222,7 @@ int do_final(void) vending->final(); rodex->final(); achievement->final(); + stylist->final(); HPM_map_do_final(); @@ -6323,6 +6350,7 @@ static CPCMD(gm_position) map->cpsd->bl.x = x; map->cpsd->bl.y = y; map->cpsd->bl.m = m; + map->cpsd->mapindex = map_id2index(m); } static CPCMD(gm_use) { @@ -6351,6 +6379,8 @@ static void map_cp_defaults(void) map->cpsd->bl.x = mapindex->default_x; map->cpsd->bl.y = mapindex->default_y; map->cpsd->bl.m = map->mapname2mapid(mapindex->default_map); + Assert_retv(map->cpsd->bl.m >= 0); + map->cpsd->mapindex = map_id2index(map->cpsd->bl.m); console->input->addCommand("gm:info",CPCMD_A(gm_position)); console->input->addCommand("gm:use",CPCMD_A(gm_use)); @@ -6403,6 +6433,8 @@ static void map_load_defaults(void) achievement_defaults(); npc_chat_defaults(); rodex_defaults(); + stylist_defaults(); + refine_defaults(); } /** * --run-once handler @@ -6707,6 +6739,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); @@ -6717,6 +6750,7 @@ int do_init(int argc, char *argv[]) elemental->init(minimal); quest->init(minimal); achievement->init(minimal); + stylist->init(minimal); npc->init(minimal); unit->init(minimal); bg->init(minimal); @@ -6742,6 +6776,8 @@ int do_init(int argc, char *argv[]) npc->event_do_oninit( false ); // Init npcs (OnInit) npc->market_fromsql(); /* after OnInit */ + npc->barter_fromsql(); /* after OnInit */ + npc->expanded_barter_fromsql(); /* after OnInit */ if (battle_config.pk_mode) ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n"); @@ -6787,6 +6823,7 @@ void map_defaults(void) map->extra_scripts_count = 0; sprintf(map->db_path ,"db"); + libconfig->set_db_path(map->db_path); sprintf(map->help_txt ,"conf/help.txt"); sprintf(map->charhelp_txt ,"conf/charhelp.txt"); @@ -6848,7 +6885,10 @@ void map_defaults(void) map->bl_list_size = 0; //all in a big chunk, respects order +PRAGMA_GCC9(GCC diagnostic push) +PRAGMA_GCC9(GCC diagnostic ignored "-Warray-bounds") memset(ZEROED_BLOCK_POS(map), 0, ZEROED_BLOCK_SIZE(map)); +PRAGMA_GCC9(GCC diagnostic pop) map->cpsd = NULL; map->list = NULL; diff --git a/src/map/map.h b/src/map/map.h index 4267c2c88..64736a6f5 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include "common/db.h" #include "common/mapindex.h" #include "common/mmo.h" +#include "map/unitdefines.h" // enum unit_dir #include <stdio.h> #include <stdarg.h> @@ -45,42 +46,6 @@ enum E_MAPSERVER_ST { MAPSERVER_ST_LAST }; -#define MAX_NPC_PER_MAP 512 -#define AREA_SIZE (battle->bc->area_size) -#define CHAT_AREA_SIZE (battle->bc->chat_area_size) -#define DEAD_AREA_SIZE (battle->bc->dead_area_size) -#define DAMAGELOG_SIZE 30 -#define LOOTITEM_SIZE 10 -#define MAX_MOBSKILL 50 -#define MAX_MOB_LIST_PER_MAP 100 -#define MAX_EVENTQUEUE 2 -#define MAX_EVENTTIMER 32 -#define NATURAL_HEAL_INTERVAL 500 -#define MIN_FLOORITEM 2 -#define MAX_FLOORITEM START_ACCOUNT_NUM -#define MAX_IGNORE_LIST 20 // official is 14 -#define MAX_VENDING 12 -#define MAX_MAP_SIZE (512*512) // Wasn't there something like this already? Can't find it.. [Shinryo] - -#define BLOCK_SIZE 8 -#define block_free_max 1048576 -#define BL_LIST_MAX 1048576 - -// The following system marks a different job ID system used by the map server, -// which makes a lot more sense than the normal one. [Skotlex] -// These marks the "level" of the job. -#define JOBL_2_1 0x0100 -#define JOBL_2_2 0x0200 -#define JOBL_2 0x0300 // JOBL_2_1 | JOBL_2_2 -#define JOBL_UPPER 0x1000 -#define JOBL_BABY 0x2000 -#define JOBL_THIRD 0x4000 - -// For filtering and quick checking. -#define MAPID_BASEMASK 0x00ff -#define MAPID_UPPERMASK 0x0fff -#define MAPID_THIRDMASK (JOBL_THIRD|MAPID_UPPERMASK) - //First Jobs //Note the oddity of the novice: //Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type. @@ -344,36 +309,6 @@ enum { STATIC_ASSERT(((MAPID_1_1_MAX - 1) | MAPID_BASEMASK) == MAPID_BASEMASK, "First class map IDs do not fit into MAPID_BASEMASK"); -// Max size for inputs to Graffiti, Talkie Box and Vending text prompts -#define MESSAGE_SIZE (79 + 1) -// String length you can write in the 'talking box' -#define CHATBOX_SIZE (70 + 1) -// Chatroom-related string sizes -#define CHATROOM_TITLE_SIZE (36 + 1) -#define CHATROOM_PASS_SIZE (8 + 1) -// Max allowed chat text length -#define CHAT_SIZE_MAX (255 + 1) -// 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS -#define EVENT_NAME_LENGTH ( NAME_LENGTH * 2 + 3 ) -#define DEFAULT_AUTOSAVE_INTERVAL (5*60*1000) -// Specifies maps where players may hit each other -#define map_flag_vs(m) ( \ - map->list[m].flag.pvp \ - || map->list[m].flag.gvg_dungeon \ - || map->list[m].flag.gvg \ - || ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle) \ - || map->list[m].flag.battleground \ - || map->list[m].flag.cvc \ - ) -// Specifies maps that have special GvG/WoE restrictions -#define map_flag_gvg(m) (map->list[m].flag.gvg || ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle)) -// Specifies if the map is tagged as GvG/WoE (regardless of map->agit_flag status) -#define map_flag_gvg2(m) (map->list[m].flag.gvg || map->list[m].flag.gvg_castle) -// No Kill Steal Protection -#define map_flag_ks(m) (map->list[m].flag.town || map->list[m].flag.pvp || map->list[m].flag.gvg || map->list[m].flag.battleground) -// No ViewID -#define map_no_view(m, view) (map->list[m].flag.noviewid & (view)) - //This stackable implementation does not means a BL can be more than one type at a time, but it's // meant to make it easier to check for multiple types at a time on invocations such as map_foreach* calls [Skotlex] enum bl_type { @@ -392,9 +327,6 @@ enum bl_type { BL_ALL = 0xFFF, }; -// For common mapforeach calls. Since pets cannot be affected, they aren't included here yet. -#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER|BL_ELEM) - enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP, TOMB }; /** @@ -518,7 +450,7 @@ struct block_list { // Mob List Held in memory for Dynamic Mobs [Wizputer] // Expanded to specify all mob-related spawn data by [Skotlex] struct spawn_data { - short class_; ///< Class, used because a mob can change it's class + int class_; ///< Class, used because a mob can change it's class unsigned short m, x, y; ///< Spawn information (map, point, spawn-area around point) signed short xs, ys; unsigned short num; ///< Number of mobs using this structure @@ -656,6 +588,7 @@ typedef enum { CELL_NOCHAT, CELL_ICEWALL, CELL_NOICEWALL, + CELL_NOSKILL, } cell_t; @@ -680,6 +613,7 @@ typedef enum { CELL_CHKNOCHAT, CELL_CHKICEWALL, CELL_CHKNOICEWALL, + CELL_CHKNOSKILL, } cell_chk; @@ -698,7 +632,8 @@ struct mapcell { novending : 1, nochat : 1, icewall : 1, - noicewall : 1; + noicewall : 1, + noskill : 1; #ifdef CELL_NOSTACK int cell_bl; //Holds amount of bls in this cell. @@ -748,16 +683,6 @@ enum map_zone_merge_type { MZMT_NEVERMERGE, ///< Cannot merge with any zones. }; -#define MAP_ZONE_NAME_LENGTH 60 -#define MAP_ZONE_ALL_NAME "All" -#define MAP_ZONE_NORMAL_NAME "Normal" -#define MAP_ZONE_PVP_NAME "PvP" -#define MAP_ZONE_GVG_NAME "GvG" -#define MAP_ZONE_BG_NAME "Battlegrounds" -#define MAP_ZONE_CVC_NAME "CvC" -#define MAP_ZONE_PK_NAME "PK Mode" -#define MAP_ZONE_MAPFLAG_LENGTH 65 - struct map_zone_data { char name[MAP_ZONE_NAME_LENGTH];/* 20'd */ enum map_zone_merge_type merge_type; @@ -784,41 +709,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 short 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. @@ -899,6 +789,8 @@ struct map_data { unsigned noautoloot : 1; unsigned pairship_startable : 1; unsigned pairship_endable : 1; + unsigned nostorage : 2; + unsigned nogstorage : 2; uint32 noviewid; ///< noviewid (bitmask - @see enum equip_pos) } flag; struct point save; @@ -956,8 +848,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; @@ -1167,6 +1059,8 @@ struct map_interface { char autotrade_merchants_db[32]; char autotrade_data_db[32]; char npc_market_data_db[32]; + char npc_barter_data_db[32]; + char npc_expanded_barter_data_db[32]; char default_codepage[32]; char default_lang_str[64]; @@ -1319,15 +1213,15 @@ END_ZEROED_BLOCK; void (*addiddb) (struct block_list *bl); void (*deliddb) (struct block_list *bl); /* */ - struct map_session_data * (*nick2sd) (const char *nick); + struct map_session_data * (*nick2sd) (const char *nick, bool allow_partial); struct mob_data * (*getmob_boss) (int16 m); struct mob_data * (*id2boss) (int id); uint32 (*race_id2mask) (int race); // reload config file looking only for npcs void (*reloadnpc) (bool clear); - int (*check_dir) (int s_dir,int t_dir); - uint8 (*calc_dir) (struct block_list *src,int16 x,int16 y); + int (*check_dir) (enum unit_dir s_dir, enum unit_dir t_dir); + enum unit_dir (*calc_dir) (const struct block_list *src, int16 x, int16 y); int (*random_dir) (struct block_list *bl, short *x, short *y); // [Skotlex] int (*cleanup_sub) (struct block_list *bl, va_list ap); @@ -1390,7 +1284,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/mapdefines.h b/src/map/mapdefines.h new file mode 100644 index 000000000..8a363e2d4 --- /dev/null +++ b/src/map/mapdefines.h @@ -0,0 +1,119 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams + * + * 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_MAPDEFINES_H +#define MAP_MAPDEFINES_H + +#include "common/mmo.h" // packet versions + +#define MAX_NPC_PER_MAP 512 +#define AREA_SIZE (battle->bc->area_size) +#define CHAT_AREA_SIZE (battle->bc->chat_area_size) +#define DEAD_AREA_SIZE (battle->bc->dead_area_size) +#define DAMAGELOG_SIZE 30 +#define LOOTITEM_SIZE 10 +#define MAX_MOBSKILL 50 + +#ifndef MAX_MOB_LIST_PER_MAP +#ifdef RENEWAL +#define MAX_MOB_LIST_PER_MAP 100 +#else +#define MAX_MOB_LIST_PER_MAP 115 +#endif +#endif + +#define MAX_EVENTQUEUE 2 +#define MAX_EVENTTIMER 32 +#define NATURAL_HEAL_INTERVAL 500 +#define MIN_FLOORITEM 2 +#define MAX_FLOORITEM START_ACCOUNT_NUM +#define MAX_IGNORE_LIST 20 // official is 14 +#define MAX_VENDING 12 +#define MAX_MAP_SIZE (512*512) // Wasn't there something like this already? Can't find it.. [Shinryo] + +#define BLOCK_SIZE 8 +#define block_free_max 1048576 +#define BL_LIST_MAX 1048576 + +// The following system marks a different job ID system used by the map server, +// which makes a lot more sense than the normal one. [Skotlex] +// These marks the "level" of the job. +#define JOBL_2_1 0x0100 +#define JOBL_2_2 0x0200 +#define JOBL_2 0x0300 // JOBL_2_1 | JOBL_2_2 +#define JOBL_UPPER 0x1000 +#define JOBL_BABY 0x2000 +#define JOBL_THIRD 0x4000 + +// For filtering and quick checking. +#define MAPID_BASEMASK 0x00ff +#define MAPID_UPPERMASK 0x0fff +#define MAPID_THIRDMASK (JOBL_THIRD|MAPID_UPPERMASK) + +// Max size for inputs to Vending text prompts +#define MESSAGE_SIZE (79 + 1) +// Max size for inputs to Graffiti, Talkie Box text prompts +#if PACKETVER_MAIN_NUM >= 20190904 || PACKETVER_RE_NUM >= 20190904 || PACKETVER_ZERO_NUM >= 20190828 +#define TALKBOX_MESSAGE_SIZE 21 +#else +#define TALKBOX_MESSAGE_SIZE (79 + 1) +#endif +// String length you can write in the 'talking box' +#define CHATBOX_SIZE (70 + 1) +// Chatroom-related string sizes +#define CHATROOM_TITLE_SIZE (36 + 1) +#define CHATROOM_PASS_SIZE (8 + 1) +// Max allowed chat text length +#define CHAT_SIZE_MAX (255 + 1) +// 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS +#define EVENT_NAME_LENGTH ( NAME_LENGTH * 2 + 3 ) +#define DEFAULT_AUTOSAVE_INTERVAL (5*60*1000) +// Specifies maps where players may hit each other +#define map_flag_vs(m) ( \ + map->list[m].flag.pvp \ + || map->list[m].flag.gvg_dungeon \ + || map->list[m].flag.gvg \ + || ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle) \ + || map->list[m].flag.battleground \ + || map->list[m].flag.cvc \ + ) +// Specifies maps that have special GvG/WoE restrictions +#define map_flag_gvg(m) (map->list[m].flag.gvg || ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle)) +// Specifies if the map is tagged as GvG/WoE (regardless of map->agit_flag status) +#define map_flag_gvg2(m) (map->list[m].flag.gvg || map->list[m].flag.gvg_castle) +// No Kill Steal Protection +#define map_flag_ks(m) (map->list[m].flag.town || map->list[m].flag.pvp || map->list[m].flag.gvg || map->list[m].flag.battleground) +// No ViewID +#define map_no_view(m, view) (map->list[m].flag.noviewid & (view)) + +// For common mapforeach calls. Since pets cannot be affected, they aren't included here yet. +#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER|BL_ELEM) + +#define MAP_ZONE_NAME_LENGTH 60 +#define MAP_ZONE_ALL_NAME "All" +#define MAP_ZONE_NORMAL_NAME "Normal" +#define MAP_ZONE_PVP_NAME "PvP" +#define MAP_ZONE_GVG_NAME "GvG" +#define MAP_ZONE_BG_NAME "Battlegrounds" +#define MAP_ZONE_CVC_NAME "CvC" +#define MAP_ZONE_PK_NAME "PK Mode" +#define MAP_ZONE_MAPFLAG_LENGTH 65 + +#endif /* MAP_MAPDEFINES_H */ diff --git a/src/map/mapreg.h b/src/map/mapreg.h index 4a9fd9e97..b3b89e1b2 100644 --- a/src/map/mapreg.h +++ b/src/map/mapreg.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/mapreg_sql.c b/src/map/mapreg_sql.c index 516be56cb..741505e17 100644 --- a/src/map/mapreg_sql.c +++ b/src/map/mapreg_sql.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/mercenary.c b/src/map/mercenary.c index 918701c9d..00fdcae9f 100644 --- a/src/map/mercenary.c +++ b/src/map/mercenary.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/mercenary.h b/src/map/mercenary.h index 65e8d5338..bd28729c5 100644 --- a/src/map/mercenary.h +++ b/src/map/mercenary.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/messages.h b/src/map/messages.h index 5bfd476a2..adf5bc7e0 100644 --- a/src/map/messages.h +++ b/src/map/messages.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2016 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/messages_ad.h b/src/map/messages_ad.h index 4345e078a..9a3ada406 100644 --- a/src/map/messages_ad.h +++ b/src/map/messages_ad.h @@ -2,7 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/messages_main.h b/src/map/messages_main.h index 4bef783ec..9f5a17662 100644 --- a/src/map/messages_main.h +++ b/src/map/messages_main.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20181107 +Latest version: 20200304 */ enum clif_messages { @@ -632,9 +632,11 @@ Fire Wind */ MSG_WIND = 0x76, -/*20031028 to latest +/*20031028 to 20191023 같은종류의 장비아이템은 한번에 한개만 살 수 있습니다. Please avoid buying 2 of the same items at one time. +20191030 to latest +같은 종류의 장비 아이템은 한 번에 한 개만 살 수 있습니다. */ MSG_EQUIPITEM_OLNY_ONE = 0x77, /*20031028 to latest @@ -759,7 +761,7 @@ Congratulations! You are the MVP! Your reward item is MSG_YOU_RECEIVE_MVP_ITEM = 0x8f, /*20031028 to latest !! -!! +! */ MSG_YOU_RECEIVE_MVP_ITEM2 = 0x90, /*20031028 to latest @@ -1238,9 +1240,11 @@ Cart Items [Alt+W] Basic Information */ MSG_BASICINFOWND = 0xee, -/*20031028 to latest +/*20031028 to 20191204 이무기로는 이스킬을 사용할수 없습니다. The skill cannot be used with this weapon. +20191211 to latest +현재 무기로는 스킬을 사용할 수 없습니다. */ MSG_USESKILL_FAIL_NOT_SUITABLE_WEAPON = 0xef, /*20031028 to latest @@ -4013,7 +4017,6 @@ Mouse wheel skills for F7 and F8 are Disabled.[/q2 OFF] MSG_EXPLAIN_QUICKSPELL2 = 0x302, /*20040112 to latest /q3 : /quickspell (/q1) + /quickspell2 (/q2) -/q3: /quickspell (/q1) + /quickspell2 (/q2) */ MSG_EXPLAIN_QUICKSPELL3 = 0x303, #endif @@ -4630,9 +4633,11 @@ Thief Knight */ MSG_KNIGHT = 0x364, -/*20040913 to latest +/*20040913 to 20181128 Prieset Priest +20181205 to latest +Priest */ MSG_PRIEST = 0x365, /*20040913 to latest @@ -5262,16 +5267,18 @@ High Wizard MSG_WIZARD_H = 0x3da, /*20050613 to latest White Smith -WhiteSmith +MasterSmith */ MSG_BLACKSMITH_H = 0x3db, /*20050613 to latest Sniper */ MSG_HUNTER_H = 0x3dc, -/*20050613 to latest +/*20050613 to 20181128 Saaassin Cross Assassin Cross +20181205 to latest +Assassin Cross */ MSG_ASSASSIN_H = 0x3dd, /*20050613 to latest @@ -5299,9 +5306,11 @@ Sniper_W Sniper */ MSG_HUNTER_H_W = 0x3e2, -/*20050613 to latest +/*20050613 to 20181128 Saaassin Cross_W Assassin Cross +20181205 to latest +Assassin Cross_W */ MSG_ASSASSIN_H_W = 0x3e3, /*20050613 to latest @@ -7076,7 +7085,6 @@ Quest List #if PACKETVER >= 20070807 /*20070807 to latest RO SHOP -RO Shop */ MSG_RO_SHOP = 0x526, #endif @@ -7255,9 +7263,11 @@ The Memorial Dungeon's entry time limit expired; it has been destroyed. The Memorial Dungeon has been removed. */ MSG_MEMORIAL_DUN_DESTROY_REQUEST = 0x544, -/*20070918 to latest +/*20070918 to 20191218 메모리얼 던전에 시스템 오류가 발생하였습니다. 정상적인 게임 진행을 위해 재접속을 해주십시오. A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing. +20191224 to latest +메모리얼 던전에 통신 장애가 발생하였습니다. 정상적인 게임 진행을 위해 잠시 후, 재접속을 해주십시오. */ MSG_MEMORIAL_DUN_ERROR = 0x545, /*20070918 to latest @@ -10954,7 +10964,6 @@ ITEM MSG_MACRO_SKILL = 0x775, /*20110228 to 20110228 Next attack time : -Next attack time: 20110308 to latest TACTIC 20130807 to 20130814 @@ -10971,6 +10980,7 @@ TACTIC MSG_MACRO_ETC = 0x777, /*20110228 to 20110228 When invited to a party +When invited to the party 20110308 to latest COMBAT 20130807 to 20130814 @@ -11015,7 +11025,6 @@ ATTACK MSG_MACRO_ATTACK = 0x77d, /*20110308 to latest Next attack time : -Next attack time: 20130807 to 20130814 ATTACK */ @@ -11024,11 +11033,11 @@ ATTACK When died 20130807 to 20130814 Next attack time : -Next attack time: */ MSG_MACRO_WHEN_DIED = 0x77f, /*20110308 to latest When invited to a party +When invited to the party 20130807 to 20130814 When died */ @@ -11037,6 +11046,7 @@ When died Pickup Item 20130807 to 20130814 When invited to a party +When invited to the party */ MSG_MACRO_PICKUP_ITEM = 0x781, /*20110308 to latest @@ -11066,7 +11076,6 @@ Any work in progress (NPC dialog, manufacturing ...) quit and try again. ExMacro_SaveData%d 20110412 to latest SaveData_ExMacro%d -SaveData_ExMacro %d 20130807 to 20130814 몬스터 사냥을 통해 얻을 수 있는 Job경험치가 %d분간 %.2f배로 증가합니다. Monster Job hunting experience that you can get through the doubling of %d is %.2f minutes. @@ -11077,7 +11086,6 @@ Monster Job hunting experience that you can get through the doubling of %d is %. Settings for [%s] are stored in. 20130807 to 20130814 SaveData_ExMacro%d -SaveData_ExMacro %d */ MSG_MACRO_SAVE_DATA2 = 0x786, #endif @@ -11345,57 +11353,44 @@ Current location of the shop and chat room creation is disabled. MSG_REPLAY_ELAPSEDTIME = 0x7a3, /*20110816 to latest Speed : X 1/4 -Speed: X 1/4 20130807 to 20130814 Elapsed time: %d:%d:%d / %d:%d:%d */ MSG_REPLAY_SPEED1_4 = 0x7a4, /*20110816 to latest Speed : X 1/2 -Speed: X 1/2 20130807 to 20130814 Speed : X 1/4 -Speed: X 1/4 */ MSG_REPLAY_SPEED1_2 = 0x7a5, /*20110816 to latest Speed : X 1 -Speed: X 1 20130807 to 20130814 Speed : X 1/2 -Speed: X 1/2 */ MSG_REPLAY_SPEED1 = 0x7a6, /*20110816 to latest Speed : X 2 -Speed: X 2 20130807 to 20130814 Speed : X 1 -Speed: X 1 */ MSG_REPLAY_SPEED2 = 0x7a7, /*20110816 to latest Speed : X 4 -Speed: X 4 20130807 to 20130814 Speed : X 2 -Speed: X 2 */ MSG_REPLAY_SPEED4 = 0x7a8, /*20110816 to latest Speed : X 8 -Speed: X 8 20130807 to 20130814 Speed : X 4 -Speed: X 4 */ MSG_REPLAY_SPEED8 = 0x7a9, /*20110816 to latest Speed : X 16 -Speed: X 16 20130807 to 20130814 Speed : X 8 -Speed: X 8 */ MSG_REPLAY_SPEED16 = 0x7aa, /*20110816 to latest @@ -11403,12 +11398,10 @@ Speed : 알수없음 Speed: Unknown 20130807 to 20130814 Speed : X 16 -Speed: X 16 */ MSG_REPLAY_SPEEDUNKNOWN = 0x7ab, /*20110816 to latest Service Info : %s -Service Info: %s 20130807 to 20130814 Speed : 알수없음 Speed: Unknown @@ -11416,25 +11409,20 @@ Speed: Unknown MSG_REPLAY_CHRVICEINFO = 0x7ac, /*20110816 to latest Character Name : %s -Character Name: %s 20130807 to 20130814 Service Info : %s -Service Info: %s */ MSG_REPLAY_CHARACTERNAME = 0x7ad, /*20110816 to latest Map Name : %s -Map Name: %s 20130807 to 20130814 Character Name : %s -Character Name: %s */ MSG_REPLAY_MAPNAME = 0x7ae, /*20110816 to latest Record Time: %d-%01d-%01d %d: %02d: %02d 20130807 to 20130814 Map Name : %s -Map Name: %s */ MSG_REPLAY_RECORDTIME = 0x7af, /*20110816 to latest @@ -11517,20 +11505,24 @@ Stop MSG_REPLAY_START2 = 0x7bb, /*20110816 to latest Open Option +Open Options 20130807 to 20130814 Input FileName -> Start */ MSG_REPLAY_OPENOPTION = 0x7bc, /*20110816 to latest Close Option +Close Options 20130807 to 20130814 Open Option +Open Options */ MSG_REPLAY_CLOSEOPION = 0x7bd, /*20110816 to latest End 20130807 to 20130814 Close Option +Close Options */ MSG_REPLAY_END = 0x7be, /*20110816 to latest @@ -11595,6 +11587,7 @@ The same file exists already. MSG_REPLAY_RECORDSTART = 0x7c6, /*20110816 to latest is Saved. +Recording saved 20130807 to 20130814 Record Start */ @@ -11603,17 +11596,16 @@ Record Start #if PACKETVER >= 20110823 /*20110823 to latest Weight : %3d / %3d -Weight: %3d / %3d 20130807 to 20130814 is Saved. +Recording saved */ MSG_WEIGHT = 0x7c8, /*20110823 to latest Total : %s C -Total: %s C +Total: %s EUR 20130807 to 20130814 Weight : %3d / %3d -Weight: %3d / %3d */ MSG_TOTAL = 0x7c9, /*20110823 to latest @@ -11621,7 +11613,7 @@ Weight: %3d / %3d [Shuriken] must be equipped. 20130807 to 20130814 Total : %s C -Total: %s C +Total: %s EUR */ MSG_FAIL_NEED_EQUIPPED_SYURIKEN = 0x7ca, #endif @@ -11641,7 +11633,6 @@ Base Lv. %d MSG__BASIC_MSG_JOB = 0x7cc, /*20110831 to latest Zeny : %s -Zeny: %s 20130807 to 20130814 Job Lv. %d */ @@ -11650,7 +11641,6 @@ Job Lv. %d Trilinear 20130807 to 20130814 Zeny : %s -Zeny: %s */ MSG_GRAPHIC_MSG_TRILINEAR = 0x7ce, /*20110831 to latest @@ -11673,6 +11663,7 @@ skill MSG_GRAPHIC_MSG_ITEM = 0x7d1, /*20110831 to latest NoCtrl +Ctrl 20130807 to 20130814 item */ @@ -11682,10 +11673,12 @@ item More 20130807 to 20130814 NoCtrl +Ctrl */ MSG_GRAPHIC_MSG_BATTLE = 0x7d3, /*20110831 to latest (Character/Total Slot) +(Characters/Total slots) 20130807 to 20130814 전장 More @@ -11696,6 +11689,7 @@ Premium Service VIP Service 20130807 to 20130814 (Character/Total Slot) +(Characters/Total slots) */ MSG_CHARACTER_MSG_PREMIUMSERVICE = 0x7d5, /*20110831 to latest @@ -13034,6 +13028,7 @@ Move Combining items will be only one kind at a time. 20120320 to latest Make Character +Create Character 20130807 to 20130814 이름변경 Rename @@ -13046,6 +13041,7 @@ You cannot have more than 30,000 stacked items. http://ro.game.gnjoy.com/ 20130807 to 20130814 Make Character +Create Character */ MSG_UAE_URL = 0x877, #endif @@ -13365,7 +13361,6 @@ Change to Default UI MSG_NAVIGATION_HELP = 0x89d, /*20120417 to latest ALL -All 20130807 to 20130814 도움말 Help @@ -13375,7 +13370,6 @@ Help Map 20130807 to 20130814 ALL -All */ MSG_NAVIGATION_MAP = 0x89f, /*20120417 to latest @@ -15510,7 +15504,7 @@ You entered more than 1 Billion Zeny, the price will be set to 1 Billion Zeny. ErrorCode : %d, ErrorValue : %d 20130612 to latest ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d) -ErrorCategory: %d, ErrorCode: %d (%d,%d,%d,%d) +ErrorCode : %d, ErrorValue : %d 20130807 to 20130814 AuthTicket is Not Vaild AuthTicket is Not Valid @@ -15525,7 +15519,7 @@ AuthTicket is Not Valid %d%% ( Basic 100%% + Premium %d%% + Internet cafe %d%% + %s Server %d%% ) 20130807 to 20130814 ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d) -ErrorCategory: %d, ErrorCode: %d (%d,%d,%d,%d) +ErrorCode : %d, ErrorValue : %d */ MSG_BASIC_EXP_MSG_INDONESIA = 0x9a6, /*20130618 to 20130925 @@ -15558,7 +15552,6 @@ The price of^0000FF %s^000000 MSG_WARNING_PRICE1 = 0x9a9, /*20130626 to latest 100000000 -1000000000 20130807 to 20130814 %s 의 가격이 The price of^0000FF %s^000000 @@ -15569,7 +15562,6 @@ The price of^0000FF %s^000000 is over ^FF0000%d^0000FF Billion^000000 Zeny and 20130807 to 20130814 100000000 -1000000000 */ MSG_WARNING_PRICE3 = 0x9ab, /*20130626 to latest @@ -16346,7 +16338,7 @@ You can not open the mail. MSG_FAILED_TO_WRITE_MAIL = 0xa2c, /*20140416 to latest You are currently joined in CLan !! -You are currently joined in Clan !! +You currently belong to a clan. */ MSG_JOINED_IN_CLAN = 0xa2d, /*20140416 to latest @@ -16417,17 +16409,14 @@ The recipient's name does not exist. #if PACKETVER >= 20140430 /*20140430 to latest E X P : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -EXP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_EXPMSG = 0xa38, /*20140430 to latest DROP : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DROPMSG = 0xa39, /*20140430 to latest DEATH : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DEATHMSG = 0xa3a, #endif @@ -16678,7 +16667,6 @@ Adventure %s GD 20141001 to latest %s -%s */ MSG_CASH_GEDARE_MONEY = 0xa5f, /*20140723 to 20140723 @@ -16919,17 +16907,14 @@ Please empty at least 5 amount of possession in item window. MSG_NOT_ENOUGH_SPACE_IN_ITEM_BODY = 0xa85, /*20140917 to latest E X P : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -EXP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_EXPMSG = 0xa86, /*20140917 to latest DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DROPMSG = 0xa87, /*20140917 to latest DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DEATHMSG = 0xa88, /*20140917 to latest @@ -17375,12 +17360,10 @@ Withdraw MSG_ID_AD7 = 0xad7, /*20150304 to latest 1 z UP -1z UP */ MSG_ID_AD8 = 0xad8, /*20150304 to latest 1 z Down -1z Down */ MSG_ID_AD9 = 0xad9, /*20150304 to latest @@ -17555,17 +17538,14 @@ Send Mail #if PACKETVER >= 20150729 /*20150729 to latest E X P : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -E X P: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AF9 = 0xaf9, /*20150729 to latest DROP : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFA = 0xafa, /*20150729 to latest DEATH : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFB = 0xafb, #endif @@ -17575,6 +17555,7 @@ DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) Name with this tag cannot be used. 20181002 to latest 해당 내용은 이름으로 사용하실 수 없습니다. +You cannot use the tag as a name. */ MSG_ID_AFC = 0xafc, #endif @@ -17797,7 +17778,6 @@ SNS통신 TWITTER 20160824 to latest Twitter -TWITTER */ MSG_ID_B1D = 0xb1d, /*20160224 to latest @@ -18161,7 +18141,6 @@ The [%s] is not present, the default AI will be used instead. %.1f%% ( Premium %.1f%% + %s %.1f%%) 20160706 to latest %.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) -%.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) */ MSG_ID_B62 = 0xb62, #endif @@ -18778,10 +18757,12 @@ Weight limit has reached toover 70%. Or less then 10 invenrory space. MSG_ID_BCE = 0xbce, /*20161123 to latest C +EUR */ MSG_ID_BCF = 0xbcf, /*20161123 to latest C +EUR */ MSG_ID_BD0 = 0xbd0, /*20161123 to latest @@ -19035,10 +19016,12 @@ SP MSG_ID_BFD = 0xbfd, /*20161228 to latest Lv +Lv. */ MSG_ID_BFE = 0xbfe, /*20161228 to latest Lv +Lv. */ MSG_ID_BFF = 0xbff, /*20161228 to latest @@ -19051,6 +19034,7 @@ Exp MSG_ID_C01 = 0xc01, /*20161228 to latest Play Replay Flie +Start replay */ MSG_ID_C02 = 0xc02, /*20161228 to latest @@ -19067,14 +19051,17 @@ Basicinfo MSG_ID_C05 = 0xc05, /*20161228 to latest Equip +Equipment */ MSG_ID_C06 = 0xc06, /*20161228 to latest Item +Items */ MSG_ID_C07 = 0xc07, /*20161228 to latest Skill +Skills */ MSG_ID_C08 = 0xc08, /*20161228 to latest @@ -19087,10 +19074,12 @@ Party MSG_ID_C0A = 0xc0a, /*20161228 to latest Chatting +Chat */ MSG_ID_C0B = 0xc0b, /*20161228 to latest Shortcut +Hotkeys */ MSG_ID_C0C = 0xc0c, /*20161228 to latest @@ -19099,15 +19088,16 @@ Status MSG_ID_C0D = 0xc0d, /*20161228 to latest ALL -All */ MSG_ID_C0E = 0xc0e, /*20161228 to latest User Defined File Name +Custom file name */ MSG_ID_C0F = 0xc0f, /*20161228 to latest Repeated File Check +Check file */ MSG_ID_C10 = 0xc10, /*20161228 to latest @@ -19116,10 +19106,12 @@ on MSG_ID_C11 = 0xc11, /*20161228 to latest <Basic Skin> +<Basic> */ MSG_ID_C12 = 0xc12, /*20161228 to latest Select Skin +Choose skin */ MSG_ID_C13 = 0xc13, #endif @@ -19669,6 +19661,7 @@ Loading the mailbox. MSG_ID_C76 = 0xc76, /*20170315 to latest NOW LOADING.. +Loading... */ MSG_ID_C77 = 0xc77, /*20170315 to latest @@ -19933,6 +19926,7 @@ BOX #if PACKETVER >= 20170628 /*20170628 to latest 다시하기 +File abusing detected. Please restart the client with clean files. */ MSG_ID_CA9 = 0xca9, /*20170628 to 20170809 @@ -20091,6 +20085,7 @@ map MSG_ID_CCC = 0xccc, /*20170809 to latest 변조된 파일이 발견되었습니다. 게임을 다시 실행시켜주세요. +File abusing detected. Please restart the client with clean files. */ MSG_ID_CCD = 0xccd, #endif @@ -20254,9 +20249,11 @@ http://member.gnjoy.com/user/pay/chargelist.asp 한 번에 구입 가능한 아이템의 가짓수는 8개입니다. */ MSG_ID_CF0 = 0xcf0, -/*20170920 to latest +/*20170920 to 20190228 http://gift.zhaouc.com/#/index http://gift.zhaouc.com/ +20190306 to latest +http://gift.zhaouc.com/ */ MSG_ID_CF1 = 0xcf1, #endif @@ -20383,6 +20380,7 @@ TokenAgency 서버 연결 실패 MSG_ID_D0D = 0xd0d, /*20171025 to latest 삭제 +Delete */ MSG_ID_D0E = 0xd0e, /*20171025 to latest @@ -20446,92 +20444,143 @@ MOTP 입력 시간이 초과되었습니다. 처음부터 다시 로그인해주 모험가 중개소 등록 */ MSG_ID_D1C = 0xd1c, -/*20171108 to latest +/*20171108 to 20191120 모집 중단 +20191127 to latest +모험가 중개소 등록하기 */ MSG_ID_D1D = 0xd1d, -/*20171108 to latest +/*20171108 to 20191120 모험가 중개소 설정 +20191127 to latest +모집 중단 */ MSG_ID_D1E = 0xd1e, -/*20171108 to latest +/*20171108 to 20191120 전 지역 +20191127 to latest +메모 +Note */ MSG_ID_D1F = 0xd1f, -/*20171108 to latest +/*20171108 to 20191120 직접기재 +20191127 to latest +퀘스트 +Quest */ MSG_ID_D20 = 0xd20, -/*20171108 to latest +/*20171108 to 20191120 검사계열 +20191127 to latest +필드 */ MSG_ID_D21 = 0xd21, -/*20171108 to latest +/*20171108 to 20191120 법사계열 +20191127 to latest +던전 +Dungeon */ MSG_ID_D22 = 0xd22, -/*20171108 to latest +/*20171108 to 20191120 궁수계열 +20191127 to latest +MD */ MSG_ID_D23 = 0xd23, -/*20171108 to latest +/*20171108 to 20191120 복사계열 +20191127 to latest +낙원단 */ MSG_ID_D24 = 0xd24, -/*20171108 to latest +/*20171108 to 20191120 상인계열 +20191127 to latest +기타 +Other */ MSG_ID_D25 = 0xd25, -/*20171108 to latest +/*20171108 to 20191120 도둑계열 +20191127 to latest +검색 +Search */ MSG_ID_D26 = 0xd26, -/*20171108 to latest +/*20171108 to 20191120 태권계열 +20191127 to latest +초기화 */ MSG_ID_D27 = 0xd27, -/*20171108 to latest +/*20171108 to 20191120 닌자계열 +20191127 to latest +파티가입신청 */ MSG_ID_D28 = 0xd28, -/*20171108 to latest +/*20171108 to 20191120 건슬링거계열 +20191127 to latest +목적 */ MSG_ID_D29 = 0xd29, -/*20171108 to latest +/*20171108 to 20191120 도람족계열 +20191127 to latest +※욕설, 현거래등 운영 규칙에 위배되는 목적으로 사용 시 처벌 받을 수 있습니다. */ MSG_ID_D2A = 0xd2a, -/*20171108 to latest +/*20171108 to 20191120 지역명 +20191127 to latest +등록하기 +Register */ MSG_ID_D2B = 0xd2b, -/*20171108 to latest +/*20171108 to 20191120 지역명 검색 +20191127 to latest +사용할 수 없는 단어가 포함되어 있습니다. */ MSG_ID_D2C = 0xd2c, -/*20171108 to latest +/*20171108 to 20191120 가입 요청하기 +20191127 to latest +파티가입 */ MSG_ID_D2D = 0xd2d, -/*20171108 to latest +/*20171108 to 20191120 신고하기 +20191127 to latest +1:1대화 */ MSG_ID_D2E = 0xd2e, -/*20171108 to latest +/*20171108 to 20191120 파티 모집 중단 +20191127 to latest +모집 중지 */ MSG_ID_D2F = 0xd2f, -/*20171108 to latest +/*20171108 to 20191120 지역명을 입력해주세요. +20191127 to latest +수정하기 */ MSG_ID_D30 = 0xd30, -/*20171108 to latest +/*20171108 to 20191120 지역 +20191127 to latest +전체 +All */ MSG_ID_D31 = 0xd31, -/*20171108 to latest +/*20171108 to 20191120 전 직업 +20191127 to latest +[%s] 님이 파티가입을 신청했습니다. */ MSG_ID_D32 = 0xd32, /*20171108 to latest @@ -20573,6 +20622,7 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D3B = 0xd3b, /*20171108 to latest 태권 +Taekwon */ MSG_ID_D3C = 0xd3c, #endif @@ -20615,10 +20665,12 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D42 = 0xd42, /*20171115 to latest 차단 리스트가 없습니다 +Ignore-list is empty */ MSG_ID_D43 = 0xd43, /*20171115 to latest -차단 리스트- +Characters in ignore-list: */ MSG_ID_D44 = 0xd44, /*20171115 to latest @@ -20701,12 +20753,17 @@ Password 회원가입 */ MSG_ID_D56 = 0xd56, -/*20171122 to latest +/*20171122 to 20191120 노비스계열 +20191127 to latest +파티장 +Leader */ MSG_ID_D57 = 0xd57, -/*20171122 to latest +/*20171122 to 20191120 슈퍼노비스계열 +20191127 to latest +파티에 가입할 수 있는 레벨이 아닙니다. */ MSG_ID_D58 = 0xd58, /*20171122 to latest @@ -20719,12 +20776,16 @@ Password MSG_ID_D5A = 0xd5a, /*20171122 to 20180328 직업을 한 개 이상 선택해주세요. -20180404 to latest +20180404 to 20191120 모집을 희망하는 파티원의 직업을 한 개 이상 선택해 주세요. +20191127 to latest +검색내용입력 */ MSG_ID_D5B = 0xd5b, -/*20171122 to latest +/*20171122 to 20191127 지역명을 두 글자 이상 입력해주세요. +20191204 to latest +탱커 */ MSG_ID_D5C = 0xd5c, /*20171122 to latest @@ -20815,8 +20876,10 @@ Password 불량 단어가 포함된 이름은 검색할 수 없습니다. */ MSG_ID_D71 = 0xd71, -/*20171213 to latest +/*20171213 to 20191127 파티 마스터가 요청을 받을 수 없는 맵에 있습니다. +20191204 to latest +파티 마스터가 요청을 받을 수 없는 상태입니다. */ MSG_ID_D72 = 0xd72, /*20171213 to latest @@ -20825,8 +20888,10 @@ Password MSG_ID_D73 = 0xd73, /*20171213 to 20180328 님의 파티요청입니다. -20180404 to latest +20180404 to 20191127 님의 파티 가입 요청입니다. +20191204 to latest +딜러 */ MSG_ID_D74 = 0xd74, #endif @@ -20985,6 +21050,7 @@ This is not the current attendance check event MSG_ID_D93 = 0xd93, /*20180207 to latest 개인 상납 경험치가 max에 도달하여, 더 이상 길드 경험치를 누적할 수 없습니다. + */ MSG_ID_D94 = 0xd94, #endif @@ -21037,10 +21103,13 @@ Enter 4 english words and 2 chinese words MSG_ID_D9D = 0xd9d, /*20180404 to latest 50% 이상의 값을 입력할 수 없습니다. +The guild tax rate can't be set to more than 50%. */ MSG_ID_D9E = 0xd9e, -/*20180404 to latest +/*20180404 to 20191204 파티 가입 요청을 보냈습니다. +20191211 to latest +파티가 모험가 중개소에 등록되어, 파티장을 변경 할 수 없습니다. */ MSG_ID_D9F = 0xd9f, /*20180404 to latest @@ -21055,8 +21124,10 @@ Enter 4 english words and 2 chinese words 거절 */ MSG_ID_DA2 = 0xda2, -/*20180404 to latest +/*20180404 to 20191127 설정 권한은 파티장에게 있습니다. +20191204 to latest +힐러 */ MSG_ID_DA3 = 0xda3, /*20180404 to latest @@ -21115,8 +21186,10 @@ Enter 4 english words and 2 chinese words 파티 가입 요청 */ MSG_ID_DB1 = 0xdb1, -/*20180404 to latest +/*20180404 to 20191120 파티장이 아닌 경우, 모험가 중개소에 파티를 등록할 수 없습니다. +20191127 to latest +파티장이 아닌 경우 모험가 중개소에 파티를 등록할 수 없습니다. */ MSG_ID_DB2 = 0xdb2, /*20180404 to 20180404 @@ -21127,8 +21200,11 @@ Enter 4 english words and 2 chinese words MSG_ID_DB3 = 0xdb3, #endif #if PACKETVER >= 20180418 -/*20180418 to latest +/*20180418 to 20191127 존재하지 않는 파티 글입니다. +20191204 to latest +보조 +Support */ MSG_ID_DB4 = 0xdb4, /*20180418 to 20180418 @@ -21169,16 +21245,20 @@ Zoom Out 기능을 해제합니다 (Off) 파티장인 경우, 가입 요청을 할 수 없습니다. */ MSG_ID_DBB = 0xdbb, -/*20180418 to latest +/*20180418 to 20191211 모험가 중개소에 등록 중입니다. 잠시만 기다려 주세요. +20191218 to latest +파티 가입 최대 레벨은 최소 레벨 설정보다 높아야 합니다. */ MSG_ID_DBC = 0xdbc, /*20180418 to latest 더 이상 직업을 선택할 수 없습니다. */ MSG_ID_DBD = 0xdbd, -/*20180418 to latest +/*20180418 to 20191120 게임 중 해당 유저의 가입 요청을 받지 않습니다. +20191127 to latest +게임 중 해당 유저의 가입 요청을 받지 않습니다.(클라이언트 종료 전까지 유효) */ MSG_ID_DBE = 0xdbe, #endif @@ -21259,20 +21339,24 @@ Emblem 테두리를 그려주지 않습니다 #if PACKETVER >= 20180718 /*20180718 to latest E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DCF = 0xdcf, /*20180718 to latest DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD0 = 0xdd0, /*20180718 to latest DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD1 = 0xdd1, #endif #if PACKETVER >= 20180829 /*20180829 to latest 영문이나 러시아어 단독으로만 사용이 가능합니다. +Forbidden symbols in character name. */ MSG_ID_DD2 = 0xdd2, /*20180829 to latest @@ -21281,28 +21365,34 @@ you must have an AccessTicket to login MSG_ID_DD3 = 0xdd3, /*20180829 to latest 창고를 불러오는 중입니다. +Loading.... */ MSG_ID_DD4 = 0xdd4, /*20180829 to latest NOW LOADING.. +Loading... */ MSG_ID_DD5 = 0xdd5, #endif #if PACKETVER >= 20181002 /*20181002 to latest 삭제 +Delete */ MSG_ID_DD6 = 0xdd6, /*20181002 to latest 답장 +Reply */ MSG_ID_DD7 = 0xdd7, /*20181002 to latest 전송 +Send */ MSG_ID_DD8 = 0xdd8, /*20181002 to latest 이름확인 +Name Check */ MSG_ID_DD9 = 0xdd9, /*20181002 to latest @@ -21412,6 +21502,1116 @@ PvP */ MSG_ID_DEE = 0xdee, #endif +#if PACKETVER >= 20181121 +/*20181121 to latest +같은 아이템을 %d개 이상 가질 수 없습니다. +*/ + MSG_ID_DEF = 0xdef, +/*20181121 to latest +소지 한계량 초과로 구입이 불가능합니다. +*/ + MSG_ID_DF0 = 0xdf0, +/*20181121 to latest +해당 아이템은 한 번에 한 개만 살 수 있습니다. +*/ + MSG_ID_DF1 = 0xdf1, +#endif +#if PACKETVER >= 20181205 +/*20181205 to latest +성제 +*/ + MSG_ID_DF2 = 0xdf2, +/*20181205 to latest +소울리퍼 +*/ + MSG_ID_DF3 = 0xdf3, +#endif +#if PACKETVER >= 20181212 +/*20181212 to latest +설정한 내용은 정상 종료 시 서버에 저장됩니다. +*/ + MSG_ID_DF4 = 0xdf4, +#endif +#if PACKETVER >= 20190109 +/*20190109 to latest +Capture Monster +Taming monster +*/ + MSG_ID_DF5 = 0xdf5, +/*20190109 to latest +message +*/ + MSG_ID_DF6 = 0xdf6, +/*20190109 to latest +TITLE +Header +*/ + MSG_ID_DF7 = 0xdf7, +/*20190109 to latest +데미지 표기 확장 +*/ + MSG_ID_DF8 = 0xdf8, +#endif +#if PACKETVER >= 20190213 +/*20190213 to latest +맵 이름 표기 +*/ + MSG_ID_DF9 = 0xdf9, +/*20190213 to latest +시스템 메세지 : 현재 노점보기 기능이 %s 상태입니다. (/노점보기) +*/ + MSG_ID_DFA = 0xdfa, +/*20190213 to latest +수직 동기 +*/ + MSG_ID_DFB = 0xdfb, +/*20190213 to latest +자료 +*/ + MSG_ID_DFC = 0xdfc, +/*20190213 to 20190228 +http://rodata.zhaouc.com/renwu.html#container +20190306 to latest +http://rodata.zhaouc.com/renwu.html +*/ + MSG_ID_DFD = 0xdfd, +/*20190213 to latest +작위 +*/ + MSG_ID_DFE = 0xdfe, +/*20190213 to latest +프레임 제한 +*/ + MSG_ID_DFF = 0xdff, +#endif +#if PACKETVER >= 20190227 +/*20190227 to latest +%d개를 초과할 경우 더 이상 설치 할 수 없습니다. +*/ + MSG_ID_E00 = 0xe00, +/*20190227 to latest +목적지 +*/ + MSG_ID_E01 = 0xe01, +#endif +#if PACKETVER >= 20190306 +/*20190306 to latest +잠시 후 다시 시도해주세요. +Please try again in a moment. +*/ + MSG_ID_E02 = 0xe02, +/*20190306 to latest +등록할 수 없는 파일입니다. +*/ + MSG_ID_E03 = 0xe03, +#endif +#if PACKETVER >= 20190320 +/*20190320 to latest +선택 삭제 +*/ + MSG_ID_E04 = 0xe04, +/*20190320 to latest +모두 삭제 +*/ + MSG_ID_E05 = 0xe05, +/*20190320 to latest +[%s]편지함의 모든 메일을 삭제하시겠습니까? +*/ + MSG_ID_E06 = 0xe06, +/*20190320 to latest +선택 받기 +*/ + MSG_ID_E07 = 0xe07, +/*20190320 to latest +모두 받기 +*/ + MSG_ID_E08 = 0xe08, +/*20190320 to latest +선택한 메일의 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E09 = 0xe09, +/*20190320 to latest +[%s]편지함의 모든 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E0A = 0xe0a, +#endif +#if PACKETVER >= 20190403 +/*20190403 to latest +스킬바2 +*/ + MSG_ID_E0B = 0xe0b, +/*20190403 to latest +스킬바 교체 +*/ + MSG_ID_E0C = 0xe0c, +/*20190403 to latest +← +*/ + MSG_ID_E0D = 0xe0d, +/*20190403 to latest +↑ +*/ + MSG_ID_E0E = 0xe0e, +/*20190403 to latest +→ +*/ + MSG_ID_E0F = 0xe0f, +/*20190403 to latest +↓ +*/ + MSG_ID_E10 = 0xe10, +/*20190403 to latest +▤ +*/ + MSG_ID_E11 = 0xe11, +/*20190403 to latest +← +*/ + MSG_ID_E12 = 0xe12, +/*20190403 to latest +로딩중에는 창을 닫을 수 없습니다. +*/ + 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 +#if PACKETVER >= 20190508 +/*20190508 to latest +자유이동권 +*/ + MSG_ID_E1A = 0xe1a, +/*20190508 to latest +%d시간 이용, %s %d개 +*/ + MSG_ID_E1B = 0xe1b, +/*20190508 to latest +자유이용권 사용중 +*/ + MSG_ID_E1C = 0xe1c, +/*20190508 to latest +특성 스테이터스 +*/ + MSG_ID_E1D = 0xe1d, +/*20190508 to latest +파워 파라메터 +^cc0000물리 공격력, 특성 공격력^ffffff 증가 +*/ + MSG_ID_E1E = 0xe1e, +/*20190508 to latest +스테미나 파라메터 +^cc0000물리 저항력^ffffff 증가 +*/ + MSG_ID_E1F = 0xe1f, +/*20190508 to latest +위즈덤 파라메터 +^cc0000마법 저항력^ffffff 증가 +*/ + MSG_ID_E20 = 0xe20, +/*20190508 to latest +스펠 파라메터 +^cc0000마법 공격력, 마법 공격력^ffffff 증가 +*/ + MSG_ID_E21 = 0xe21, +/*20190508 to latest +컨센트레이션 파라메터 +^cc0000명중률, 회피율, 특성 물리/마법 공격력^ffffff 증가 +*/ + MSG_ID_E22 = 0xe22, +/*20190508 to latest +크리에이티브 파라메터 +^cc0000특성 힐 회복량, 크리티컬 데미지 비율^ffffff 증가 +*/ + MSG_ID_E23 = 0xe23, +/*20190508 to latest +특성 물리 공격력 +*/ + MSG_ID_E24 = 0xe24, +/*20190508 to latest +특성 마법 공격력 +*/ + MSG_ID_E25 = 0xe25, +/*20190508 to latest +물리 저항력 +*/ + MSG_ID_E26 = 0xe26, +/*20190508 to latest +마법 저항력 +*/ + MSG_ID_E27 = 0xe27, +/*20190508 to latest +특성 힐 회복량 +*/ + MSG_ID_E28 = 0xe28, +/*20190508 to latest +크리티컬 데미지 비율 +*/ + MSG_ID_E29 = 0xe29, +/*20190508 to latest +특성 파라메터 레벨업에 사용되는 포인트 +*/ + MSG_ID_E2A = 0xe2a, +/*20190508 to latest +J.Lv +*/ + MSG_ID_E2B = 0xe2b, +/*20190508 to latest +AP +*/ + MSG_ID_E2C = 0xe2c, +#endif +#if PACKETVER >= 20190522 +/*20190522 to latest +영지로 이동 +*/ + MSG_ID_E2D = 0xe2d, +/*20190522 to latest +상업도/방어도 확인 +*/ + MSG_ID_E2E = 0xe2e, +/*20190522 to 20190605 +관리영지 "%s"(으)로 이동하시겠습니까? +(1회 이동 시 마다 1,000제니가 소모됩니다. +공성전 시간에는 제니의 소모가 100배로 증가합니다.) +20190619 to 20190619 +관리영지 "%s"(으)로 이동하시겠습니까? +이동 시 마다 %d제니가 소모됩니다. +20190703 to latest +관리영지 "%s"(으)로 이동하시겠습니까? +이동 시 마다 %d제니가 소모됩니다. +(공성전 시간에는 %d제니가 소모됩니다.) +*/ + MSG_ID_E2F = 0xe2f, +/*20190522 to latest + +관리영지 "%s" + +방어도: %d / %d +상업도: %d / %d +*/ + MSG_ID_E30 = 0xe30, +/*20190522 to 20190717 +공성 영지 내에서는 다른 공성 영지로 이동 할 수 없습니다. +20190717 to latest +공성 영지에서는 사용할 수 없는 기능입니다. +*/ + MSG_ID_E31 = 0xe31, +#endif +#if PACKETVER >= 20190619 +/*20190619 to latest +기본 기능 스킬을 습득하지 않은 캐릭터입니다. +*/ + MSG_ID_E32 = 0xe32, +#endif +#if PACKETVER >= 20190703 +/*20190703 to latest +접속하신 IP는 라그나로크 제로 이용이 불가능합니다. +고객센터 또는 홈페이지로 문의해 주십시오. +*/ + MSG_ID_E33 = 0xe33, +#endif +#if PACKETVER >= 20190717 +/*20190717 to latest +탑승/장착 해제 +*/ + MSG_ID_E34 = 0xe34, +/*20190717 to latest +가져오기 +*/ + MSG_ID_E35 = 0xe35, +/*20190717 to latest +직전 노점에 등록된 아이템 정보를 가져옵니다. +*/ + MSG_ID_E36 = 0xe36, +/*20190717 to latest +서버 이슈로 인해 로그인이 제한되고 있습니다. +*/ + MSG_ID_E37 = 0xe37, +/*20190717 to latest +메시지 +*/ + MSG_ID_E38 = 0xe38, +/*20190717 to latest ++18 이상만 플레이가 가능합니다. +*/ + MSG_ID_E39 = 0xe39, +/*20190717 to latest +3시간 이상의 게임 플레이를 권장하지 않습니다. +*/ + MSG_ID_E3A = 0xe3a, +/*20190717 to latest +AP가 부족합니다. +*/ + MSG_ID_E3B = 0xe3b, +#endif +#if PACKETVER >= 20190731 +/*20190731 to latest +제련도가 높아서 조합할 수 없습니다. +*/ + MSG_ID_E3C = 0xe3c, +/*20190731 to latest +제련도가 조합에 필요한 요구치보다 높습니다. +*/ + MSG_ID_E3D = 0xe3d, +/*20190731 to 20191107 +'카드가 끼워져있거나 인챈트 되어 있습니다. +20191113 to latest +카드가 끼워져있거나 인챈트 되어 있습니다. +*/ + MSG_ID_E3E = 0xe3e, +/*20190731 to latest +∞ +? +*/ + MSG_ID_E3F = 0xe3f, +/*20190731 to latest +Z +*/ + MSG_ID_E40 = 0xe40, +/*20190731 to latest +Total : %s Zeny +*/ + MSG_ID_E41 = 0xe41, +#endif +#if PACKETVER >= 20190821 +/*20190821 to latest +계정한정판매 등록창 +Limited Account Registration Window +*/ + MSG_ID_E42 = 0xe42, +/*20190821 to latest +아이템 DB명 +Item DB Name +*/ + MSG_ID_E43 = 0xe43, +/*20190821 to latest +아이템 DB번호 +Item DB Number +*/ + MSG_ID_E44 = 0xe44, +/*20190821 to latest +판매 갯수 +Number of Sale +*/ + MSG_ID_E45 = 0xe45, +/*20190821 to latest +판매 시작시간 +Sale Start Time +*/ + MSG_ID_E46 = 0xe46, +/*20190821 to latest +판매 종료시간 +Sale end time +*/ + MSG_ID_E47 = 0xe47, +/*20190821 to latest +계정 한정 +Account only +*/ + MSG_ID_E48 = 0xe48, +/*20190821 to latest +판매기간 : %d월 %d일 %d시 %d분 +Sale period:% d month% d day% d hours% d +*/ + MSG_ID_E49 = 0xe49, +/*20190821 to 20191002 +구입가능 %d개 +% D available +20191016 to latest +계정당 구매가능 +*/ + MSG_ID_E4A = 0xe4a, +/*20190821 to latest +%d개 한정 +limited to% d +*/ + MSG_ID_E4B = 0xe4b, +/*20190821 to latest +>> ItemName : %s / 수량 : %d / 판매기간 : %d월:%d일:%d시:%d분 ~ %d월:%d일:%d시:%d분 +>> ItemName:% s / Quantity:% d / Sales Period:% d Month:% d Day:% d Hour:% d Minute ~% d Month:% d Day:% d Hour:% d Minute +*/ + MSG_ID_E4C = 0xe4c, +/*20190821 to latest +Sold Out +*/ + MSG_ID_E4D = 0xe4d, +/*20190821 to latest +[%s]은(는) 현재 소환할 수 없는 지역에 있습니다. +% s] is currently in a region that cannot be summoned. +*/ + MSG_ID_E4E = 0xe4e, +/*20190821 to latest +~ %d월 %d일 %d시 %d분 +% d min% d days% d days +*/ + MSG_ID_E4F = 0xe4f, +/*20190821 to latest +상품을 더이상 추가할 수 없습니다 +Can't add any more items +*/ + MSG_ID_E50 = 0xe50, +#endif +#if PACKETVER >= 20190828 +/*20190828 to latest +장착 중인 아이템은 교환할 수 없습니다. 장착을 해제한 뒤 시도해 주시길 바랍니다. +The item being mounted cannot be exchanged. Please unmount it and try again. +*/ + MSG_ID_E51 = 0xe51, +#endif +#if PACKETVER >= 20190904 +/*20190904 to latest +길드 창고 이용 중엔 캐릭터 선택창으로 이동 할 수 없습니다. +You can not move to the character selection window while using the Guild Warehouse. +*/ + MSG_ID_E52 = 0xe52, +/*20190904 to latest +아이템 태그가 포함되어 있어 사용할 수 없습니다. +Item tag is included and cannot be used. +*/ + MSG_ID_E53 = 0xe53, +/*20190904 to latest +Monster +*/ + MSG_ID_E54 = 0xe54, +/*20190904 to latest +Unknown +*/ + MSG_ID_E55 = 0xe55, +/*20190904 to latest +Undead +*/ + MSG_ID_E56 = 0xe56, +/*20190904 to latest +Animal +*/ + MSG_ID_E57 = 0xe57, +/*20190904 to latest +Plant +*/ + MSG_ID_E58 = 0xe58, +/*20190904 to latest +Insect +*/ + MSG_ID_E59 = 0xe59, +/*20190904 to latest +Marine +*/ + MSG_ID_E5A = 0xe5a, +/*20190904 to latest +Devil +*/ + MSG_ID_E5B = 0xe5b, +/*20190904 to latest +Human +*/ + MSG_ID_E5C = 0xe5c, +/*20190904 to latest +Angel +*/ + MSG_ID_E5D = 0xe5d, +/*20190904 to latest +Dragon +*/ + MSG_ID_E5E = 0xe5e, +#endif +#if PACKETVER >= 20190918 +/*20190918 to latest +Balance: %s %c +*/ + MSG_ID_E5F = 0xe5f, +/*20190918 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money will be deducted from your total balance. +*/ + MSG_ID_E60 = 0xe60, +/*20190918 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %s캐시, 무료 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money and %s Free Points will be deducted from your total balance. +*/ + MSG_ID_E61 = 0xe61, +/*20190918 to 20190918 +[%s]의 호출이 거부되었습니다. +20190925 to latest +호출이 거부되었습니다. +*/ + MSG_ID_E62 = 0xe62, +/*20190918 to latest +구매 한도액은 소지액을 초과할 수 없습니다. +*/ + MSG_ID_E63 = 0xe63, +/*20190918 to latest +노점을 여는데 실패하였습니다. 구매노점 개설을 닫아주시기 바랍니다. +*/ + MSG_ID_E64 = 0xe64, +#endif +#if PACKETVER >= 20191002 +/*20191002 to latest +판매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E65 = 0xe65, +/*20191002 to latest +구매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E66 = 0xe66, +/*20191002 to latest +VTC 인증에 실패하였습니다. +*/ + MSG_ID_E67 = 0xe67, +#endif +#if PACKETVER >= 20191016 +/*20191016 to latest +물물교환 중에는 장비를 착용할 수 없습니다. +*/ + MSG_ID_E68 = 0xe68, +/*20191016 to latest +교환하려는 품목 +*/ + MSG_ID_E69 = 0xe69, +/*20191016 to latest + 1차, 2차, 3차 직업 스킬 %d개를 더 올려 주십시오. +*/ + MSG_ID_E6A = 0xe6a, +#endif +#if PACKETVER >= 20191030 +/*20191030 to latest +같은 아이템은 %d개까지 소유할 수 있습니다. +*/ + MSG_ID_E6B = 0xe6b, +/*20191030 to latest +같은 아이템은 한 번에 %d개까지 교환할 수 있습니다. +*/ + MSG_ID_E6C = 0xe6c, +/*20191030 to latest +해당 로덱스는 "%s"서버에서 열 수 있습니다. +*/ + MSG_ID_E6D = 0xe6d, +/*20191030 to latest +[공지]편지함의 모든 메일을 삭제하시겠습니까 ? +(해당 "%s"서버의 메일만 삭제됩니다.) +*/ + MSG_ID_E6E = 0xe6e, +/*20191030 to latest +[공지]편지함의 모든 첨부 물품을 받으시겠습니까? +(해당 "%s"서버의 메일의 물품만 받으실 수 있습니다.) +*/ + MSG_ID_E6F = 0xe6f, +/*20191030 to latest +해당 메일은 "%s"서버에서만 삭제 가능합니다. +*/ + MSG_ID_E70 = 0xe70, +/*20191030 to latest +해당 메일은 "%s"서버에서만 물품 받기가 가능합니다. +*/ + MSG_ID_E71 = 0xe71, +/*20191030 to latest +해당 메일은 "%s"서버에서만 내용읽기가 가능합니다. +*/ + MSG_ID_E72 = 0xe72, +/*20191030 to latest +합계 : %d z +*/ + MSG_ID_E73 = 0xe73, +/*20191030 to latest +메세지 +*/ + MSG_ID_E74 = 0xe74, +#endif +#if PACKETVER >= 20191106 +/*20191106 to latest +구매상점 개설 중에는 개인상점의 물건을 구입할 수 없습니다. +*/ + MSG_ID_E75 = 0xe75, +#endif +#if PACKETVER >= 20191113 +/*20191113 to latest +등급강화가 성공적으로 되었습니다. +*/ + MSG_ID_E76 = 0xe76, +/*20191113 to latest +등급강화가 실패하였습니다. +*/ + MSG_ID_E77 = 0xe77, +/*20191113 to latest +등급 수치가 하향 조정 되었습니다. +*/ + MSG_ID_E78 = 0xe78, +/*20191113 to latest +장비가 파괴되었습니다. +*/ + MSG_ID_E79 = 0xe79, +/*20191113 to latest +장비가 보호되었습니다. +*/ + MSG_ID_E7A = 0xe7a, +/*20191113 to latest +재료가 선택되지 않았습니다. +*/ + MSG_ID_E7B = 0xe7b, +/*20191113 to latest +재료가 부족합니다. +*/ + MSG_ID_E7C = 0xe7c, +/*20191113 to latest +소지 금액이 부족합니다. +*/ + MSG_ID_E7D = 0xe7d, +/*20191113 to latest +아이템 공간이 부족합니다. +*/ + MSG_ID_E7E = 0xe7e, +/*20191113 to latest +장비가 보호되고 있습니다. +*/ + MSG_ID_E7F = 0xe7f, +/*20191113 to latest +장비가 파괴될 수 있습니다. +*/ + MSG_ID_E80 = 0xe80, +/*20191113 to latest +등급강화 실패 시 등급이 내려갑니다. +*/ + MSG_ID_E81 = 0xe81, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. 로덱스를 강제 종료하였습니다. +*/ + MSG_ID_E82 = 0xe82, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. +*/ + MSG_ID_E83 = 0xe83, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. 은행을 강제 종료하였습니다. +*/ + MSG_ID_E84 = 0xe84, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. +*/ + MSG_ID_E85 = 0xe85, +/*20191113 to latest +[%s] 님이 등급강화를 성공하여, [%s등급 %s] 아이템을 획득하였습니다. +*/ + MSG_ID_E86 = 0xe86, +/*20191113 to latest +[%s] 님이 [%s등급 %s] 아이템의 등급강화에 실패하였습니다. +*/ + MSG_ID_E87 = 0xe87, +/*20191113 to latest +등급 강화가 불가능한 장비입니다. +*/ + MSG_ID_E88 = 0xe88, +/*20191113 to latest +체인지 메테리얼 +*/ + MSG_ID_E89 = 0xe89, +/*20191113 to latest +가나다 정렬 +*/ + MSG_ID_E8A = 0xe8a, +/*20191113 to 20191224 +기본 결과물은 %s %d개 이나, 낮은 확률로 최대 %d개까지 생성될 수 있습니다. +20200108 to latest +※[%s] %d~%d개 제작 +*/ + MSG_ID_E8B = 0xe8b, +/*20191113 to latest +성공 %d%% +*/ + MSG_ID_E8C = 0xe8c, +/*20191113 to latest +아이템 태그는 소지한 아이템만 태그할 수 있습니다. +*/ + MSG_ID_E8D = 0xe8d, +#endif +#if PACKETVER >= 20191127 +/*20191127 to latest +공격력 및 특성 공격력이 증가되었습니다. +*/ + MSG_ID_E8E = 0xe8e, +/*20191127 to latest +강인한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E8F = 0xe8f, +/*20191127 to latest +HP 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_E90 = 0xe90, +/*20191127 to latest +굳건한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E91 = 0xe91, +/*20191127 to latest +공격 속도 및 유도 공격 확률이 증가되었습니다. +*/ + MSG_ID_E92 = 0xe92, +/*20191127 to latest +신실한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E93 = 0xe93, +/*20191127 to latest +POW, CRT 및 CON 이 증가되었습니다. +*/ + MSG_ID_E94 = 0xe94, +/*20191127 to latest +베네딕툼 효과가 해제되었습니다. +*/ + MSG_ID_E95 = 0xe95, +/*20191127 to latest +SPL, WIS 및 CRT 가 증가되었습니다. +*/ + MSG_ID_E96 = 0xe96, +/*20191127 to latest +렐리지오 효과가 해제되었습니다. +*/ + MSG_ID_E97 = 0xe97, +/*20191127 to latest +특성 공격력 및 특성 마법 공격력이 증가되었습니다. +*/ + MSG_ID_E98 = 0xe98, +/*20191127 to latest +콤페텐티아 효과가 해제되었습니다. +*/ + MSG_ID_E99 = 0xe99, +/*20191127 to latest +크리티컬 데미지 비율이 증가되었습니다. +*/ + MSG_ID_E9A = 0xe9a, +/*20191127 to latest +프레센스 아치에스 효과가 해제되었습니다. +*/ + MSG_ID_E9B = 0xe9b, +/*20191127 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9C = 0xe9c, +/*20191127 to latest +아르구투스 텔룸 효과가 해제되었습니다. +*/ + MSG_ID_E9D = 0xe9d, +/*20191127 to latest +마법 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9E = 0xe9e, +/*20191127 to latest +아르구투스 비타 효과가 해제되었습니다. +*/ + MSG_ID_E9F = 0xe9f, +/*20191127 to latest +Num: %d/%d Weight: %d/%d +*/ + MSG_ID_EA0 = 0xea0, +#endif +#if PACKETVER >= 20191204 +/*20191204 to latest +서번트 웨폰 효과가 해제되었습니다. +*/ + MSG_ID_EA1 = 0xea1, +/*20191204 to latest +챠징 피어스 효과가 해제되었습니다. +*/ + MSG_ID_EA2 = 0xea2, +/*20191204 to latest +비고르 효과가 해제되었습니다. +*/ + MSG_ID_EA3 = 0xea3, +/*20191204 to latest +공격력이 증가하고, 방어력이 감소되었습니다. +*/ + MSG_ID_EA4 = 0xea4, +/*20191204 to latest +어택 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA5 = 0xea5, +/*20191204 to latest +방어력이 증가하고, 공격력이 감소되었습니다. +*/ + MSG_ID_EA6 = 0xea6, +/*20191204 to latest +가드 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA7 = 0xea7, +/*20191204 to latest +배리어가 부여되었습니다. +*/ + MSG_ID_EA8 = 0xea8, +/*20191204 to latest +가디언 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EA9 = 0xea9, +/*20191204 to latest +헌신 효과로 받는 데미지가 감소되었습니다. +*/ + MSG_ID_EAA = 0xeaa, +/*20191204 to latest +리바운드 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAB = 0xeab, +/*20191204 to latest +성속성 마법 데미지와 암/불사속성 내성이 증가되었습니다. +*/ + MSG_ID_EAC = 0xeac, +/*20191204 to latest +홀리 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAD = 0xead, +/*20191204 to latest +즉시 부활 효과가 부여되었습니다. +*/ + MSG_ID_EAE = 0xeae, +/*20191204 to latest +얼티메이트 세크리파이스 효과가 해제되었습니다. +*/ + MSG_ID_EAF = 0xeaf, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB0 = 0xeb0, +/*20191204 to latest +클라이막스 효과가 해제되었습니다. +*/ + MSG_ID_EB1 = 0xeb1, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB2 = 0xeb2, +/*20191204 to latest +쉐도우 익시드 효과가 해제되었습니다. +*/ + MSG_ID_EB3 = 0xeb3, +/*20191204 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_EB4 = 0xeb4, +/*20191204 to latest +포텐트 베넘 효과가 해제되었습니다. +*/ + MSG_ID_EB5 = 0xeb5, +/*20191204 to latest +매 공격 시, 일정 확률로 대상이 받는 데미지를 증가시킵니다. +*/ + MSG_ID_EB6 = 0xeb6, +/*20191204 to latest +인챈팅 쉐도우 효과가 해제되었습니다. +*/ + MSG_ID_EB7 = 0xeb7, +#endif +#if PACKETVER >= 20191211 +/*20191211 to latest +토벌 카운트 On +*/ + MSG_ID_EB8 = 0xeb8, +/*20191211 to latest +토벌 카운트 Off +*/ + MSG_ID_EB9 = 0xeb9, +/*20191211 to latest +대상의 AP가 최대치입니다 +*/ + MSG_ID_EBA = 0xeba, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %d캐시가 차감됩니다. +*/ + MSG_ID_EBB = 0xebb, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %d캐시, 무료 %d캐시가 차감됩니다. +*/ + MSG_ID_EBC = 0xebc, +#endif +#if PACKETVER >= 20191218 +/*20191218 to latest +서번트 웨폰 %d개가 필요합니다. +*/ + MSG_ID_EBD = 0xebd, +/*20191218 to latest +https://member.gnjoy.com.tw/billing.aspx +*/ + MSG_ID_EBE = 0xebe, +#endif +#if PACKETVER >= 20200108 +/*20200108 to latest +역순 정렬 +*/ + MSG_ID_EBF = 0xebf, +/*20200108 to latest +검색 내용 입력 +*/ + MSG_ID_EC0 = 0xec0, +/*20200108 to latest +검색 +Search +*/ + MSG_ID_EC1 = 0xec1, +#endif +#if PACKETVER >= 20200122 +/*20200122 to latest +리서치 리포트 상태가 됩니다. +*/ + MSG_ID_EC2 = 0xec2, +/*20200122 to latest +리서치 리포트 상태가 해제됩니다. +*/ + MSG_ID_EC3 = 0xec3, +/*20200122 to latest +제조에 성공 했습니다. +*/ + MSG_ID_EC4 = 0xec4, +/*20200122 to latest +제조에 실패 했습니다. +*/ + MSG_ID_EC5 = 0xec5, +/*20200122 to latest +쉐도우 장비가 파괴 및 해제에서 보호됩니다. +*/ + MSG_ID_EC6 = 0xec6, +/*20200122 to latest +풀 쉐도우 프로텍션이 해제됩니다. +*/ + MSG_ID_EC7 = 0xec7, +/*20200122 to latest +식물형, 무형 몬스터에게 주는 데미지가 증가합니다. +*/ + MSG_ID_EC8 = 0xec8, +/*20200122 to latest +지옥 나무의 가루효과가 사라집니다. +*/ + MSG_ID_EC9 = 0xec9, +#endif +#if PACKETVER >= 20200129 +/*20200129 to latest +공격 장치가 활성화되었습니다. +*/ + MSG_ID_ECA = 0xeca, +/*20200129 to latest +공격 장치가 해제되었습니다. +*/ + MSG_ID_ECB = 0xecb, +/*20200129 to latest +물리 방어력 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_ECC = 0xecc, +/*20200129 to latest +방어 장치가 해제되었습니다. +*/ + MSG_ID_ECD = 0xecd, +/*20200129 to latest +검색 +Search +*/ + MSG_ID_ECE = 0xece, +#endif +#if PACKETVER >= 20200212 +/*20200212 to latest +합주를 혼자 사용할 수 있습니다. +*/ + MSG_ID_ECF = 0xecf, +/*20200212 to latest +크바시르의 지혜가 사라집니다. +*/ + MSG_ID_ED0 = 0xed0, +/*20200212 to latest +미스틱 심포니의 효과가 부여됩니다. +*/ + MSG_ID_ED1 = 0xed1, +/*20200212 to latest +미스틱 심포니의 효과가 사라집니다. +*/ + MSG_ID_ED2 = 0xed2, +/*20200212 to latest +마법 저항력이 감소했습니다. +*/ + MSG_ID_ED3 = 0xed3, +/*20200212 to latest +게페니아 녹턴의 효과가 해제 되었습니다. +*/ + MSG_ID_ED4 = 0xed4, +/*20200212 to latest +물리 저항력이 감소했습니다. +*/ + MSG_ID_ED5 = 0xed5, +/*20200212 to latest +마인워커 랩소디 상태가 해제되었습니다. +*/ + MSG_ID_ED6 = 0xed6, +/*20200212 to latest +물리 저항력이 증가했습니다. +*/ + MSG_ID_ED7 = 0xed7, +/*20200212 to latest +뮤지컬 인터루드 상태가 해제되었습니다. +*/ + MSG_ID_ED8 = 0xed8, +/*20200212 to latest +특성 마법 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_ED9 = 0xed9, +/*20200212 to latest +저녁 노을의 세레나데 효과가 해제되었습니다. +*/ + MSG_ID_EDA = 0xeda, +/*20200212 to latest +특성 물리 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_EDB = 0xedb, +/*20200212 to latest + 프론테라의 행진곡 효과가 해제되었습니다. +*/ + MSG_ID_EDC = 0xedc, +/*20200212 to latest +바람의 분노가 시전자에게 흘러 들어옵니다. +*/ + MSG_ID_EDD = 0xedd, +/*20200212 to latest +캘러미티 가일 상태가 해제되었습니다. +*/ + MSG_ID_EDE = 0xede, +/*20200212 to latest +바람에 의해 약점과 모습이 드러납니다. +*/ + MSG_ID_EDF = 0xedf, +/*20200212 to latest +윈드 사인 효과가 사라집니다. +*/ + MSG_ID_EE0 = 0xee0, +#endif +#if PACKETVER >= 20200304 +/*20200304 to latest +E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE1 = 0xee1, +/*20200304 to latest +DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE2 = 0xee2, +/*20200304 to latest +DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE3 = 0xee3, +#endif }; #endif /* MAP_MESSAGES_MAIN_H */ diff --git a/src/map/messages_re.h b/src/map/messages_re.h index ee1a05b93..e32f6b275 100644 --- a/src/map/messages_re.h +++ b/src/map/messages_re.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20181107 +Latest version: 20200304 */ enum clif_messages { @@ -625,9 +625,11 @@ Fire Wind */ MSG_WIND = 0x76, -/*20080827 to latest +/*20080827 to 20191023 같은종류의 장비아이템은 한번에 한개만 살 수 있습니다. Please avoid buying 2 of the same items at one time. +20191030 to latest +같은 종류의 장비 아이템은 한 번에 한 개만 살 수 있습니다. */ MSG_EQUIPITEM_OLNY_ONE = 0x77, /*20080827 to latest @@ -752,7 +754,7 @@ Congratulations! You are the MVP! Your reward item is MSG_YOU_RECEIVE_MVP_ITEM = 0x8f, /*20080827 to latest !! -!! +! */ MSG_YOU_RECEIVE_MVP_ITEM2 = 0x90, /*20080827 to latest @@ -1229,9 +1231,11 @@ Cart Items [Alt+W] Basic Information */ MSG_BASICINFOWND = 0xee, -/*20080827 to latest +/*20080827 to 20191204 이무기로는 이스킬을 사용할수 없습니다. The skill cannot be used with this weapon. +20191211 to latest +현재 무기로는 스킬을 사용할 수 없습니다. */ MSG_USESKILL_FAIL_NOT_SUITABLE_WEAPON = 0xef, /*20080827 to latest @@ -3914,7 +3918,6 @@ Mouse wheel skills for F7 and F8 are Disabled.[/q2 OFF] MSG_EXPLAIN_QUICKSPELL2 = 0x302, /*20080827 to latest /q3 : /quickspell (/q1) + /quickspell2 (/q2) -/q3: /quickspell (/q1) + /quickspell2 (/q2) */ MSG_EXPLAIN_QUICKSPELL3 = 0x303, /*20080827 to latest @@ -4395,9 +4398,11 @@ Thief Knight */ MSG_KNIGHT = 0x364, -/*20080827 to latest +/*20080827 to 20181128 Prieset Priest +20181205 to latest +Priest */ MSG_PRIEST = 0x365, /*20080827 to latest @@ -4965,16 +4970,18 @@ High Wizard MSG_WIZARD_H = 0x3da, /*20080827 to latest White Smith -WhiteSmith +MasterSmith */ MSG_BLACKSMITH_H = 0x3db, /*20080827 to latest Sniper */ MSG_HUNTER_H = 0x3dc, -/*20080827 to latest +/*20080827 to 20181128 Saaassin Cross Assassin Cross +20181205 to latest +Assassin Cross */ MSG_ASSASSIN_H = 0x3dd, /*20080827 to latest @@ -5002,9 +5009,11 @@ Sniper_W Sniper */ MSG_HUNTER_H_W = 0x3e2, -/*20080827 to latest +/*20080827 to 20181128 Saaassin Cross_W Assassin Cross +20181205 to latest +Assassin Cross_W */ MSG_ASSASSIN_H_W = 0x3e3, /*20080827 to latest @@ -6617,7 +6626,6 @@ Quest List MSG_QUESTWIN = 0x525, /*20080827 to latest RO SHOP -RO Shop */ MSG_RO_SHOP = 0x526, /*20080827 to latest @@ -6772,9 +6780,11 @@ The Memorial Dungeon's entry time limit expired; it has been destroyed. The Memorial Dungeon has been removed. */ MSG_MEMORIAL_DUN_DESTROY_REQUEST = 0x544, -/*20080827 to latest +/*20080827 to 20191218 메모리얼 던전에 시스템 오류가 발생하였습니다. 정상적인 게임 진행을 위해 재접속을 해주십시오. A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing. +20191224 to latest +메모리얼 던전에 통신 장애가 발생하였습니다. 정상적인 게임 진행을 위해 잠시 후, 재접속을 해주십시오. */ MSG_MEMORIAL_DUN_ERROR = 0x545, /*20080827 to latest @@ -10428,7 +10438,6 @@ ITEM MSG_MACRO_SKILL = 0x775, /*20110228 to 20110228 Next attack time : -Next attack time: 20110308 to latest TACTIC 20130807 to 20130814 @@ -10445,6 +10454,7 @@ TACTIC MSG_MACRO_ETC = 0x777, /*20110228 to 20110228 When invited to a party +When invited to the party 20110308 to latest COMBAT 20130807 to 20130814 @@ -10489,7 +10499,6 @@ ATTACK MSG_MACRO_ATTACK = 0x77d, /*20110308 to latest Next attack time : -Next attack time: 20130807 to 20130814 ATTACK */ @@ -10498,11 +10507,11 @@ ATTACK When died 20130807 to 20130814 Next attack time : -Next attack time: */ MSG_MACRO_WHEN_DIED = 0x77f, /*20110308 to latest When invited to a party +When invited to the party 20130807 to 20130814 When died */ @@ -10511,6 +10520,7 @@ When died Pickup Item 20130807 to 20130814 When invited to a party +When invited to the party */ MSG_MACRO_PICKUP_ITEM = 0x781, /*20110308 to latest @@ -10540,7 +10550,6 @@ Any work in progress (NPC dialog, manufacturing ...) quit and try again. ExMacro_SaveData%d 20110412 to latest SaveData_ExMacro%d -SaveData_ExMacro %d 20130807 to 20130814 몬스터 사냥을 통해 얻을 수 있는 Job경험치가 %d분간 %.2f배로 증가합니다. Monster Job hunting experience that you can get through the doubling of %d is %.2f minutes. @@ -10551,7 +10560,6 @@ Monster Job hunting experience that you can get through the doubling of %d is %. Settings for [%s] are stored in. 20130807 to 20130814 SaveData_ExMacro%d -SaveData_ExMacro %d */ MSG_MACRO_SAVE_DATA2 = 0x786, #endif @@ -10819,57 +10827,44 @@ Current location of the shop and chat room creation is disabled. MSG_REPLAY_ELAPSEDTIME = 0x7a3, /*20110816 to latest Speed : X 1/4 -Speed: X 1/4 20130807 to 20130814 Elapsed time: %d:%d:%d / %d:%d:%d */ MSG_REPLAY_SPEED1_4 = 0x7a4, /*20110816 to latest Speed : X 1/2 -Speed: X 1/2 20130807 to 20130814 Speed : X 1/4 -Speed: X 1/4 */ MSG_REPLAY_SPEED1_2 = 0x7a5, /*20110816 to latest Speed : X 1 -Speed: X 1 20130807 to 20130814 Speed : X 1/2 -Speed: X 1/2 */ MSG_REPLAY_SPEED1 = 0x7a6, /*20110816 to latest Speed : X 2 -Speed: X 2 20130807 to 20130814 Speed : X 1 -Speed: X 1 */ MSG_REPLAY_SPEED2 = 0x7a7, /*20110816 to latest Speed : X 4 -Speed: X 4 20130807 to 20130814 Speed : X 2 -Speed: X 2 */ MSG_REPLAY_SPEED4 = 0x7a8, /*20110816 to latest Speed : X 8 -Speed: X 8 20130807 to 20130814 Speed : X 4 -Speed: X 4 */ MSG_REPLAY_SPEED8 = 0x7a9, /*20110816 to latest Speed : X 16 -Speed: X 16 20130807 to 20130814 Speed : X 8 -Speed: X 8 */ MSG_REPLAY_SPEED16 = 0x7aa, /*20110816 to latest @@ -10877,12 +10872,10 @@ Speed : 알수없음 Speed: Unknown 20130807 to 20130814 Speed : X 16 -Speed: X 16 */ MSG_REPLAY_SPEEDUNKNOWN = 0x7ab, /*20110816 to latest Service Info : %s -Service Info: %s 20130807 to 20130814 Speed : 알수없음 Speed: Unknown @@ -10890,25 +10883,20 @@ Speed: Unknown MSG_REPLAY_CHRVICEINFO = 0x7ac, /*20110816 to latest Character Name : %s -Character Name: %s 20130807 to 20130814 Service Info : %s -Service Info: %s */ MSG_REPLAY_CHARACTERNAME = 0x7ad, /*20110816 to latest Map Name : %s -Map Name: %s 20130807 to 20130814 Character Name : %s -Character Name: %s */ MSG_REPLAY_MAPNAME = 0x7ae, /*20110816 to latest Record Time: %d-%01d-%01d %d: %02d: %02d 20130807 to 20130814 Map Name : %s -Map Name: %s */ MSG_REPLAY_RECORDTIME = 0x7af, /*20110816 to latest @@ -10991,20 +10979,24 @@ Stop MSG_REPLAY_START2 = 0x7bb, /*20110816 to latest Open Option +Open Options 20130807 to 20130814 Input FileName -> Start */ MSG_REPLAY_OPENOPTION = 0x7bc, /*20110816 to latest Close Option +Close Options 20130807 to 20130814 Open Option +Open Options */ MSG_REPLAY_CLOSEOPION = 0x7bd, /*20110816 to latest End 20130807 to 20130814 Close Option +Close Options */ MSG_REPLAY_END = 0x7be, /*20110816 to latest @@ -11069,6 +11061,7 @@ The same file exists already. MSG_REPLAY_RECORDSTART = 0x7c6, /*20110816 to latest is Saved. +Recording saved 20130807 to 20130814 Record Start */ @@ -11077,17 +11070,16 @@ Record Start #if PACKETVER >= 20110823 /*20110823 to latest Weight : %3d / %3d -Weight: %3d / %3d 20130807 to 20130814 is Saved. +Recording saved */ MSG_WEIGHT = 0x7c8, /*20110823 to latest Total : %s C -Total: %s C +Total: %s EUR 20130807 to 20130814 Weight : %3d / %3d -Weight: %3d / %3d */ MSG_TOTAL = 0x7c9, /*20110823 to latest @@ -11095,7 +11087,7 @@ Weight: %3d / %3d [Shuriken] must be equipped. 20130807 to 20130814 Total : %s C -Total: %s C +Total: %s EUR */ MSG_FAIL_NEED_EQUIPPED_SYURIKEN = 0x7ca, #endif @@ -11115,7 +11107,6 @@ Base Lv. %d MSG__BASIC_MSG_JOB = 0x7cc, /*20110831 to latest Zeny : %s -Zeny: %s 20130807 to 20130814 Job Lv. %d */ @@ -11124,7 +11115,6 @@ Job Lv. %d Trilinear 20130807 to 20130814 Zeny : %s -Zeny: %s */ MSG_GRAPHIC_MSG_TRILINEAR = 0x7ce, /*20110831 to latest @@ -11147,6 +11137,7 @@ skill MSG_GRAPHIC_MSG_ITEM = 0x7d1, /*20110831 to latest NoCtrl +Ctrl 20130807 to 20130814 item */ @@ -11156,10 +11147,12 @@ item More 20130807 to 20130814 NoCtrl +Ctrl */ MSG_GRAPHIC_MSG_BATTLE = 0x7d3, /*20110831 to latest (Character/Total Slot) +(Characters/Total slots) 20130807 to 20130814 전장 More @@ -11170,6 +11163,7 @@ Premium Service VIP Service 20130807 to 20130814 (Character/Total Slot) +(Characters/Total slots) */ MSG_CHARACTER_MSG_PREMIUMSERVICE = 0x7d5, /*20110831 to latest @@ -12508,6 +12502,7 @@ Move Combining items will be only one kind at a time. 20120320 to latest Make Character +Create Character 20130807 to 20130814 이름변경 Rename @@ -12520,6 +12515,7 @@ You cannot have more than 30,000 stacked items. http://ro.game.gnjoy.com/ 20130807 to 20130814 Make Character +Create Character */ MSG_UAE_URL = 0x877, #endif @@ -12841,7 +12837,6 @@ Change to Default UI MSG_NAVIGATION_HELP = 0x89d, /*20120417 to latest ALL -All 20130807 to 20130814 도움말 Help @@ -12851,7 +12846,6 @@ Help Map 20130807 to 20130814 ALL -All */ MSG_NAVIGATION_MAP = 0x89f, /*20120417 to latest @@ -14986,7 +14980,7 @@ You entered more than 1 Billion Zeny, the price will be set to 1 Billion Zeny. ErrorCode : %d, ErrorValue : %d 20130612 to latest ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d) -ErrorCategory: %d, ErrorCode: %d (%d,%d,%d,%d) +ErrorCode : %d, ErrorValue : %d 20130807 to 20130814 AuthTicket is Not Vaild AuthTicket is Not Valid @@ -15001,7 +14995,7 @@ AuthTicket is Not Valid %d%% ( Basic 100%% + Premium %d%% + Internet cafe %d%% + %s Server %d%% ) 20130807 to 20130814 ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d) -ErrorCategory: %d, ErrorCode: %d (%d,%d,%d,%d) +ErrorCode : %d, ErrorValue : %d */ MSG_BASIC_EXP_MSG_INDONESIA = 0x9a6, /*20130618 to 20130925 @@ -15034,7 +15028,6 @@ The price of^0000FF %s^000000 MSG_WARNING_PRICE1 = 0x9a9, /*20130626 to latest 100000000 -1000000000 20130807 to 20130814 %s 의 가격이 The price of^0000FF %s^000000 @@ -15045,7 +15038,6 @@ The price of^0000FF %s^000000 is over ^FF0000%d^0000FF Billion^000000 Zeny and 20130807 to 20130814 100000000 -1000000000 */ MSG_WARNING_PRICE3 = 0x9ab, /*20130626 to latest @@ -15822,7 +15814,7 @@ You can not open the mail. MSG_FAILED_TO_WRITE_MAIL = 0xa2c, /*20140416 to latest You are currently joined in CLan !! -You are currently joined in Clan !! +You currently belong to a clan. */ MSG_JOINED_IN_CLAN = 0xa2d, /*20140416 to latest @@ -15893,17 +15885,14 @@ The recipient's name does not exist. #if PACKETVER >= 20140430 /*20140430 to latest E X P : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -EXP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_EXPMSG = 0xa38, /*20140430 to latest DROP : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DROPMSG = 0xa39, /*20140430 to latest DEATH : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DEATHMSG = 0xa3a, #endif @@ -16154,7 +16143,6 @@ Adventure %s GD 20141001 to latest %s -%s */ MSG_CASH_GEDARE_MONEY = 0xa5f, /*20140723 to 20140723 @@ -16395,17 +16383,14 @@ Please empty at least 5 amount of possession in item window. MSG_NOT_ENOUGH_SPACE_IN_ITEM_BODY = 0xa85, /*20140917 to latest E X P : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -EXP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_EXPMSG = 0xa86, /*20140917 to latest DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DROPMSG = 0xa87, /*20140917 to latest DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DEATHMSG = 0xa88, /*20140917 to latest @@ -16851,12 +16836,10 @@ Withdraw MSG_ID_AD7 = 0xad7, /*20150304 to latest 1 z UP -1z UP */ MSG_ID_AD8 = 0xad8, /*20150304 to latest 1 z Down -1z Down */ MSG_ID_AD9 = 0xad9, /*20150304 to latest @@ -17031,17 +17014,14 @@ Send Mail #if PACKETVER >= 20150729 /*20150729 to latest E X P : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -E X P: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AF9 = 0xaf9, /*20150729 to latest DROP : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFA = 0xafa, /*20150729 to latest DEATH : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFB = 0xafb, #endif @@ -17051,6 +17031,7 @@ DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) Name with this tag cannot be used. 20181002 to latest 해당 내용은 이름으로 사용하실 수 없습니다. +You cannot use the tag as a name. */ MSG_ID_AFC = 0xafc, #endif @@ -17273,7 +17254,6 @@ SNS통신 TWITTER 20160824 to latest Twitter -TWITTER */ MSG_ID_B1D = 0xb1d, /*20160224 to latest @@ -17637,7 +17617,6 @@ The [%s] is not present, the default AI will be used instead. %.1f%% ( Premium %.1f%% + %s %.1f%%) 20160706 to latest %.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) -%.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) */ MSG_ID_B62 = 0xb62, #endif @@ -18259,10 +18238,12 @@ Weight limit has reached toover 70%. Or less then 10 invenrory space. MSG_ID_BCE = 0xbce, /*20161123 to latest C +EUR */ MSG_ID_BCF = 0xbcf, /*20161123 to latest C +EUR */ MSG_ID_BD0 = 0xbd0, /*20161123 to latest @@ -18516,10 +18497,12 @@ SP MSG_ID_BFD = 0xbfd, /*20161228 to latest Lv +Lv. */ MSG_ID_BFE = 0xbfe, /*20161228 to latest Lv +Lv. */ MSG_ID_BFF = 0xbff, /*20161228 to latest @@ -18532,6 +18515,7 @@ Exp MSG_ID_C01 = 0xc01, /*20161228 to latest Play Replay Flie +Start replay */ MSG_ID_C02 = 0xc02, /*20161228 to latest @@ -18548,14 +18532,17 @@ Basicinfo MSG_ID_C05 = 0xc05, /*20161228 to latest Equip +Equipment */ MSG_ID_C06 = 0xc06, /*20161228 to latest Item +Items */ MSG_ID_C07 = 0xc07, /*20161228 to latest Skill +Skills */ MSG_ID_C08 = 0xc08, /*20161228 to latest @@ -18568,10 +18555,12 @@ Party MSG_ID_C0A = 0xc0a, /*20161228 to latest Chatting +Chat */ MSG_ID_C0B = 0xc0b, /*20161228 to latest Shortcut +Hotkeys */ MSG_ID_C0C = 0xc0c, /*20161228 to latest @@ -18580,15 +18569,16 @@ Status MSG_ID_C0D = 0xc0d, /*20161228 to latest ALL -All */ MSG_ID_C0E = 0xc0e, /*20161228 to latest User Defined File Name +Custom file name */ MSG_ID_C0F = 0xc0f, /*20161228 to latest Repeated File Check +Check file */ MSG_ID_C10 = 0xc10, /*20161228 to latest @@ -18597,10 +18587,12 @@ on MSG_ID_C11 = 0xc11, /*20161228 to latest <Basic Skin> +<Basic> */ MSG_ID_C12 = 0xc12, /*20161228 to latest Select Skin +Choose skin */ MSG_ID_C13 = 0xc13, #endif @@ -19150,6 +19142,7 @@ Loading the mailbox. MSG_ID_C76 = 0xc76, /*20170315 to latest NOW LOADING.. +Loading... */ MSG_ID_C77 = 0xc77, /*20170315 to latest @@ -19412,6 +19405,7 @@ BOX #if PACKETVER >= 20170628 /*20170628 to latest 다시하기 +File abusing detected. Please restart the client with clean files. */ MSG_ID_CA9 = 0xca9, /*20170628 to 20170809 @@ -19570,6 +19564,7 @@ map MSG_ID_CCC = 0xccc, /*20170809 to latest 변조된 파일이 발견되었습니다. 게임을 다시 실행시켜주세요. +File abusing detected. Please restart the client with clean files. */ MSG_ID_CCD = 0xccd, #endif @@ -19733,9 +19728,11 @@ http://member.gnjoy.com/user/pay/chargelist.asp 한 번에 구입 가능한 아이템의 가짓수는 8개입니다. */ MSG_ID_CF0 = 0xcf0, -/*20170920 to latest +/*20170920 to 20190228 http://gift.zhaouc.com/#/index http://gift.zhaouc.com/ +20190306 to latest +http://gift.zhaouc.com/ */ MSG_ID_CF1 = 0xcf1, #endif @@ -19862,6 +19859,7 @@ TokenAgency 서버 연결 실패 MSG_ID_D0D = 0xd0d, /*20171025 to latest 삭제 +Delete */ MSG_ID_D0E = 0xd0e, /*20171025 to latest @@ -19925,92 +19923,143 @@ MOTP 입력 시간이 초과되었습니다. 처음부터 다시 로그인해주 모험가 중개소 등록 */ MSG_ID_D1C = 0xd1c, -/*20171108 to latest +/*20171108 to 20191120 모집 중단 +20191127 to latest +모험가 중개소 등록하기 */ MSG_ID_D1D = 0xd1d, -/*20171108 to latest +/*20171108 to 20191120 모험가 중개소 설정 +20191127 to latest +모집 중단 */ MSG_ID_D1E = 0xd1e, -/*20171108 to latest +/*20171108 to 20191120 전 지역 +20191127 to latest +메모 +Note */ MSG_ID_D1F = 0xd1f, -/*20171108 to latest +/*20171108 to 20191120 직접기재 +20191127 to latest +퀘스트 +Quest */ MSG_ID_D20 = 0xd20, -/*20171108 to latest +/*20171108 to 20191120 검사계열 +20191127 to latest +필드 */ MSG_ID_D21 = 0xd21, -/*20171108 to latest +/*20171108 to 20191120 법사계열 +20191127 to latest +던전 +Dungeon */ MSG_ID_D22 = 0xd22, -/*20171108 to latest +/*20171108 to 20191120 궁수계열 +20191127 to latest +MD */ MSG_ID_D23 = 0xd23, -/*20171108 to latest +/*20171108 to 20191120 복사계열 +20191127 to latest +낙원단 */ MSG_ID_D24 = 0xd24, -/*20171108 to latest +/*20171108 to 20191120 상인계열 +20191127 to latest +기타 +Other */ MSG_ID_D25 = 0xd25, -/*20171108 to latest +/*20171108 to 20191120 도둑계열 +20191127 to latest +검색 +Search */ MSG_ID_D26 = 0xd26, -/*20171108 to latest +/*20171108 to 20191120 태권계열 +20191127 to latest +초기화 */ MSG_ID_D27 = 0xd27, -/*20171108 to latest +/*20171108 to 20191120 닌자계열 +20191127 to latest +파티가입신청 */ MSG_ID_D28 = 0xd28, -/*20171108 to latest +/*20171108 to 20191120 건슬링거계열 +20191127 to latest +목적 */ MSG_ID_D29 = 0xd29, -/*20171108 to latest +/*20171108 to 20191120 도람족계열 +20191127 to latest +※욕설, 현거래등 운영 규칙에 위배되는 목적으로 사용 시 처벌 받을 수 있습니다. */ MSG_ID_D2A = 0xd2a, -/*20171108 to latest +/*20171108 to 20191120 지역명 +20191127 to latest +등록하기 +Register */ MSG_ID_D2B = 0xd2b, -/*20171108 to latest +/*20171108 to 20191120 지역명 검색 +20191127 to latest +사용할 수 없는 단어가 포함되어 있습니다. */ MSG_ID_D2C = 0xd2c, -/*20171108 to latest +/*20171108 to 20191120 가입 요청하기 +20191127 to latest +파티가입 */ MSG_ID_D2D = 0xd2d, -/*20171108 to latest +/*20171108 to 20191120 신고하기 +20191127 to latest +1:1대화 */ MSG_ID_D2E = 0xd2e, -/*20171108 to latest +/*20171108 to 20191120 파티 모집 중단 +20191127 to latest +모집 중지 */ MSG_ID_D2F = 0xd2f, -/*20171108 to latest +/*20171108 to 20191120 지역명을 입력해주세요. +20191127 to latest +수정하기 */ MSG_ID_D30 = 0xd30, -/*20171108 to latest +/*20171108 to 20191120 지역 +20191127 to latest +전체 +All */ MSG_ID_D31 = 0xd31, -/*20171108 to latest +/*20171108 to 20191120 전 직업 +20191127 to latest +[%s] 님이 파티가입을 신청했습니다. */ MSG_ID_D32 = 0xd32, /*20171108 to latest @@ -20052,6 +20101,7 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D3B = 0xd3b, /*20171108 to latest 태권 +Taekwon */ MSG_ID_D3C = 0xd3c, /*20171108 to 20171115 @@ -20094,10 +20144,12 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D42 = 0xd42, /*20171115 to latest 차단 리스트가 없습니다 +Ignore-list is empty */ MSG_ID_D43 = 0xd43, /*20171115 to latest -차단 리스트- +Characters in ignore-list: */ MSG_ID_D44 = 0xd44, /*20171115 to latest @@ -20180,12 +20232,17 @@ Password 회원가입 */ MSG_ID_D56 = 0xd56, -/*20171122 to latest +/*20171122 to 20191120 노비스계열 +20191127 to latest +파티장 +Leader */ MSG_ID_D57 = 0xd57, -/*20171122 to latest +/*20171122 to 20191120 슈퍼노비스계열 +20191127 to latest +파티에 가입할 수 있는 레벨이 아닙니다. */ MSG_ID_D58 = 0xd58, /*20171122 to latest @@ -20198,12 +20255,16 @@ Password MSG_ID_D5A = 0xd5a, /*20171122 to 20180328 직업을 한 개 이상 선택해주세요. -20180404 to latest +20180404 to 20191120 모집을 희망하는 파티원의 직업을 한 개 이상 선택해 주세요. +20191127 to latest +검색내용입력 */ MSG_ID_D5B = 0xd5b, -/*20171122 to latest +/*20171122 to 20191127 지역명을 두 글자 이상 입력해주세요. +20191204 to latest +탱커 */ MSG_ID_D5C = 0xd5c, /*20171122 to latest @@ -20294,8 +20355,10 @@ Password 불량 단어가 포함된 이름은 검색할 수 없습니다. */ MSG_ID_D71 = 0xd71, -/*20171213 to latest +/*20171213 to 20191127 파티 마스터가 요청을 받을 수 없는 맵에 있습니다. +20191204 to latest +파티 마스터가 요청을 받을 수 없는 상태입니다. */ MSG_ID_D72 = 0xd72, /*20171213 to latest @@ -20304,8 +20367,10 @@ Password MSG_ID_D73 = 0xd73, /*20171213 to 20180328 님의 파티요청입니다. -20180404 to latest +20180404 to 20191127 님의 파티 가입 요청입니다. +20191204 to latest +딜러 */ MSG_ID_D74 = 0xd74, #endif @@ -20464,6 +20529,7 @@ This is not the current attendance check event MSG_ID_D93 = 0xd93, /*20180207 to latest 개인 상납 경험치가 max에 도달하여, 더 이상 길드 경험치를 누적할 수 없습니다. + */ MSG_ID_D94 = 0xd94, #endif @@ -20516,10 +20582,13 @@ Enter 4 english words and 2 chinese words MSG_ID_D9D = 0xd9d, /*20180404 to latest 50% 이상의 값을 입력할 수 없습니다. +The guild tax rate can't be set to more than 50%. */ MSG_ID_D9E = 0xd9e, -/*20180404 to latest +/*20180404 to 20191204 파티 가입 요청을 보냈습니다. +20191211 to latest +파티가 모험가 중개소에 등록되어, 파티장을 변경 할 수 없습니다. */ MSG_ID_D9F = 0xd9f, /*20180404 to latest @@ -20534,8 +20603,10 @@ Enter 4 english words and 2 chinese words 거절 */ MSG_ID_DA2 = 0xda2, -/*20180404 to latest +/*20180404 to 20191127 설정 권한은 파티장에게 있습니다. +20191204 to latest +힐러 */ MSG_ID_DA3 = 0xda3, /*20180404 to latest @@ -20594,8 +20665,10 @@ Enter 4 english words and 2 chinese words 파티 가입 요청 */ MSG_ID_DB1 = 0xdb1, -/*20180404 to latest +/*20180404 to 20191120 파티장이 아닌 경우, 모험가 중개소에 파티를 등록할 수 없습니다. +20191127 to latest +파티장이 아닌 경우 모험가 중개소에 파티를 등록할 수 없습니다. */ MSG_ID_DB2 = 0xdb2, /*20180404 to 20180404 @@ -20606,8 +20679,11 @@ Enter 4 english words and 2 chinese words MSG_ID_DB3 = 0xdb3, #endif #if PACKETVER >= 20180418 -/*20180418 to latest +/*20180418 to 20191127 존재하지 않는 파티 글입니다. +20191204 to latest +보조 +Support */ MSG_ID_DB4 = 0xdb4, /*20180418 to 20180418 @@ -20648,16 +20724,20 @@ Zoom Out 기능을 해제합니다 (Off) 파티장인 경우, 가입 요청을 할 수 없습니다. */ MSG_ID_DBB = 0xdbb, -/*20180418 to latest +/*20180418 to 20191204 모험가 중개소에 등록 중입니다. 잠시만 기다려 주세요. +20191211 to latest +파티 가입 최대 레벨은 최소 레벨 설정보다 높아야 합니다. */ MSG_ID_DBC = 0xdbc, /*20180418 to latest 더 이상 직업을 선택할 수 없습니다. */ MSG_ID_DBD = 0xdbd, -/*20180418 to latest +/*20180418 to 20191120 게임 중 해당 유저의 가입 요청을 받지 않습니다. +20191127 to latest +게임 중 해당 유저의 가입 요청을 받지 않습니다.(클라이언트 종료 전까지 유효) */ MSG_ID_DBE = 0xdbe, #endif @@ -20738,20 +20818,24 @@ Emblem 테두리를 그려주지 않습니다 #if PACKETVER >= 20180718 /*20180718 to latest E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DCF = 0xdcf, /*20180718 to latest DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD0 = 0xdd0, /*20180718 to latest DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD1 = 0xdd1, #endif #if PACKETVER >= 20180829 /*20180829 to latest 영문이나 러시아어 단독으로만 사용이 가능합니다. +Forbidden symbols in character name. */ MSG_ID_DD2 = 0xdd2, /*20180829 to latest @@ -20760,28 +20844,34 @@ you must have an AccessTicket to login MSG_ID_DD3 = 0xdd3, /*20180829 to latest 창고를 불러오는 중입니다. +Loading.... */ MSG_ID_DD4 = 0xdd4, /*20180829 to latest NOW LOADING.. +Loading... */ MSG_ID_DD5 = 0xdd5, #endif #if PACKETVER >= 20180919 /*20180919 to latest 삭제 +Delete */ MSG_ID_DD6 = 0xdd6, /*20180919 to latest 답장 +Reply */ MSG_ID_DD7 = 0xdd7, /*20180919 to latest 전송 +Send */ MSG_ID_DD8 = 0xdd8, /*20180919 to latest 이름확인 +Name Check */ MSG_ID_DD9 = 0xdd9, /*20180919 to latest @@ -20891,6 +20981,1114 @@ PvP */ MSG_ID_DEE = 0xdee, #endif +#if PACKETVER >= 20181121 +/*20181121 to latest +같은 아이템을 %d개 이상 가질 수 없습니다. +*/ + MSG_ID_DEF = 0xdef, +/*20181121 to latest +소지 한계량 초과로 구입이 불가능합니다. +*/ + MSG_ID_DF0 = 0xdf0, +/*20181121 to latest +해당 아이템은 한 번에 한 개만 살 수 있습니다. +*/ + MSG_ID_DF1 = 0xdf1, +#endif +#if PACKETVER >= 20181205 +/*20181205 to latest +성제 +*/ + MSG_ID_DF2 = 0xdf2, +/*20181205 to latest +소울리퍼 +*/ + MSG_ID_DF3 = 0xdf3, +#endif +#if PACKETVER >= 20181212 +/*20181212 to latest +설정한 내용은 정상 종료 시 서버에 저장됩니다. +*/ + MSG_ID_DF4 = 0xdf4, +#endif +#if PACKETVER >= 20190109 +/*20190109 to latest +Capture Monster +Taming monster +*/ + MSG_ID_DF5 = 0xdf5, +/*20190109 to latest +message +*/ + MSG_ID_DF6 = 0xdf6, +/*20190109 to latest +TITLE +Header +*/ + MSG_ID_DF7 = 0xdf7, +/*20190109 to latest +데미지 표기 확장 +*/ + MSG_ID_DF8 = 0xdf8, +#endif +#if PACKETVER >= 20190213 +/*20190213 to latest +맵 이름 표기 +*/ + MSG_ID_DF9 = 0xdf9, +/*20190213 to latest +시스템 메세지 : 현재 노점보기 기능이 %s 상태입니다. (/노점보기) +*/ + MSG_ID_DFA = 0xdfa, +/*20190213 to latest +수직 동기 +*/ + MSG_ID_DFB = 0xdfb, +/*20190213 to latest +자료 +*/ + MSG_ID_DFC = 0xdfc, +/*20190213 to 20190228 +http://rodata.zhaouc.com/renwu.html#container +20190306 to latest +http://rodata.zhaouc.com/renwu.html +*/ + MSG_ID_DFD = 0xdfd, +/*20190213 to latest +작위 +*/ + MSG_ID_DFE = 0xdfe, +/*20190213 to latest +프레임 제한 +*/ + MSG_ID_DFF = 0xdff, +#endif +#if PACKETVER >= 20190220 +/*20190220 to latest +%d개를 초과할 경우 더 이상 설치 할 수 없습니다. +*/ + MSG_ID_E00 = 0xe00, +/*20190220 to latest +목적지 +*/ + MSG_ID_E01 = 0xe01, +#endif +#if PACKETVER >= 20190306 +/*20190306 to latest +잠시 후 다시 시도해주세요. +Please try again in a moment. +*/ + MSG_ID_E02 = 0xe02, +/*20190306 to latest +등록할 수 없는 파일입니다. +*/ + MSG_ID_E03 = 0xe03, +#endif +#if PACKETVER >= 20190320 +/*20190320 to latest +선택 삭제 +*/ + MSG_ID_E04 = 0xe04, +/*20190320 to latest +모두 삭제 +*/ + MSG_ID_E05 = 0xe05, +/*20190320 to latest +[%s]편지함의 모든 메일을 삭제하시겠습니까? +*/ + MSG_ID_E06 = 0xe06, +/*20190320 to latest +선택 받기 +*/ + MSG_ID_E07 = 0xe07, +/*20190320 to latest +모두 받기 +*/ + MSG_ID_E08 = 0xe08, +/*20190320 to latest +선택한 메일의 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E09 = 0xe09, +/*20190320 to latest +[%s]편지함의 모든 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E0A = 0xe0a, +#endif +#if PACKETVER >= 20190403 +/*20190403 to latest +스킬바2 +*/ + MSG_ID_E0B = 0xe0b, +/*20190403 to latest +스킬바 교체 +*/ + MSG_ID_E0C = 0xe0c, +/*20190403 to latest +← +*/ + MSG_ID_E0D = 0xe0d, +/*20190403 to latest +↑ +*/ + MSG_ID_E0E = 0xe0e, +/*20190403 to latest +→ +*/ + MSG_ID_E0F = 0xe0f, +/*20190403 to latest +↓ +*/ + MSG_ID_E10 = 0xe10, +/*20190403 to latest +▤ +*/ + MSG_ID_E11 = 0xe11, +/*20190403 to latest +← +*/ + MSG_ID_E12 = 0xe12, +/*20190403 to latest +로딩중에는 창을 닫을 수 없습니다. +*/ + 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 +#if PACKETVER >= 20190508 +/*20190508 to latest +자유이동권 +*/ + MSG_ID_E1A = 0xe1a, +/*20190508 to latest +%d시간 이용, %s %d개 +*/ + MSG_ID_E1B = 0xe1b, +/*20190508 to latest +자유이용권 사용중 +*/ + MSG_ID_E1C = 0xe1c, +/*20190508 to latest +특성 스테이터스 +*/ + MSG_ID_E1D = 0xe1d, +/*20190508 to latest +파워 파라메터 +^cc0000물리 공격력, 특성 공격력^ffffff 증가 +*/ + MSG_ID_E1E = 0xe1e, +/*20190508 to latest +스테미나 파라메터 +^cc0000물리 저항력^ffffff 증가 +*/ + MSG_ID_E1F = 0xe1f, +/*20190508 to latest +위즈덤 파라메터 +^cc0000마법 저항력^ffffff 증가 +*/ + MSG_ID_E20 = 0xe20, +/*20190508 to latest +스펠 파라메터 +^cc0000마법 공격력, 마법 공격력^ffffff 증가 +*/ + MSG_ID_E21 = 0xe21, +/*20190508 to latest +컨센트레이션 파라메터 +^cc0000명중률, 회피율, 특성 물리/마법 공격력^ffffff 증가 +*/ + MSG_ID_E22 = 0xe22, +/*20190508 to latest +크리에이티브 파라메터 +^cc0000특성 힐 회복량, 크리티컬 데미지 비율^ffffff 증가 +*/ + MSG_ID_E23 = 0xe23, +/*20190508 to latest +특성 물리 공격력 +*/ + MSG_ID_E24 = 0xe24, +/*20190508 to latest +특성 마법 공격력 +*/ + MSG_ID_E25 = 0xe25, +/*20190508 to latest +물리 저항력 +*/ + MSG_ID_E26 = 0xe26, +/*20190508 to latest +마법 저항력 +*/ + MSG_ID_E27 = 0xe27, +/*20190508 to latest +특성 힐 회복량 +*/ + MSG_ID_E28 = 0xe28, +/*20190508 to latest +크리티컬 데미지 비율 +*/ + MSG_ID_E29 = 0xe29, +/*20190508 to latest +특성 파라메터 레벨업에 사용되는 포인트 +*/ + MSG_ID_E2A = 0xe2a, +/*20190508 to latest +J.Lv +*/ + MSG_ID_E2B = 0xe2b, +/*20190508 to latest +AP +*/ + MSG_ID_E2C = 0xe2c, +#endif +#if PACKETVER >= 20190522 +/*20190522 to latest +영지로 이동 +*/ + MSG_ID_E2D = 0xe2d, +/*20190522 to latest +상업도/방어도 확인 +*/ + MSG_ID_E2E = 0xe2e, +/*20190522 to 20190605 +관리영지 "%s"(으)로 이동하시겠습니까? +(1회 이동 시 마다 1,000제니가 소모됩니다. +공성전 시간에는 제니의 소모가 100배로 증가합니다.) +20190619 to 20190626 +관리영지 "%s"(으)로 이동하시겠습니까? +이동 시 마다 %d제니가 소모됩니다. +20190703 to latest +관리영지 "%s"(으)로 이동하시겠습니까? +이동 시 마다 %d제니가 소모됩니다. +(공성전 시간에는 %d제니가 소모됩니다.) +*/ + MSG_ID_E2F = 0xe2f, +/*20190522 to latest + +관리영지 "%s" + +방어도: %d / %d +상업도: %d / %d +*/ + MSG_ID_E30 = 0xe30, +/*20190522 to 20190703 +공성 영지 내에서는 다른 공성 영지로 이동 할 수 없습니다. +20190717 to latest +공성 영지에서는 사용할 수 없는 기능입니다. +*/ + MSG_ID_E31 = 0xe31, +#endif +#if PACKETVER >= 20190619 +/*20190619 to latest +기본 기능 스킬을 습득하지 않은 캐릭터입니다. +*/ + MSG_ID_E32 = 0xe32, +#endif +#if PACKETVER >= 20190703 +/*20190703 to latest +접속하신 IP는 라그나로크 제로 이용이 불가능합니다. +고객센터 또는 홈페이지로 문의해 주십시오. +*/ + MSG_ID_E33 = 0xe33, +#endif +#if PACKETVER >= 20190717 +/*20190717 to latest +탑승/장착 해제 +*/ + MSG_ID_E34 = 0xe34, +/*20190717 to latest +가져오기 +*/ + MSG_ID_E35 = 0xe35, +/*20190717 to latest +직전 노점에 등록된 아이템 정보를 가져옵니다. +*/ + MSG_ID_E36 = 0xe36, +/*20190717 to latest +서버 이슈로 인해 로그인이 제한되고 있습니다. +*/ + MSG_ID_E37 = 0xe37, +/*20190717 to latest +메시지 +*/ + MSG_ID_E38 = 0xe38, +/*20190717 to latest ++18 이상만 플레이가 가능합니다. +*/ + MSG_ID_E39 = 0xe39, +/*20190717 to latest +3시간 이상의 게임 플레이를 권장하지 않습니다. +*/ + MSG_ID_E3A = 0xe3a, +/*20190717 to latest +AP가 부족합니다. +*/ + MSG_ID_E3B = 0xe3b, +#endif +#if PACKETVER >= 20190731 +/*20190731 to latest +제련도가 높아서 조합할 수 없습니다. +*/ + MSG_ID_E3C = 0xe3c, +/*20190731 to latest +제련도가 조합에 필요한 요구치보다 높습니다. +*/ + MSG_ID_E3D = 0xe3d, +/*20190731 to 20191107 +'카드가 끼워져있거나 인챈트 되어 있습니다. +20191113 to latest +카드가 끼워져있거나 인챈트 되어 있습니다. +*/ + MSG_ID_E3E = 0xe3e, +/*20190731 to latest +∞ +? +*/ + MSG_ID_E3F = 0xe3f, +/*20190731 to latest +Z +*/ + MSG_ID_E40 = 0xe40, +/*20190731 to latest +Total : %s Zeny +*/ + MSG_ID_E41 = 0xe41, +#endif +#if PACKETVER >= 20190821 +/*20190821 to latest +계정한정판매 등록창 +Limited Account Registration Window +*/ + MSG_ID_E42 = 0xe42, +/*20190821 to latest +아이템 DB명 +Item DB Name +*/ + MSG_ID_E43 = 0xe43, +/*20190821 to latest +아이템 DB번호 +Item DB Number +*/ + MSG_ID_E44 = 0xe44, +/*20190821 to latest +판매 갯수 +Number of Sale +*/ + MSG_ID_E45 = 0xe45, +/*20190821 to latest +판매 시작시간 +Sale Start Time +*/ + MSG_ID_E46 = 0xe46, +/*20190821 to latest +판매 종료시간 +Sale end time +*/ + MSG_ID_E47 = 0xe47, +/*20190821 to latest +계정 한정 +Account only +*/ + MSG_ID_E48 = 0xe48, +/*20190821 to latest +판매기간 : %d월 %d일 %d시 %d분 +Sale period:% d month% d day% d hours% d +*/ + MSG_ID_E49 = 0xe49, +/*20190821 to 20191002 +구입가능 %d개 +% D available +20191016 to latest +계정당 구매가능 +*/ + MSG_ID_E4A = 0xe4a, +/*20190821 to latest +%d개 한정 +limited to% d +*/ + MSG_ID_E4B = 0xe4b, +/*20190821 to latest +>> ItemName : %s / 수량 : %d / 판매기간 : %d월:%d일:%d시:%d분 ~ %d월:%d일:%d시:%d분 +>> ItemName:% s / Quantity:% d / Sales Period:% d Month:% d Day:% d Hour:% d Minute ~% d Month:% d Day:% d Hour:% d Minute +*/ + MSG_ID_E4C = 0xe4c, +/*20190821 to latest +Sold Out +*/ + MSG_ID_E4D = 0xe4d, +/*20190821 to latest +[%s]은(는) 현재 소환할 수 없는 지역에 있습니다. +% s] is currently in a region that cannot be summoned. +*/ + MSG_ID_E4E = 0xe4e, +/*20190821 to latest +~ %d월 %d일 %d시 %d분 +% d min% d days% d days +*/ + MSG_ID_E4F = 0xe4f, +/*20190821 to latest +상품을 더이상 추가할 수 없습니다 +Can't add any more items +*/ + MSG_ID_E50 = 0xe50, +#endif +#if PACKETVER >= 20190828 +/*20190828 to latest +장착 중인 아이템은 교환할 수 없습니다. 장착을 해제한 뒤 시도해 주시길 바랍니다. +The item being mounted cannot be exchanged. Please unmount it and try again. +*/ + MSG_ID_E51 = 0xe51, +#endif +#if PACKETVER >= 20190904 +/*20190904 to latest +길드 창고 이용 중엔 캐릭터 선택창으로 이동 할 수 없습니다. +You can not move to the character selection window while using the Guild Warehouse. +*/ + MSG_ID_E52 = 0xe52, +/*20190904 to latest +아이템 태그가 포함되어 있어 사용할 수 없습니다. +Item tag is included and cannot be used. +*/ + MSG_ID_E53 = 0xe53, +/*20190904 to latest +Monster +*/ + MSG_ID_E54 = 0xe54, +/*20190904 to latest +Unknown +*/ + MSG_ID_E55 = 0xe55, +/*20190904 to latest +Undead +*/ + MSG_ID_E56 = 0xe56, +/*20190904 to latest +Animal +*/ + MSG_ID_E57 = 0xe57, +/*20190904 to latest +Plant +*/ + MSG_ID_E58 = 0xe58, +/*20190904 to latest +Insect +*/ + MSG_ID_E59 = 0xe59, +/*20190904 to latest +Marine +*/ + MSG_ID_E5A = 0xe5a, +/*20190904 to latest +Devil +*/ + MSG_ID_E5B = 0xe5b, +/*20190904 to latest +Human +*/ + MSG_ID_E5C = 0xe5c, +/*20190904 to latest +Angel +*/ + MSG_ID_E5D = 0xe5d, +/*20190904 to latest +Dragon +*/ + MSG_ID_E5E = 0xe5e, +#endif +#if PACKETVER >= 20190918 +/*20190918 to latest +Balance: %s %c +*/ + MSG_ID_E5F = 0xe5f, +/*20190918 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money will be deducted from your total balance. +*/ + MSG_ID_E60 = 0xe60, +/*20190918 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %s캐시, 무료 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money and %s Free Points will be deducted from your total balance. +*/ + MSG_ID_E61 = 0xe61, +/*20190918 to latest +호출이 거부되었습니다. +*/ + MSG_ID_E62 = 0xe62, +/*20190918 to latest +구매 한도액은 소지액을 초과할 수 없습니다. +*/ + MSG_ID_E63 = 0xe63, +/*20190918 to latest +노점을 여는데 실패하였습니다. 구매노점 개설을 닫아주시기 바랍니다. +*/ + MSG_ID_E64 = 0xe64, +#endif +#if PACKETVER >= 20191002 +/*20191002 to latest +판매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E65 = 0xe65, +/*20191002 to latest +구매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E66 = 0xe66, +/*20191002 to latest +VTC 인증에 실패하였습니다. +*/ + MSG_ID_E67 = 0xe67, +#endif +#if PACKETVER >= 20191016 +/*20191016 to latest +물물교환 중에는 장비를 착용할 수 없습니다. +*/ + MSG_ID_E68 = 0xe68, +/*20191016 to latest +교환하려는 품목 +*/ + MSG_ID_E69 = 0xe69, +/*20191016 to latest + 1차, 2차, 3차 직업 스킬 %d개를 더 올려 주십시오. +*/ + MSG_ID_E6A = 0xe6a, +#endif +#if PACKETVER >= 20191030 +/*20191030 to latest +같은 아이템은 %d개까지 소유할 수 있습니다. +*/ + MSG_ID_E6B = 0xe6b, +/*20191030 to latest +같은 아이템은 한 번에 %d개까지 교환할 수 있습니다. +*/ + MSG_ID_E6C = 0xe6c, +/*20191030 to latest +해당 로덱스는 "%s"서버에서 열 수 있습니다. +*/ + MSG_ID_E6D = 0xe6d, +/*20191030 to latest +[공지]편지함의 모든 메일을 삭제하시겠습니까 ? +(해당 "%s"서버의 메일만 삭제됩니다.) +*/ + MSG_ID_E6E = 0xe6e, +/*20191030 to latest +[공지]편지함의 모든 첨부 물품을 받으시겠습니까? +(해당 "%s"서버의 메일의 물품만 받으실 수 있습니다.) +*/ + MSG_ID_E6F = 0xe6f, +/*20191030 to latest +해당 메일은 "%s"서버에서만 삭제 가능합니다. +*/ + MSG_ID_E70 = 0xe70, +/*20191030 to latest +해당 메일은 "%s"서버에서만 물품 받기가 가능합니다. +*/ + MSG_ID_E71 = 0xe71, +/*20191030 to latest +해당 메일은 "%s"서버에서만 내용읽기가 가능합니다. +*/ + MSG_ID_E72 = 0xe72, +/*20191030 to latest +합계 : %d z +*/ + MSG_ID_E73 = 0xe73, +/*20191030 to latest +메세지 +*/ + MSG_ID_E74 = 0xe74, +#endif +#if PACKETVER >= 20191106 +/*20191106 to latest +구매상점 개설 중에는 개인상점의 물건을 구입할 수 없습니다. +*/ + MSG_ID_E75 = 0xe75, +#endif +#if PACKETVER >= 20191113 +/*20191113 to latest +등급강화가 성공적으로 되었습니다. +*/ + MSG_ID_E76 = 0xe76, +/*20191113 to latest +등급강화가 실패하였습니다. +*/ + MSG_ID_E77 = 0xe77, +/*20191113 to latest +등급 수치가 하향 조정 되었습니다. +*/ + MSG_ID_E78 = 0xe78, +/*20191113 to latest +장비가 파괴되었습니다. +*/ + MSG_ID_E79 = 0xe79, +/*20191113 to latest +장비가 보호되었습니다. +*/ + MSG_ID_E7A = 0xe7a, +/*20191113 to latest +재료가 선택되지 않았습니다. +*/ + MSG_ID_E7B = 0xe7b, +/*20191113 to latest +재료가 부족합니다. +*/ + MSG_ID_E7C = 0xe7c, +/*20191113 to latest +소지 금액이 부족합니다. +*/ + MSG_ID_E7D = 0xe7d, +/*20191113 to latest +아이템 공간이 부족합니다. +*/ + MSG_ID_E7E = 0xe7e, +/*20191113 to latest +장비가 보호되고 있습니다. +*/ + MSG_ID_E7F = 0xe7f, +/*20191113 to latest +장비가 파괴될 수 있습니다. +*/ + MSG_ID_E80 = 0xe80, +/*20191113 to latest +등급강화 실패 시 등급이 내려갑니다. +*/ + MSG_ID_E81 = 0xe81, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. 로덱스를 강제 종료하였습니다. +*/ + MSG_ID_E82 = 0xe82, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. +*/ + MSG_ID_E83 = 0xe83, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. 은행을 강제 종료하였습니다. +*/ + MSG_ID_E84 = 0xe84, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. +*/ + MSG_ID_E85 = 0xe85, +/*20191113 to latest +[%s] 님이 등급강화를 성공하여, [%s등급 %s] 아이템을 획득하였습니다. +*/ + MSG_ID_E86 = 0xe86, +/*20191113 to latest +[%s] 님이 [%s등급 %s] 아이템의 등급강화에 실패하였습니다. +*/ + MSG_ID_E87 = 0xe87, +/*20191113 to latest +등급 강화가 불가능한 장비입니다. +*/ + MSG_ID_E88 = 0xe88, +/*20191113 to latest +체인지 메테리얼 +*/ + MSG_ID_E89 = 0xe89, +/*20191113 to latest +가나다 정렬 +*/ + MSG_ID_E8A = 0xe8a, +/*20191113 to 20191224 +기본 결과물은 %s %d개 이나, 낮은 확률로 최대 %d개까지 생성될 수 있습니다. +20200108 to latest +※[%s] %d~%d개 제작 +*/ + MSG_ID_E8B = 0xe8b, +/*20191113 to latest +성공 %d%% +*/ + MSG_ID_E8C = 0xe8c, +/*20191113 to latest +아이템 태그는 소지한 아이템만 태그할 수 있습니다. +*/ + MSG_ID_E8D = 0xe8d, +#endif +#if PACKETVER >= 20191127 +/*20191127 to latest +공격력 및 특성 공격력이 증가되었습니다. +*/ + MSG_ID_E8E = 0xe8e, +/*20191127 to latest +강인한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E8F = 0xe8f, +/*20191127 to latest +HP 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_E90 = 0xe90, +/*20191127 to latest +굳건한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E91 = 0xe91, +/*20191127 to latest +공격 속도 및 유도 공격 확률이 증가되었습니다. +*/ + MSG_ID_E92 = 0xe92, +/*20191127 to latest +신실한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E93 = 0xe93, +/*20191127 to latest +POW, CRT 및 CON 이 증가되었습니다. +*/ + MSG_ID_E94 = 0xe94, +/*20191127 to latest +베네딕툼 효과가 해제되었습니다. +*/ + MSG_ID_E95 = 0xe95, +/*20191127 to latest +SPL, WIS 및 CRT 가 증가되었습니다. +*/ + MSG_ID_E96 = 0xe96, +/*20191127 to latest +렐리지오 효과가 해제되었습니다. +*/ + MSG_ID_E97 = 0xe97, +/*20191127 to latest +특성 공격력 및 특성 마법 공격력이 증가되었습니다. +*/ + MSG_ID_E98 = 0xe98, +/*20191127 to latest +콤페텐티아 효과가 해제되었습니다. +*/ + MSG_ID_E99 = 0xe99, +/*20191127 to latest +크리티컬 데미지 비율이 증가되었습니다. +*/ + MSG_ID_E9A = 0xe9a, +/*20191127 to latest +프레센스 아치에스 효과가 해제되었습니다. +*/ + MSG_ID_E9B = 0xe9b, +/*20191127 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9C = 0xe9c, +/*20191127 to latest +아르구투스 텔룸 효과가 해제되었습니다. +*/ + MSG_ID_E9D = 0xe9d, +/*20191127 to latest +마법 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9E = 0xe9e, +/*20191127 to latest +아르구투스 비타 효과가 해제되었습니다. +*/ + MSG_ID_E9F = 0xe9f, +/*20191127 to latest +Num: %d/%d Weight: %d/%d +*/ + MSG_ID_EA0 = 0xea0, +#endif +#if PACKETVER >= 20191204 +/*20191204 to latest +서번트 웨폰 효과가 해제되었습니다. +*/ + MSG_ID_EA1 = 0xea1, +/*20191204 to latest +챠징 피어스 효과가 해제되었습니다. +*/ + MSG_ID_EA2 = 0xea2, +/*20191204 to latest +비고르 효과가 해제되었습니다. +*/ + MSG_ID_EA3 = 0xea3, +/*20191204 to latest +공격력이 증가하고, 방어력이 감소되었습니다. +*/ + MSG_ID_EA4 = 0xea4, +/*20191204 to latest +어택 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA5 = 0xea5, +/*20191204 to latest +방어력이 증가하고, 공격력이 감소되었습니다. +*/ + MSG_ID_EA6 = 0xea6, +/*20191204 to latest +가드 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA7 = 0xea7, +/*20191204 to latest +배리어가 부여되었습니다. +*/ + MSG_ID_EA8 = 0xea8, +/*20191204 to latest +가디언 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EA9 = 0xea9, +/*20191204 to latest +헌신 효과로 받는 데미지가 감소되었습니다. +*/ + MSG_ID_EAA = 0xeaa, +/*20191204 to latest +리바운드 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAB = 0xeab, +/*20191204 to latest +성속성 마법 데미지와 암/불사속성 내성이 증가되었습니다. +*/ + MSG_ID_EAC = 0xeac, +/*20191204 to latest +홀리 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAD = 0xead, +/*20191204 to latest +즉시 부활 효과가 부여되었습니다. +*/ + MSG_ID_EAE = 0xeae, +/*20191204 to latest +얼티메이트 세크리파이스 효과가 해제되었습니다. +*/ + MSG_ID_EAF = 0xeaf, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB0 = 0xeb0, +/*20191204 to latest +클라이막스 효과가 해제되었습니다. +*/ + MSG_ID_EB1 = 0xeb1, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB2 = 0xeb2, +/*20191204 to latest +쉐도우 익시드 효과가 해제되었습니다. +*/ + MSG_ID_EB3 = 0xeb3, +/*20191204 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_EB4 = 0xeb4, +/*20191204 to latest +포텐트 베넘 효과가 해제되었습니다. +*/ + MSG_ID_EB5 = 0xeb5, +/*20191204 to latest +매 공격 시, 일정 확률로 대상이 받는 데미지를 증가시킵니다. +*/ + MSG_ID_EB6 = 0xeb6, +/*20191204 to latest +인챈팅 쉐도우 효과가 해제되었습니다. +*/ + MSG_ID_EB7 = 0xeb7, +#endif +#if PACKETVER >= 20191211 +/*20191211 to latest +토벌 카운트 On +*/ + MSG_ID_EB8 = 0xeb8, +/*20191211 to latest +토벌 카운트 Off +*/ + MSG_ID_EB9 = 0xeb9, +/*20191211 to latest +대상의 AP가 최대치입니다 +*/ + MSG_ID_EBA = 0xeba, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %d캐시가 차감됩니다. +*/ + MSG_ID_EBB = 0xebb, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %d캐시, 무료 %d캐시가 차감됩니다. +*/ + MSG_ID_EBC = 0xebc, +#endif +#if PACKETVER >= 20191218 +/*20191218 to latest +서번트 웨폰 %d개가 필요합니다. +*/ + MSG_ID_EBD = 0xebd, +/*20191218 to latest +https://member.gnjoy.com.tw/billing.aspx +*/ + MSG_ID_EBE = 0xebe, +#endif +#if PACKETVER >= 20200108 +/*20200108 to latest +역순 정렬 +*/ + MSG_ID_EBF = 0xebf, +/*20200108 to latest +검색 내용 입력 +*/ + MSG_ID_EC0 = 0xec0, +/*20200108 to latest +검색 +Search +*/ + MSG_ID_EC1 = 0xec1, +#endif +#if PACKETVER >= 20200122 +/*20200122 to latest +리서치 리포트 상태가 됩니다. +*/ + MSG_ID_EC2 = 0xec2, +/*20200122 to latest +리서치 리포트 상태가 해제됩니다. +*/ + MSG_ID_EC3 = 0xec3, +/*20200122 to latest +제조에 성공 했습니다. +*/ + MSG_ID_EC4 = 0xec4, +/*20200122 to latest +제조에 실패 했습니다. +*/ + MSG_ID_EC5 = 0xec5, +/*20200122 to latest +쉐도우 장비가 파괴 및 해제에서 보호됩니다. +*/ + MSG_ID_EC6 = 0xec6, +/*20200122 to latest +풀 쉐도우 프로텍션이 해제됩니다. +*/ + MSG_ID_EC7 = 0xec7, +/*20200122 to latest +식물형, 무형 몬스터에게 주는 데미지가 증가합니다. +*/ + MSG_ID_EC8 = 0xec8, +/*20200122 to latest +지옥 나무의 가루효과가 사라집니다. +*/ + MSG_ID_EC9 = 0xec9, +#endif +#if PACKETVER >= 20200205 +/*20200205 to latest +공격 장치가 활성화되었습니다. +*/ + MSG_ID_ECA = 0xeca, +/*20200205 to latest +공격 장치가 해제되었습니다. +*/ + MSG_ID_ECB = 0xecb, +/*20200205 to latest +물리 방어력 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_ECC = 0xecc, +/*20200205 to latest +방어 장치가 해제되었습니다. +*/ + MSG_ID_ECD = 0xecd, +/*20200205 to latest +검색 +Search +*/ + MSG_ID_ECE = 0xece, +#endif +#if PACKETVER >= 20200212 +/*20200212 to latest +합주를 혼자 사용할 수 있습니다. +*/ + MSG_ID_ECF = 0xecf, +/*20200212 to latest +크바시르의 지혜가 사라집니다. +*/ + MSG_ID_ED0 = 0xed0, +/*20200212 to latest +미스틱 심포니의 효과가 부여됩니다. +*/ + MSG_ID_ED1 = 0xed1, +/*20200212 to latest +미스틱 심포니의 효과가 사라집니다. +*/ + MSG_ID_ED2 = 0xed2, +/*20200212 to latest +마법 저항력이 감소했습니다. +*/ + MSG_ID_ED3 = 0xed3, +/*20200212 to latest +게페니아 녹턴의 효과가 해제 되었습니다. +*/ + MSG_ID_ED4 = 0xed4, +/*20200212 to latest +물리 저항력이 감소했습니다. +*/ + MSG_ID_ED5 = 0xed5, +/*20200212 to latest +마인워커 랩소디 상태가 해제되었습니다. +*/ + MSG_ID_ED6 = 0xed6, +/*20200212 to latest +물리 저항력이 증가했습니다. +*/ + MSG_ID_ED7 = 0xed7, +/*20200212 to latest +뮤지컬 인터루드 상태가 해제되었습니다. +*/ + MSG_ID_ED8 = 0xed8, +/*20200212 to latest +특성 마법 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_ED9 = 0xed9, +/*20200212 to latest +저녁 노을의 세레나데 효과가 해제되었습니다. +*/ + MSG_ID_EDA = 0xeda, +/*20200212 to latest +특성 물리 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_EDB = 0xedb, +/*20200212 to latest + 프론테라의 행진곡 효과가 해제되었습니다. +*/ + MSG_ID_EDC = 0xedc, +/*20200212 to latest +바람의 분노가 시전자에게 흘러 들어옵니다. +*/ + MSG_ID_EDD = 0xedd, +/*20200212 to latest +캘러미티 가일 상태가 해제되었습니다. +*/ + MSG_ID_EDE = 0xede, +/*20200212 to latest +바람에 의해 약점과 모습이 드러납니다. +*/ + MSG_ID_EDF = 0xedf, +/*20200212 to latest +윈드 사인 효과가 사라집니다. +*/ + MSG_ID_EE0 = 0xee0, +#endif +#if PACKETVER >= 20200304 +/*20200304 to latest +E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE1 = 0xee1, +/*20200304 to latest +DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE2 = 0xee2, +/*20200304 to latest +DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE3 = 0xee3, +#endif }; #endif /* MAP_MESSAGES_RE_H */ diff --git a/src/map/messages_sak.h b/src/map/messages_sak.h index 2cfe085c8..723f1f1f4 100644 --- a/src/map/messages_sak.h +++ b/src/map/messages_sak.h @@ -2,7 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/messages_zero.h b/src/map/messages_zero.h index d814b2305..babe9384c 100644 --- a/src/map/messages_zero.h +++ b/src/map/messages_zero.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ /* This file is autogenerated, please do not commit manual changes -Latest version: 20181114 +Latest version: 20200304 */ enum clif_messages { @@ -625,9 +625,11 @@ Fire Wind */ MSG_WIND = 0x76, -/*20171018 to latest +/*20171018 to 20191008 같은종류의 장비아이템은 한번에 한개만 살 수 있습니다. Please avoid buying 2 of the same items at one time. +20191023 to latest +같은 종류의 장비 아이템은 한 번에 한 개만 살 수 있습니다. */ MSG_EQUIPITEM_OLNY_ONE = 0x77, /*20171018 to latest @@ -752,7 +754,7 @@ Congratulations! You are the MVP! Your reward item is MSG_YOU_RECEIVE_MVP_ITEM = 0x8f, /*20171018 to latest !! -!! +! */ MSG_YOU_RECEIVE_MVP_ITEM2 = 0x90, /*20171018 to latest @@ -1225,9 +1227,11 @@ Cart Items [Alt+W] Basic Information */ MSG_BASICINFOWND = 0xee, -/*20171018 to latest +/*20171018 to 20191204 이무기로는 이스킬을 사용할수 없습니다. The skill cannot be used with this weapon. +20191211 to latest +현재 무기로는 스킬을 사용할 수 없습니다. */ MSG_USESKILL_FAIL_NOT_SUITABLE_WEAPON = 0xef, /*20171018 to latest @@ -3902,7 +3906,6 @@ Mouse wheel skills for F7 and F8 are Disabled.[/q2 OFF] MSG_EXPLAIN_QUICKSPELL2 = 0x302, /*20171018 to latest /q3 : /quickspell (/q1) + /quickspell2 (/q2) -/q3: /quickspell (/q1) + /quickspell2 (/q2) */ MSG_EXPLAIN_QUICKSPELL3 = 0x303, /*20171018 to latest @@ -4383,9 +4386,11 @@ Thief Knight */ MSG_KNIGHT = 0x364, -/*20171018 to latest +/*20171018 to 20181128 Prieset Priest +20181212 to latest +Priest */ MSG_PRIEST = 0x365, /*20171018 to latest @@ -4953,16 +4958,18 @@ High Wizard MSG_WIZARD_H = 0x3da, /*20171018 to latest White Smith -WhiteSmith +MasterSmith */ MSG_BLACKSMITH_H = 0x3db, /*20171018 to latest Sniper */ MSG_HUNTER_H = 0x3dc, -/*20171018 to latest +/*20171018 to 20181128 Saaassin Cross Assassin Cross +20181212 to latest +Assassin Cross */ MSG_ASSASSIN_H = 0x3dd, /*20171018 to latest @@ -4990,9 +4997,11 @@ Sniper_W Sniper */ MSG_HUNTER_H_W = 0x3e2, -/*20171018 to latest +/*20171018 to 20181128 Saaassin Cross_W Assassin Cross +20181212 to latest +Assassin Cross_W */ MSG_ASSASSIN_H_W = 0x3e3, /*20171018 to latest @@ -6571,7 +6580,6 @@ Quest List MSG_QUESTWIN = 0x525, /*20171018 to latest RO SHOP -RO Shop */ MSG_RO_SHOP = 0x526, /*20171018 to latest @@ -6726,9 +6734,11 @@ The Memorial Dungeon's entry time limit expired; it has been destroyed. The Memorial Dungeon has been removed. */ MSG_MEMORIAL_DUN_DESTROY_REQUEST = 0x544, -/*20171018 to latest +/*20171018 to 20191224 메모리얼 던전에 시스템 오류가 발생하였습니다. 정상적인 게임 진행을 위해 재접속을 해주십시오. A system error has occurred in the Memorial Dungeon. Please relog in to the game to continue playing. +20200115 to latest +메모리얼 던전에 통신 장애가 발생하였습니다. 정상적인 게임 진행을 위해 잠시 후, 재접속을 해주십시오. */ MSG_MEMORIAL_DUN_ERROR = 0x545, /*20171018 to latest @@ -9552,7 +9562,6 @@ ATTACK MSG_MACRO_ATTACK = 0x77d, /*20171018 to latest Next attack time : -Next attack time: */ MSG_MACRO_NEXT_ATK_TIME = 0x77e, /*20171018 to latest @@ -9561,6 +9570,7 @@ When died MSG_MACRO_WHEN_DIED = 0x77f, /*20171018 to latest When invited to a party +When invited to the party */ MSG_MACRO_WHEN_INVITED_PARTY = 0x780, /*20171018 to latest @@ -9583,7 +9593,6 @@ Monster Job hunting experience that you can get through the doubling of %d is %. MSG_PLUSONLYJOBEXP2 = 0x784, /*20171018 to latest SaveData_ExMacro%d -SaveData_ExMacro %d */ MSG_MACRO_SAVE = 0x785, /*20171018 to latest @@ -9739,37 +9748,30 @@ Elapsed time: %d:%d:%d / %d:%d:%d MSG_REPLAY_ELAPSEDTIME = 0x7a3, /*20171018 to latest Speed : X 1/4 -Speed: X 1/4 */ MSG_REPLAY_SPEED1_4 = 0x7a4, /*20171018 to latest Speed : X 1/2 -Speed: X 1/2 */ MSG_REPLAY_SPEED1_2 = 0x7a5, /*20171018 to latest Speed : X 1 -Speed: X 1 */ MSG_REPLAY_SPEED1 = 0x7a6, /*20171018 to latest Speed : X 2 -Speed: X 2 */ MSG_REPLAY_SPEED2 = 0x7a7, /*20171018 to latest Speed : X 4 -Speed: X 4 */ MSG_REPLAY_SPEED4 = 0x7a8, /*20171018 to latest Speed : X 8 -Speed: X 8 */ MSG_REPLAY_SPEED8 = 0x7a9, /*20171018 to latest Speed : X 16 -Speed: X 16 */ MSG_REPLAY_SPEED16 = 0x7aa, /*20171018 to latest @@ -9779,17 +9781,14 @@ Speed: Unknown MSG_REPLAY_SPEEDUNKNOWN = 0x7ab, /*20171018 to latest Service Info : %s -Service Info: %s */ MSG_REPLAY_CHRVICEINFO = 0x7ac, /*20171018 to latest Character Name : %s -Character Name: %s */ MSG_REPLAY_CHARACTERNAME = 0x7ad, /*20171018 to latest Map Name : %s -Map Name: %s */ MSG_REPLAY_MAPNAME = 0x7ae, /*20171018 to latest @@ -9849,10 +9848,12 @@ Input FileName -> Start MSG_REPLAY_START2 = 0x7bb, /*20171018 to latest Open Option +Open Options */ MSG_REPLAY_OPENOPTION = 0x7bc, /*20171018 to latest Close Option +Close Options */ MSG_REPLAY_CLOSEOPION = 0x7bd, /*20171018 to latest @@ -9899,16 +9900,16 @@ Record Start MSG_REPLAY_RECORDSTART = 0x7c6, /*20171018 to latest is Saved. +Recording saved */ MSG_REPLAY_RECORDEND = 0x7c7, /*20171018 to latest Weight : %3d / %3d -Weight: %3d / %3d */ MSG_WEIGHT = 0x7c8, /*20171018 to latest Total : %s C -Total: %s C +Total: %s EUR */ MSG_TOTAL = 0x7c9, /*20171018 to latest @@ -9926,7 +9927,6 @@ Job Lv. %d MSG__BASIC_MSG_JOB = 0x7cc, /*20171018 to latest Zeny : %s -Zeny: %s */ MSG_BASIC_MSG_ZENY = 0x7cd, /*20171018 to latest @@ -9947,6 +9947,7 @@ item MSG_GRAPHIC_MSG_ITEM = 0x7d1, /*20171018 to latest NoCtrl +Ctrl */ MSG_GRAPHIC_MSG_NOCTRL = 0x7d2, /*20171018 to latest @@ -9956,6 +9957,7 @@ More MSG_GRAPHIC_MSG_BATTLE = 0x7d3, /*20171018 to latest (Character/Total Slot) +(Characters/Total slots) */ MSG_CHARACTER_MSG_CHARACTERTOTALSLOT = 0x7d4, /*20171018 to latest @@ -10755,6 +10757,7 @@ Rename MSG_CHANGE_CHARACTER_NAME = 0x875, /*20171018 to latest Make Character +Create Character */ MSG_MSG_MAKECHARCTER = 0x876, /*20171018 to latest @@ -10952,7 +10955,6 @@ Help MSG_NAVIGATION_HELP = 0x89d, /*20171018 to latest ALL -All */ MSG_NAVIGATION_ALL = 0x89e, /*20171018 to latest @@ -12217,7 +12219,7 @@ AuthTicket is Not Valid MSG_NOT_VALID_AUTH_TICKET = 0x9a4, /*20171018 to latest ErrorCategory : %d, ErrorCode : %d (%d,%d,%d,%d) -ErrorCategory: %d, ErrorCode: %d (%d,%d,%d,%d) +ErrorCode : %d, ErrorValue : %d */ MSG_STEAMAGENCY_ERROR = 0x9a5, /*20171018 to latest @@ -12242,7 +12244,6 @@ The price of^0000FF %s^000000 MSG_WARNING_PRICE1 = 0x9a9, /*20171018 to latest 100000000 -1000000000 */ MSG_WARNING_PRICE2 = 0x9aa, /*20171018 to latest @@ -12891,7 +12892,7 @@ You can not open the mail. MSG_FAILED_TO_WRITE_MAIL = 0xa2c, /*20171018 to latest You are currently joined in CLan !! -You are currently joined in Clan !! +You currently belong to a clan. */ MSG_JOINED_IN_CLAN = 0xa2d, /*20171018 to latest @@ -12946,17 +12947,14 @@ The recipient's name does not exist. MSG_FAILE_MAIL_RECIEVER_INFO = 0xa37, /*20171018 to latest E X P : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -EXP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_EXPMSG = 0xa38, /*20171018 to latest DROP : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DROPMSG = 0xa39, /*20171018 to latest DEATH : %.1f%% ( basic %.1f%% premium %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic %.1f%% Premium %.1f%% + %s %.1f%%) */ MSG_TAIWAN_PERSONALINFO_DEATHMSG = 0xa3a, /*20171018 to latest @@ -13140,7 +13138,6 @@ Cracker is low. MSG_CASH_GEDARE_FAIL_MONEY = 0xa5e, /*20171018 to latest %s -%s */ MSG_CASH_GEDARE_MONEY = 0xa5f, /*20171018 to latest @@ -13335,17 +13332,14 @@ Please empty at least 5 amount of possession in item window. MSG_NOT_ENOUGH_SPACE_IN_ITEM_BODY = 0xa85, /*20171018 to latest E X P : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -EXP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_EXPMSG = 0xa86, /*20171018 to latest DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DROP : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DROPMSG = 0xa87, /*20171018 to latest DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%%) -DEATH : %.1f%% ( basic 100.0%% pccafe %.1f%% + %s %.1f%% ) */ MSG_JPN_PERSONALINFO_DEATHMSG = 0xa88, /*20171018 to latest @@ -13752,12 +13746,10 @@ Withdraw MSG_ID_AD7 = 0xad7, /*20171018 to latest 1 z UP -1z UP */ MSG_ID_AD8 = 0xad8, /*20171018 to latest 1 z Down -1z Down */ MSG_ID_AD9 = 0xad9, /*20171018 to latest @@ -13914,17 +13906,14 @@ Send Mail MSG_ID_AF8 = 0xaf8, /*20171018 to latest E X P : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -E X P: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AF9 = 0xaf9, /*20171018 to latest DROP : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DROP : %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFA = 0xafa, /*20171018 to latest DEATH : %.1f%% ( basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) -DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) */ MSG_ID_AFB = 0xafb, /*20171018 to 20180928 @@ -13932,6 +13921,7 @@ DEATH: %.1f%% (Basic 100.0%% VIP Bonus %.1f%% + %s %.1f%%) Name with this tag cannot be used. 20181010 to latest 해당 내용은 이름으로 사용하실 수 없습니다. +You cannot use the tag as a name. */ MSG_ID_AFC = 0xafc, /*20171018 to latest @@ -14099,7 +14089,6 @@ Screenshots are not attached MSG_ID_B1C = 0xb1c, /*20171018 to latest Twitter -TWITTER */ MSG_ID_B1D = 0xb1d, /*20171018 to latest @@ -14437,7 +14426,6 @@ The [%s] is not present, the default AI will be used instead. MSG_ID_B61 = 0xb61, /*20171018 to latest %.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) -%.1f%% ( Basic 100.0%% + Premium %.1f%% + %s %.1f%%) */ MSG_ID_B62 = 0xb62, /*20171018 to latest @@ -14998,10 +14986,12 @@ Weight limit has reached toover 70%. Or less then 10 invenrory space. MSG_ID_BCE = 0xbce, /*20171018 to latest C +EUR */ MSG_ID_BCF = 0xbcf, /*20171018 to latest C +EUR */ MSG_ID_BD0 = 0xbd0, /*20171018 to latest @@ -15233,10 +15223,12 @@ SP MSG_ID_BFD = 0xbfd, /*20171018 to latest Lv +Lv. */ MSG_ID_BFE = 0xbfe, /*20171018 to latest Lv +Lv. */ MSG_ID_BFF = 0xbff, /*20171018 to latest @@ -15249,6 +15241,7 @@ Exp MSG_ID_C01 = 0xc01, /*20171018 to latest Play Replay Flie +Start replay */ MSG_ID_C02 = 0xc02, /*20171018 to latest @@ -15265,14 +15258,17 @@ Basicinfo MSG_ID_C05 = 0xc05, /*20171018 to latest Equip +Equipment */ MSG_ID_C06 = 0xc06, /*20171018 to latest Item +Items */ MSG_ID_C07 = 0xc07, /*20171018 to latest Skill +Skills */ MSG_ID_C08 = 0xc08, /*20171018 to latest @@ -15285,10 +15281,12 @@ Party MSG_ID_C0A = 0xc0a, /*20171018 to latest Chatting +Chat */ MSG_ID_C0B = 0xc0b, /*20171018 to latest Shortcut +Hotkeys */ MSG_ID_C0C = 0xc0c, /*20171018 to latest @@ -15297,15 +15295,16 @@ Status MSG_ID_C0D = 0xc0d, /*20171018 to latest ALL -All */ MSG_ID_C0E = 0xc0e, /*20171018 to latest User Defined File Name +Custom file name */ MSG_ID_C0F = 0xc0f, /*20171018 to latest Repeated File Check +Check file */ MSG_ID_C10 = 0xc10, /*20171018 to latest @@ -15314,10 +15313,12 @@ on MSG_ID_C11 = 0xc11, /*20171018 to latest <Basic Skin> +<Basic> */ MSG_ID_C12 = 0xc12, /*20171018 to latest Select Skin +Choose skin */ MSG_ID_C13 = 0xc13, /*20171018 to latest @@ -15830,6 +15831,7 @@ Loading the mailbox. MSG_ID_C76 = 0xc76, /*20171018 to latest NOW LOADING.. +Loading... */ MSG_ID_C77 = 0xc77, /*20171018 to latest @@ -16049,6 +16051,7 @@ BOX MSG_ID_CA8 = 0xca8, /*20171018 to latest 다시하기 +File abusing detected. Please restart the client with clean files. */ MSG_ID_CA9 = 0xca9, /*20171018 to latest @@ -16195,6 +16198,7 @@ map MSG_ID_CCC = 0xccc, /*20171018 to latest 변조된 파일이 발견되었습니다. 게임을 다시 실행시켜주세요. +File abusing detected. Please restart the client with clean files. */ MSG_ID_CCD = 0xccd, /*20171018 to latest @@ -16350,9 +16354,11 @@ http://member.gnjoy.com/user/pay/chargelist.asp 한 번에 구입 가능한 아이템의 가짓수는 8개입니다. */ MSG_ID_CF0 = 0xcf0, -/*20171018 to latest +/*20171018 to 20190227 http://gift.zhaouc.com/#/index http://gift.zhaouc.com/ +20190313 to latest +http://gift.zhaouc.com/ */ MSG_ID_CF1 = 0xcf1, /*20171018 to latest @@ -16474,6 +16480,7 @@ TokenAgency 서버 연결 실패 MSG_ID_D0D = 0xd0d, /*20171023 to latest 삭제 +Delete */ MSG_ID_D0E = 0xd0e, /*20171023 to latest @@ -16539,92 +16546,143 @@ MOTP 입력 시간이 초과되었습니다. 처음부터 다시 로그인해주 모험가 중개소 등록 */ MSG_ID_D1C = 0xd1c, -/*20171031 to latest +/*20171031 to 20191113 모집 중단 +20191127 to latest +모험가 중개소 등록하기 */ MSG_ID_D1D = 0xd1d, -/*20171031 to latest +/*20171031 to 20191113 모험가 중개소 설정 +20191127 to latest +모집 중단 */ MSG_ID_D1E = 0xd1e, -/*20171031 to latest +/*20171031 to 20191113 전 지역 +20191127 to latest +메모 +Note */ MSG_ID_D1F = 0xd1f, -/*20171031 to latest +/*20171031 to 20191113 직접기재 +20191127 to latest +퀘스트 +Quest */ MSG_ID_D20 = 0xd20, -/*20171031 to latest +/*20171031 to 20191113 검사계열 +20191127 to latest +필드 */ MSG_ID_D21 = 0xd21, -/*20171031 to latest +/*20171031 to 20191113 법사계열 +20191127 to latest +던전 +Dungeon */ MSG_ID_D22 = 0xd22, -/*20171031 to latest +/*20171031 to 20191113 궁수계열 +20191127 to latest +MD */ MSG_ID_D23 = 0xd23, -/*20171031 to latest +/*20171031 to 20191113 복사계열 +20191127 to latest +낙원단 */ MSG_ID_D24 = 0xd24, -/*20171031 to latest +/*20171031 to 20191113 상인계열 +20191127 to latest +기타 +Other */ MSG_ID_D25 = 0xd25, -/*20171031 to latest +/*20171031 to 20191113 도둑계열 +20191127 to latest +검색 +Search */ MSG_ID_D26 = 0xd26, -/*20171031 to latest +/*20171031 to 20191113 태권계열 +20191127 to latest +초기화 */ MSG_ID_D27 = 0xd27, -/*20171031 to latest +/*20171031 to 20191113 닌자계열 +20191127 to latest +파티가입신청 */ MSG_ID_D28 = 0xd28, -/*20171031 to latest +/*20171031 to 20191113 건슬링거계열 +20191127 to latest +목적 */ MSG_ID_D29 = 0xd29, -/*20171031 to latest +/*20171031 to 20191113 도람족계열 +20191127 to latest +※욕설, 현거래등 운영 규칙에 위배되는 목적으로 사용 시 처벌 받을 수 있습니다. */ MSG_ID_D2A = 0xd2a, -/*20171031 to latest +/*20171031 to 20191113 지역명 +20191127 to latest +등록하기 +Register */ MSG_ID_D2B = 0xd2b, -/*20171031 to latest +/*20171031 to 20191113 지역명 검색 +20191127 to latest +사용할 수 없는 단어가 포함되어 있습니다. */ MSG_ID_D2C = 0xd2c, -/*20171031 to latest +/*20171031 to 20191113 가입 요청하기 +20191127 to latest +파티가입 */ MSG_ID_D2D = 0xd2d, -/*20171031 to latest +/*20171031 to 20191113 신고하기 +20191127 to latest +1:1대화 */ MSG_ID_D2E = 0xd2e, -/*20171031 to latest +/*20171031 to 20191113 파티 모집 중단 +20191127 to latest +모집 중지 */ MSG_ID_D2F = 0xd2f, -/*20171031 to latest +/*20171031 to 20191113 지역명을 입력해주세요. +20191127 to latest +수정하기 */ MSG_ID_D30 = 0xd30, -/*20171031 to latest +/*20171031 to 20191113 지역 +20191127 to latest +전체 +All */ MSG_ID_D31 = 0xd31, -/*20171031 to latest +/*20171031 to 20191113 전 직업 +20191127 to latest +[%s] 님이 파티가입을 신청했습니다. */ MSG_ID_D32 = 0xd32, #endif @@ -16668,6 +16726,7 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D3B = 0xd3b, /*20171109 to latest 태권 +Taekwon */ MSG_ID_D3C = 0xd3c, /*20171109 to 20171117 @@ -16708,10 +16767,12 @@ NPC가 있는 맵의 랜덤 좌표로 이동 됩니다. MSG_ID_D42 = 0xd42, /*20171109 to latest 차단 리스트가 없습니다 +Ignore-list is empty */ MSG_ID_D43 = 0xd43, /*20171109 to latest -차단 리스트- +Characters in ignore-list: */ MSG_ID_D44 = 0xd44, #endif @@ -16796,12 +16857,17 @@ Password 회원가입 */ MSG_ID_D56 = 0xd56, -/*20171115 to latest +/*20171115 to 20191113 노비스계열 +20191127 to latest +파티장 +Leader */ MSG_ID_D57 = 0xd57, -/*20171115 to latest +/*20171115 to 20191113 슈퍼노비스계열 +20191127 to latest +파티에 가입할 수 있는 레벨이 아닙니다. */ MSG_ID_D58 = 0xd58, /*20171115 to latest @@ -16814,12 +16880,16 @@ Password MSG_ID_D5A = 0xd5a, /*20171115 to 20180321 직업을 한 개 이상 선택해주세요. -20180328 to latest +20180328 to 20191113 모집을 희망하는 파티원의 직업을 한 개 이상 선택해 주세요. +20191127 to latest +검색내용입력 */ MSG_ID_D5B = 0xd5b, -/*20171115 to latest +/*20171115 to 20191127 지역명을 두 글자 이상 입력해주세요. +20191204 to latest +탱커 */ MSG_ID_D5C = 0xd5c, /*20171115 to latest @@ -16918,8 +16988,10 @@ Password 불량 단어가 포함된 이름은 검색할 수 없습니다. */ MSG_ID_D71 = 0xd71, -/*20171213 to latest +/*20171213 to 20191127 파티 마스터가 요청을 받을 수 없는 맵에 있습니다. +20191204 to latest +파티 마스터가 요청을 받을 수 없는 상태입니다. */ MSG_ID_D72 = 0xd72, /*20171213 to latest @@ -16928,8 +17000,10 @@ Password MSG_ID_D73 = 0xd73, /*20171213 to 20180321 님의 파티요청입니다. -20180328 to latest +20180328 to 20191127 님의 파티 가입 요청입니다. +20191204 to latest +딜러 */ MSG_ID_D74 = 0xd74, /*20171213 to latest @@ -17088,6 +17162,7 @@ This is not the current attendance check event MSG_ID_D93 = 0xd93, /*20180207 to latest 개인 상납 경험치가 max에 도달하여, 더 이상 길드 경험치를 누적할 수 없습니다. + */ MSG_ID_D94 = 0xd94, #endif @@ -17138,10 +17213,13 @@ Enter 4 english words and 2 chinese words MSG_ID_D9D = 0xd9d, /*20180328 to latest 50% 이상의 값을 입력할 수 없습니다. +The guild tax rate can't be set to more than 50%. */ MSG_ID_D9E = 0xd9e, -/*20180328 to latest +/*20180328 to 20191204 파티 가입 요청을 보냈습니다. +20191211 to latest +파티가 모험가 중개소에 등록되어, 파티장을 변경 할 수 없습니다. */ MSG_ID_D9F = 0xd9f, /*20180328 to latest @@ -17156,8 +17234,10 @@ Enter 4 english words and 2 chinese words 거절 */ MSG_ID_DA2 = 0xda2, -/*20180328 to latest +/*20180328 to 20191127 설정 권한은 파티장에게 있습니다. +20191204 to latest +힐러 */ MSG_ID_DA3 = 0xda3, /*20180328 to latest @@ -17218,16 +17298,21 @@ Enter 4 english words and 2 chinese words 파티 가입 요청 */ MSG_ID_DB1 = 0xdb1, -/*20180411 to latest +/*20180411 to 20191113 파티장이 아닌 경우, 모험가 중개소에 파티를 등록할 수 없습니다. +20191127 to latest +파티장이 아닌 경우 모험가 중개소에 파티를 등록할 수 없습니다. */ MSG_ID_DB2 = 0xdb2, /*20180411 to latest 파티 모집을 중단할 수 없습니다. */ MSG_ID_DB3 = 0xdb3, -/*20180411 to latest +/*20180411 to 20191127 존재하지 않는 파티 글입니다. +20191204 to latest +보조 +Support */ MSG_ID_DB4 = 0xdb4, #endif @@ -17260,16 +17345,20 @@ Zoom Out 기능을 해제합니다 (Off) 파티장인 경우, 가입 요청을 할 수 없습니다. */ MSG_ID_DBB = 0xdbb, -/*20180425 to latest +/*20180425 to 20191211 모험가 중개소에 등록 중입니다. 잠시만 기다려 주세요. +20191224 to latest +파티 가입 최대 레벨은 최소 레벨 설정보다 높아야 합니다. */ MSG_ID_DBC = 0xdbc, /*20180425 to latest 더 이상 직업을 선택할 수 없습니다. */ MSG_ID_DBD = 0xdbd, -/*20180425 to latest +/*20180425 to 20191113 게임 중 해당 유저의 가입 요청을 받지 않습니다. +20191127 to latest +게임 중 해당 유저의 가입 요청을 받지 않습니다.(클라이언트 종료 전까지 유효) */ MSG_ID_DBE = 0xdbe, /*20180425 to latest @@ -17348,14 +17437,17 @@ Emblem 테두리를 그려주지 않습니다 #if PACKETVER >= 20180711 /*20180711 to latest E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DCF = 0xdcf, /*20180711 to latest DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD0 = 0xdd0, /*20180711 to latest DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) */ MSG_ID_DD1 = 0xdd1, #endif @@ -17364,6 +17456,7 @@ DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) ' 20180808 to latest 영문이나 러시아어 단독으로만 사용이 가능합니다. +Forbidden symbols in character name. */ MSG_ID_DD2 = 0xdd2, /*20180725 to 20180801 @@ -17376,30 +17469,36 @@ you must have an AccessTicket to login -- 20180905 to latest 창고를 불러오는 중입니다. +Loading.... */ MSG_ID_DD4 = 0xdd4, #endif #if PACKETVER >= 20180905 /*20180905 to latest NOW LOADING.. +Loading... */ MSG_ID_DD5 = 0xdd5, #endif #if PACKETVER >= 20180919 /*20180919 to latest 삭제 +Delete */ MSG_ID_DD6 = 0xdd6, /*20180919 to latest 답장 +Reply */ MSG_ID_DD7 = 0xdd7, /*20180919 to latest 전송 +Send */ MSG_ID_DD8 = 0xdd8, /*20180919 to latest 이름확인 +Name Check */ MSG_ID_DD9 = 0xdd9, /*20180919 to latest @@ -17509,6 +17608,1099 @@ PvP */ MSG_ID_DF1 = 0xdf1, #endif +#if PACKETVER >= 20181212 +/*20181212 to latest +성제 +*/ + MSG_ID_DF2 = 0xdf2, +/*20181212 to latest +소울리퍼 +*/ + MSG_ID_DF3 = 0xdf3, +/*20181212 to latest +설정한 내용은 정상 종료 시 서버에 저장됩니다. +*/ + MSG_ID_DF4 = 0xdf4, +#endif +#if PACKETVER >= 20181226 +/*20181226 to latest +Capture Monster +Taming monster +*/ + MSG_ID_DF5 = 0xdf5, +/*20181226 to latest +message +*/ + MSG_ID_DF6 = 0xdf6, +#endif +#if PACKETVER >= 20190116 +/*20190116 to latest +TITLE +Header +*/ + MSG_ID_DF7 = 0xdf7, +/*20190116 to latest +데미지 표기 확장 +*/ + MSG_ID_DF8 = 0xdf8, +#endif +#if PACKETVER >= 20190130 +/*20190130 to latest +맵 이름 표기 +*/ + MSG_ID_DF9 = 0xdf9, +#endif +#if PACKETVER >= 20190213 +/*20190213 to latest +시스템 메세지 : 현재 노점보기 기능이 %s 상태입니다. (/노점보기) +*/ + MSG_ID_DFA = 0xdfa, +/*20190213 to latest +수직 동기 +*/ + MSG_ID_DFB = 0xdfb, +/*20190213 to latest +자료 +*/ + MSG_ID_DFC = 0xdfc, +/*20190213 to 20190227 +http://rodata.zhaouc.com/renwu.html#container +20190313 to latest +http://rodata.zhaouc.com/renwu.html +*/ + MSG_ID_DFD = 0xdfd, +/*20190213 to latest +작위 +*/ + MSG_ID_DFE = 0xdfe, +/*20190213 to latest +프레임 제한 +*/ + MSG_ID_DFF = 0xdff, +#endif +#if PACKETVER >= 20190220 +/*20190220 to latest +%d개를 초과할 경우 더 이상 설치 할 수 없습니다. +*/ + MSG_ID_E00 = 0xe00, +/*20190220 to latest +목적지 +*/ + MSG_ID_E01 = 0xe01, +#endif +#if PACKETVER >= 20190313 +/*20190313 to latest +잠시 후 다시 시도해주세요. +Please try again in a moment. +*/ + MSG_ID_E02 = 0xe02, +/*20190313 to latest +등록할 수 없는 파일입니다. +*/ + MSG_ID_E03 = 0xe03, +#endif +#if PACKETVER >= 20190327 +/*20190327 to latest +선택 삭제 +*/ + MSG_ID_E04 = 0xe04, +/*20190327 to latest +모두 삭제 +*/ + MSG_ID_E05 = 0xe05, +/*20190327 to latest +[%s]편지함의 모든 메일을 삭제하시겠습니까? +*/ + MSG_ID_E06 = 0xe06, +/*20190327 to latest +선택 받기 +*/ + MSG_ID_E07 = 0xe07, +/*20190327 to latest +모두 받기 +*/ + MSG_ID_E08 = 0xe08, +/*20190327 to latest +선택한 메일의 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E09 = 0xe09, +/*20190327 to latest +[%s]편지함의 모든 첨부 물품을 받으시겠습니까? +*/ + MSG_ID_E0A = 0xe0a, +/*20190327 to latest +스킬바2 +*/ + MSG_ID_E0B = 0xe0b, +/*20190327 to latest +스킬바 교체 +*/ + MSG_ID_E0C = 0xe0c, +#endif +#if PACKETVER >= 20190403 +/*20190403 to latest +← +*/ + MSG_ID_E0D = 0xe0d, +/*20190403 to latest +↑ +*/ + MSG_ID_E0E = 0xe0e, +/*20190403 to latest +→ +*/ + MSG_ID_E0F = 0xe0f, +/*20190403 to latest +↓ +*/ + MSG_ID_E10 = 0xe10, +/*20190403 to latest +▤ +*/ + MSG_ID_E11 = 0xe11, +/*20190403 to latest +← +*/ + MSG_ID_E12 = 0xe12, +/*20190403 to latest +로딩중에는 창을 닫을 수 없습니다. +*/ + 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 +#if PACKETVER >= 20190515 +/*20190515 to latest +영지로 이동 +*/ + MSG_ID_E2D = 0xe2d, +/*20190515 to latest +상업도/방어도 확인 +*/ + MSG_ID_E2E = 0xe2e, +/*20190515 to 20190605 +관리영지 "%s"(으)로 이동하시겠습니까? +(1회 이동 시 마다 1,000제니가 소모됩니다. +공성전 시간에는 제니의 소모가 100배로 증가합니다.) +20190626 to latest +관리영지 "%s"(으)로 이동하시겠습니까? +이동 시 마다 %d제니가 소모됩니다. +(공성전 시간에는 %d제니가 소모됩니다.) +*/ + MSG_ID_E2F = 0xe2f, +/*20190515 to latest + +관리영지 "%s" + +방어도: %d / %d +상업도: %d / %d +*/ + MSG_ID_E30 = 0xe30, +/*20190515 to 20190717 +공성 영지 내에서는 다른 공성 영지로 이동 할 수 없습니다. +20190724 to latest +공성 영지에서는 사용할 수 없는 기능입니다. +*/ + MSG_ID_E31 = 0xe31, +#endif +#if PACKETVER >= 20190626 +/*20190626 to latest +기본 기능 스킬을 습득하지 않은 캐릭터입니다. +*/ + MSG_ID_E32 = 0xe32, +/*20190626 to latest +접속하신 IP는 라그나로크 제로 이용이 불가능합니다. +고객센터 또는 홈페이지로 문의해 주십시오. +*/ + MSG_ID_E33 = 0xe33, +#endif +#if PACKETVER >= 20190709 +/*20190709 to latest +탑승/장착 해제 +*/ + MSG_ID_E34 = 0xe34, +/*20190709 to latest +가져오기 +*/ + MSG_ID_E35 = 0xe35, +/*20190709 to latest +직전 노점에 등록된 아이템 정보를 가져옵니다. +*/ + MSG_ID_E36 = 0xe36, +/*20190709 to latest +서버 이슈로 인해 로그인이 제한되고 있습니다. +*/ + MSG_ID_E37 = 0xe37, +/*20190709 to latest +메시지 +*/ + MSG_ID_E38 = 0xe38, +/*20190709 to latest ++18 이상만 플레이가 가능합니다. +*/ + MSG_ID_E39 = 0xe39, +/*20190709 to latest +3시간 이상의 게임 플레이를 권장하지 않습니다. +*/ + MSG_ID_E3A = 0xe3a, +/*20190709 to latest +AP가 부족합니다. +*/ + MSG_ID_E3B = 0xe3b, +#endif +#if PACKETVER >= 20190724 +/*20190724 to latest +제련도가 높아서 조합할 수 없습니다. +*/ + MSG_ID_E3C = 0xe3c, +/*20190724 to latest +제련도가 조합에 필요한 요구치보다 높습니다. +*/ + MSG_ID_E3D = 0xe3d, +/*20190724 to 20191106 +'카드가 끼워져있거나 인챈트 되어 있습니다. +20191113 to latest +카드가 끼워져있거나 인챈트 되어 있습니다. +*/ + MSG_ID_E3E = 0xe3e, +#endif +#if PACKETVER >= 20190814 +/*20190814 to latest +∞ +? +*/ + MSG_ID_E3F = 0xe3f, +/*20190814 to latest +Z +*/ + MSG_ID_E40 = 0xe40, +/*20190814 to latest +Total : %s Zeny +*/ + MSG_ID_E41 = 0xe41, +/*20190814 to latest +계정한정판매 등록창 +Limited Account Registration Window +*/ + MSG_ID_E42 = 0xe42, +/*20190814 to latest +아이템 DB명 +Item DB Name +*/ + MSG_ID_E43 = 0xe43, +/*20190814 to latest +아이템 DB번호 +Item DB Number +*/ + MSG_ID_E44 = 0xe44, +/*20190814 to latest +판매 갯수 +Number of Sale +*/ + MSG_ID_E45 = 0xe45, +/*20190814 to latest +판매 시작시간 +Sale Start Time +*/ + MSG_ID_E46 = 0xe46, +/*20190814 to latest +판매 종료시간 +Sale end time +*/ + MSG_ID_E47 = 0xe47, +/*20190814 to latest +계정 한정 +Account only +*/ + MSG_ID_E48 = 0xe48, +/*20190814 to 20190814 +판매기간 : %d월 %d일 ~ %d월 %d일 +20190828 to latest +판매기간 : %d월 %d일 %d시 %d분 +Sale period:% d month% d day% d hours% d +*/ + MSG_ID_E49 = 0xe49, +/*20190814 to 20190925 +구입가능 %d개 +% D available +20191008 to latest +계정당 구매가능 +*/ + MSG_ID_E4A = 0xe4a, +/*20190814 to latest +%d개 한정 +limited to% d +*/ + MSG_ID_E4B = 0xe4b, +/*20190814 to latest +>> ItemName : %s / 수량 : %d / 판매기간 : %d월:%d일:%d시:%d분 ~ %d월:%d일:%d시:%d분 +>> ItemName:% s / Quantity:% d / Sales Period:% d Month:% d Day:% d Hour:% d Minute ~% d Month:% d Day:% d Hour:% d Minute +*/ + MSG_ID_E4C = 0xe4c, +/*20190814 to latest +Sold Out +*/ + MSG_ID_E4D = 0xe4d, +/*20190814 to latest +[%s]은(는) 현재 소환할 수 없는 지역에 있습니다. +% s] is currently in a region that cannot be summoned. +*/ + MSG_ID_E4E = 0xe4e, +#endif +#if PACKETVER >= 20190828 +/*20190828 to latest +~ %d월 %d일 %d시 %d분 +% d min% d days% d days +*/ + MSG_ID_E4F = 0xe4f, +/*20190828 to latest +상품을 더이상 추가할 수 없습니다 +Can't add any more items +*/ + MSG_ID_E50 = 0xe50, +/*20190828 to latest +장착 중인 아이템은 교환할 수 없습니다. 장착을 해제한 뒤 시도해 주시길 바랍니다. +The item being mounted cannot be exchanged. Please unmount it and try again. +*/ + MSG_ID_E51 = 0xe51, +#endif +#if PACKETVER >= 20190911 +/*20190911 to latest +길드 창고 이용 중엔 캐릭터 선택창으로 이동 할 수 없습니다. +You can not move to the character selection window while using the Guild Warehouse. +*/ + MSG_ID_E52 = 0xe52, +/*20190911 to latest +아이템 태그가 포함되어 있어 사용할 수 없습니다. +Item tag is included and cannot be used. +*/ + MSG_ID_E53 = 0xe53, +/*20190911 to latest +Monster +*/ + MSG_ID_E54 = 0xe54, +/*20190911 to latest +Unknown +*/ + MSG_ID_E55 = 0xe55, +/*20190911 to latest +Undead +*/ + MSG_ID_E56 = 0xe56, +/*20190911 to latest +Animal +*/ + MSG_ID_E57 = 0xe57, +/*20190911 to latest +Plant +*/ + MSG_ID_E58 = 0xe58, +/*20190911 to latest +Insect +*/ + MSG_ID_E59 = 0xe59, +/*20190911 to latest +Marine +*/ + MSG_ID_E5A = 0xe5a, +/*20190911 to latest +Devil +*/ + MSG_ID_E5B = 0xe5b, +/*20190911 to latest +Human +*/ + MSG_ID_E5C = 0xe5c, +/*20190911 to latest +Angel +*/ + MSG_ID_E5D = 0xe5d, +/*20190911 to latest +Dragon +*/ + MSG_ID_E5E = 0xe5e, +/*20190911 to latest +Balance: %s %c +*/ + MSG_ID_E5F = 0xe5f, +/*20190911 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money will be deducted from your total balance. +*/ + MSG_ID_E60 = 0xe60, +/*20190911 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %s캐시, 무료 %s캐시가 차감됩니다. +Do you really want to purchase this item? %s Money and %s Free Points will be deducted from your total balance. +*/ + MSG_ID_E61 = 0xe61, +/*20190911 to latest +호출이 거부되었습니다. +20190918 to 20190918 +[%s]의 호출이 거부되었습니다. +*/ + MSG_ID_E62 = 0xe62, +#endif +#if PACKETVER >= 20190918 +/*20190918 to latest +구매 한도액은 소지액을 초과할 수 없습니다. +*/ + MSG_ID_E63 = 0xe63, +/*20190918 to latest +노점을 여는데 실패하였습니다. 구매노점 개설을 닫아주시기 바랍니다. +*/ + MSG_ID_E64 = 0xe64, +#endif +#if PACKETVER >= 20190925 +/*20190925 to latest +판매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E65 = 0xe65, +/*20190925 to latest +구매 노점 아이템 리스트가 저장되었습니다. +*/ + MSG_ID_E66 = 0xe66, +#endif +#if PACKETVER >= 20191008 +/*20191008 to latest +VTC 인증에 실패하였습니다. +*/ + MSG_ID_E67 = 0xe67, +/*20191008 to latest +물물교환 중에는 장비를 착용할 수 없습니다. +*/ + MSG_ID_E68 = 0xe68, +/*20191008 to latest +교환하려는 품목 +*/ + MSG_ID_E69 = 0xe69, +/*20191008 to latest + 1차, 2차, 3차 직업 스킬 %d개를 더 올려 주십시오. +*/ + MSG_ID_E6A = 0xe6a, +#endif +#if PACKETVER >= 20191023 +/*20191023 to latest +같은 아이템은 %d개까지 소유할 수 있습니다. +*/ + MSG_ID_E6B = 0xe6b, +/*20191023 to latest +같은 아이템은 한 번에 %d개까지 교환할 수 있습니다. +*/ + MSG_ID_E6C = 0xe6c, +/*20191023 to latest +해당 로덱스는 "%s"서버에서 열 수 있습니다. +*/ + MSG_ID_E6D = 0xe6d, +/*20191023 to latest +[공지]편지함의 모든 메일을 삭제하시겠습니까 ? +(해당 "%s"서버의 메일만 삭제됩니다.) +*/ + MSG_ID_E6E = 0xe6e, +/*20191023 to latest +[공지]편지함의 모든 첨부 물품을 받으시겠습니까? +(해당 "%s"서버의 메일의 물품만 받으실 수 있습니다.) +*/ + MSG_ID_E6F = 0xe6f, +/*20191023 to latest +해당 메일은 "%s"서버에서만 삭제 가능합니다. +*/ + MSG_ID_E70 = 0xe70, +/*20191023 to latest +해당 메일은 "%s"서버에서만 물품 받기가 가능합니다. +*/ + MSG_ID_E71 = 0xe71, +/*20191023 to latest +해당 메일은 "%s"서버에서만 내용읽기가 가능합니다. +*/ + MSG_ID_E72 = 0xe72, +#endif +#if PACKETVER >= 20191106 +/*20191106 to latest +합계 : %d z +*/ + MSG_ID_E73 = 0xe73, +/*20191106 to latest +메세지 +*/ + MSG_ID_E74 = 0xe74, +/*20191106 to latest +구매상점 개설 중에는 개인상점의 물건을 구입할 수 없습니다. +*/ + MSG_ID_E75 = 0xe75, +#endif +#if PACKETVER >= 20191113 +/*20191113 to latest +등급강화가 성공적으로 되었습니다. +*/ + MSG_ID_E76 = 0xe76, +/*20191113 to latest +등급강화가 실패하였습니다. +*/ + MSG_ID_E77 = 0xe77, +/*20191113 to latest +등급 수치가 하향 조정 되었습니다. +*/ + MSG_ID_E78 = 0xe78, +/*20191113 to latest +장비가 파괴되었습니다. +*/ + MSG_ID_E79 = 0xe79, +/*20191113 to latest +장비가 보호되었습니다. +*/ + MSG_ID_E7A = 0xe7a, +/*20191113 to latest +재료가 선택되지 않았습니다. +*/ + MSG_ID_E7B = 0xe7b, +/*20191113 to latest +재료가 부족합니다. +*/ + MSG_ID_E7C = 0xe7c, +/*20191113 to latest +소지 금액이 부족합니다. +*/ + MSG_ID_E7D = 0xe7d, +/*20191113 to latest +아이템 공간이 부족합니다. +*/ + MSG_ID_E7E = 0xe7e, +/*20191113 to latest +장비가 보호되고 있습니다. +*/ + MSG_ID_E7F = 0xe7f, +/*20191113 to latest +장비가 파괴될 수 있습니다. +*/ + MSG_ID_E80 = 0xe80, +/*20191113 to latest +등급강화 실패 시 등급이 내려갑니다. +*/ + MSG_ID_E81 = 0xe81, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. 로덱스를 강제 종료하였습니다. +*/ + MSG_ID_E82 = 0xe82, +/*20191113 to latest +등급강화 중에는 로덱스를 사용할 수 없습니다. +*/ + MSG_ID_E83 = 0xe83, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. 은행을 강제 종료하였습니다. +*/ + MSG_ID_E84 = 0xe84, +/*20191113 to latest +등급강화 중에는 은행을 사용할 수 없습니다. +*/ + MSG_ID_E85 = 0xe85, +/*20191113 to latest +[%s] 님이 등급강화를 성공하여, [%s등급 %s] 아이템을 획득하였습니다. +*/ + MSG_ID_E86 = 0xe86, +/*20191113 to latest +[%s] 님이 [%s등급 %s] 아이템의 등급강화에 실패하였습니다. +*/ + MSG_ID_E87 = 0xe87, +/*20191113 to latest +등급 강화가 불가능한 장비입니다. +*/ + MSG_ID_E88 = 0xe88, +/*20191113 to latest +체인지 메테리얼 +*/ + MSG_ID_E89 = 0xe89, +/*20191113 to latest +가나다 정렬 +*/ + MSG_ID_E8A = 0xe8a, +/*20191113 to 20191224 +기본 결과물은 %s %d개 이나, 낮은 확률로 최대 %d개까지 생성될 수 있습니다. +20200115 to latest +※[%s] %d~%d개 제작 +*/ + MSG_ID_E8B = 0xe8b, +/*20191113 to latest +성공 %d%% +*/ + MSG_ID_E8C = 0xe8c, +/*20191113 to latest +아이템 태그는 소지한 아이템만 태그할 수 있습니다. +*/ + MSG_ID_E8D = 0xe8d, +#endif +#if PACKETVER >= 20191127 +/*20191127 to latest +공격력 및 특성 공격력이 증가되었습니다. +*/ + MSG_ID_E8E = 0xe8e, +/*20191127 to latest +강인한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E8F = 0xe8f, +/*20191127 to latest +HP 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_E90 = 0xe90, +/*20191127 to latest +굳건한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E91 = 0xe91, +/*20191127 to latest +공격 속도 및 유도 공격 확률이 증가되었습니다. +*/ + MSG_ID_E92 = 0xe92, +/*20191127 to latest +신실한 신념 효과가 해제되었습니다. +*/ + MSG_ID_E93 = 0xe93, +/*20191127 to latest +POW, CRT 및 CON 이 증가되었습니다. +*/ + MSG_ID_E94 = 0xe94, +/*20191127 to latest +베네딕툼 효과가 해제되었습니다. +*/ + MSG_ID_E95 = 0xe95, +/*20191127 to latest +SPL, WIS 및 CRT 가 증가되었습니다. +*/ + MSG_ID_E96 = 0xe96, +/*20191127 to latest +렐리지오 효과가 해제되었습니다. +*/ + MSG_ID_E97 = 0xe97, +/*20191127 to latest +특성 공격력 및 특성 마법 공격력이 증가되었습니다. +*/ + MSG_ID_E98 = 0xe98, +/*20191127 to latest +콤페텐티아 효과가 해제되었습니다. +*/ + MSG_ID_E99 = 0xe99, +/*20191127 to latest +크리티컬 데미지 비율이 증가되었습니다. +*/ + MSG_ID_E9A = 0xe9a, +/*20191127 to latest +프레센스 아치에스 효과가 해제되었습니다. +*/ + MSG_ID_E9B = 0xe9b, +/*20191127 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9C = 0xe9c, +/*20191127 to latest +아르구투스 텔룸 효과가 해제되었습니다. +*/ + MSG_ID_E9D = 0xe9d, +/*20191127 to latest +마법 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_E9E = 0xe9e, +/*20191127 to latest +아르구투스 비타 효과가 해제되었습니다. +*/ + MSG_ID_E9F = 0xe9f, +/*20191127 to latest +Num: %d/%d Weight: %d/%d +*/ + MSG_ID_EA0 = 0xea0, +#endif +#if PACKETVER >= 20191204 +/*20191204 to latest +서번트 웨폰 효과가 해제되었습니다. +*/ + MSG_ID_EA1 = 0xea1, +/*20191204 to latest +챠징 피어스 효과가 해제되었습니다. +*/ + MSG_ID_EA2 = 0xea2, +/*20191204 to latest +비고르 효과가 해제되었습니다. +*/ + MSG_ID_EA3 = 0xea3, +/*20191204 to latest +공격력이 증가하고, 방어력이 감소되었습니다. +*/ + MSG_ID_EA4 = 0xea4, +/*20191204 to latest +어택 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA5 = 0xea5, +/*20191204 to latest +방어력이 증가하고, 공격력이 감소되었습니다. +*/ + MSG_ID_EA6 = 0xea6, +/*20191204 to latest +가드 스탠스 효과가 해제되었습니다. +*/ + MSG_ID_EA7 = 0xea7, +/*20191204 to latest +배리어가 부여되었습니다. +*/ + MSG_ID_EA8 = 0xea8, +/*20191204 to latest +가디언 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EA9 = 0xea9, +/*20191204 to latest +헌신 효과로 받는 데미지가 감소되었습니다. +*/ + MSG_ID_EAA = 0xeaa, +/*20191204 to latest +리바운드 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAB = 0xeab, +/*20191204 to latest +성속성 마법 데미지와 암/불사속성 내성이 증가되었습니다. +*/ + MSG_ID_EAC = 0xeac, +/*20191204 to latest +홀리 쉴드 효과가 해제되었습니다. +*/ + MSG_ID_EAD = 0xead, +/*20191204 to latest +즉시 부활 효과가 부여되었습니다. +*/ + MSG_ID_EAE = 0xeae, +/*20191204 to latest +얼티메이트 세크리파이스 효과가 해제되었습니다. +*/ + MSG_ID_EAF = 0xeaf, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB0 = 0xeb0, +/*20191204 to latest +클라이막스 효과가 해제되었습니다. +*/ + MSG_ID_EB1 = 0xeb1, +/*20191204 to latest +특정 스킬의 효능이 변경되었습니다. +*/ + MSG_ID_EB2 = 0xeb2, +/*20191204 to latest +쉐도우 익시드 효과가 해제되었습니다. +*/ + MSG_ID_EB3 = 0xeb3, +/*20191204 to latest +물리 저항력 무시 효과가 부여되었습니다. +*/ + MSG_ID_EB4 = 0xeb4, +/*20191204 to latest +포텐트 베넘 효과가 해제되었습니다. +*/ + MSG_ID_EB5 = 0xeb5, +/*20191204 to latest +매 공격 시, 일정 확률로 대상이 받는 데미지를 증가시킵니다. +*/ + MSG_ID_EB6 = 0xeb6, +/*20191204 to latest +인챈팅 쉐도우 효과가 해제되었습니다. +*/ + MSG_ID_EB7 = 0xeb7, +/*20191204 to latest +토벌 카운트 On +*/ + MSG_ID_EB8 = 0xeb8, +/*20191204 to latest +토벌 카운트 Off +*/ + MSG_ID_EB9 = 0xeb9, +#endif +#if PACKETVER >= 20191211 +/*20191211 to latest +대상의 AP가 최대치입니다 +*/ + MSG_ID_EBA = 0xeba, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 %d캐시가 차감됩니다. +*/ + MSG_ID_EBB = 0xebb, +/*20191211 to latest + ^ff0000본 아이템을 구매 후 7일 이내에는 청약 철회가 가능합니다. 다만, 7일이 지났거나 아이템을 개봉하시면 청약 철회 대상에서 제외 됩니다.또한 구매시 사용된 무료캐시는 청약철회시 반환되지 않습니다.일부(이벤트) 아이템의 경우 이벤트 종료 시 삭제될 수 있으며, 아이템 설명 내 기재된 삭제기간을 확인해 주시기 바랍니다.^000000 정말로 아이템을 구매하시겠습니까? 구매하실 경우 일반 %d캐시, 무료 %d캐시가 차감됩니다. +*/ + MSG_ID_EBC = 0xebc, +#endif +#if PACKETVER >= 20191224 +/*20191224 to latest +서번트 웨폰 %d개가 필요합니다. +*/ + MSG_ID_EBD = 0xebd, +/*20191224 to latest +https://member.gnjoy.com.tw/billing.aspx +*/ + MSG_ID_EBE = 0xebe, +#endif +#if PACKETVER >= 20200115 +/*20200115 to latest +역순 정렬 +*/ + MSG_ID_EBF = 0xebf, +/*20200115 to latest +검색 내용 입력 +*/ + MSG_ID_EC0 = 0xec0, +/*20200115 to latest +검색 +Search +*/ + MSG_ID_EC1 = 0xec1, +/*20200115 to latest +리서치 리포트 상태가 됩니다. +*/ + MSG_ID_EC2 = 0xec2, +/*20200115 to latest +리서치 리포트 상태가 해제됩니다. +*/ + MSG_ID_EC3 = 0xec3, +/*20200115 to latest +제조에 성공 했습니다. +*/ + MSG_ID_EC4 = 0xec4, +/*20200115 to latest +제조에 실패 했습니다. +*/ + MSG_ID_EC5 = 0xec5, +/*20200115 to latest +쉐도우 장비가 파괴 및 해제에서 보호됩니다. +*/ + MSG_ID_EC6 = 0xec6, +/*20200115 to latest +풀 쉐도우 프로텍션이 해제됩니다. +*/ + MSG_ID_EC7 = 0xec7, +/*20200115 to latest +식물형, 무형 몬스터에게 주는 데미지가 증가합니다. +*/ + MSG_ID_EC8 = 0xec8, +/*20200115 to latest +지옥 나무의 가루효과가 사라집니다. +*/ + MSG_ID_EC9 = 0xec9, +#endif +#if PACKETVER >= 20200129 +/*20200129 to latest +공격 장치가 활성화되었습니다. +*/ + MSG_ID_ECA = 0xeca, +/*20200129 to latest +공격 장치가 해제되었습니다. +*/ + MSG_ID_ECB = 0xecb, +/*20200129 to latest +물리 방어력 및 물리 저항력이 증가되었습니다. +*/ + MSG_ID_ECC = 0xecc, +/*20200129 to latest +방어 장치가 해제되었습니다. +*/ + MSG_ID_ECD = 0xecd, +#endif +#if PACKETVER >= 20200212 +/*20200212 to latest +검색 +Search +*/ + MSG_ID_ECE = 0xece, +/*20200212 to latest +합주를 혼자 사용할 수 있습니다. +*/ + MSG_ID_ECF = 0xecf, +/*20200212 to latest +크바시르의 지혜가 사라집니다. +*/ + MSG_ID_ED0 = 0xed0, +/*20200212 to latest +미스틱 심포니의 효과가 부여됩니다. +*/ + MSG_ID_ED1 = 0xed1, +/*20200212 to latest +미스틱 심포니의 효과가 사라집니다. +*/ + MSG_ID_ED2 = 0xed2, +/*20200212 to latest +마법 저항력이 감소했습니다. +*/ + MSG_ID_ED3 = 0xed3, +/*20200212 to latest +게페니아 녹턴의 효과가 해제 되었습니다. +*/ + MSG_ID_ED4 = 0xed4, +/*20200212 to latest +물리 저항력이 감소했습니다. +*/ + MSG_ID_ED5 = 0xed5, +/*20200212 to latest +마인워커 랩소디 상태가 해제되었습니다. +*/ + MSG_ID_ED6 = 0xed6, +/*20200212 to latest +물리 저항력이 증가했습니다. +*/ + MSG_ID_ED7 = 0xed7, +/*20200212 to latest +뮤지컬 인터루드 상태가 해제되었습니다. +*/ + MSG_ID_ED8 = 0xed8, +/*20200212 to latest +특성 마법 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_ED9 = 0xed9, +/*20200212 to latest +저녁 노을의 세레나데 효과가 해제되었습니다. +*/ + MSG_ID_EDA = 0xeda, +/*20200212 to latest +특성 물리 공격력과 이동 속도가 증가합니다. +*/ + MSG_ID_EDB = 0xedb, +/*20200212 to latest + 프론테라의 행진곡 효과가 해제되었습니다. +*/ + MSG_ID_EDC = 0xedc, +/*20200212 to latest +바람의 분노가 시전자에게 흘러 들어옵니다. +*/ + MSG_ID_EDD = 0xedd, +/*20200212 to latest +캘러미티 가일 상태가 해제되었습니다. +*/ + MSG_ID_EDE = 0xede, +/*20200212 to latest +바람에 의해 약점과 모습이 드러납니다. +*/ + MSG_ID_EDF = 0xedf, +/*20200212 to latest +윈드 사인 효과가 사라집니다. +*/ + MSG_ID_EE0 = 0xee0, +#endif +#if PACKETVER >= 20200304 +/*20200304 to latest +E X P : %.1f%% ( basic 100.0%% %s %.1f%%) +EXP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE1 = 0xee1, +/*20200304 to latest +DROP : %.1f%% ( basic 100.0%% %s %.1f%%) +DROP: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE2 = 0xee2, +/*20200304 to latest +DEATH : %.1f%% ( basic 100.0%% %s %.1f%%) +DEATH: %.1f%% (basic: 100.0%%, %s: %.1f%%) +*/ + MSG_ID_EE3 = 0xee3, +#endif }; #endif /* MAP_MESSAGES_ZERO_H */ diff --git a/src/map/mob.c b/src/map/mob.c index 765acafe7..dcbdccedd 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,6 +78,7 @@ struct mob_interface *mob; // Move probability for mobs away from players (rate of 1000 minute) // in Aegis, this is 100% for mobs that have been activated by players and none otherwise. #define MOB_LAZYMOVEPERC(md) ((md)->state.spotted?1000:0) +#define MOB_MAX_CASTTIME (10 * 60 * 1000) // Maximum cast time for monster skills. (10 minutes) #define MOB_MAX_DELAY (24*3600*1000) #define MAX_MINCHASE 30 //Max minimum chase value to use for mobs. #define RUDE_ATTACKED_COUNT 2 //After how many rude-attacks should the skill be used? @@ -239,6 +240,14 @@ static void mvptomb_destroy(struct mob_data *md) m = nd->bl.m; + struct s_mapiterator *iter = mapit_geteachpc(); + for (struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { + if (sd->npc_id == nd->bl.id) { + sd->state.npc_unloaded = 1; + } + } + mapit->free(iter); + clif->clearunit_area(&nd->bl,CLR_OUTSIGHT); map->delblock(&nd->bl); @@ -329,15 +338,23 @@ static int mob_parse_dataset(struct spawn_data *data) return 1; } -/*========================================== - * Generates the basic mob data using the spawn_data provided. - *------------------------------------------*/ -static struct mob_data *mob_spawn_dataset(struct spawn_data *data) + +/** + * Generates basic mob data by using the passed spawn data. + * + * @param data The mobs spawn data. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The generated mob data, later used to spawn the mob. + * + **/ +static struct mob_data *mob_spawn_dataset(struct spawn_data *data, int npc_id) { - struct mob_data *md = NULL; nullpo_retr(NULL, data); - CREATE(md, struct mob_data, 1); - md->bl.id= npc->get_new_npc_id(); + + struct mob_data *md = (struct mob_data *)aCalloc(1, sizeof(struct mob_data)); + + memcpy(md->name, data->name, NAME_LENGTH); + md->bl.id = npc->get_new_npc_id(); md->bl.type = BL_MOB; md->bl.m = data->m; md->bl.x = data->x; @@ -345,24 +362,29 @@ static struct mob_data *mob_spawn_dataset(struct spawn_data *data) md->class_ = data->class_; md->state.boss = data->state.boss; md->db = mob->db(md->class_); + md->npc_id = npc_id; + md->spawn_timer = INVALID_TIMER; + md->deletetimer = INVALID_TIMER; + md->skill_idx = -1; + if (data->level > 0 && data->level <= MAX_LEVEL) md->level = data->level; - memcpy(md->name, data->name, NAME_LENGTH); - if (data->state.ai) + + if (data->state.ai > 0) md->special_state.ai = data->state.ai; - if (data->state.size) + + if (data->state.size > 0) md->special_state.size = data->state.size; - if (data->eventname[0] && strlen(data->eventname) >= 4) + + if (data->eventname[0] != '\0' && strlen(data->eventname) >= 4) memcpy(md->npc_event, data->eventname, 50); - if(md->db->status.mode&MD_LOOTER) - md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); - md->spawn_timer = INVALID_TIMER; - md->deletetimer = INVALID_TIMER; - md->skill_idx = -1; + + if ((md->db->status.mode & MD_LOOTER) == MD_LOOTER) + md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE, sizeof(struct item)); + status->set_viewdata(&md->bl, md->class_); status->change_init(&md->bl); unit->dataset(&md->bl); - map->addiddb(&md->bl); return md; } @@ -425,6 +447,9 @@ static bool mob_ksprotected(struct block_list *src, struct block_list *target) if( !battle_config.ksprotection ) return false; // KS Protection Disabled + if (status->isdead(target) != 0) + return false; // Target is dead. + if( !(md = BL_CAST(BL_MOB,target)) ) return false; // Target is not MOB @@ -471,7 +496,7 @@ static bool mob_ksprotected(struct block_list *src, struct block_list *target) // Message to KS if( DIFF_TICK(sd->ks_floodprotect_tick, tick) <= 0 ) { - sprintf(output, "[KS Warning!! - Owner : %s]", pl_sd->status.name); + sprintf(output, msg_sd(sd, 890), pl_sd->status.name); // [KS Warning!! - Owner : %s] clif_disp_onlyself(sd, output); sd->ks_floodprotect_tick = tick + 2000; @@ -480,7 +505,7 @@ static bool mob_ksprotected(struct block_list *src, struct block_list *target) // Message to Owner if( DIFF_TICK(pl_sd->ks_floodprotect_tick, tick) <= 0 ) { - sprintf(output, "[Watch out! %s is trying to KS you!]", sd->status.name); + sprintf(output, msg_sd(pl_sd, 891), sd->status.name); // [Watch out! %s is trying to KS you!] clif_disp_onlyself(pl_sd, output); pl_sd->ks_floodprotect_tick = tick + 2000; @@ -495,7 +520,24 @@ static bool mob_ksprotected(struct block_list *src, struct block_list *target) return false; } -static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai) +/** + * Prepares a mob's spawn data. + * + * @param bl The invoking character's block list. + * @param m The ID of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's ID in database. + * @param event The name of the event which should be executed when the mob is killed. + * @param size The mob's size. + * @param ai The mob's AI. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The mob data generated by mob->spawn_dataset(). + * + **/ +static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, + const char *event, unsigned int size, unsigned int ai, int npc_id) { struct spawn_data data; @@ -513,76 +555,100 @@ static struct mob_data *mob_once_spawn_sub(struct block_list *bl, int16 m, int16 else strcpy(data.name, DEFAULT_MOB_JNAME); - if (event) + if (event != NULL) safestrncpy(data.eventname, event, sizeof(data.eventname)); - // Locate spot next to player. - if (bl && (x < 0 || y < 0)) + /** Locate spot next to player. **/ + if (bl != NULL && (x < 0 || y < 0)) map->search_freecell(bl, m, &x, &y, 1, 1, 0); - // if none found, pick random position on map + /** If none found, pick random position on map. **/ if (x <= 0 || x >= map->list[m].xs || y <= 0 || y >= map->list[m].ys) map->search_freecell(NULL, m, &x, &y, -1, -1, 1); data.x = x; data.y = y; - if (!mob->parse_dataset(&data)) + if (mob->parse_dataset(&data) == 0) return NULL; - return mob->spawn_dataset(&data); + return mob->spawn_dataset(&data, npc_id); } -/*========================================== - * Spawn a single mob on the specified coordinates. - *------------------------------------------*/ -static int mob_once_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai) +/** + * Spawns a given amount of mobs. + * + * @param sd The invoking character. + * @param m The ID of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's ID in database. + * @param amount The amount of mobs to spawn. + * @param event The name of the event which should be executed when the mob is killed. + * @param size The mob's size. + * @param ai The mob's AI. + * @return The last spawned mob's GID, or 0 if spawning failed. + * + **/ +static int mob_once_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, + int amount, const char *event, unsigned int size, unsigned int ai) { - struct mob_data* md = NULL; - int count, lv; bool no_guardian_data = false; - if( ai && ai&0x200 ) { + if (ai > 0 && (ai & 0x200) == 0x200) { no_guardian_data = true; - ai &=~ 0x200; + ai &= ~0x200; } if (m < 0 || amount <= 0) - return 0; // invalid input + return 0; - lv = (sd) ? sd->status.base_level : 255; + struct mob_data *md = NULL; - for (count = 0; count < amount; count++) { - int c = (class_ >= 0) ? class_ : mob->get_random_id(-class_ - 1, (battle_config.random_monster_checklv) ? 3 : 1, lv); - md = mob->once_spawn_sub((sd) ? &sd->bl : NULL, m, x, y, mobname, c, event, size, ai); + for (int i = 0; i < amount; i++) { + int mob_id = class_; - if (!md) + if (mob_id < 0) { + mob_id = mob->get_random_id(-class_ - 1, (battle_config.random_monster_checklv == 1) ? 3 : 1, + (sd != NULL) ? sd->status.base_level : 255); + } + + md = mob->once_spawn_sub((sd != NULL) ? &sd->bl : NULL, m, x, y, mobname, mob_id, event, size, ai, + (sd != NULL) ? sd->npc_id : 0); + + if (md == NULL) continue; if (class_ == MOBID_EMPELIUM && !no_guardian_data) { - struct guild_castle* gc = guild->mapindex2gc(map_id2index(m)); - struct guild* g = (gc) ? guild->search(gc->guild_id) : NULL; - if( gc ) { + struct guild_castle *gc = guild->mapindex2gc(map_id2index(m)); + + if (gc != NULL) { + struct guild *g = guild->search(gc->guild_id); + md->guardian_data = (struct guardian_data*)aCalloc(1, sizeof(struct guardian_data)); md->guardian_data->castle = gc; md->guardian_data->number = MAX_GUARDIANS; - if( g ) + + if (g != NULL) md->guardian_data->g = g; - else if( gc->guild_id ) //Guild not yet available, retry in 5. - timer->add(timer->gettick()+5000,mob->spawn_guardian_sub,md->bl.id,gc->guild_id); + else if (gc->guild_id > 0) /// Guild not yet available, retry in 5s. + timer->add(timer->gettick() + 5000, mob->spawn_guardian_sub, md->bl.id, + gc->guild_id); } - } // end addition [Valaris] + } mob->spawn(md); - if (class_ < 0 && battle_config.dead_branch_active) { - //Behold Aegis's masterful decisions yet again... - //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3 - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE|MD_ANGRY, 0, 60000); + if (class_ < 0 && battle_config.dead_branch_active == 1) { + /// Behold Aegis' masterful decisions yet again... + /// "I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" [Poki] + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE|MD_ANGRY, + 0, 60000); } } - return (md) ? md->bl.id : 0; // id of last spawned mob + return (md != NULL) ? md->bl.id : 0; /// ID of last spawned mob. } /*========================================== @@ -641,196 +707,236 @@ static int mob_once_spawn_area(struct map_session_data *sd, int16 m, int16 x0, i } /** - * Sets a guardian's guild data and liberates castle if couldn't retrieve guild data - * @param data (int)guild_id - * @retval Always 0 + * Sets a guardian's guild data and liberates castle if couldn't retrieve guild data. + * Required because the guild data may not be available at guardian spawn time. + * + * @param tid Required parameter for timer functions. Unused inside the function. + * @param tick Required parameter for timer functions. Unused inside the function. + * @param id The guardian mob's GID. + * @param data The guild ID. + * @return 1 on success, 0 on failure. + * * @author Skotlex + * **/ static int mob_spawn_guardian_sub(int tid, int64 tick, int id, intptr_t data) { - //Needed because the guild data may not be available at guardian spawn time. - struct block_list* bl = map->id2bl(id); - struct mob_data* md; - struct guild* g; + struct block_list *bl = map->id2bl(id); - if( bl == NULL ) //It is possible mob was already removed from map when the castle has no owner. [Skotlex] + if (bl == NULL || bl->type != BL_MOB) /// It is possible mob was already removed from map when the castle has no owner. [Skotlex] return 0; - Assert_ret(bl->type == BL_MOB); - md = BL_UCAST(BL_MOB, bl); + struct mob_data *md = BL_UCAST(BL_MOB, bl); - nullpo_ret(md->guardian_data); - g = guild->search((int)data); + if (md->guardian_data == NULL) + return 0; + + struct guild *g = guild->search((int)data); - if( g == NULL ) { //Liberate castle, if the guild is not found this is an error! [Skotlex] + if (g == NULL) { /// Liberate castle, if the guild is not found this is an error! [Skotlex] ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n", (int)data); - //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. - if (md->class_ == MOBID_EMPELIUM && md->guardian_data) { + + /// Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. + if (md->class_ == MOBID_EMPELIUM) { md->guardian_data->g = NULL; - if( md->guardian_data->castle->guild_id ) {//Free castle up. - ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name); + + if (md->guardian_data->castle->guild_id > 0) { /// Free castle up. + ShowNotice("mob_spawn_guardian_sub: Clearing ownership of castle %d (%s).\n", + md->guardian_data->castle->castle_id, + md->guardian_data->castle->castle_name); guild->castledatasave(md->guardian_data->castle->castle_id, 1, 0); } } else { - if( md->guardian_data && md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS - && md->guardian_data->castle->guardian[md->guardian_data->number].visible ) - guild->castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + if (md->guardian_data->number >= 0 && md->guardian_data->number < MAX_GUARDIANS && + md->guardian_data->castle->guardian[md->guardian_data->number].visible == 1) + guild->castledatasave(md->guardian_data->castle->castle_id, + 10 + md->guardian_data->number, 0); - unit->free(&md->bl,CLR_OUTSIGHT); // Remove guardian. + unit->free(&md->bl, CLR_OUTSIGHT); /// Remove guardian. } + return 0; } - if( guild->checkskill(g,GD_GUARDUP) ) - status_calc_mob(md, SCO_NONE); // Give bonuses. + if (guild->checkskill(g, GD_GUARDUP) > 0) + status_calc_mob(md, SCO_NONE); /// Give bonuses. - return 0; + return 1; } -/*========================================== - * Summoning Guardians [Valaris] - *------------------------------------------*/ -static int mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index) +/** + * Summons a castle guardian mob. + * + * @param mapname The name of the map where the guardian should be spawned. + * @param x The x coordinate where the guardian should be spawned. + * @param y The y coordinate where the guardian should be spawned. + * @param mobname The guardian's display name. + * @param class_ The guardian's mob ID in database. + * @param event The name of the event which should be executed when the guardian is killed. + * @param guardian The guardian's index. + * @param has_index If false, the guardian will be temporarily. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The spawned guardian's GID, or 0 if spawning failed. + * + * @author Valaris + * + **/ +static int mob_spawn_guardian(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, + int guardian, bool has_index, int npc_id) { - struct mob_data *md=NULL; - struct spawn_data data; - struct guild *g=NULL; - struct guild_castle *gc; - int16 m; - nullpo_ret(mapname); nullpo_ret(mobname); nullpo_ret(event); - memset(&data, 0, sizeof(struct spawn_data)); - data.num = 1; - - m=map->mapname2mapid(mapname); + const int map_id = map->mapname2mapid(mapname); - if(m<0) - { + if (map_id == INDEX_NOT_FOUND) { ShowWarning("mob_spawn_guardian: Map [%s] not found.\n", mapname); return 0; } - data.m = m; - data.num = 1; - if(class_<=0) { - class_ = mob->get_random_id(-class_-1, 1, 99); - if (!class_) return 0; + + if ((x <= 0 || y <= 0) && map->search_freecell(NULL, map_id, &x, &y, -1, -1, 1) == 0) { + ShowWarning("mob_spawn_guardian: Couldn't locate a spawn cell for guardian class %d (index %d) on castle map %s.\n", + class_, guardian, mapname); + return 0; } - data.class_ = class_; + if (class_ <= 0 && (class_ = mob->get_random_id(-class_ - 1, 1, 99)) == 0) + return 0; - if( !has_index ) { + if (!has_index) { guardian = -1; - } else if( guardian < 0 || guardian >= MAX_GUARDIANS ) { - ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map->list[m].name); + } else if (guardian < 0 || guardian >= MAX_GUARDIANS) { + ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d on castle map %s.\n", + guardian, class_, mapname); return 0; } - if((x<=0 || y<=0) && !map->search_freecell(NULL, m, &x, &y, -1,-1, 1)) { - ShowWarning("mob_spawn_guardian: Couldn't locate a spawn cell for guardian class %d (index %d) at castle map %s\n",class_, guardian, map->list[m].name); - return 0; - } + struct spawn_data data; + + memset(&data, 0, sizeof(struct spawn_data)); + data.num = 1; + data.m = map_id; + data.class_ = class_; data.x = x; data.y = y; safestrncpy(data.name, mobname, sizeof(data.name)); safestrncpy(data.eventname, event, sizeof(data.eventname)); - if (!mob->parse_dataset(&data)) + + if (mob->parse_dataset(&data) == 0) return 0; - gc=guild->mapname2gc(map->list[m].name); + struct guild_castle *gc = guild->mapname2gc(mapname); + if (gc == NULL) { - ShowError("mob_spawn_guardian: No castle set at map %s\n", map->list[m].name); + ShowError("mob_spawn_guardian: No castle set on map %s.\n", mapname); return 0; } - if (!gc->guild_id) - ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map->list[m].name); + + struct guild *g = NULL; + + if (gc->guild_id == 0) + ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle map %s with no guild.\n", + class_, mapname); else g = guild->search(gc->guild_id); - if( has_index && gc->guardian[guardian].id ) { - //Check if guardian already exists, refuse to spawn if so. - struct block_list *bl2 = map->id2bl(gc->guardian[guardian].id); // TODO: Why does this not use map->id2md? - struct mob_data *md2 = BL_CAST(BL_MOB, bl2); - if (md2 != NULL && md2->guardian_data != NULL && md2->guardian_data->number == guardian) { - ShowError("mob_spawn_guardian: Attempted to spawn guardian in position %d which already has a guardian (castle map %s)\n", guardian, map->list[m].name); + if (has_index && gc->guardian[guardian].id != 0) { /// Check if guardian already exists, refuse to spawn if so. + struct mob_data *md = map->id2md(gc->guardian[guardian].id); + + if (md != NULL && md->guardian_data != NULL && md->guardian_data->number == guardian) { + ShowError("mob_spawn_guardian: Attempted to spawn guardian in position %d which already has a guardian on castle map %s.\n", + guardian, mapname); return 0; } } - md = mob->spawn_dataset(&data); + struct mob_data *md = mob->spawn_dataset(&data, npc_id); + md->guardian_data = (struct guardian_data*)aCalloc(1, sizeof(struct guardian_data)); md->guardian_data->number = guardian; md->guardian_data->castle = gc; - if( has_index ) - {// permanent guardian + + if (has_index) { /// Permanent guardian. gc->guardian[guardian].id = md->bl.id; - } - else - {// temporary guardian + } else { /// Temporary guardian. int i; + ARR_FIND(0, gc->temp_guardians_max, i, gc->temp_guardians[i] == 0); - if( i == gc->temp_guardians_max ) - { + + if (i == gc->temp_guardians_max) { ++(gc->temp_guardians_max); RECREATE(gc->temp_guardians, int, gc->temp_guardians_max); } + gc->temp_guardians[i] = md->bl.id; } - if( g ) + + if (g != NULL) md->guardian_data->g = g; - else if( gc->guild_id ) - timer->add(timer->gettick()+5000,mob->spawn_guardian_sub,md->bl.id,gc->guild_id); - mob->spawn(md); + else if (gc->guild_id > 0) + timer->add(timer->gettick() + 5000, mob->spawn_guardian_sub, md->bl.id, gc->guild_id); + mob->spawn(md); return md->bl.id; } -/*========================================== - * Summoning BattleGround [Zephyrus] - *------------------------------------------*/ -static int mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id) +/** + * Spawn a mob with allegiance to the given battle group. + * + * @param mapname The name of the map where the mob should be spawned. + * @param x The x coordinate where the mob should be spawned. + * @param y The y coordinate where the mob should be spawned. + * @param mobname The mob's display name. + * @param class_ The mob's mob ID in database. + * @param event The name of the event which should be executed when the mob is killed. + * @param bg_id The battle group ID. + * @param npc_id If spawned by NPC script, this holds the ID of the invoking NPC. + * @return The spawned mob's GID, or 0 if spawning failed. + * + * @author Zephyrus + * + **/ +static int mob_spawn_bg(const char *mapname, short x, short y, const char *mobname, int class_, const char *event, + unsigned int bg_id, int npc_id) { - struct mob_data *md = NULL; - struct spawn_data data; - int16 m; - nullpo_ret(mapname); nullpo_ret(mobname); nullpo_ret(event); - if( (m = map->mapname2mapid(mapname)) < 0 ) { + const int map_id = map->mapname2mapid(mapname); + + if (map_id == INDEX_NOT_FOUND) { ShowWarning("mob_spawn_bg: Map [%s] not found.\n", mapname); return 0; } - memset(&data, 0, sizeof(struct spawn_data)); - data.m = m; - data.num = 1; - if( class_ <= 0 ) - { - class_ = mob->get_random_id(-class_-1,1,99); - if( !class_ ) return 0; + if ((x <= 0 || y <= 0) && map->search_freecell(NULL, map_id, &x, &y, -1, -1, 1) == 0) { + ShowWarning("mob_spawn_bg: Couldn't locate a spawn cell for guardian class %d (bg_id %u) on map %s.\n", class_, bg_id, mapname); + return 0; } - data.class_ = class_; - if( (x <= 0 || y <= 0) && !map->search_freecell(NULL, m, &x, &y, -1,-1, 1) ) { - ShowWarning("mob_spawn_bg: Couldn't locate a spawn cell for guardian class %d (bg_id %u) at map %s\n", class_, bg_id, map->list[m].name); + if (class_ <= 0 && (class_ = mob->get_random_id(-class_ - 1, 1, 99)) == 0) return 0; - } + struct spawn_data data; + + memset(&data, 0, sizeof(struct spawn_data)); + data.num = 1; + data.m = map_id; + data.class_ = class_; data.x = x; data.y = y; safestrncpy(data.name, mobname, sizeof(data.name)); safestrncpy(data.eventname, event, sizeof(data.eventname)); - if( !mob->parse_dataset(&data) ) + + if (mob->parse_dataset(&data) == 0) return 0; - md = mob->spawn_dataset(&data); - mob->spawn(md); - md->bg_id = bg_id; // BG Team ID + struct mob_data *md = mob->spawn_dataset(&data, npc_id); + mob->spawn(md); + md->bg_id = bg_id; return md->bl.id; } @@ -1165,13 +1271,15 @@ static int mob_ai_sub_hard_activesearch(struct block_list *bl, va_list ap) battle->check_range(&md->bl,bl,md->db->range2) ) { //Pick closest target? #ifdef ACTIVEPATHSEARCH - struct walkpath_data wpd; - if (!path->search(&wpd, &md->bl, md->bl.m, md->bl.x, md->bl.y, bl->x, bl->y, 0, CELL_CHKNOPASS)) // Count walk path cells - return 0; - //Standing monsters use range2, walking monsters use range3 - if ((md->ud.walktimer == INVALID_TIMER && wpd.path_len > md->db->range2) - || (md->ud.walktimer != INVALID_TIMER && wpd.path_len > md->db->range3)) - return 0; + struct walkpath_data wpd; + bool is_standing = (md->ud.walktimer == INVALID_TIMER); + if (!path->search(&wpd, &md->bl, md->bl.m, md->bl.x, md->bl.y, bl->x, bl->y, 0, CELL_CHKNOPASS) // Count walk path cells + || (is_standing && wpd.path_len > md->db->range2) //Standing monsters use range2, walking monsters use range3 + || (!is_standing && wpd.path_len > md->db->range3)) { + if (!check_distance_bl(&md->bl, bl, md->status.rhw.range) + || !path->search_long(NULL, &md->bl, md->bl.m, md->bl.x, md->bl.y, bl->x, bl->y, CELL_CHKWALL)) + return 0; + } #endif (*target) = bl; md->target_id=bl->id; @@ -1289,6 +1397,27 @@ static int mob_warpchase_sub(struct block_list *bl, va_list ap) } return 0; } + +/** + * Checks if a monster is currently involved in battle, + * may it be due to aggression or being attacked. + * @param bl: monster's bl + * @return true if in battle, false otherwise + */ +static bool mob_is_in_battle_state(const struct mob_data *md) +{ + nullpo_retr(false, md); + switch (md->state.skillstate) { + case MSS_BERSERK: + case MSS_ANGRY: + case MSS_RUSH: + case MSS_FOLLOW: + return true; + default: + return false; + } +} + /*========================================== * Processing of slave monsters *------------------------------------------*/ @@ -1333,8 +1462,11 @@ static int mob_ai_sub_hard_slavemob(struct mob_data *md, int64 tick) ) { short x = bl->x, y = bl->y; mob_stop_attack(md); - if(map->search_freecell(&md->bl, bl->m, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 1) - && unit->walktoxy(&md->bl, x, y, 0)) + const struct mob_data *m_md = BL_CCAST(BL_MOB, bl); // Can be NULL due to master being BL_PC + // If master is BL_MOB and in battle, lock & chase to master's target instead, unless configured not to. + if ((battle_config.slave_chase_masters_chasetarget == 0 || (m_md != NULL && !mob->is_in_battle_state(m_md))) + && map->search_freecell(&md->bl, bl->m, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 1) + && unit->walk_toxy(&md->bl, x, y, 0) == 0) return 1; } } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) { @@ -1345,26 +1477,28 @@ static int mob_ai_sub_hard_slavemob(struct mob_data *md, int64 tick) //Avoid attempting to lock the master's target too often to avoid unnecessary overload. [Skotlex] if (DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME && !md->target_id) { - struct unit_data *ud = unit->bl2ud(bl); + struct unit_data *ud = unit->bl2ud(bl); + struct mob_data *m_md = BL_CAST(BL_MOB, bl); // Can be NULL due to master being BL_PC + nullpo_retr(0, ud); md->last_linktime = tick; - - if (ud) { - struct block_list *tbl=NULL; - if (ud->target && ud->state.attack_continue) - tbl=map->id2bl(ud->target); - else if (ud->skilltarget) { - tbl = map->id2bl(ud->skilltarget); - //Required check as skilltarget is not always an enemy. [Skotlex] - if (tbl && battle->check_target(&md->bl, tbl, BCT_ENEMY) <= 0) - tbl = NULL; - } - if (tbl && status->check_skilluse(&md->bl, tbl, 0, 0)) { - md->target_id=tbl->id; - md->min_chase=md->db->range3+distance_bl(&md->bl, tbl); - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - return 1; - } + struct block_list *tbl = NULL; + + if (battle_config.slave_chase_masters_chasetarget == 1 && m_md != NULL && m_md->target_id != 0) { // possibly chasing something + tbl = map->id2bl(m_md->target_id); + } else if (ud->target != 0 && ud->state.attack_continue != 0) { + tbl = map->id2bl(ud->target); + } else if (ud->skilltarget != 0) { + tbl = map->id2bl(ud->skilltarget); + //Required check as skilltarget is not always an enemy. [Skotlex] + if (tbl != NULL && battle->check_target(&md->bl, tbl, BCT_ENEMY) <= 0) + tbl = NULL; + } + if (tbl != NULL && status->check_skilluse(&md->bl, tbl, 0, 0) != 0) { + md->target_id = tbl->id; + md->min_chase = md->db->range3 + distance_bl(&md->bl, tbl); + if (md->min_chase > MAX_MINCHASE) + md->min_chase = MAX_MINCHASE; + return 1; } } return 0; @@ -1389,7 +1523,7 @@ static int mob_unlocktarget(struct mob_data *md, int64 tick) FALLTHROUGH case MSS_IDLE: // Idle skill. - if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mob->skill_use(md, tick, -1)) + if ((++md->ud.walk_count % IDLE_SKILL_INTERVAL) == 0 && mob->skill_use(md, tick, -1) == 0) break; //Random walk. if (!md->master_id && @@ -1414,7 +1548,7 @@ static int mob_unlocktarget(struct mob_data *md, int64 tick) unit->set_target(&md->ud, 0); } if(battle_config.official_cell_stack_limit && map->count_oncell(md->bl.m, md->bl.x, md->bl.y, BL_CHAR|BL_NPC, 0x1 | 0x2) > battle_config.official_cell_stack_limit) { - unit->walktoxy(&md->bl, md->bl.x, md->bl.y, 8); + unit->walk_toxy(&md->bl, md->bl.x, md->bl.y, 8); } return 0; @@ -1446,9 +1580,9 @@ static int mob_randomwalk(struct mob_data *md, int64 tick) x+=md->bl.x; y+=md->bl.y; - if (((x != md->bl.x) || (y != md->bl.y)) && map->getcell(md->bl.m, &md->bl, x, y, CELL_CHKPASS) && unit->walktoxy(&md->bl, x, y, 8)) { + if ((x != md->bl.x || y != md->bl.y) && map->getcell(md->bl.m, &md->bl, x, y, CELL_CHKPASS) != 0 + && unit->walk_toxy(&md->bl, x, y, 8) == 0) break; - } } if(i==retrycount){ md->move_fail_count++; @@ -1569,7 +1703,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, int64 tick) || !mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) ) && md->state.attacked_count++ >= RUDE_ATTACKED_COUNT - && !mob->skill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack + && mob->skill_use(md, tick, MSC_RUDEATTACKED) == 0 // If can't rude Attack && can_move && unit->escape(&md->bl, tbl, rnd()%10 +1) // Attempt escape ) { //Escaped @@ -1597,7 +1731,7 @@ static bool mob_ai_sub_hard(struct mob_data *md, int64 tick) ) { // Rude attacked if (md->state.attacked_count++ >= RUDE_ATTACKED_COUNT - && !mob->skill_use(md, tick, MSC_RUDEATTACKED) && can_move + && mob->skill_use(md, tick, MSC_RUDEATTACKED) == 0 && can_move != 0 && !tbl && unit->escape(&md->bl, abl, rnd()%10 +1) ) { //Escaped. @@ -1900,15 +2034,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; @@ -2162,6 +2334,14 @@ static void mob_log_damage(struct mob_data *md, struct block_list *src, int dama md->dmglog[minpos].flag= flag; md->dmglog[minpos].dmg = damage; } +#if (PACKETVER >= 20120404 && PACKETVER < 20131223) + // Show HP bar to all chars who hit the mob (fixes TF_STEAL not showing HP bar right away but only when target leaves/re-enters sight range) + if (battle_config.show_monster_hp_bar != 0 && (md->status.mode & MD_BOSS) == 0) { + struct map_session_data *sd = map->charid2sd(char_id); + if (sd != NULL && check_distance_bl(&md->bl, &sd->bl, AREA_SIZE)) // check if in range + clif->monster_hp_bar(md, sd); + } +#endif } return; } @@ -2196,7 +2376,7 @@ static void mob_damage(struct mob_data *md, struct block_list *src, int damage) } if (battle_config.show_mob_info&3) - clif->charnameack (0, &md->bl); + clif->blname_ack(0, &md->bl); #if PACKETVER >= 20131223 // Resend ZC_NOTIFY_MOVEENTRY to Update the HP @@ -2521,7 +2701,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) { @@ -2532,13 +2712,13 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) // Announce first, or else ditem will be freed. [Lance] // By popular demand, use base drop rate for autoloot code. [Skotlex] - mob->item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p, homkillonly); + mob->item_drop(md, dlist, ditem, 0, battle_config.autoloot_adjust ? drop_rate : md->db->dropitem[i].p, homkillonly); } // 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); } } @@ -2546,7 +2726,7 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) if(sd) { // process script-granted extra drop bonuses int itemid = 0; - for (i = 0; i < ARRAYLENGTH(sd->add_drop) && (sd->add_drop[i].id || sd->add_drop[i].group); i++) + for (i = 0; i < ARRAYLENGTH(sd->add_drop) && (sd->add_drop[i].id != 0 || sd->add_drop[i].is_group); i++) { if ( sd->add_drop[i].race == -md->class_ || ( sd->add_drop[i].race > 0 && ( @@ -2568,9 +2748,9 @@ static int mob_dead(struct mob_data *md, struct block_list *src, int type) if (rnd()%10000 >= drop_rate) continue; - itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id : itemdb->chain_item(sd->add_drop[i].group,&drop_rate); + 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 +2809,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 +2821,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 +2841,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; @@ -2796,7 +2976,7 @@ static void mob_revive(struct mob_data *md, unsigned int hp) skill->unit_move(&md->bl,tick,1); mob->skill_use(md, tick, MSC_SPAWN); if (battle_config.show_mob_info&3) - clif->charnameack (0, &md->bl); + clif->blname_ack(0, &md->bl); } static int mob_guardian_guildchange(struct mob_data *md) @@ -2921,7 +3101,7 @@ static int mob_class_change(struct mob_data *md, int class_) md->target_id = md->attacked_id = 0; //Need to update name display. - clif->charnameack(0, &md->bl); + clif->blname_ack(0, &md->bl); status_change_end(&md->bl,SC_KEEPING,INVALID_TIMER); return 0; } @@ -2933,7 +3113,7 @@ static void mob_heal(struct mob_data *md, unsigned int heal) { nullpo_retv(md); if (battle_config.show_mob_info&3) - clif->charnameack (0, &md->bl); + clif->blname_ack(0, &md->bl); #if PACKETVER >= 20131223 // Resend ZC_NOTIFY_MOVEENTRY to Update the HP if (battle_config.show_monster_hp_bar) @@ -3018,18 +3198,25 @@ static int mob_countslave(struct block_list *bl) return map->foreachinmap(mob->countslave_sub, bl->m, BL_MOB,bl->id); } -/*========================================== - * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex] - *------------------------------------------*/ +/** + * Summons amount slaves contained in the value[5] array using round-robin. + * + * @param md2 The mob which summons the slaves. + * @param value Array with slave mob IDs. + * @param amount The amount of slaves to spawn. + * @param skill_id The Id of the skill which summons the slaves. + * @return 1 on success, 0 on failure. + * + * @author Skotlex + * + **/ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 skill_id) { - struct mob_data *md; - struct spawn_data data; - int count = 0,k=0,hp_rate=0; - nullpo_ret(md2); nullpo_ret(value); + struct spawn_data data; + memset(&data, 0, sizeof(struct spawn_data)); data.m = md2->bl.m; data.x = md2->bl.x; @@ -3038,31 +3225,42 @@ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 data.state.size = md2->special_state.size; data.state.ai = md2->special_state.ai; - if(mob->db_checkid(value[0]) == 0) + if (mob->db_checkid(value[0]) == 0) return 0; - /** - * Flags this monster is able to summon; saves a worth amount of memory upon deletion - **/ - md2->can_summon = 1; - while(count < 5 && mob->db_checkid(value[count])) count++; - if(count < 1) return 0; - if (amount > 0 && amount < count) { //Do not start on 0, pick some random sub subset [Skotlex] - k = rnd()%count; - amount+=k; //Increase final value by same amount to preserve total number to summon. + md2->can_summon = 1; /// Flags this monster is able to summon; saves a worth amount of memory upon deletion. + + int count = 0; + + while (count < 5 && mob->db_checkid(value[count]) != 0) + count++; + + if (count < 1) + return 0; + + int k = 0; + + if (amount > 0 && amount < count) { /// Do not start on 0, pick some random sub subset. [Skotlex] + k = rnd() % count; + amount += k; /// Increase final value by same amount to preserve total number to summon. } - if (!battle_config.monster_class_change_recover && - (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS)) + int hp_rate = 0; + + if ((skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS) && + battle_config.monster_class_change_recover == 0) hp_rate = get_percentage(md2->status.hp, md2->status.max_hp); - for(;k<amount;k++) { - short x,y; - data.class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex] + for (; k < amount; k++) { + data.class_ = value[k % count]; /// Summon slaves in round-robin fashion. [Skotlex] + if (mob->db_checkid(data.class_) == 0) continue; - if (map->search_freecell(&md2->bl, 0, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 0)) { + short x; + short y; + + if (map->search_freecell(&md2->bl, 0, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 0) != 0) { data.x = x; data.y = y; } else { @@ -3070,49 +3268,52 @@ static int mob_summonslave(struct mob_data *md2, int *value, int amount, uint16 data.y = md2->bl.y; } - //These two need to be loaded from the db for each slave. if (battle_config.override_mob_names == 1) strcpy(data.name, DEFAULT_MOB_NAME); else strcpy(data.name, DEFAULT_MOB_JNAME); - if (!mob->parse_dataset(&data)) + if (mob->parse_dataset(&data) == 0) continue; - md= mob->spawn_dataset(&data); - if(skill_id == NPC_SUMMONSLAVE){ - md->master_id=md2->bl.id; + struct mob_data *md = mob->spawn_dataset(&data, 0); + + if (skill_id == NPC_SUMMONSLAVE) { + md->master_id = md2->bl.id; md->special_state.ai = md2->special_state.ai; } + mob->spawn(md); - if (hp_rate) //Scale HP - md->status.hp = md->status.max_hp*hp_rate/100; + if (hp_rate > 0) /// Scale HP. + md->status.hp = md->status.max_hp * hp_rate / 100; - //Inherit the aggressive mode of the master. - if (battle_config.slaves_inherit_mode && md->master_id) { + /** Inherit the aggressive mode of the master. **/ + if (battle_config.slaves_inherit_mode > 0 && md->master_id > 0) { switch (battle_config.slaves_inherit_mode) { - case 1: //Always aggressive - if (!(md->status.mode&MD_AGGRESSIVE)) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, MD_AGGRESSIVE, 0, 0); + case 1: /// Always aggressive. + if ((md->status.mode & MD_AGGRESSIVE) == 0) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0); + break; - case 2: //Always passive - if (md->status.mode&MD_AGGRESSIVE) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, 0, MD_AGGRESSIVE, 0); + case 2: /// Always passive. + if ((md->status.mode & MD_AGGRESSIVE) == MD_AGGRESSIVE) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0); + break; - default: //Copy master. - if (md2->status.mode&MD_AGGRESSIVE) - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, MD_AGGRESSIVE, 0, 0); + default: /// Copy master. + if ((md2->status.mode & MD_AGGRESSIVE) == MD_AGGRESSIVE) + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0); else - sc_start4(NULL, &md->bl, SC_MODECHANGE, 100,1,0, 0, MD_AGGRESSIVE, 0); + sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0); break; } } - clif->skill_nodamage(&md->bl,&md->bl,skill_id,amount,1); + clif->skill_nodamage(&md->bl, &md->bl, skill_id, amount, 1); } - return 0; + return 1; } /*========================================== @@ -3190,272 +3391,344 @@ static struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md, int rate return NULL; } -/*========================================== - * What a status state suits by nearby MOB is looked for. - *------------------------------------------*/ + +/** + * Checks if the passed monster/character meets the passed status change requirements + * and returns it by reference parameter on success. + * + * @param bl The monster/character to check. + * @param ap List of arguments. (Source monster, MSC_* flag, SC_* flag, reference bl.) + * @return Always 0. + * + **/ static int mob_getfriendstatus_sub(struct block_list *bl, va_list ap) { - int cond1,cond2; - struct mob_data **fr = NULL, *md = NULL, *mmd = NULL; - int flag=0; - nullpo_ret(bl); - Assert_ret(bl->type == BL_MOB); - md = BL_UCAST(BL_MOB, bl); - nullpo_ret(mmd=va_arg(ap,struct mob_data *)); - if( mmd->bl.id == bl->id && !(battle_config.mob_ai&0x10) ) + struct mob_data *md = va_arg(ap, struct mob_data *); + + nullpo_ret(md); + + if (md->bl.id == bl->id && (battle_config.mob_ai & 0x10) == 0) + return 0; + + if (battle->check_target(&md->bl, bl, BCT_ENEMY) > 0) return 0; - if (battle->check_target(&mmd->bl,bl,BCT_ENEMY)>0) + int cond1 = va_arg(ap, int); + int cond2 = va_arg(ap, int); + struct block_list **fr = va_arg(ap, struct block_list **); + + if ((*fr) != NULL) // A friend was already found. return 0; - cond1=va_arg(ap,int); - cond2=va_arg(ap,int); - fr=va_arg(ap,struct mob_data **); - if( cond2==-1 ){ - int j; - for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){ - if ((flag=(md->sc.data[j] != NULL))) //Once an effect was found, break out. [Skotlex] + + int flag = 0; + struct status_change *sc = status->get_sc(bl); + + if (cond2 == -1) { // Check for any of the common status alignments. + for (int i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) { + if ((flag = (sc->data[i] != NULL)) != 0) // Once an effect was found, break out. [Skotlex] break; } - }else - flag=( md->sc.data[cond2] != NULL ); - if( flag^( cond1==MSC_FRIENDSTATUSOFF ) ) - (*fr)=md; + } else { + flag = (sc->data[cond2] != NULL); + } + + if ((flag ^ (cond1 == MSC_FRIENDSTATUSOFF)) != 0) + (*fr) = bl; return 0; } -static struct mob_data *mob_getfriendstatus(struct mob_data *md, int cond1, int cond2) +/** + * Gets a random monster/character within a range of 8 cells around md. + * If md is summoned (no monster slave!) a character is picker, otherwise a monster. + * + * @param md The monster which tries to cast a skill. + * @param cond1 Whether to check for active or inactive status change. (MSC_FRIENDSTATUSON/MSC_FRIENDSTATUSOFF) + * @param cond2 The status change (SC_* flag) to check. + * @return A randomly picked monster/character within range. + * + **/ +static struct block_list *mob_getfriendstatus(struct mob_data *md, int cond1, int cond2) { - struct mob_data* fr = NULL; nullpo_ret(md); - map->foreachinrange(mob->getfriendstatus_sub, &md->bl, 8,BL_MOB, md,cond1,cond2,&fr); + int type = (md->special_state.ai != AI_NONE) ? BL_PC : BL_MOB; + struct block_list *fr = NULL; + + map->foreachinrange(mob->getfriendstatus_sub, &md->bl, 8, type, md, cond1, cond2, &fr); + return fr; } -/*========================================== - * Skill use judging - *------------------------------------------*/ -static int mobskill_use(struct mob_data *md, int64 tick, int event) +/** + * Checks if skill cast condition in fulfilled and executes the skill in case of success. + * + * @param md The monster which tries to cast a skill. + * @param tick The timestamp of skill execution. + * @param event The MSC_* flag which triggered the skill execution. (-1 for non-event skill conditions.) + * @return 0 on success, 1 on failure. + * + **/ +static int mob_skill_use(struct mob_data *md, int64 tick, int event) { - struct mob_skill *ms; - struct block_list *fbl = NULL; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex] - struct block_list *bl; - struct mob_data *fmd = NULL; - int i,j,n; - nullpo_ret(md); - nullpo_ret(ms = md->db->skill); - if (!battle_config.mob_skill_rate || md->ud.skilltimer != INVALID_TIMER || !md->db->maxskill) - return 0; + struct mob_skill *ms = md->db->skill; + + nullpo_ret(ms); + + if (battle_config.mob_skill_rate == 0 || md->ud.skilltimer != INVALID_TIMER || md->db->maxskill == 0) + return 1; if (event == -1 && DIFF_TICK(md->ud.canact_tick, tick) > 0) - return 0; //Skill act delay only affects non-event skills. + return 1; // Skill act delay only affects non-event skill conditions. - //Pick a starting position and loop from that. - i = (battle_config.mob_ai&0x100) ? rnd()%md->db->maxskill : 0; - for (n = 0; n < md->db->maxskill; i++, n++) { - int c2, flag = 0; + // Pick a starting position and loop from that. + int skill_idx = ((battle_config.mob_ai & 0x100) != 0) ? rnd() % md->db->maxskill : 0; - if (i == md->db->maxskill) - i = 0; + for (int i = 0; i < md->db->maxskill; skill_idx++, i++) { + if (skill_idx == md->db->maxskill) + skill_idx = 0; - if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i].delay) + if (DIFF_TICK(tick, md->skilldelay[skill_idx]) < ms[skill_idx].delay) continue; - c2 = ms[i].cond2; + enum MobSkillState state = ms[skill_idx].state; - if (ms[i].state != md->state.skillstate) { - if (md->state.skillstate != MSS_DEAD && (ms[i].state == MSS_ANY || - (ms[i].state == MSS_ANYTARGET && md->target_id && md->state.skillstate != MSS_LOOT) - )) //ANYTARGET works with any state as long as there's a target. [Skotlex] - ; - else + if (state != md->state.skillstate) { + bool state_dead = (md->state.skillstate == MSS_DEAD); + bool any_target = (state == MSS_ANYTARGET && md->target_id != 0 && md->state.skillstate != MSS_LOOT); + + // MSS_ANYTARGET works with any state as long as there's a target. [Skotlex] + if (state_dead || (state != MSS_ANY && !any_target)) continue; } - if (rnd() % 10000 > ms[i].permillage) //Lupus (max value = 10000) + + if (rnd() % 10000 > ms[skill_idx].permillage) continue; - if (ms[i].cond1 == event) - flag = 1; //Trigger skill. - else if (ms[i].cond1 == MSC_SKILLUSED) - flag = ((event & 0xffff) == MSC_SKILLUSED && ((event >> 16) == c2 || c2 == 0)); - else if(event == -1){ - //Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function in battle. - switch (ms[i].cond1) - { - case MSC_ALWAYS: - flag = 1; break; - case MSC_MYHPLTMAXRATE: // HP< maxhp% - flag = get_percentage(md->status.hp, md->status.max_hp); - flag = (flag <= c2); - break; - case MSC_MYHPINRATE: - flag = get_percentage(md->status.hp, md->status.max_hp); - flag = (flag >= c2 && flag <= ms[i].val[0]); - break; - case MSC_MYSTATUSON: // status[num] on - case MSC_MYSTATUSOFF: // status[num] off - if (!md->sc.count) { - flag = 0; - } else if (ms[i].cond2 == -1) { - for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++) - if ((flag = (md->sc.data[j]!=NULL)) != 0) - break; - } else { - flag = (md->sc.data[ms[i].cond2]!=NULL); + int cast_cond = ms[skill_idx].cond1; + int cond_data = ms[skill_idx].cond2; + int flag = 0; + struct block_list *fbl = NULL; // Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex] + + if (cast_cond == event) { + flag = 1; // Trigger skill. + } else if (cast_cond == MSC_SKILLUSED) { + flag = ((event & 0xFFFF) == MSC_SKILLUSED && ((event >> 16) == cond_data || cond_data == 0)); + } else if (event == -1) { + // Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function in battle. + switch (cast_cond) { + case MSC_ALWAYS: + flag = 1; + break; + case MSC_MYHPLTMAXRATE: // HP <= x% + flag = get_percentage(md->status.hp, md->status.max_hp); + flag = (flag <= cond_data); + break; + case MSC_MYHPINRATE: // HP >= x% && HP <= y% + flag = get_percentage(md->status.hp, md->status.max_hp); + flag = (flag >= cond_data && flag <= ms[skill_idx].val[0]); + break; + case MSC_MYSTATUSON: // Status change x is active. + case MSC_MYSTATUSOFF: // Status change x is inactive. + if (cond_data == -1) { // Check for any of the common status alignments. + for (int j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++) { + if ((flag = (md->sc.data[j] != NULL)) != 0) + break; } - flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); break; - case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% - flag = ((fbl = mob->getfriendhprate(md, 0, ms[i].cond2)) != NULL); break; - case MSC_FRIENDHPINRATE: - flag = ((fbl = mob->getfriendhprate(md, ms[i].cond2, ms[i].val[0])) != NULL); break; - case MSC_FRIENDSTATUSON: // friend status[num] on - case MSC_FRIENDSTATUSOFF: // friend status[num] off - flag = ((fmd = mob->getfriendstatus(md, ms[i].cond1, ms[i].cond2)) != NULL); break; - case MSC_SLAVELT: // slave < num - flag = (mob->countslave(&md->bl) < c2 ); break; - case MSC_ATTACKPCGT: // attack pc > num - flag = (unit->counttargeted(&md->bl) > c2); break; - case MSC_SLAVELE: // slave <= num - flag = (mob->countslave(&md->bl) <= c2 ); break; - case MSC_ATTACKPCGE: // attack pc >= num - flag = (unit->counttargeted(&md->bl) >= c2); break; - case MSC_AFTERSKILL: - flag = (md->ud.skill_id == c2); break; - case MSC_RUDEATTACKED: - flag = (md->state.attacked_count >= RUDE_ATTACKED_COUNT); - if (flag) md->state.attacked_count = 0; //Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex] - break; - case MSC_MASTERHPLTMAXRATE: - flag = ((fbl = mob->getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break; - case MSC_MASTERATTACKED: - flag = (md->master_id > 0 && (fbl=map->id2bl(md->master_id)) != NULL && unit->counttargeted(fbl) > 0); - break; - case MSC_ALCHEMIST: - flag = (md->state.alchemist); - break; + } else { + flag = (md->sc.data[cond_data] != NULL); + } + + flag ^= (cast_cond == MSC_MYSTATUSOFF); + break; + case MSC_FRIENDHPLTMAXRATE: // FriendHP <= x% + flag = ((fbl = mob->getfriendhprate(md, 0, cond_data)) != NULL); + break; + case MSC_FRIENDHPINRATE: // FriendHP >= x% && FriendHP <= y% + flag = ((fbl = mob->getfriendhprate(md, cond_data, ms[skill_idx].val[0])) != NULL); + break; + case MSC_FRIENDSTATUSON: // Friend's status change x is active. + case MSC_FRIENDSTATUSOFF: // Friend's status change x is inactive. + flag = ((fbl = mob->getfriendstatus(md, cast_cond, cond_data)) != NULL); + break; + case MSC_SLAVELT: // Monster has less than x active slaves. + flag = (mob->countslave(&md->bl) < cond_data); + break; + case MSC_ATTACKPCGT: // Monster is attacked by more than x units. + flag = (unit->counttargeted(&md->bl) > cond_data); + break; + case MSC_SLAVELE: // Monster has x or less active slaves. + flag = (mob->countslave(&md->bl) <= cond_data); + break; + case MSC_ATTACKPCGE: // Monster is attacked by x or more units. + flag = (unit->counttargeted(&md->bl) >= cond_data); + break; + case MSC_AFTERSKILL: // Monster used skill x, or any skill if x is 0. + flag = (md->ud.skill_id == cond_data || cond_data == 0); + break; + case MSC_RUDEATTACKED: // Monster was rude attacked RUDE_ATTACKED_COUNT or more times. + flag = (md->state.attacked_count >= RUDE_ATTACKED_COUNT); + + // Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex] + if (flag) + md->state.attacked_count = 0; + + break; + case MSC_MASTERHPLTMAXRATE: // MasterHP < x% + flag = ((fbl = mob->getmasterhpltmaxrate(md, cond_data)) != NULL); + break; + case MSC_MASTERATTACKED: // Monster's master is under attack. + flag = (md->master_id > 0 && (fbl = map->id2bl(md->master_id)) != NULL); + flag = (fbl != NULL && unit->counttargeted(fbl) > 0); + break; + case MSC_ALCHEMIST: // Monster was summoned by an Alchemist. + flag = (md->state.alchemist != 0); + break; } } - if (!flag) - continue; //Skill requisite failed to be fulfilled. + if (flag == 0) // Skill cast condition not fulfilled. + continue; - //Execute skill - if (skill->get_casttype(ms[i].skill_id) == CAST_GROUND) {//Ground skill. - short x, y; - switch (ms[i].target) { - case MST_RANDOM: //Pick a random enemy within skill range. - bl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), - skill->get_range2(&md->bl, ms[i].skill_id, ms[i].skill_lv)); - break; - case MST_TARGET: - case MST_AROUND5: - case MST_AROUND6: - case MST_AROUND7: - case MST_AROUND8: - bl = map->id2bl(md->target_id); - break; - case MST_MASTER: - bl = &md->bl; - if (md->master_id) - bl = map->id2bl(md->master_id); - if (bl) //Otherwise, fall through. - break; - FALLTHROUGH - case MST_FRIEND: - bl = fbl?fbl:(fmd?&fmd->bl:&md->bl); - break; - default: - bl = &md->bl; + // Execute skill. + if (skill->get_casttype(ms[skill_idx].skill_id) == CAST_GROUND) { // Ground skill. + int target_type = ms[skill_idx].target; + int skill_range = skill->get_range2(&md->bl, ms[skill_idx].skill_id, ms[skill_idx].skill_lv); + struct block_list *bl; + + switch (target_type) { + case MST_RANDOM: // Pick a random enemy within skill range. Skill center is monster position. + bl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), skill_range); + break; + case MST_TARGET: // Monster's current target is within skill range. Skill center is monster position. + case MST_AROUND5: // Monster's current target is within skill range. Skill center is a random cell within a range of 1. + case MST_AROUND6: // Monster's current target is within skill range. Skill center is a random cell within a range of 2. + case MST_AROUND7: // Monster's current target is within skill range. Skill center is a random cell within a range of 3. + case MST_AROUND8: // Monster's current target is within skill range. Skill center is a random cell within a range of 4. + bl = map->id2bl(md->target_id); + break; + case MST_MASTER: // Monster's master is within skill range. Skill center is monster position. + // If monster has no master, use the monster as target, + bl = (md->master_id != 0) ? map->id2bl(md->master_id) : &md->bl; + + if (bl != NULL) break; + + // If monster has a master but master wasn't found, try a friend. + FALLTHROUGH + case MST_FRIEND: // Monster's friend is within skill range. Skill center is monster position. + bl = (fbl != NULL) ? fbl : &md->bl; + break; + default: // Monster is within skill range. Skill center is monster position. + bl = &md->bl; + break; } - if (!bl) continue; - - x = bl->x; - y = bl->y; - // Look for an area to cast the spell around... - if (ms[i].target >= MST_AROUND1 || ms[i].target >= MST_AROUND5) { - j = ms[i].target >= MST_AROUND1? - (ms[i].target-MST_AROUND1) +1: - (ms[i].target-MST_AROUND5) +1; - map->search_freecell(&md->bl, md->bl.m, &x, &y, j, j, 3); + + if (bl == NULL) // No target found. + continue; + + short x = bl->x; + short y = bl->y; + + // Find a target cell. + if (target_type >= MST_AROUND5 && target_type <= MST_AROUND) { + int range = target_type - ((target_type >= MST_AROUND1) ? MST_AROUND1 : MST_AROUND5) + 1; + map->search_freecell(&md->bl, md->bl.m, &x, &y, range, range, 3); } - md->skill_idx = i; + + md->skill_idx = skill_idx; map->freeblock_lock(); - if( !battle->check_range(&md->bl,bl,skill->get_range2(&md->bl, ms[i].skill_id,ms[i].skill_lv)) - || !unit->skilluse_pos2(&md->bl, x, y,ms[i].skill_id, ms[i].skill_lv,ms[i].casttime, ms[i].cancel) - ) { + + uint16 sk_id = ms[skill_idx].skill_id; + uint16 sk_lv = ms[skill_idx].skill_lv; + int casttime = ms[skill_idx].casttime; + short cancel = ms[skill_idx].cancel; + + if (!battle->check_range(&md->bl, bl, skill_range) + || unit->skilluse_pos2(&md->bl, x, y, sk_id, sk_lv, casttime, cancel) == 0) { map->freeblock_unlock(); continue; } - } else { - //Targeted skill - switch (ms[i].target) { - case MST_RANDOM: //Pick a random enemy within skill range. - bl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), - skill->get_range2(&md->bl, ms[i].skill_id, ms[i].skill_lv)); - break; - case MST_TARGET: - bl = map->id2bl(md->target_id); - break; - case MST_MASTER: - bl = &md->bl; - if (md->master_id) - bl = map->id2bl(md->master_id); - if (bl) //Otherwise, fall through. - break; - FALLTHROUGH - case MST_FRIEND: - if (fbl) { - bl = fbl; - break; - } else if (fmd) { - bl = &fmd->bl; - break; - } // else fall through - FALLTHROUGH - default: - bl = &md->bl; + } else { // Targeted skill. + int skill_range = skill->get_range2(&md->bl, ms[skill_idx].skill_id, ms[skill_idx].skill_lv); + struct block_list *bl; + + switch (ms[skill_idx].target) { + case MST_RANDOM: // Pick a random enemy within skill range. + bl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), skill_range); + break; + case MST_TARGET: // Monster's current target is within skill range. + bl = map->id2bl(md->target_id); + break; + case MST_MASTER: // Monster's master is within skill range. + // If monster has no master, use the monster as target, + bl = (md->master_id != 0) ? map->id2bl(md->master_id) : &md->bl; + + if (bl != NULL) break; + + // If monster has a master but master wasn't found, try a friend. + FALLTHROUGH + case MST_FRIEND: // Monster's friend is within skill range. + bl = (fbl != NULL) ? fbl : &md->bl; + break; + default: // Monster is within skill range. + bl = &md->bl; + break; } - if (!bl) continue; - md->skill_idx = i; + if (bl == NULL) // No target found. + continue; + + md->skill_idx = skill_idx; map->freeblock_lock(); - if( !battle->check_range(&md->bl,bl,skill->get_range2(&md->bl, ms[i].skill_id,ms[i].skill_lv)) - || !unit->skilluse_id2(&md->bl, bl->id,ms[i].skill_id, ms[i].skill_lv,ms[i].casttime, ms[i].cancel) - ) { + + uint16 sk_id = ms[skill_idx].skill_id; + uint16 sk_lv = ms[skill_idx].skill_lv; + int casttime = ms[skill_idx].casttime; + short cancel = ms[skill_idx].cancel; + + if (!battle->check_range(&md->bl, bl, skill_range) + || unit->skilluse_id2(&md->bl, bl->id, sk_id, sk_lv, casttime, cancel) == 0) { map->freeblock_unlock(); continue; } } - //Skill used. Post-setups... - if ( ms[ i ].msg_id ){ //Display color message [SnakeDrak] - struct mob_chat *mc = mob->chat(ms[i].msg_id); + + // Skill used. + if (ms[skill_idx].msg_id != 0) { // Display color message. [SnakeDrak] char temp[CHAT_SIZE_MAX]; char name[NAME_LENGTH]; - snprintf(name, sizeof name,"%s", md->name); - strtok(name, "#"); // discard extra name identifier if present [Daegaladh] - safesnprintf(temp, sizeof temp,"%s : %s", name, mc->msg); + struct mob_chat *mc = mob->chat(ms[skill_idx].msg_id); + + snprintf(name, sizeof(name), "%s", md->name); + strtok(name, "#"); // Discard extra name identifier if present. [Daegaladh] + safesnprintf(temp, sizeof(temp), "%s : %s", name, mc->msg); clif->messagecolor(&md->bl, mc->color, temp); } - if(!(battle_config.mob_ai&0x200)) { //pass on delay to same skill. - for (j = 0; j < md->db->maxskill; j++) - if (md->db->skill[j].skill_id == ms[i].skill_id) - md->skilldelay[j]=tick; - } else - md->skilldelay[i]=tick; + + if ((battle_config.mob_ai & 0x200) == 0) { // Pass on delay to same skill. + for (int j = 0; j < md->db->maxskill; j++) { + if (md->db->skill[j].skill_id == ms[skill_idx].skill_id) + md->skilldelay[j] = tick; + } + } else { + md->skilldelay[skill_idx] = tick; + } + map->freeblock_unlock(); - return 1; + return 0; } - //No skill was used. + + // No skill was used. md->skill_idx = -1; - return 0; + return 1; } + /*========================================== * Skill use event processing *------------------------------------------*/ @@ -3466,7 +3739,7 @@ static int mobskill_event(struct mob_data *md, struct block_list *src, int64 tic nullpo_ret(md); nullpo_ret(src); if(md->bl.prev == NULL || md->status.hp <= 0) - return 0; + return 1; if (md->special_state.ai == AI_SPHERE) {//LOne WOlf explained that ANYONE can trigger the marine countdown skill. [Skotlex] md->state.alchemist = 1; @@ -3486,7 +3759,7 @@ static int mobskill_event(struct mob_data *md, struct block_list *src, int64 tic else if (flag&BF_LONG && !(flag&BF_MAGIC)) //Long-attacked should not include magic. res = mob->skill_use(md, tick, MSC_LONGRANGEATTACKED); - if (!res) + if (res != 0) //Restore previous target only if skill condition failed to trigger. [Skotlex] md->target_id = target_id; //Otherwise check if the target is an enemy, and unlock if needed. @@ -3506,211 +3779,239 @@ static int mob_is_clone(int class_) return class_; } -//Flag values: -//&1: Set special AI (fight mobs, not players) -//If mode is not passed, a default aggressive mode is used. -//If master_id is passed, clone is attached to him. -//Returns: ID of newly crafted copy. -static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, uint32 mode, int flag, unsigned int duration) +/** + * Spawns a mob which is a clone of another character. + * + * @param sd The character which should be cloned. + * @param m The ID of the map where the clone should be spawned. + * @param x The x coordinate where the clone should be spawned. + * @param y The y coordinate where the clone should be spawned. + * @param event The name of the event which should be executed when the clone is killed. + * @param master_id If passed, the clone will be attached to this account ID. + * @param mode The clone's mob mode(s). (Defaults to MD_CANMOVE|MD_AGGRESSIVE|MD_ASSIST|MD_CANATTACK.) + * @param flag 0 - target characters; 1 - target mobs. + * @param duration How long the clone will live before it is auto-removed. (ms) + * @return The spawned clone's GID, or 0 if spawning failed. + * + **/ +static int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, const char *event, int master_id, + uint32 mode, int flag, unsigned int duration) { - int class_; - int i,j,h,inf, fd; - struct mob_data *md; - struct mob_skill *ms; - struct mob_db* db; - struct status_data *mstatus; - nullpo_ret(sd); - if(pc_isdead(sd) && master_id && flag&1) + if (pc_isdead(sd) && master_id != 0 && flag == 1) return 0; - ARR_FIND( MOB_CLONE_START, MOB_CLONE_END, class_, mob->db_data[class_] == NULL ); - if(class_ < 0 || class_ >= MOB_CLONE_END) + int class_; + + ARR_FIND(MOB_CLONE_START, MOB_CLONE_END, class_, mob->db_data[class_] == NULL); + + if (class_ < 0 || class_ >= MOB_CLONE_END) return 0; - db = mob->db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db)); - mstatus = &db->status; - strcpy(db->sprite,sd->status.name); - strcpy(db->name,sd->status.name); - strcpy(db->jname,sd->status.name); - db->lv=status->get_lv(&sd->bl); + mob->db_data[class_] = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); + + struct mob_db *db = mob->db_data[class_]; + struct status_data *mstatus = &db->status; + + strcpy(db->sprite, sd->status.name); + strcpy(db->name, sd->status.name); + strcpy(db->jname, sd->status.name); + db->lv = status->get_lv(&sd->bl); + db->dmg_taken_rate = 100; memcpy(mstatus, &sd->base_status, sizeof(struct status_data)); - mstatus->rhw.atk2= mstatus->dex + mstatus->rhw.atk + mstatus->rhw.atk2; //Max ATK - mstatus->rhw.atk = mstatus->dex; //Min ATK - if (mstatus->lhw.atk) { - mstatus->lhw.atk2= mstatus->dex + mstatus->lhw.atk + mstatus->lhw.atk2; //Max ATK - mstatus->lhw.atk = mstatus->dex; //Min ATK + mstatus->rhw.atk2 = mstatus->dex + mstatus->rhw.atk + mstatus->rhw.atk2; /// Max ATK. + mstatus->rhw.atk = mstatus->dex; /// Min ATK. + + if (mstatus->lhw.atk > 0) { + mstatus->lhw.atk2 = mstatus->dex + mstatus->lhw.atk + mstatus->lhw.atk2; /// Max ATK. + mstatus->lhw.atk = mstatus->dex; /// Min ATK. } - if (mode != MD_NONE) //User provided mode. + + if (mode != MD_NONE) /// User provided mode. mstatus->mode = mode; - else if (flag&1) //Friendly Character, remove looting. + else if (flag == 1) /// Friendly Character, remove looting. mstatus->mode &= ~MD_LOOTER; + mstatus->hp = mstatus->max_hp; mstatus->sp = mstatus->max_sp; memcpy(&db->vd, &sd->vd, sizeof(struct view_data)); - db->base_exp=1; - db->job_exp=1; - db->range2=AREA_SIZE; //Let them have the same view-range as players. - db->range3=AREA_SIZE; //Min chase of a screen. - db->option=sd->sc.option; + db->base_exp = 1; + db->job_exp = 1; + db->range2 = AREA_SIZE; /// Let them have the same view-range as players. + db->range3 = AREA_SIZE; /// Min chase of a screen. + db->option = sd->sc.option; - //Skill copy [Skotlex] - ms = &db->skill[0]; + const int fd = sd->fd; - /** - * We temporarily disable sd's fd so it doesn't receive the messages from skill_check_condition_castbegin - **/ - fd = sd->fd; - sd->fd = 0; - - //Go Backwards to give better priority to advanced skills. - for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) { - int idx = pc->skill_tree[pc->class2idx(sd->status.class)][j].idx; - int skill_id = pc->skill_tree[pc->class2idx(sd->status.class)][j].id; - if (!skill_id || sd->status.skill[idx].lv < 1 || - (skill->dbs->db[idx].inf2&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) - ) + sd->fd = 0; /// Temporarily disable sd's fd so it doesn't receive the messages from skill_check_condition_castbegin. + + struct mob_skill *mob_skills = &db->skill[0]; + + /// Go Backwards to give better priority to advanced skills. + for (int i = 0, j = MAX_SKILL_TREE - 1; j >= 0 && i < MAX_MOBSKILL; j--) { + const int idx = pc->skill_tree[pc->class2idx(sd->status.class)][j].idx; + const int skill_id = pc->skill_tree[pc->class2idx(sd->status.class)][j].id; + + if (skill_id == 0 || sd->status.skill[idx].lv < 1 || + (skill->dbs->db[idx].inf2 & (INF2_WEDDING_SKILL | INF2_GUILD_SKILL)) > 0) continue; - for(h = 0; h < map->list[sd->bl.m].zone->disabled_skills_count; h++) { - if( skill_id == map->list[sd->bl.m].zone->disabled_skills[h]->nameid && map->list[sd->bl.m].zone->disabled_skills[h]->subtype == MZS_CLONE ) { + + int h; + + for (h = 0; h < map->list[sd->bl.m].zone->disabled_skills_count; h++) { + if (skill_id == map->list[sd->bl.m].zone->disabled_skills[h]->nameid && + map->list[sd->bl.m].zone->disabled_skills[h]->subtype == MZS_CLONE) break; - } } - if( h < map->list[sd->bl.m].zone->disabled_skills_count ) + + if (h < map->list[sd->bl.m].zone->disabled_skills_count) continue; - //Normal aggressive mob, disable skills that cannot help them fight - //against players (those with flags UF_NOMOB and UF_NOPC are specific - //to always aid players!) [Skotlex] - if (!(flag&1) && - skill->get_unit_id(skill_id, 0) && - skill->get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC)) + + /// Normal aggressive mob. Disable skills that cannot help fighting against players. (Those with flags UF_NOMOB and UF_NOPC are specific to always aid players!) [Skotlex] + if (flag == 0 && skill->get_unit_id(skill_id, 0) != 0 && + (skill->get_unit_flag(skill_id) & (UF_NOMOB | UF_NOPC)) > 0) continue; - /** - * The clone should be able to cast the skill (e.g. have the required weapon) bugreport:5299) - **/ - if( !skill->check_condition_castbegin(sd,skill_id,sd->status.skill[idx].lv) ) + + /// The clone should be able to cast the skill. (E.g. have the required weapon.) [bugreport:5299] + if (skill->check_condition_castbegin(sd, skill_id, sd->status.skill[idx].lv) == 0) continue; - memset (&ms[i], 0, sizeof(struct mob_skill)); - ms[i].skill_id = skill_id; - ms[i].skill_lv = sd->status.skill[idx].lv; - ms[i].state = MSS_ANY; - ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5% - ms[i].emotion = -1; - ms[i].cancel = 0; - ms[i].casttime = skill->cast_fix(&sd->bl,skill_id, ms[i].skill_lv); - ms[i].delay = 5000+skill->delay_fix(&sd->bl,skill_id, ms[i].skill_lv); - - inf = skill->dbs->db[idx].inf; - if (inf&INF_ATTACK_SKILL) { - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill->get_range(skill_id, ms[i].skill_lv) > 3) - ms[i].state = MSS_ANYTARGET; + memset(&mob_skills[i], 0, sizeof(struct mob_skill)); + mob_skills[i].skill_id = skill_id; + mob_skills[i].skill_lv = sd->status.skill[idx].lv; + mob_skills[i].state = MSS_ANY; + mob_skills[i].permillage = 500 * battle_config.mob_skill_rate / 100; /// Default chance of all skills: 5% + mob_skills[i].emotion = -1; + mob_skills[i].cancel = 0; + mob_skills[i].casttime = skill->cast_fix(&sd->bl, skill_id, mob_skills[i].skill_lv); + mob_skills[i].delay = 5000 + skill->delay_fix(&sd->bl, skill_id, mob_skills[i].skill_lv); + + const int inf = skill->dbs->db[idx].inf; + + if ((inf & INF_ATTACK_SKILL) == INF_ATTACK_SKILL) { + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill->get_range(skill_id, mob_skills[i].skill_lv) > 3) + mob_skills[i].state = MSS_ANYTARGET; else - ms[i].state = MSS_BERSERK; - } else if(inf&INF_GROUND_SKILL) { - if (skill->get_inf2(skill_id)&INF2_TRAP) { //Traps! - ms[i].state = MSS_IDLE; - ms[i].target = MST_AROUND2; - ms[i].delay = 60000; - } else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy - ms[i].state = MSS_ANYTARGET; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - } else { //Target allies - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 95; + mob_skills[i].state = MSS_BERSERK; + } else if ((inf & INF_GROUND_SKILL) == INF_GROUND_SKILL) { + if ((skill->get_inf2(skill_id) & INF2_TRAP) == INF2_TRAP) { /// Traps! + mob_skills[i].state = MSS_IDLE; + mob_skills[i].target = MST_AROUND2; + mob_skills[i].delay = 60000; + } else if (skill->get_unit_target(skill_id) == BCT_ENEMY) { /// Target Enemy. + mob_skills[i].state = MSS_ANYTARGET; + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + } else { /// Target allies. + mob_skills[i].target = MST_FRIEND; + mob_skills[i].cond1 = MSC_FRIENDHPLTMAXRATE; + mob_skills[i].cond2 = 95; } - } else if (inf&INF_SELF_SKILL) { - if (skill->get_inf2(skill_id)&INF2_NO_TARGET_SELF) { //auto-select target skill. - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill->get_range(skill_id, ms[i].skill_lv) > 3) { - ms[i].state = MSS_ANYTARGET; - } else { - ms[i].state = MSS_BERSERK; - } - } else { //Self skill - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; - ms[i].cond2 = 90; - ms[i].permillage = 2000; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill->get_time(skill_id, ms[i].skill_lv) + skill->get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 5000) - ms[i].delay = 5000; //With a minimum of 5 secs. + } else if ((inf & INF_SELF_SKILL) == INF_SELF_SKILL) { + if ((skill->get_inf2(skill_id) & INF2_NO_TARGET_SELF) == INF2_NO_TARGET_SELF) { /// Auto-select target skill. + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill->get_range(skill_id, mob_skills[i].skill_lv) > 3) + mob_skills[i].state = MSS_ANYTARGET; + else + mob_skills[i].state = MSS_BERSERK; + } else { /// Self skill. + mob_skills[i].target = MST_SELF; + mob_skills[i].cond1 = MSC_MYHPLTMAXRATE; + mob_skills[i].cond2 = 90; + mob_skills[i].permillage = 2000; + + const int time1 = skill->get_time(skill_id, mob_skills[i].skill_lv); + const int time2 = skill->get_time2(skill_id, mob_skills[i].skill_lv); + + /** Delay: Remove the stock 5 secs and add half of the support time. **/ + mob_skills[i].delay += -5000 + (time1 + time2) / 2; + + if (mob_skills[i].delay < 5000) + mob_skills[i].delay = 5000; /// With a minimum of 5 seconds. } - } else if (inf&INF_SUPPORT_SKILL) { - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 90; + } else if ((inf & INF_SUPPORT_SKILL) == INF_SUPPORT_SKILL) { + mob_skills[i].target = MST_FRIEND; + mob_skills[i].cond1 = MSC_FRIENDHPLTMAXRATE; + mob_skills[i].cond2 = 90; + if (skill_id == AL_HEAL) - ms[i].permillage = 5000; //Higher skill rate usage for heal. + mob_skills[i].permillage = 5000; /// Higher skill rate usage for heal. else if (skill_id == ALL_RESURRECTION) - ms[i].cond2 = 1; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill->get_time(skill_id, ms[i].skill_lv) + skill->get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 2000) - ms[i].delay = 2000; //With a minimum of 2 secs. - - if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self. - memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill)); + mob_skills[i].cond2 = 1; + + const int time1 = skill->get_time(skill_id, mob_skills[i].skill_lv); + const int time2 = skill->get_time2(skill_id, mob_skills[i].skill_lv); + + /** Delay: Remove the stock 5 secs and add half of the support time. **/ + mob_skills[i].delay += -5000 + (time1 + time2) / 2; + + if (mob_skills[i].delay < 2000) + mob_skills[i].delay = 2000; /// With a minimum of 2 seconds. + + if (i + 1 < MAX_MOBSKILL) { /// Duplicate this so it also triggers on self. + memcpy(&mob_skills[i + 1], &mob_skills[i], sizeof(struct mob_skill)); db->maxskill = ++i; - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; + mob_skills[i].target = MST_SELF; + mob_skills[i].cond1 = MSC_MYHPLTMAXRATE; } } else { - switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered. - case MO_TRIPLEATTACK: - case TF_DOUBLE: - case GS_CHAINACTION: - ms[i].state = MSS_BERSERK; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - ms[i].permillage = skill_id==MO_TRIPLEATTACK?(3000-ms[i].skill_lv*100):(ms[i].skill_lv*500); - ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits". - break; - default: //Untreated Skill - continue; + switch (skill_id) { /// Certain special skills that are passive, and thus, never triggered. + case MO_TRIPLEATTACK: + FALLTHROUGH + case TF_DOUBLE: + FALLTHROUGH + case GS_CHAINACTION: + mob_skills[i].state = MSS_BERSERK; + mob_skills[i].target = MST_TARGET; + mob_skills[i].cond1 = MSC_ALWAYS; + + if (skill_id == MO_TRIPLEATTACK) + mob_skills[i].permillage = 3000 - mob_skills[i].skill_lv * 100; + else + mob_skills[i].permillage = mob_skills[i].skill_lv * 500; + + mob_skills[i].delay -= 5000; /// Remove the added delay as these could trigger on "all hits". + break; + default: /// Untreated skill. + continue; } } - if (battle_config.mob_skill_rate!= 100) - ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100; - if (battle_config.mob_skill_delay != 100) - ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100; + mob_skills[i].permillage = mob_skills[i].permillage * battle_config.mob_skill_rate / 100; + mob_skills[i].delay = mob_skills[i].delay * battle_config.mob_skill_delay / 100; db->maxskill = ++i; } - /** - * We grant the session it's fd value back. - **/ - sd->fd = fd; + sd->fd = fd; /// We grant the session it's fd value back. + + /// Finally spawn it. + struct mob_data *md = mob->once_spawn_sub(&sd->bl, m, x, y, DEFAULT_MOB_NAME, class_, event, SZ_SMALL, AI_NONE, 0); - //Finally, spawn it. - md = mob->once_spawn_sub(&sd->bl, m, x, y, DEFAULT_MOB_NAME, class_, event, SZ_SMALL, AI_NONE); - if (!md) return 0; //Failed? + if (md == NULL) + return 0; /// Failed? md->special_state.clone = 1; - if (master_id || flag || duration) { //Further manipulate crafted char. - if (flag&1) //Friendly Character - md->special_state.ai = AI_ATTACK; - if (master_id) //Attach to Master - md->master_id = master_id; - if (duration) //Auto Delete after a while. - { - if( md->deletetimer != INVALID_TIMER ) - timer->delete(md->deletetimer, mob->timer_delete); - md->deletetimer = timer->add(timer->gettick() + duration, mob->timer_delete, md->bl.id, 0); - } + if (flag == 1) /// Friendly character. + md->special_state.ai = AI_ATTACK; + + if (master_id != 0) /// Attach to master. + md->master_id = master_id; + + if (duration > 0) { /// Auto delete after a while. + if (md->deletetimer != INVALID_TIMER) + timer->delete(md->deletetimer, mob->timer_delete); + + md->deletetimer = timer->add(timer->gettick() + duration, mob->timer_delete, md->bl.id, 0); } mob->spawn(md); - return md->bl.id; } @@ -3861,6 +4162,210 @@ 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) +{ + char filepath[256]; + libconfig->format_db_path("option_drop_groups.conf", filepath, sizeof(filepath)); + + 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 @@ -3892,6 +4397,54 @@ static void mob_read_db_stats_sub(struct mob_db *entry, struct config_setting_t } /** + * Processes the view data for a mob_db entry. + * + * @param[in,out] entry The destination mob_db entry, already initialized + * (mob_id, status.mode are expected to be already set). + * @param[in] t The libconfig entry. + */ +static void mob_read_db_viewdata_sub(struct mob_db *entry, struct config_setting_t *t) +{ + nullpo_retv(entry); + nullpo_retv(t); + + struct config_setting_t *it; + int i32; + + if ((it = libconfig->setting_get_member(t, "SpriteId")) != NULL) + entry->vd.class = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "WeaponId")) != NULL) + entry->vd.weapon = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "ShieldId")) != NULL) + entry->vd.shield = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "RobeId")) != NULL) + entry->vd.robe = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "HeadTopId")) != NULL) + entry->vd.head_top = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "HeadMidId")) != NULL) + entry->vd.head_mid = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "HeadLowId")) != NULL) + entry->vd.head_bottom = libconfig->setting_get_int(it); + + if ((it = libconfig->setting_get_member(t, "HairStyleId")) != NULL) + entry->vd.hair_style = libconfig->setting_get_int(it); + else + entry->vd.hair_style = 1; + + if ((it = libconfig->setting_get_member(t, "BodyStyleId")) != NULL) + entry->vd.body_style = libconfig->setting_get_int(it); + if ((it = libconfig->setting_get_member(t, "HairColorId")) != NULL) + entry->vd.hair_color = libconfig->setting_get_uint16(it); + if ((it = libconfig->setting_get_member(t, "BodyColorId")) != NULL) + entry->vd.cloth_color = libconfig->setting_get_uint16(it); + if (mob->lookup_const(t, "Gender", &i32) && i32 >= 0) { + entry->vd.sex = (char)i32; + } + if ((it = libconfig->setting_get_member(t, "Options")) != NULL) + entry->option = libconfig->setting_get_int(it) &~ (OPTION_HIDE | OPTION_CLOAK | OPTION_INVISIBLE); +} + +/** * Processes the mode for a mob_db entry. * * @param[in] entry The destination mob_db entry, already initialized. @@ -3941,6 +4494,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 +4563,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 +4588,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 +4632,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 +4651,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++; @@ -4295,14 +4913,33 @@ static int mob_read_db_sub(struct config_setting_t *mobt, int n, const char *sou * AttackMotion: attack motion * DamageMotion: damage motion * MvpExp: mvp experience + * DamageTakenRate: damage taken rate * MvpDrops: { * AegisName: chance * ... * } * Drops: { * AegisName: chance + * // or + * AegisName: (chance, "Option Drop Group") * ... * } + * DamageTakenRate: damage taken rate + * ViewData: { + * SpriteId: sprite id + * WeaponId: weapon id + * ShieldId: shield id + * RobeId: garment id + * HeadTopId: top headgear id + * HeadMidId: middle headgear id + * HeadLowId: lower headgear id + * HairStyleId: hair style id + * BodyStyleId: clothes id + * HairColorId: hair color id + * BodyColorId: clothes color id + * Gender: gender + * Options: options + * } */ if (!libconfig->setting_lookup_int(mobt, "Id", &i32)) { @@ -4451,12 +5088,10 @@ static int mob_read_db_sub(struct config_setting_t *mobt, int n, const char *sou md.status.def_ele = i32; md.status.ele_lv = value; } else if (!inherit) { - ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id); md.status.def_ele = ELE_NEUTRAL; md.status.ele_lv = 1; } } else if (!inherit) { - ShowWarning("mob_read_db_sub: Missing element for monster ID %d.\n", md.mob_id); md.status.def_ele = ELE_NEUTRAL; md.status.ele_lv = 1; } @@ -4526,6 +5161,18 @@ static int mob_read_db_sub(struct config_setting_t *mobt, int n, const char *sou } } + if (mob->lookup_const(mobt, "DamageTakenRate", &i32) && i32 >= 0) { + md.dmg_taken_rate = cap_value(i32, 1, INT_MAX); + } else if (!inherit) { + md.dmg_taken_rate = 100; + } + + if ((t = libconfig->setting_get_member(mobt, "ViewData"))) { + if (config_setting_is_group(t)) { + mob->read_db_viewdata_sub(&md, t); + } + } + mob->read_db_additional_fields(&md, mobt, n, source); return mob->db_validate_entry(&md, n, source); @@ -4641,7 +5288,7 @@ static int mob_read_libconfig(const char *filename, bool ignore_missing) } } libconfig->destroy(&mob_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); return count; } @@ -4661,46 +5308,15 @@ static void mob_name_constants(void) #endif // ENABLE_CASE_CHECK } -/*========================================== - * MOB display graphic change data reading - *------------------------------------------*/ -static bool mob_readdb_mobavail(char *str[], int columns, int current) +static void mob_mobavail_removal_notice(void) { - int class_, view_class; - - nullpo_retr(false, str); - class_=atoi(str[0]); - - if(mob->db(class_) == mob->dummy) { - // invalid class (probably undefined in db) - ShowWarning("mob_readdb_mobavail: Unknown mob id %d.\n", class_); - return false; - } - - view_class = atoi(str[1]); + char filepath[256]; - memset(&mob->db_data[class_]->vd, 0, sizeof(struct view_data)); - mob->db_data[class_]->vd.class = view_class; + safesnprintf(filepath, sizeof(filepath), "%s/mob_avail.txt", map->db_path); - //Player sprites - if (pc->db_checkid(view_class) && columns == 12) { - mob->db_data[class_]->vd.sex=atoi(str[2]); - mob->db_data[class_]->vd.hair_style=atoi(str[3]); - mob->db_data[class_]->vd.hair_color=atoi(str[4]); - mob->db_data[class_]->vd.weapon=atoi(str[5]); - mob->db_data[class_]->vd.shield=atoi(str[6]); - mob->db_data[class_]->vd.head_top=atoi(str[7]); - mob->db_data[class_]->vd.head_mid=atoi(str[8]); - mob->db_data[class_]->vd.head_bottom=atoi(str[9]); - mob->db_data[class_]->option=atoi(str[10])&~(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE); - mob->db_data[class_]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris + if (exists(filepath)) { + ShowError("mob_mobavail_removal_notice: the usage of mob_avail.txt is no longer supported, move your data using tools/mobavailconverter.py and delete the database file to suspend this message.\n"); } - else if(columns==3) - mob->db_data[class_]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] - else if( columns != 2 ) - return false; - - return true; } /*========================================== @@ -4764,7 +5380,7 @@ static int mob_read_randommonster(void) summon[i].qty = 1; } fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",count,mobfile[i]); + ShowStatus("Done reading '"CL_WHITE"%u"CL_RESET"' entries in '"CL_WHITE"%s/%s"CL_RESET"'.\n",count, map->db_path, mobfile[i]); } return 0; } @@ -4881,7 +5497,7 @@ static void mob_readchatdb(void) count++; } fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, arc); + ShowStatus("Done reading '"CL_WHITE"%"PRIu32""CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); } /*========================================== @@ -4949,115 +5565,134 @@ static bool mob_skill_db_libconfig_sub(struct config_setting_t *it, int n) return true; } +/** + * Reads a single monster skill from DB. + * + * @param it The libconfig settings block, which contains the skill data. + * @param n The skill data block's index within the parent monster block. + * @param mob_id The monster's ID. + * @return true on success, false on failure. + * + **/ static bool mob_skill_db_libconfig_sub_skill(struct config_setting_t *it, int n, int mob_id) { - int i, j, idx = 0; - int i32; - int skill_id = 0; - int skill_idx = 0; - bool clearskills = false; - const char *name = config_setting_name(it); - struct mob_skill *ms, gms; - nullpo_retr(false, it); Assert_retr(false, mob_id <= 0 || mob->db(mob_id) != mob->dummy); - if (!(skill_id = skill->name2id(name))) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Non existant skill id %d in monster %d, skipping.\n", skill_id, mob_id); + int skill_id = 0; + const char *name = config_setting_name(it); + const char *mob_str = (mob_id < 0) ? "global ID" : "monster"; + + if ((skill_id = skill->name2id(name)) == 0) { + ShowWarning("%s: Non existant skill %d in %s %d, skipping.\n", __func__, skill_id, mob_str, mob_id); return false; } + const char *skill_name = skill->get_name(skill_id); + bool clearskills = false; + // If ClearSkills flag is enabled clear all the previous skills. - if (libconfig->setting_lookup_bool_real(it, "ClearSkills", &clearskills) && clearskills) { - if (mob_id < 0) // Clearing skills globaly is not supported + if (libconfig->setting_lookup_bool_real(it, "ClearSkills", &clearskills) == CONFIG_TRUE && clearskills) { + if (mob_id < 0) { + ShowError("%s: Global skill clearing is not supported, skipping. (Global ID %d, skill %d (%s).)\n", + __func__, mob_id, skill_id, skill_name); return false; + } + memset(mob->db_data[mob_id]->skill, 0, sizeof(struct mob_skill) * MAX_MOBSKILL); mob->db_data[mob_id]->maxskill = 0; return true; } - if (mob_id < 0) { - // Prepare global skill. [Skotlex] + struct mob_skill *ms; + + if (mob_id < 0) { // Prepare global skill. [Skotlex] + struct mob_skill gms; memset(&gms, 0, sizeof (struct mob_skill)); ms = &gms; } else { + int idx = 0; + ARR_FIND(0, MAX_MOBSKILL, idx, (ms = &mob->db_data[mob_id]->skill[idx])->skill_id == 0); + if (idx == MAX_MOBSKILL) { - ShowError("mob_skill_db_libconfig_sub_skill: Too many skills for monster %d\n", mob_id); + ShowError("%s: Too many skills for monster %d, skipping.\n", __func__, mob_id); return false; } + + mob->db_data[mob_id]->maxskill = idx + 1; } + ms->skill_id = skill_id; + int i32 = MSS_ANY; if (mob->lookup_const(it, "SkillState", &i32) && (i32 < MSS_ANY || i32 > MSS_ANYTARGET)) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Invalid skill state %d for skill id %d in monster %d, defaulting to MSS_ANY.\n", i32, skill_id, mob_id); + ShowWarning("%s: Invalid skill state %d for skill %d (%s) in %s %d, defaulting to MSS_ANY.\n", + __func__, i32, skill_id, skill_name, mob_str, mob_id); i32 = MSS_ANY; } ms->state = i32; - if (!libconfig->setting_lookup_int(it, "SkillLevel", &i32) || i32 <= 0) - i32 = 1; - ms->skill_lv = i32 > battle_config.mob_max_skilllvl ? battle_config.mob_max_skilllvl : i32; //we strip max skill level + int res = libconfig->setting_lookup_int(it, "SkillLevel", &i32); + ms->skill_lv = (res == CONFIG_FALSE) ? 1 : cap_value(i32, 1, battle_config.mob_max_skilllvl); - //Apply battle_config modifiers to rate (permillage) and delay [Skotlex] - if (libconfig->setting_lookup_int(it, "Rate", &i32)) - ms->permillage = i32; + res = libconfig->setting_lookup_int(it, "Rate", &i32); + ms->permillage = (res == CONFIG_FALSE) ? 1 : cap_value(i32, 1, 10000); + // Apply battle_config modifier to rate (permillage). if (battle_config.mob_skill_rate != 100) ms->permillage = ms->permillage * battle_config.mob_skill_rate / 100; + if (ms->permillage > 10000) ms->permillage = 10000; - else if (ms->permillage == 0 && battle_config.mob_skill_rate) + else if (ms->permillage == 0 && battle_config.mob_skill_rate != 0) ms->permillage = 1; - if (libconfig->setting_lookup_int(it, "CastTime", &i32) && i32 > 0) - ms->casttime = i32; + res = libconfig->setting_lookup_int(it, "CastTime", &i32); + ms->casttime = (res == CONFIG_FALSE) ? 0 : cap_value(i32, 0, MOB_MAX_CASTTIME); - if (libconfig->setting_lookup_int(it, "Delay", &i32)) - ms->delay = i32; + res = libconfig->setting_lookup_int(it, "Delay", &i32); + ms->delay = (res == CONFIG_FALSE) ? 0 : cap_value(i32, 0, MOB_MAX_DELAY); + + // Apply battle_config modifier to delay. if (battle_config.mob_skill_delay != 100) ms->delay = ms->delay * battle_config.mob_skill_delay / 100; - if (ms->delay < 0 || ms->delay > MOB_MAX_DELAY) //time overflow? - ms->delay = MOB_MAX_DELAY; - if (libconfig->setting_lookup_bool(it, "Cancelable", &i32)) - ms->cancel = (i32 == 0) ? 0 : 1; + ms->delay = min(ms->delay, MOB_MAX_DELAY); + + res = libconfig->setting_lookup_bool(it, "Cancelable", &i32); + ms->cancel = (res == CONFIG_FALSE) ? 0 : cap_value(i32, 0, 1); + i32 = MST_TARGET; if (mob->lookup_const(it, "SkillTarget", &i32) && (i32 < MST_TARGET || i32 > MST_AROUND)) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Invalid skill target %d for skill id %d in monster %d, defaulting to MST_TARGET.\n", i32, skill_id, mob_id); - ms->target = MST_TARGET; + ShowWarning("%s: Invalid skill target %d for skill %d (%s) in %s %d, defaulting to MST_TARGET.\n", + __func__, i32, skill_id, skill_name, mob_str, mob_id); + i32 = MST_TARGET; } ms->target = i32; - //Check that the target condition is right for the skill type. [Skotlex] - skill_idx = skill->get_index(skill_id); - if (skill->get_casttype2(skill_idx) == CAST_GROUND) {//Ground skill. - if (ms->target > MST_AROUND) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Wrong mob skill target for ground skill %d (%s) for %s.\n", - ms->skill_id, skill->dbs->db[skill_idx].name, - mob_id < 0 ? "all mobs" : mob->db_data[mob_id]->sprite); - ms->target = MST_TARGET; - } - } else if (ms->target > MST_MASTER) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Wrong mob skill target 'around' for non-ground skill %d (%s) for %s.\n", - ms->skill_id, skill->dbs->db[skill_idx].name, - mob_id < 0 ? "all mobs" : mob->db_data[mob_id]->sprite); + // Check the target condition for non-ground skills. (Ground skills can use every target.) + if (skill->get_casttype2(skill->get_index(skill_id)) != CAST_GROUND && ms->target > MST_MASTER) { + ShowWarning("%s: Wrong skill target %d for non-ground skill %d (%s) in %s %d, defaulting to MST_TARGET.\n", + __func__, ms->target, skill_id, skill_name, mob_str, mob_id); ms->target = MST_TARGET; } + i32 = MSC_ALWAYS; if (mob->lookup_const(it, "CastCondition", &i32) && (i32 < MSC_ALWAYS || i32 > MSC_SPAWN)) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Invalid skill condition %d for skill id %d in monster %d, defaulting to MSC_ALWAYS.\n", i32, skill_id, mob_id); - ms->cond1 = MSC_ALWAYS; + ShowWarning("%s: Invalid skill condition %d for skill id %d (%s) in %s %d, defaulting to MSC_ALWAYS.\n", + __func__, i32, skill_id, skill_name, mob_str, mob_id); + i32 = MSC_ALWAYS; } ms->cond1 = i32; - if (mob->lookup_const(it, "ConditionData", &i32)) - ms->cond2 = i32; + ms->cond2 = !mob->lookup_const(it, "ConditionData", &i32) ? 0 : cap_value(i32, SHRT_MIN, SHRT_MAX); - for (i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { char valname[16]; sprintf(valname, "val%1d", i); - if (libconfig->setting_lookup_int(it, valname, &i32)) + + if (libconfig->setting_lookup_int(it, valname, &i32) == CONFIG_TRUE) ms->val[i] = i32; } @@ -5068,60 +5703,64 @@ static bool mob_skill_db_libconfig_sub_skill(struct config_setting_t *it, int n, if (mob_id > 0 && (uint32)ms->val[1] == mob->db(mob_id)->status.mode) { ms->val[1] = MD_NONE; - ms->val[4] = 1; //request to return mode to normal. + ms->val[4] = 1; // Request to return mode to normal. } } if (ms->skill_id == NPC_EMOTION_ON && mob_id > 0 && ms->val[1] != MD_NONE) { - //Adds a mode to the mob. - //Remove aggressive mode when the new mob type is passive. - if (!(ms->val[1] & MD_AGGRESSIVE)) + // Add a mode to the mob and remove aggressive mode if the new mode is passive. + if ((ms->val[1] & MD_AGGRESSIVE) == 0) ms->val[3] |= MD_AGGRESSIVE; - ms->val[2] |= (uint32)ms->val[1]; //Add the new mode. - ms->val[1] = MD_NONE; //Do not "set" it. + + ms->val[2] |= (uint32)ms->val[1]; // Add the new mode. + ms->val[1] = MD_NONE; // Do not "set" it. } - if (libconfig->setting_lookup_int(it, "Emotion", &i32)) - ms->emotion = i32; - else - ms->emotion = -1; + res = libconfig->setting_lookup_int(it, "Emotion", &i32); + ms->emotion = (res == CONFIG_FALSE) ? -1 : cap_value(i32, -1, SHRT_MAX); - if (libconfig->setting_lookup_int(it, "ChatMsgID", &i32) && i32 > 0 && i32 <= MAX_MOB_CHAT) { - if (mob->chat_db[i32] == NULL) { - ShowWarning("mob_skill_db_libconfig_sub_skill: Invalid msg id %d for skill id %d in monster %d, ignoring.\n", i32, skill_id, mob_id); + if (libconfig->setting_lookup_int(it, "ChatMsgID", &i32) == CONFIG_TRUE) { + if (i32 <= 0 || i32 > MAX_MOB_CHAT || mob->chat_db[i32] == NULL) { + ShowWarning("%s: Invalid message ID %d for skill %d (%s) in %s %d, ignoring.\n", + __func__, i32, skill_id, skill_name, mob_str, mob_id); } else { ms->msg_id = i32; } } - if (mob_id < 0) { - // Set this skill to ALL mobs. [Skotlex] - mob_id *= -1; - for (i = 1; i < MAX_MOB_DB; i++) { + if (mob_id < 0) { // Global skill assignment. + mob_id = -mob_id; + + for (int i = 1; i < MAX_MOB_DB; i++) { if (mob->db_data[i] == NULL) continue; - if (mob->db_data[i]->status.mode & MD_BOSS) { - if (!(mob_id & 2)) //Skill not for bosses + + if ((mob->db_data[i]->status.mode & MD_BOSS) != 0) { + if ((mob_id & 2) == 0) // Skill not for boss monsters. continue; } else { - if (!(mob_id & 1)) //Skill not for normal enemies. + if ((mob_id & 1) == 0) // Skill not for normal monsters. continue; } - ARR_FIND(0, MAX_MOBSKILL, j, mob->db_data[i]->skill[j].skill_id == 0); - if (j == MAX_MOBSKILL) + + int idx; + + ARR_FIND(0, MAX_MOBSKILL, idx, mob->db_data[i]->skill[idx].skill_id == 0); + + if (idx == MAX_MOBSKILL) { + ShowError("%s: Too many skills for monster %d in global ID %d, skipping.\n", + __func__, i, -mob_id); continue; + } - memcpy(&mob->db_data[i]->skill[j], ms, sizeof(struct mob_skill)); - mob->db_data[i]->maxskill = j + 1; + memcpy(&mob->db_data[i]->skill[idx], ms, sizeof(struct mob_skill)); + mob->db_data[i]->maxskill = idx + 1; } - } else { //Skill set on a single mob. - mob->db_data[mob_id]->maxskill = idx + 1; } return true; } - /*========================================== * mob_skill_db.txt reading *------------------------------------------*/ @@ -5208,15 +5847,17 @@ static bool mob_readdb_itemratio(char *str[], int columns, int current) static void mob_load(bool minimal) { if (minimal) { - // Only read the mob db in minimal mode + // Only read the mob db and option drops in minimal mode + mob->read_optdrops_db(); mob->readdb(); 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(); - sv->readdb(map->db_path, "mob_avail.txt", ',', 2, 12, -1, mob->readdb_mobavail); + mob->mobavail_removal_notice(); mob->read_randommonster(); sv->readdb(map->db_path, DBPATH"mob_race2_db.txt", ',', 2, 20, -1, mob->readdb_race2); } @@ -5234,6 +5875,29 @@ static int mob_final_ratio_sub(union DBKey key, struct DBData *data, va_list ap) return 0; } +static int mob_reload_sub_mob(struct mob_data *md, va_list args) +{ + nullpo_ret(md); + md->db = mob_db(md->class_); + + status_calc_mob(md, SCO_FIRST); + + // If the view data was not overwritten manually + if (md->vd != NULL) { + // Get the new view data from the mob database + md->vd = mob_get_viewdata(md->class_); + + // If they are spawned right now + if (md->bl.prev != NULL) { + // Respawn all mobs on client side so that they are displayed correctly(if their view id changed) + clif->clearunit_area(&md->bl, CLR_OUTSIGHT); + clif->spawn(&md->bl); + } + } + + return 0; +} + static void mob_reload(void) { int i; @@ -5254,7 +5918,10 @@ 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); + map->foreachmob(mob->reload_sub_mob); } /** @@ -5309,6 +5976,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 +6010,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) @@ -5394,6 +6078,7 @@ void mob_defaults(void) /* */ mob->reload = mob_reload; + mob->reload_sub_mob = mob_reload_sub_mob; mob->init = do_init_mob; mob->final = do_final_mob; /* */ @@ -5433,6 +6118,7 @@ void mob_defaults(void) mob->ai_sub_hard_bg_ally = mob_ai_sub_hard_bg_ally; mob->ai_sub_hard_lootsearch = mob_ai_sub_hard_lootsearch; mob->warpchase_sub = mob_warpchase_sub; + mob->is_in_battle_state = mob_is_in_battle_state; mob->ai_sub_hard_slavemob = mob_ai_sub_hard_slavemob; mob->unlocktarget = mob_unlocktarget; mob->randomwalk = mob_randomwalk; @@ -5443,6 +6129,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; @@ -5469,13 +6156,17 @@ void mob_defaults(void) mob->getmasterhpltmaxrate = mob_getmasterhpltmaxrate; mob->getfriendstatus_sub = mob_getfriendstatus_sub; mob->getfriendstatus = mob_getfriendstatus; - mob->skill_use = mobskill_use; + mob->skill_use = mob_skill_use; mob->skill_event = mobskill_event; mob->is_clone = mob_is_clone; mob->clone_spawn = mob_clone_spawn; 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,9 +6177,11 @@ 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->read_db_viewdata_sub = mob_read_db_viewdata_sub; mob->name_constants = mob_name_constants; - mob->readdb_mobavail = mob_readdb_mobavail; + mob->mobavail_removal_notice = mob_mobavail_removal_notice; mob->read_randommonster = mob_read_randommonster; mob->parse_row_chatdb = mob_parse_row_chatdb; mob->readchatdb = mob_readchatdb; @@ -5501,6 +6194,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 4cb3877ed..6ad1ce705 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,12 +35,11 @@ struct hplugin_data_store; // Change this to increase the table size in your mob_db to accommodate a larger mob database. // Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes. // Notice that the last 1000 entries are used for player clones, so always set this to desired value +1000 -#define MAX_MOB_DB 5000 +#define MAX_MOB_DB 22000 //The number of drops all mobs have and the max drop-slot that the steal skill will attempt to steal from. #define MAX_MOB_DROP 10 #define MAX_MVP_DROP 3 -#define MAX_STEAL_DROP 7 //Min time between AI executions #define MIN_MOBTHINKTIME 100 @@ -152,6 +151,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,13 +198,14 @@ 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; int summonper[MAX_RANDOMMONSTER]; int maxskill; + int dmg_taken_rate; struct mob_skill skill[MAX_MOBSKILL]; struct spawn_info spawn[10]; struct hplugin_data_store *hdata; ///< HPM Plugin Data Store @@ -205,16 +244,18 @@ struct mob_data { unsigned int dmg; unsigned int flag : 2; //0: Normal. 1: Homunc exp. 2: Pet exp } dmglog[DAMAGELOG_SIZE]; + int dmg_taken_rate; struct spawn_data *spawn; //Spawn data. int spawn_timer; //Required for Convex Mirror struct item *lootitem; - short class_; + int class_; unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex] int level; int target_id,attacked_id; int areanpc_id; //Required in OnTouchNPC (to avoid multiple area touchs) unsigned int bg_id; // BattleGround System int clan_id; // Clan System + int npc_id; // NPC ID if spawned with monster/areamonster/guardian/bg_monster/atcommand("@monster xy") (Used to kill mob on NPC unload.) int64 next_walktime, last_thinktime, last_linktime, last_pcneartime, dmgtick; short move_fail_count; @@ -282,6 +323,13 @@ enum { MSC_SPAWN, }; +/** Special monster(-name) constants used to assign skills to a group of monsters. **/ +enum mob_group { + ALL_MOBS_NONBOSS = -1, + ALL_MOBS_BOSS = -2, + ALL_MOBS = -3, +}; + /** * Mob IDs */ @@ -436,6 +484,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]; @@ -446,6 +497,7 @@ struct mob_interface { int (*init) (bool mimimal); int (*final) (void); void (*reload) (void); + int (*reload_sub_mob) (struct mob_data *md, va_list args); /* */ struct mob_db* (*db) (int index); struct mob_chat* (*chat) (short id); @@ -463,14 +515,14 @@ struct mob_interface { int (*db_checkid) (const int id); struct view_data* (*get_viewdata) (int class_); int (*parse_dataset) (struct spawn_data *data); - struct mob_data* (*spawn_dataset) (struct spawn_data *data); + struct mob_data* (*spawn_dataset) (struct spawn_data *data, int npc_id); int (*get_random_id) (int type, int flag, int lv); bool (*ksprotected) (struct block_list *src, struct block_list *target); - struct mob_data* (*once_spawn_sub) (struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai); + struct mob_data* (*once_spawn_sub) (struct block_list *bl, int16 m, int16 x, int16 y, const char *mobname, int class_, const char *event, unsigned int size, unsigned int ai, int npc_id); int (*once_spawn) (struct map_session_data *sd, int16 m, int16 x, int16 y, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); int (*once_spawn_area) (struct map_session_data *sd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, const char *mobname, int class_, int amount, const char *event, unsigned int size, unsigned int ai); - int (*spawn_guardian) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index); - int (*spawn_bg) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id); + int (*spawn_guardian) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, int guardian, bool has_index, int npc_id); + int (*spawn_bg) (const char *mapname, short x, short y, const char *mobname, int class_, const char *event, unsigned int bg_id, int npc_id); int (*can_reach) (struct mob_data *md, struct block_list *bl, int range, int state); int (*linksearch) (struct block_list *bl, va_list ap); int (*delayspawn) (int tid, int64 tick, int id, intptr_t data); @@ -484,6 +536,7 @@ struct mob_interface { int (*ai_sub_hard_bg_ally) (struct block_list *bl, va_list ap); int (*ai_sub_hard_lootsearch) (struct block_list *bl, va_list ap); int (*warpchase_sub) (struct block_list *bl, va_list ap); + bool (*is_in_battle_state) (const struct mob_data *md); int (*ai_sub_hard_slavemob) (struct mob_data *md, int64 tick); int (*unlocktarget) (struct mob_data *md, int64 tick); int (*randomwalk) (struct mob_data *md, int64 tick); @@ -494,7 +547,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); @@ -519,7 +573,7 @@ struct mob_interface { struct block_list* (*getfriendhprate) (struct mob_data *md, int min_rate, int max_rate); struct block_list* (*getmasterhpltmaxrate) (struct mob_data *md, int rate); int (*getfriendstatus_sub) (struct block_list *bl, va_list ap); - struct mob_data* (*getfriendstatus) (struct mob_data *md, int cond1, int cond2); + struct block_list *(*getfriendstatus) (struct mob_data *md, int cond1, int cond2); int (*skill_use) (struct mob_data *md, int64 tick, int event); int (*skill_event) (struct mob_data *md, struct block_list *src, int64 tick, int flag); int (*is_clone) (int class_); @@ -527,6 +581,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,9 +595,11 @@ 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 (*read_db_viewdata_sub) (struct mob_db *entry, struct config_setting_t *t); void (*name_constants) (void); - bool (*readdb_mobavail) (char *str[], int columns, int current); + void (*mobavail_removal_notice) (void); int (*read_randommonster) (void); bool (*parse_row_chatdb) (char **str, const char *source, int line, int *last_msg_id); void (*readchatdb) (void); @@ -552,6 +612,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 7f57a9c50..40ec380ee 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -1257,6 +1258,9 @@ static void run_tomb(struct map_session_data *sd, struct npc_data *nd) char time[10]; nullpo_retv(nd); + + sd->npc_id = nd->bl.id; + strftime(time, sizeof(time), "%H:%M", localtime(&nd->u.tomb.kill_time)); // TODO: Find exact color? @@ -1331,49 +1335,68 @@ static int npc_click(struct map_session_data *sd, struct npc_data *nd) return 0; } -/*========================================== +/** + * Validates a character's script related data and (re-)runs the script if validation was successful. * - *------------------------------------------*/ + * Is called when: + * - The Next/Close button was clicked. + * - A menu option was selected. + * - A value was entered by input() script command. + * - A progress bar has reached 100%. + * - The character timed out because of idling. + * + * @param sd The character's session data. + * @param id The NPC ID. + * @param closing Whether the script is closing, or not. + * @return 0 on success, otherwise 1. + * +**/ static int npc_scriptcont(struct map_session_data *sd, int id, bool closing) { - struct block_list *target = map->id2bl(id); nullpo_retr(1, sd); - if( id != sd->npc_id ){ - struct npc_data *nd_sd = map->id2nd(sd->npc_id); - struct npc_data *nd = BL_CAST(BL_NPC, target); - ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n", - nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id, - nd?(char*)nd->name:"'Unknown NPC'", (int)id); - return 1; + struct block_list *target = map->id2bl(id); + +#ifdef SECURE_NPCTIMEOUT + if (sd->npc_idle_timer != INVALID_TIMER) { /// Not yet timed out. +#endif + if (id != sd->npc_id) { + struct npc_data *nd_sd = map->id2nd(sd->npc_id); + struct npc_data *nd = BL_CAST(BL_NPC, target); + + ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n", + (nd_sd != NULL) ? nd_sd->name : "'Unknown NPC'", sd->npc_id, + (nd != NULL) ? nd->name : "'Unknown NPC'", id); + + return 1; + } +#ifdef SECURE_NPCTIMEOUT } +#endif - if(id != npc->fake_nd->bl.id) { // Not item script - if ((npc->checknear(sd,target)) == NULL){ - ShowWarning("npc_scriptcont: failed npc->checknear test.\n"); + if (id != npc->fake_nd->bl.id) { /// Not an item script. + if (sd->state.npc_unloaded != 0) { + sd->state.npc_unloaded = 0; + } else if (npc->checknear(sd, target) == NULL) { + ShowWarning("npc_scriptcont: Failed npc->checknear test.\n"); return 1; } } - /** - * For the Secure NPC Timeout option (check config/Secure.h) [RR] - **/ + #ifdef SECURE_NPCTIMEOUT - /** - * Update the last NPC iteration - **/ - sd->npc_idle_tick = timer->gettick(); + sd->npc_idle_tick = timer->gettick(); /// Update the last NPC iteration. #endif - /** - * WPE can get to this point with a progressbar; we deny it. - **/ - if( sd->progressbar.npc_id && DIFF_TICK(sd->progressbar.timeout,timer->gettick()) > 0 ) + /// WPE can get to this point with a progressbar; we deny it. + if (sd->progressbar.npc_id != 0 && DIFF_TICK(sd->progressbar.timeout, timer->gettick()) > 0) return 1; - if( !sd->st ) + if (sd->st == NULL) { + sd->npc_id = 0; return 1; + } - if( closing && sd->st->state == CLOSE ) + if (closing && sd->st->state == CLOSE) sd->st->state = END; script->run_main(sd->st); @@ -1449,11 +1472,16 @@ static int npc_cashshop_buylist(struct map_session_data *sd, int points, struct return ERROR_TYPE_NPC; if( nd->subtype != CASHSHOP ) { - if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) { + if (nd->subtype == SCRIPT && nd->u.scr.shop && + nd->u.scr.shop->type != NST_ZENY && + nd->u.scr.shop->type != NST_MARKET && + nd->u.scr.shop->type != NST_BARTER && + nd->u.scr.shop->type != NST_EXPANDED_BARTER) { shop = nd->u.scr.shop->item; shop_size = nd->u.scr.shop->items; - } else + } else { return ERROR_TYPE_NPC; + } } else { shop = nd->u.shop.shop_item; shop_size = nd->u.shop.count; @@ -1584,11 +1612,11 @@ static void npc_market_fromsql(void) if( !(nd = npc->name2id(name)) ) { ShowError("npc_market_fromsql: NPC '%s' not found! skipping...\n",name); - npc->market_delfromsql_sub(name, USHRT_MAX); + npc->market_delfromsql_sub(name, INT_MAX); continue; - } else if ( nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET ) { + } else if (nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET) { ShowError("npc_market_fromsql: NPC '%s' is not proper for market, skipping...\n",name); - npc->market_delfromsql_sub(name, USHRT_MAX); + npc->market_delfromsql_sub(name, INT_MAX); continue; } @@ -1610,20 +1638,20 @@ static void npc_market_fromsql(void) /** * Saves persistent NPC Market Data into SQL **/ -static void npc_market_tosql(struct npc_data *nd, unsigned short index) +static void npc_market_tosql(struct npc_data *nd, int index) { nullpo_retv(nd); - Assert_retv(index < nd->u.scr.shop->items); - if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%u')", + Assert_retv(index >= 0 && index < nd->u.scr.shop->items); + if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%d')", map->npc_market_data_db, nd->exname, nd->u.scr.shop->item[index].nameid, nd->u.scr.shop->item[index].qty)) Sql_ShowDebug(map->mysql_handle); } /** * Removes persistent NPC Market Data from SQL */ -static void npc_market_delfromsql_sub(const char *npcname, unsigned short index) +static void npc_market_delfromsql_sub(const char *npcname, int index) { - if( index == USHRT_MAX ) { + if (index == INT_MAX ) { if( SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_market_data_db, npcname) ) Sql_ShowDebug(map->mysql_handle); } else { @@ -1635,12 +1663,290 @@ static void npc_market_delfromsql_sub(const char *npcname, unsigned short index) /** * Removes persistent NPC Market Data from SQL **/ -static void npc_market_delfromsql(struct npc_data *nd, unsigned short index) +static void npc_market_delfromsql(struct npc_data *nd, int index) +{ + nullpo_retv(nd); + Assert_retv(index == INT_MAX || (index >= 0 && index < nd->u.scr.shop->items)); + npc->market_delfromsql_sub(nd->exname, index == INT_MAX ? index : nd->u.scr.shop->item[index].nameid); +} + +/** + * Loads persistent NPC Barter Data from SQL + **/ +static void npc_barter_fromsql(void) +{ + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + char name[NAME_LENGTH + 1]; + int itemid; + int amount; + int removeId; + int removeAmount; + + if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `name`, `itemId`, `amount`, `priceId`, `priceAmount` FROM `%s`", map->npc_barter_data_db) + || SQL_ERROR == SQL->StmtExecute(stmt) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return; + } + + SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof name, NULL, NULL); + SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, sizeof itemid, NULL, NULL); + SQL->StmtBindColumn(stmt, 2, SQLDT_UINT32, &amount, sizeof amount, NULL, NULL); + SQL->StmtBindColumn(stmt, 3, SQLDT_INT, &removeId, sizeof removeId, NULL, NULL); + SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &removeAmount, sizeof removeAmount, NULL, NULL); + + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + struct npc_data *nd = NULL; + unsigned short i; + + if (!(nd = npc->name2id(name))) { + ShowError("npc_barter_fromsql: NPC '%s' not found! skipping...\n",name); + npc->barter_delfromsql_sub(name, INT_MAX, 0, 0); + continue; + } else if (nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_BARTER) { + ShowError("npc_barter_fromsql: NPC '%s' is not proper for barter, skipping...\n",name); + npc->barter_delfromsql_sub(name, INT_MAX, 0, 0); + continue; + } + + for (i = 0; i < nd->u.scr.shop->items; i++) { + struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == itemid && item->value == removeId && item->value2 == removeAmount) { + item->qty = amount; + break; + } + } + + if (i == nd->u.scr.shop->items) { + ShowError("npc_barter_fromsql: NPC '%s' does not sell item %d (qty %d), deleting...\n", name, itemid, amount); + npc->barter_delfromsql_sub(name, itemid, removeId, removeAmount); + continue; + } + } + SQL->StmtFree(stmt); +} + +/** + * Saves persistent NPC Barter Data into SQL + **/ +static void npc_barter_tosql(struct npc_data *nd, int index) { nullpo_retv(nd); - Assert_retv(index == USHRT_MAX || index < nd->u.scr.shop->items); - npc->market_delfromsql_sub(nd->exname, index == USHRT_MAX ? index : nd->u.scr.shop->item[index].nameid); + Assert_retv(index >= 0 && index < nd->u.scr.shop->items); + const struct npc_item_list *const item = &nd->u.scr.shop->item[index]; + if (item->qty == -1) + return; + if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s', '%d', '%d', '%u', '%d')", + map->npc_barter_data_db, nd->exname, item->nameid, item->qty, item->value, item->value2)) { + Sql_ShowDebug(map->mysql_handle); + } } + +/** + * Removes persistent NPC Barter Data from SQL + */ +static void npc_barter_delfromsql_sub(const char *npcname, int itemId, int itemId2, int amount2) +{ + if (itemId == INT_MAX) { + if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_barter_data_db, npcname)) + Sql_ShowDebug(map->mysql_handle); + } else { + if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s' AND `itemId`='%d' AND `priceId`='%d' AND `priceAmount`='%d' LIMIT 1", + map->npc_barter_data_db, npcname, itemId, itemId2, amount2)) { + Sql_ShowDebug(map->mysql_handle); + } + } +} + +/** + * Removes persistent NPC Barter Data from SQL + **/ +static void npc_barter_delfromsql(struct npc_data *nd, int index) +{ + nullpo_retv(nd); + if (index == INT_MAX) { + npc->barter_delfromsql_sub(nd->exname, INT_MAX, 0, 0); + } else { + Assert_retv(index >= 0 && index < nd->u.scr.shop->items); + const struct npc_item_list *const item = &nd->u.scr.shop->item[index]; + npc->barter_delfromsql_sub(nd->exname, item->nameid, item->value, item->value2); + } +} + + +/** + * Loads persistent NPC Expanded Barter Data from SQL + **/ +static void npc_expanded_barter_fromsql(void) +{ + struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle); + char name[NAME_LENGTH + 1]; + int itemid; + int amount; + int zeny; + StringBuf buf; + + StrBuf->Init(&buf); + StrBuf->AppendStr(&buf, "SELECT `name`, `itemId`, `amount`, `zeny`"); + for (int k = 1; k < 11; k ++) { + StrBuf->Printf(&buf, ", `currencyId%d`, `currencyAmount%d`, `currencyRefine%d`", k, k, k); + } + StrBuf->Printf(&buf, " FROM `%s`", map->npc_expanded_barter_data_db); + + if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf)) + || SQL_ERROR == SQL->StmtExecute(stmt) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); + return; + } + + struct npc_barter_currency tempCurrency[10]; + SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof name, NULL, NULL); + SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, sizeof itemid, NULL, NULL); + SQL->StmtBindColumn(stmt, 2, SQLDT_UINT32, &amount, sizeof amount, NULL, NULL); + SQL->StmtBindColumn(stmt, 3, SQLDT_UINT32, &zeny, sizeof zeny, NULL, NULL); + for (int k = 0; k < 10; k ++) { + SQL->StmtBindColumn(stmt, k * 3 + 4, SQLDT_INT, &tempCurrency[k].nameid, sizeof tempCurrency[k].nameid, NULL, NULL); + SQL->StmtBindColumn(stmt, k * 3 + 5, SQLDT_INT, &tempCurrency[k].amount, sizeof tempCurrency[k].amount, NULL, NULL); + SQL->StmtBindColumn(stmt, k * 3 + 6, SQLDT_INT, &tempCurrency[k].refine, sizeof tempCurrency[k].refine, NULL, NULL); + } + + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + struct npc_data *nd = NULL; + unsigned short i; + + if ((nd = npc->name2id(name)) == NULL) { + ShowError("npc_expanded_barter_fromsql: NPC '%s' not found! skipping...\n",name); + npc->expanded_barter_delfromsql_sub(name, INT_MAX, 0, 0, NULL); + continue; + } else if (nd->subtype != SCRIPT || nd->u.scr.shop == NULL || nd->u.scr.shop->items == 0 || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowError("npc_expanded_barter_fromsql: NPC '%s' is not proper for barter, skipping...\n",name); + npc->expanded_barter_delfromsql_sub(name, INT_MAX, 0, 0, NULL); + continue; + } + + for (i = 0; i < nd->u.scr.shop->items; i++) { + struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == itemid && item->value == zeny) { + int count = nd->u.scr.shop->item[i].value2; + if (count > 10) + count = 10; + int curIndex; + for (curIndex = 0; curIndex < count; curIndex ++) { + struct npc_barter_currency *currency = &nd->u.scr.shop->item[i].currency[curIndex]; + struct npc_barter_currency *currency2 = &tempCurrency[curIndex]; + if (currency->nameid != currency2->nameid || + currency->amount != currency2->amount || + currency->refine != currency2->refine) { + break; + } + } + if (curIndex == count) { + item->qty = amount; + break; + } + } + } + + if (i == nd->u.scr.shop->items) { + ShowError("npc_expanded_barter_fromsql: NPC '%s' does not sell item %d (qty %d), deleting...\n", name, itemid, amount); + npc->expanded_barter_delfromsql_sub(name, itemid, zeny, 10, &tempCurrency[0]); + continue; + } + } + SQL->StmtFree(stmt); + StrBuf->Destroy(&buf); +} + +/** + * Saves persistent NPC Expanded Barter Data into SQL + **/ +static void npc_expanded_barter_tosql(struct npc_data *nd, int index) +{ + nullpo_retv(nd); + Assert_retv(index >= 0 && index < nd->u.scr.shop->items); + const struct npc_item_list *const item = &nd->u.scr.shop->item[index]; + if (item->qty == -1) + return; + + npc->expanded_barter_delfromsql(nd, index); + + StringBuf buf; + StrBuf->Init(&buf); + StrBuf->Printf(&buf, "INSERT INTO `%s` VALUES ('%s', '%d', '%d', '%u'", map->npc_expanded_barter_data_db, nd->exname, item->nameid, item->qty, item->value); + int currencyCount = item->value2; + if (currencyCount > 10) + currencyCount = 10; + int k; + for (k = 0; k < currencyCount; k++) { + struct npc_barter_currency *currency = &item->currency[k]; + StrBuf->Printf(&buf, ", '%d', '%d', '%d'", currency->nameid, currency->amount, currency->refine); + } + for (; k < 10; k ++) { + StrBuf->Printf(&buf, ", '0', '0', '0'"); + } + StrBuf->AppendStr(&buf, ")"); + + if (SQL_ERROR == SQL->QueryStr(map->mysql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(map->mysql_handle); + } + StrBuf->Destroy(&buf); +} + +/** + * Removes persistent NPC Expanded Barter Data from SQL + */ +static void npc_expanded_barter_delfromsql_sub(const char *npcname, int itemId, int zeny, int currencyCount, struct npc_barter_currency* currency) +{ + if (itemId == INT_MAX) { + if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_expanded_barter_data_db, npcname)) + Sql_ShowDebug(map->mysql_handle); + } else { + StringBuf buf; + + StrBuf->Init(&buf); + StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE `name`='%s' AND `itemId`='%d' AND `zeny`='%d'", + map->npc_expanded_barter_data_db, npcname, itemId, zeny); + int k = 0; + if (currencyCount > 10) + currencyCount = 10; + for (k = 0; k < currencyCount; k++) { + struct npc_barter_currency *currency1 = ¤cy[k]; + StrBuf->Printf(&buf, " AND currencyId%d='%d' and currencyAmount%d='%d' and currencyRefine%d='%d'", + k + 1, currency1->nameid, k + 1, currency1->amount, k + 1, currency1->refine); + } + for (; k < 10; k ++) { + StrBuf->Printf(&buf, " AND currencyId%d='0' and currencyAmount%d='0' and currencyRefine%d='0'", + k + 1, k + 1, k + 1); + } + StrBuf->AppendStr(&buf, " LIMIT 1"); + + if (SQL_ERROR == SQL->QueryStr(map->mysql_handle, StrBuf->Value(&buf))) { + Sql_ShowDebug(map->mysql_handle); + } + StrBuf->Destroy(&buf); + } +} + + +/** + * Removes persistent NPC Expanded Barter Data from SQL + **/ +static void npc_expanded_barter_delfromsql(struct npc_data *nd, int index) +{ + nullpo_retv(nd); + if (index == INT_MAX) { + npc->expanded_barter_delfromsql_sub(nd->exname, INT_MAX, 0, 0, NULL); + } else { + Assert_retv(index >= 0 && index < nd->u.scr.shop->items); + const struct npc_item_list *const item = &nd->u.scr.shop->item[index]; + npc->expanded_barter_delfromsql_sub(nd->exname, item->nameid, item->value, item->value2, &item->currency[0]); + } +} + /** * Judges whether to allow and spawn a trader's window. **/ @@ -1673,6 +1979,12 @@ static bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) clif->npc_market_open(sd,nd); } break; + case NST_BARTER: + clif->npc_barter_open(sd, nd); + break; + case NST_EXPANDED_BARTER: + clif->npc_expanded_barter_open(sd, nd); + break; default: clif->cashshop_show(sd,nd); break; @@ -1799,11 +2111,16 @@ static int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, return ERROR_TYPE_ITEM_ID; // Invalid Item if( nd->subtype != CASHSHOP ) { - if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) { + if (nd->subtype == SCRIPT && nd->u.scr.shop && + nd->u.scr.shop->type != NST_ZENY && + nd->u.scr.shop->type != NST_MARKET && + nd->u.scr.shop->type != NST_BARTER && + nd->u.scr.shop->type != NST_EXPANDED_BARTER) { shop = nd->u.scr.shop->item; shop_size = nd->u.scr.shop->items; - } else + } else { return ERROR_TYPE_NPC; + } } else { shop = nd->u.shop.shop_item; shop_size = nd->u.shop.count; @@ -1948,7 +2265,11 @@ static int npc_buylist(struct map_session_data *sd, struct itemlist *item_list) break; case ADDITEM_OVERAMOUNT: +#if PACKETVER >= 20110705 + return 9; +#else return 2; +#endif } value = pc->modifybuyvalue(sd,value); @@ -2002,7 +2323,7 @@ static int npc_buylist(struct map_session_data *sd, struct itemlist *item_list) /** * Processes incoming npc market purchase list **/ -static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list) +static enum market_buy_result npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list) { struct npc_data* nd; struct npc_item_list *shop = NULL; @@ -2016,7 +2337,7 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item nd = npc->checknear(sd,map->id2bl(sd->npc_shopid)); if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_MARKET) - return 1; + return MARKET_BUY_RESULT_ERROR; shop = nd->u.scr.shop->item; shop_size = nd->u.scr.shop->items; @@ -2036,18 +2357,18 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item entry->id == itemdb_viewid(shop[j].nameid) //item_avail replacement ); if (j == shop_size) /* TODO find official response for this */ - return 1; // no such item in shop + return MARKET_BUY_RESULT_ERROR; // no such item in shop entry->id = shop[j].nameid; //item_avail replacement if (entry->amount > (int)shop[j].qty) - return 1; + return MARKET_BUY_RESULT_AMOUNT_TOO_BIG; value = shop[j].value; npc_market_qty[i] = j; if (!itemdb->exists(entry->id)) /* TODO find official response for this */ - return 1; // item no longer in itemdb + return MARKET_BUY_RESULT_ERROR; // item no longer in itemdb if (!itemdb->isstackable(entry->id) && entry->amount > 1) { //Exploit? You can't buy more than 1 of equipment types o.O @@ -2071,13 +2392,13 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item } if (z > sd->status.zeny) /* TODO find official response for this */ - return 1; // Not enough Zeny + return MARKET_BUY_RESULT_NO_ZENY; // Not enough Zeny if( w + sd->weight > sd->max_weight ) /* TODO find official response for this */ - return 1; // Too heavy + return MARKET_BUY_RESULT_OVER_WEIGHT; // Too heavy if( pc->inventoryblank(sd) < new_ ) /* TODO find official response for this */ - return 1; // Not enough space to store items + return MARKET_BUY_RESULT_OUT_OF_SPACE; // Not enough space to store items pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL); @@ -2087,7 +2408,7 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item j = npc_market_qty[i]; if (entry->amount > (int)shop[j].qty) /* wohoo someone tampered with the packet. */ - return 1; + return MARKET_BUY_RESULT_AMOUNT_TOO_BIG; shop[j].qty -= entry->amount; @@ -2105,7 +2426,283 @@ static int npc_market_buylist(struct map_session_data *sd, struct itemlist *item } } - return 0; + return MARKET_BUY_RESULT_SUCCESS; +} + +/** + * Processes incoming npc barter purchase list + **/ +static int npc_barter_buylist(struct map_session_data *sd, struct barteritemlist *item_list) +{ + struct npc_data* nd; + struct npc_item_list *shop = NULL; + int w, new_; + unsigned short shop_size = 0; + + nullpo_retr(1, sd); + nullpo_retr(1, item_list); + + nd = npc->checknear(sd, map->id2bl(sd->npc_shopid)); + + if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_BARTER) + return 11; + + shop = nd->u.scr.shop->item; + shop_size = nd->u.scr.shop->items; + + w = 0; + new_ = 0; + + int items[MAX_INVENTORY] = { 0 }; + + // process entries in buy list, one by one + for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + + const int n = entry->removeIndex; + if (n < 0 || n >= sd->status.inventorySize) + return 11; // wrong inventory index + + if (entry->addAmount <= 0) + return 14; // not enough item amount in inventory + + int removeId = sd->status.inventory[n].nameid; + const int j = entry->shopIndex; + if (j < 0 || j >= shop_size) + return 13; // no such item in shop + if (entry->addId != shop[j].nameid && entry->addId != itemdb_viewid(shop[j].nameid)) + return 13; // no such item in shop + if (removeId != shop[j].value && removeId != itemdb_viewid(shop[j].value)) + return 13; // no such item in shop + entry->addId = shop[j].nameid; // item_avail replacement + removeId = shop[j].value; // item_avail replacement + + if (!itemdb->exists(entry->addId)) + return 13; // item no longer in itemdb + + const int removeAmount = shop[j].value2; + + if ((int)shop[j].qty != -1 && entry->addAmount > (int)shop[j].qty) + return 14; // not enough item amount in shop + + if (removeAmount * entry->addAmount > sd->status.inventory[n].amount) + return 14; // not enough item amount in inventory + + items[n] += removeAmount * entry->addAmount; + + if (items[n] > sd->status.inventory[n].amount) + return 14; // not enough item amount in inventory + + entry->addId = shop[j].nameid; //item_avail replacement + + npc_market_qty[i] = j; + + if (!itemdb->isstackable(entry->addId) && entry->addAmount > 1) { + //Exploit? You can't buy more than 1 of equipment types o.O + ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n", + sd->status.name, sd->status.account_id, sd->status.char_id, entry->addAmount, entry->addId); + entry->addAmount = 1; + } + + switch (pc->checkadditem(sd, entry->addId, entry->addAmount)) { + case ADDITEM_EXIST: + break; + case ADDITEM_NEW: + new_++; + break; + case ADDITEM_OVERAMOUNT: /* TODO find official response for this */ + return 1; + } + + w += itemdb_weight(entry->addId) * entry->addAmount; + w -= itemdb_weight(removeId) * removeAmount; + } + + if (w + sd->weight > sd->max_weight) + return 2; // Too heavy + + if (pc->inventoryblank(sd) < new_) + return 3; // Not enough space to store items + + for (int i = 0; i < sd->status.inventorySize; ++i) { + const int removeAmountTotal = items[i]; + if (removeAmountTotal == 0) + continue; + if (pc->delitem(sd, i, removeAmountTotal, 0, DELITEM_SOLD, LOG_TYPE_NPC) != 0) { + return 11; // unknown exploit + } + } + + for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + const int shopIdx = npc_market_qty[i]; + + if ((int)shop[shopIdx].qty != -1) { + if (entry->addAmount > (int)shop[shopIdx].qty) /* wohoo someone tampered with the packet. */ + return 14; + shop[shopIdx].qty -= entry->addAmount; + } + + npc->barter_tosql(nd, shopIdx); + + if (itemdb_type(entry->addId) == IT_PETEGG) { + pet->create_egg(sd, entry->addId); + } else { + struct item item_tmp; + memset(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = entry->addId; + item_tmp.identify = 1; + pc->additem(sd, &item_tmp, entry->addAmount, LOG_TYPE_NPC); + } + } + + return 12; +} + + +/** + * Processes incoming npc expanded barter purchase list + **/ +static int npc_expanded_barter_buylist(struct map_session_data *sd, struct barteritemlist *item_list) +{ + nullpo_retr(1, sd); + nullpo_retr(1, item_list); + + struct npc_data* nd = npc->checknear(sd, map->id2bl(sd->npc_shopid)); + + if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || + !nd->u.scr.shop || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + return 11; + } + + struct npc_item_list *shop = nd->u.scr.shop->item; + unsigned short shop_size = nd->u.scr.shop->items; + int w = 0; + int new_ = 0; + int64 z = 0; + int items[MAX_INVENTORY] = { 0 }; + + // process entries in buy list, one by one + for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + + if (entry->addAmount <= 0) + return 14; // not enough item amount in inventory + + const int j = entry->shopIndex; + if (j < 0 || j >= shop_size) + return 13; // no such item in shop + if (entry->addId != shop[j].nameid && entry->addId != itemdb_viewid(shop[j].nameid)) + return 13; // no such item in shop + entry->addId = shop[j].nameid; // item_avail replacement + if (!itemdb->exists(entry->addId)) + return 13; // item no longer in itemdb + + if ((int)shop[j].qty != -1 && entry->addAmount > (int)shop[j].qty) + return 14; // not enough item amount in shop + + int currencyCount = shop[j].value2; + for (int currencyIndex = 0; currencyIndex < currencyCount; currencyIndex ++) { + struct npc_barter_currency *currency = &shop[j].currency[currencyIndex]; + const int currencyItemId = currency->nameid; + const int currencyRefine = currency->refine; + int removeAmount = currency->amount * entry->addAmount; + if (removeAmount <= 0) + continue; + for (int n = 0; n < sd->status.inventorySize && removeAmount > 0; ++n) { + // check item id and existing amount + if (sd->status.inventory[n].nameid == currencyItemId && sd->status.inventory[n].amount > 0) { + // check item refine level + if (currencyRefine != -1 && sd->status.inventory[n].refine != currencyRefine) + continue; + if (sd->status.inventory[n].amount >= removeAmount) { + items[n] += removeAmount; + removeAmount = 0; + w -= itemdb_weight(currencyItemId) * removeAmount; + break; + } else { + items[n] += sd->status.inventory[n].amount; + removeAmount -= sd->status.inventory[n].amount; + w -= itemdb_weight(currencyItemId) * sd->status.inventory[n].amount; + } + } + if (items[n] > sd->status.inventory[n].amount) + return 14; // not enough item amount in inventory + } + if (removeAmount != 0) { + return 14; // not enough item amount in inventory + } + } + + entry->addId = shop[j].nameid; //item_avail replacement + + npc_market_qty[i] = j; + + if (!itemdb->isstackable(entry->addId) && entry->addAmount > 1) { + //Exploit? You can't buy more than 1 of equipment types o.O + ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n", + sd->status.name, sd->status.account_id, sd->status.char_id, entry->addAmount, entry->addId); + entry->addAmount = 1; + } + + switch (pc->checkadditem(sd, entry->addId, entry->addAmount)) { + case ADDITEM_EXIST: + break; + case ADDITEM_NEW: + new_++; + break; + case ADDITEM_OVERAMOUNT: /* TODO find official response for this */ + return 1; + } + + z += (int64)shop[j].value * entry->addAmount; + w += itemdb_weight(entry->addId) * entry->addAmount; + } + + if (z > sd->status.zeny) + return 3; // Not enough Zeny + + if ((int64)w + sd->weight > sd->max_weight) + return 2; // Too heavy + + if (pc->inventoryblank(sd) < new_) + return 3; // Not enough space to store items + + for (int i = 0; i < sd->status.inventorySize; ++i) { + const int removeAmountTotal = items[i]; + if (removeAmountTotal == 0) + continue; + if (pc->delitem(sd, i, removeAmountTotal, 0, DELITEM_SOLD, LOG_TYPE_NPC) != 0) { + return 11; // unknown exploit + } + } + + pc->payzeny(sd, (int)z, LOG_TYPE_NPC, NULL); + + for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) { + struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); + const int shopIdx = npc_market_qty[i]; + + if ((int)shop[shopIdx].qty != -1) { + if (entry->addAmount > (int)shop[shopIdx].qty) /* wohoo someone tampered with the packet. */ + return 14; + shop[shopIdx].qty -= entry->addAmount; + } + + npc->expanded_barter_tosql(nd, shopIdx); + + if (itemdb_type(entry->addId) == IT_PETEGG) { + pet->create_egg(sd, entry->addId); + } else { + struct item item_tmp; + memset(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = entry->addId; + item_tmp.identify = 1; + pc->additem(sd, &item_tmp, entry->addAmount, LOG_TYPE_NPC); + } + } + + return 12; } /// npc_selllist for script-controlled shops @@ -2231,7 +2828,7 @@ static int npc_selllist(struct map_session_data *sd, struct itemlist *item_list) struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i); int nameid, value, idx = entry->id; - if (idx >= MAX_INVENTORY || idx < 0 || entry->amount < 0) { + if (idx >= sd->status.inventorySize || idx < 0 || entry->amount < 0) { return 1; } @@ -2354,120 +2951,193 @@ static int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap) return 0; } -//Chk if npc matches src_id, then unload. -//Sub-function used to find duplicates. +/** + * Unloads a NPC if it's a duplicate of the passed one. + * + * @param nd The NPC to check. + * @param args List of arguments. + * @return Always 0. + * + **/ static int npc_unload_dup_sub(struct npc_data *nd, va_list args) { - int src_id; - nullpo_ret(nd); - src_id = va_arg(args, int); + + const int src_id = va_arg(args, int); + const int unload_mobs = va_arg(args, int); + if (nd->src_id == src_id) - npc->unload(nd, true); + npc->unload(nd, true, (unload_mobs == 1)); + return 0; } -//Removes all npcs that are duplicates of the passed one. [Skotlex] -static void npc_unload_duplicates(struct npc_data *nd) +/** + * Unloads all NPCs which are duplicates of the passed one. + * + * @param nd The source NPC. + * @param unload_mobs If true, mobs spawned by duplicates will be removed. + * + * @author Skotlex + * + **/ +static void npc_unload_duplicates(struct npc_data *nd, bool unload_mobs) { nullpo_retv(nd); - map->foreachnpc(npc->unload_dup_sub,nd->bl.id); + + map->foreachnpc(npc->unload_dup_sub, nd->bl.id, unload_mobs); } -//Removes an npc from map and db. -//Single is to free name (for duplicates). -static int npc_unload(struct npc_data *nd, bool single) +/** + * Removes a mob, which was spawned by a NPC (monster/areamonster/guardian/bg_monster/atcommand("@monster xy")). + * + * @param md The mob to remove. + * @param args List of arguments. + * @return 1 on success, 0 on failure. + * + * @author Kenpachi + * + **/ +static int npc_unload_mob(struct mob_data *md, va_list args) +{ + nullpo_ret(md); + + const int npc_id = va_arg(args, int); + + if (md->npc_id == npc_id) { + md->state.npc_killmonster = 1; + status_kill(&md->bl); + return 1; + } + + return 0; +} + +/** + * Removes a NPC from map and database. + * + * @param nd The NPC which should be removed. + * @param single If true, names are freed. (For duplicates.) + * @param unload_mobs If true, mobs spawned by the NPC will be removed. + * @return Always 0. + * + **/ +static int npc_unload(struct npc_data *nd, bool single, bool unload_mobs) { nullpo_ret(nd); - if( nd->ud && nd->ud != &npc->base_ud ) { + if (nd->ud != NULL && nd->ud != &npc->base_ud) skill->clear_unitgroup(&nd->bl); - } npc->remove_map(nd); map->deliddb(&nd->bl); - if( single ) + + if (single) strdb_remove(npc->name_db, nd->exname); - if (nd->chat_id) // remove npc chatroom object and kick users + if (nd->chat_id != 0) /// Remove NPC chatroom object and kick users. chat->delete_npc_chat(nd); - npc_chat->finalize(nd); // deallocate npc PCRE data structures + npc_chat->finalize(nd); /// Deallocate NPC PCRE data structures. if (single && nd->path != NULL) { npc->releasepathreference(nd->path); nd->path = NULL; } - if( single && nd->bl.m != -1 ) - map->remove_questinfo(nd->bl.m,nd); + if (single && nd->bl.m != INDEX_NOT_FOUND) + 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] - aFree(nd->u.shop.shop_item); + if (nd->src_id == 0 && (nd->subtype == SHOP || nd->subtype == CASHSHOP)) { + aFree(nd->u.shop.shop_item); /// src check for duplicate shops. [Orcao] } else if (nd->subtype == SCRIPT) { - struct s_mapiterator *iter; - struct map_session_data *sd = NULL; + char evname[EVENT_NAME_LENGTH]; + + snprintf(evname, ARRAYLENGTH(evname), "%s::OnNPCUnload", nd->exname); + + struct event_data *ev = strdb_get(npc->ev_db, evname); + + if (ev != NULL) + script->run_npc(nd->u.scr.script, ev->pos, 0, nd->bl.id); /// Run OnNPCUnload. - if( single ) { - npc->ev_db->foreach(npc->ev_db,npc->unload_ev,nd->exname); //Clean up all events related - npc->ev_label_db->foreach(npc->ev_label_db,npc->unload_ev_label,nd); + if (single) { + npc->ev_db->foreach(npc->ev_db, npc->unload_ev, nd->exname); /// Clean up all related events. + npc->ev_label_db->foreach(npc->ev_label_db, npc->unload_ev_label, nd); } - iter = mapit_geteachpc(); - for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { - if (sd->npc_timer_id != INVALID_TIMER ) { + struct s_mapiterator *iter = mapit_geteachpc(); + struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter)); + + for (; mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) { + if (sd->npc_timer_id != INVALID_TIMER) { const struct TimerData *td = timer->get(sd->npc_timer_id); - if( td && td->id != nd->bl.id ) + if (td != NULL && td->id != nd->bl.id) continue; - if( td && td->data ) + if (td != NULL && td->data != 0) ers_free(npc->timer_event_ers, (void*)td->data); + timer->delete(sd->npc_timer_id, npc->timerevent); sd->npc_timer_id = INVALID_TIMER; } } + mapit->free(iter); if (nd->u.scr.timerid != INVALID_TIMER) { - const struct TimerData *td; - td = timer->get(nd->u.scr.timerid); - if (td && td->data) + const struct TimerData *td = timer->get(nd->u.scr.timerid); + + if (td != NULL && td->data != 0) ers_free(npc->timer_event_ers, (void*)td->data); + timer->delete(nd->u.scr.timerid, npc->timerevent); } - if (nd->u.scr.timer_event) + + if (nd->u.scr.timer_event != NULL) aFree(nd->u.scr.timer_event); + if (nd->src_id == 0) { - if(nd->u.scr.script) { + if (nd->u.scr.script != NULL) { script->free_code(nd->u.scr.script); nd->u.scr.script = NULL; } - if (nd->u.scr.label_list) { + + if (nd->u.scr.label_list != NULL) { aFree(nd->u.scr.label_list); nd->u.scr.label_list = NULL; nd->u.scr.label_list_num = 0; } - if(nd->u.scr.shop) { - if(nd->u.scr.shop->item) + + if (nd->u.scr.shop != NULL) { + if (nd->u.scr.shop->item != NULL) { + for (int i = 0; i < nd->u.scr.shop->items; i ++) { + if (nd->u.scr.shop->item[i].currency != NULL) + aFree(nd->u.scr.shop->item[i].currency); + } aFree(nd->u.scr.shop->item); + } + aFree(nd->u.scr.shop); } } - if( nd->u.scr.guild_id ) + + if (nd->u.scr.guild_id > 0) guild->flag_remove(nd); } - if( nd->ud && nd->ud != &npc->base_ud ) { + if (nd->ud != NULL && nd->ud != &npc->base_ud) { aFree(nd->ud); nd->ud = NULL; } - HPM->data_store_destroy(&nd->hdata); + if (unload_mobs) + map->foreachmob(npc->unload_mob, nd->bl.id); + HPM->data_store_destroy(&nd->hdata); aFree(nd); - return 0; } @@ -2722,7 +3392,7 @@ static bool npc_viewisid(const char *viewid) * @param class_ The NPC view class. * @return A pointer to the created NPC data (ownership passed to the caller). */ -static struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, uint8 dir, int16 class_) +static struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, enum unit_dir dir, int class_) { struct npc_data *nd; @@ -2739,6 +3409,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; } @@ -3672,14 +4343,14 @@ static void npc_setdisplayname(struct npc_data *nd, const char *newname) safestrncpy(nd->name, newname, sizeof(nd->name)); if( map->list[nd->bl.m].users ) - clif->charnameack(0, &nd->bl); + clif->blname_ack(0, &nd->bl); } /// Changes the display class of the npc. /// /// @param nd Target npc /// @param class_ New display class -static void npc_setclass(struct npc_data *nd, short class_) +static void npc_setclass(struct npc_data *nd, int class_) { nullpo_retv(nd); @@ -3843,18 +4514,23 @@ static const char *npc_parse_function(const char *w1, const char *w2, const char return end; } -/*========================================== - * Parse Mob 1 - Parse mob list into each map - * Parse Mob 2 - Actually Spawns Mob - * [Wizputer] - *------------------------------------------*/ +/** + * Spawns a mob by using the passed spawn data. (Permanent mob spawns.) + * npc_parse_mob() - Parses mob list into each map. + * npc_parse_mob2() - Actually spawns mob. + * + * @param mobspawn The mobs spawn data. + * + * @author Wizputer + * + **/ static void npc_parse_mob2(struct spawn_data *mobspawn) { - int i; - nullpo_retv(mobspawn); - for( i = mobspawn->active; i < mobspawn->num; ++i ) { - struct mob_data* md = mob->spawn_dataset(mobspawn); + + for (int i = mobspawn->active; i < mobspawn->num; ++i) { + struct mob_data *md = mob->spawn_dataset(mobspawn, 0); + md->spawn = mobspawn; md->spawn->active++; mob->spawn(md); @@ -3960,7 +4636,7 @@ static const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, mobspawn.num = (unsigned short)num; mobspawn.active = 0; - mobspawn.class_ = (short) class_; + mobspawn.class_ = class_; mobspawn.x = (unsigned short)x; mobspawn.y = (unsigned short)y; mobspawn.xs = (signed short)xs; @@ -4126,8 +4802,7 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char if (!strcmpi(w3, "nosave")) { char savemap[32]; int savex, savey; - if (state == 0) - ; //Map flag disabled. + if (state == 0); //Map flag disabled. else if (w4 && !strcmpi(w4, "SavePoint")) { map->list[m].save.map = 0; map->list[m].save.x = -1; @@ -4420,7 +5095,8 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char } } - if( modifier[0] == '\0' ) { + if (state == 0); //Map flag disabled. + else if (modifier[0] == '\0') { ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_unit_duration' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; } else if( !( skill_id = skill->name2id(skill_name) ) || !skill->get_unit_id( skill->name2id(skill_name), 0) ) { @@ -4479,7 +5155,8 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char } } - if( modifier[0] == '\0' ) { + if (state == 0); //Map flag disabled. + else if (modifier[0] == '\0') { ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_skill_damage' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer)); if (retval) *retval = EXIT_FAILURE; } else if( !( skill_id = skill->name2id(skill_name) ) ) { @@ -4555,6 +5232,10 @@ static const char *npc_parse_mapflag(const char *w1, const char *w2, const char map->list[m].flag.pairship_startable = (state) ? 1 : 0; } else if (!strcmpi(w3, "pairship_endable")) { map->list[m].flag.pairship_endable = (state) ? 1 : 0; + } else if (!strcmpi(w3, "nostorage")) { + map->list[m].flag.nostorage = (state) ? cap_value(atoi(w4), 1, 3) : 0; + } else if (!strcmpi(w3, "nogstorage")) { + map->list[m].flag.nogstorage = (state) ? cap_value(atoi(w4), 1, 3) : 0; } else { npc->parse_unknown_mapflag(mapname, w3, w4, start, buffer, filepath, retval); } @@ -4906,127 +5587,143 @@ static void npc_process_files(int npc_min) npc->npc_id - npc_min, npc->npc_warp, npc->npc_shop, npc->npc_script, npc->npc_mob, npc->npc_cache_mob, npc->npc_delay_mob); } -//Clear then reload npcs files +/** + * Clears and then reloads all NPC files. + * + * @return Always 0. + * + **/ static int npc_reload(void) { - int npc_new_min = npc->npc_id; - struct s_mapiterator* iter; - struct block_list* bl; - - if (map->retval == EXIT_FAILURE) - map->retval = EXIT_SUCCESS; // Clear return status in case something failed before. - - /* clear guild flag cache */ - guild->flags_clear(); + if (map->retval == EXIT_FAILURE) /// Clear return status in case something failed before. + map->retval = EXIT_SUCCESS; + guild->flags_clear(); /// Clear guild flag cache. npc->path_db->clear(npc->path_db, npc->path_db_clear_sub); - db_clear(npc->name_db); db_clear(npc->ev_db); npc->ev_label_db->clear(npc->ev_label_db, npc->ev_label_db_clear_sub); - npc->npc_last_npd = NULL; npc->npc_last_path = NULL; npc->npc_last_ref = NULL; + + const int npc_new_min = npc->npc_id; + struct s_mapiterator *iter = mapit_geteachiddb(); - //Remove all npcs/mobs. [Skotlex] - iter = mapit_geteachiddb(); - for (bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) { - switch(bl->type) { - case BL_NPC: - if( bl->id != npc->fake_nd->bl.id )// don't remove fake_nd - npc->unload(BL_UCAST(BL_NPC, bl), false); - break; - case BL_MOB: - unit->free(bl,CLR_OUTSIGHT); - break; + /** Remove all NPCs/mobs. [Skotlex] **/ + for (struct block_list *bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) { + switch (bl->type) { + case BL_NPC: + if (bl->id != npc->fake_nd->bl.id) /// Don't remove fake_nd. + npc->unload(BL_UCAST(BL_NPC, bl), false, false); + + break; + case BL_MOB: + unit->free(bl, CLR_OUTSIGHT); + break; + default: + break; } } + mapit->free(iter); - if(battle_config.dynamic_mobs) {// dynamic check by [random] - int16 m; - for (m = 0; m < map->count; m++) { - int16 i; - for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) { + if (battle_config.dynamic_mobs) { /// Dynamic check. [random] + for (int m = 0; m < map->count; m++) { + for (int i = 0; i < MAX_MOB_LIST_PER_MAP; i++) { if (map->list[m].moblist[i] != NULL) { aFree(map->list[m].moblist[i]); map->list[m].moblist[i] = NULL; } - if( map->list[m].mob_delete_timer != INVALID_TIMER ) - { // Mobs were removed anyway,so delete the timer [Inkfish] + + if (map->list[m].mob_delete_timer != INVALID_TIMER) { /// Mobs were removed anyway, so delete the timer. [Inkfish] timer->delete(map->list[m].mob_delete_timer, map->removemobs_timer); map->list[m].mob_delete_timer = INVALID_TIMER; } } + if (map->list[m].npc_num > 0) - ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map->list[m].npc_num, map->list[m].name); + ShowWarning("npc_reload: %d NPCs weren't removed from map %s!\n", + map->list[m].npc_num, map->list[m].name); } } - // clear mob spawn lookup index mob->clear_spawninfo(); - - npc->npc_warp = npc->npc_shop = npc->npc_script = 0; - npc->npc_mob = npc->npc_cache_mob = npc->npc_delay_mob = 0; - - // reset mapflags + npc->npc_warp = 0; + npc->npc_shop = 0; + npc->npc_script = 0; + npc->npc_mob = 0; + npc->npc_cache_mob = 0; + npc->npc_delay_mob = 0; map->zone_reload(); map->flags_init(); - - // Reprocess npc files and reload constants itemdb->name_constants(); clan->set_constants(); - npc_process_files( npc_new_min ); - + npc_process_files(npc_new_min); instance->reload(); - map->zone_init(); - - npc->motd = npc->name2id("HerculesMOTD"); /* [Ind/Hercules] */ - - //Re-read the NPC Script Events cache. + npc->motd = npc->name2id("HerculesMOTD"); /// [Ind/Hercules] npc->read_event_script(); - // Execute main initialisation events - // The correct initialisation order is: - // OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2 - npc->event_do_oninit( true ); + /** + * Execute main initialization events + * The correct initialization order is: + * OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2 + * + **/ + npc->event_do_oninit(true); + npc->market_fromsql(); - // Execute rest of the startup events if connected to char-server. [Lance] - // Executed when connection is established with char-server in chrif_connectack - if( !intif->CheckForCharServer() ) { - ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInit")); - ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInitOnce")); - } - // Refresh guild castle flags on both woe setups - // These events are only executed after receiving castle information from char-server + npc->barter_fromsql(); + npc->expanded_barter_fromsql(); + + /* + * Execute rest of the startup events if connected to char-server. [Lance] + * Executed when connection is established with char-server in chrif_connectack(). + */ + if (intif->CheckForCharServer() == 0) { + ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", + npc->event_doall("OnInterIfInit")); + ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", + npc->event_doall("OnInterIfInitOnce")); + } + + /* + * Refresh guild castle flags on both WoE setups. + * These events are only executed after receiving castle information from char-server. + */ npc->event_doall("OnAgitInit"); npc->event_doall("OnAgitInit2"); return 0; } -//Unload all npc in the given file -static bool npc_unloadfile(const char *filepath) +/** + * Unloads all NPCs in the given file. + * + * @param filepath Path to the file which should be unloaded. + * @param unload_mobs If true, mobs spawned by NPCs in the file will be removed. + * @return true if at least one NPC was unloaded, otherwise false. + * + **/ +static bool npc_unloadfile(const char *filepath, bool unload_mobs) { + nullpo_retr(false, filepath); + struct DBIterator *iter = db_iterator(npc->name_db); - struct npc_data* nd = NULL; bool found = false; - nullpo_retr(false, filepath); - - for( nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter) ) { - if( nd->path && strcasecmp(nd->path,filepath) == 0 ) { // FIXME: This can break in case-sensitive file systems + for (struct npc_data *nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter)) { + if (nd->path != NULL && strcasecmp(nd->path, filepath) == 0) { // FIXME: This can break in case-sensitive file systems. found = true; - npc->unload_duplicates(nd);/* unload any npcs which could duplicate this but be in a different file */ - npc->unload(nd, true); + npc->unload_duplicates(nd, unload_mobs); /// Unload any NPC which could duplicate this but be in a different file. + npc->unload(nd, true, unload_mobs); } } dbi_destroy(iter); - if( found ) /* refresh event cache */ + if (found) /// Refresh event cache. npc->read_event_script(); return found; @@ -5091,6 +5788,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 *------------------------------------------*/ @@ -5247,6 +5956,7 @@ void npc_defaults(void) npc->unload_ev_label = npc_unload_ev_label; npc->unload_dup_sub = npc_unload_dup_sub; npc->unload_duplicates = npc_unload_duplicates; + npc->unload_mob = npc_unload_mob; npc->unload = npc_unload; npc->clearsrcfile = npc_clearsrcfile; npc->addsrcfile = npc_addsrcfile; @@ -5299,11 +6009,22 @@ void npc_defaults(void) npc->trader_pay = npc_trader_pay; npc->trader_update = npc_trader_update; npc->market_buylist = npc_market_buylist; + npc->barter_buylist = npc_barter_buylist; + npc->expanded_barter_buylist = npc_expanded_barter_buylist; npc->trader_open = npc_trader_open; npc->market_fromsql = npc_market_fromsql; npc->market_tosql = npc_market_tosql; npc->market_delfromsql = npc_market_delfromsql; npc->market_delfromsql_sub = npc_market_delfromsql_sub; + npc->barter_fromsql = npc_barter_fromsql; + npc->barter_tosql = npc_barter_tosql; + npc->barter_delfromsql = npc_barter_delfromsql; + npc->barter_delfromsql_sub = npc_barter_delfromsql_sub; + npc->expanded_barter_fromsql = npc_expanded_barter_fromsql; + npc->expanded_barter_tosql = npc_expanded_barter_tosql; + npc->expanded_barter_delfromsql = npc_expanded_barter_delfromsql; + npc->expanded_barter_delfromsql_sub = npc_expanded_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 ed5f4138d..1585a2bc8 100644 --- a/src/map/npc.h +++ b/src/map/npc.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +34,8 @@ struct hplugin_data_store; struct itemlist; // map/itemdb.h struct view_data; +enum market_buy_result; + enum npc_parse_options { NPO_NONE = 0x0, NPO_ONINIT = 0x1, @@ -41,10 +43,12 @@ enum npc_parse_options { }; enum npc_shop_types { - NST_ZENY,/* default */ - NST_CASH,/* official npc cash shop */ - NST_MARKET,/* official npc market type */ + NST_ZENY, /* default */ + NST_CASH, /* official npc cash shop */ + NST_MARKET, /* official npc market type */ NST_CUSTOM, + NST_BARTER, /* official npc barter type */ + NST_EXPANDED_BARTER, /* official npc expanded barter type */ /* */ NST_MAX, }; @@ -56,15 +60,26 @@ struct npc_label_list { char name[NAME_LENGTH]; int pos; }; + +struct npc_barter_currency { + int nameid; + int refine; + int amount; +}; + struct npc_item_list { int nameid; - unsigned int value; - unsigned int qty; + unsigned int value; // price or barter currency item id + int value2; // barter currency item amount / expanded barter currency size + int qty; + struct npc_barter_currency *currency; }; + struct npc_shop_data { unsigned char type;/* what am i */ struct npc_item_list *item;/* list */ unsigned int items;/* total */ + int shop_last_index; // only for NST_EXPANDED_BARTER }; struct npc_parse; struct npc_data { @@ -73,14 +88,14 @@ struct npc_data { struct view_data vd; unsigned int option; struct npc_data *master_nd; - short class_; + int class_; short speed; char name[NAME_LENGTH+1];// display name char exname[NAME_LENGTH+1];// unique npc name int chat_id; int touching_id; int64 next_walktime; - uint8 dir; + enum unit_dir dir; uint8 area_size; int clan_id; @@ -125,6 +140,7 @@ struct npc_data { int spawn_timer; } tomb; } u; + VECTOR_DECL(struct questinfo) qi_data; struct hplugin_data_store *hdata; ///< HPM Plugin Data Store }; @@ -144,7 +160,7 @@ enum actor_classes { #define MAX_NPC_CLASS 1000 // New NPC range #define MAX_NPC_CLASS2_START 10001 -#define MAX_NPC_CLASS2_END 10310 +#define MAX_NPC_CLASS2_END 10344 //Script NPC events. enum npce_event { @@ -254,8 +270,9 @@ struct npc_interface { int (*unload_ev) (union DBKey key, struct DBData *data, va_list ap); int (*unload_ev_label) (union DBKey key, struct DBData *data, va_list ap); int (*unload_dup_sub) (struct npc_data *nd, va_list args); - void (*unload_duplicates) (struct npc_data *nd); - int (*unload) (struct npc_data *nd, bool single); + void (*unload_duplicates) (struct npc_data *nd, bool unload_mobs); + int (*unload_mob) (struct mob_data *md, va_list args); + int (*unload) (struct npc_data *nd, bool single, bool unload_mobs); void (*clearsrcfile) (void); void (*addsrcfile) (const char *name); void (*delsrcfile) (const char *name); @@ -264,7 +281,7 @@ struct npc_interface { void (*parsename) (struct npc_data *nd, const char *name, const char *start, const char *buffer, const char *filepath); int (*parseview) (const char *w4, const char *start, const char *buffer, const char *filepath); bool (*viewisid) (const char *viewid); - struct npc_data *(*create_npc) (enum npc_subtype subtype, int m, int x, int y, uint8 dir, int16 class_); + struct npc_data *(*create_npc) (enum npc_subtype subtype, int m, int x, int y, enum unit_dir dir, int class_); struct npc_data* (*add_warp) (char *name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y); const char *(*parse_warp) (const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval); const char *(*parse_shop) (const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval); @@ -284,7 +301,7 @@ struct npc_interface { void (*unsetcells) (struct npc_data *nd); void (*movenpc) (struct npc_data *nd, int16 x, int16 y); void (*setdisplayname) (struct npc_data *nd, const char *newname); - void (*setclass) (struct npc_data *nd, short class_); + void (*setclass) (struct npc_data *nd, int class_); int (*do_atcmd_event) (struct map_session_data *sd, const char *command, const char *message, const char *eventname); const char *(*parse_function) (const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval); void (*parse_mob2) (struct spawn_data *mobspawn); @@ -297,7 +314,7 @@ struct npc_interface { int (*path_db_clear_sub) (union DBKey key, struct DBData *data, va_list args); int (*ev_label_db_clear_sub) (union DBKey key, struct DBData *data, va_list args); int (*reload) (void); - bool (*unloadfile) (const char *filepath); + bool (*unloadfile) (const char *filepath, bool unload_mobs); void (*do_clear_npc) (void); void (*debug_warps_sub) (struct npc_data *nd); void (*debug_warps) (void); @@ -305,14 +322,25 @@ struct npc_interface { void (*trader_count_funds) (struct npc_data *nd, struct map_session_data *sd); bool (*trader_pay) (struct npc_data *nd, struct map_session_data *sd, int price, int points); void (*trader_update) (int master); - int (*market_buylist) (struct map_session_data *sd, struct itemlist *item_list); + enum market_buy_result (*market_buylist) (struct map_session_data *sd, struct itemlist *item_list); + int (*barter_buylist) (struct map_session_data *sd, struct barteritemlist *item_list); + int (*expanded_barter_buylist) (struct map_session_data *sd, struct barteritemlist *item_list); bool (*trader_open) (struct map_session_data *sd, struct npc_data *nd); void (*market_fromsql) (void); - void (*market_tosql) (struct npc_data *nd, unsigned short index); - void (*market_delfromsql) (struct npc_data *nd, unsigned short index); - void (*market_delfromsql_sub) (const char *npcname, unsigned short index); + void (*market_tosql) (struct npc_data *nd, int index); + void (*market_delfromsql) (struct npc_data *nd, int index); + void (*market_delfromsql_sub) (const char *npcname, int index); + void (*barter_fromsql) (void); + void (*barter_tosql) (struct npc_data *nd, int index); + void (*barter_delfromsql) (struct npc_data *nd, int index); + void (*barter_delfromsql_sub) (const char *npcname, int itemId, int itemId2, int amount2); + void (*expanded_barter_fromsql) (void); + void (*expanded_barter_tosql) (struct npc_data *nd, int index); + void (*expanded_barter_delfromsql) (struct npc_data *nd, int index); + void (*expanded_barter_delfromsql_sub) (const char *npcname, int itemId, int zeny, int currencyCount, struct npc_barter_currency* currency); 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/npc_chat.c b/src/map/npc_chat.c index 92393122e..0ca84cff4 100644 --- a/src/map/npc_chat.c +++ b/src/map/npc_chat.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/packets.h b/src/map/packets.h index 036fd4f2a..e30acbdf7 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2015 Hercules Dev Team + * Copyright (C) 2013-2020 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 @@ -147,6 +147,7 @@ packet(0x01ba,clif->pGMShift,2); packet(0x01bb,clif->pGMShift,2); packet(0x01bc,clif->pGMRecall,2); packet(0x01bd,clif->pGMRecall,2); +packet(0x01c0,clif->pReqRemainTime); packet(0x01ce,clif->pAutoSpell,2); packet(0x01d5,clif->pNpcStringInput,2,4,8); packet(0x01df,clif->pGMReqAccountName,2); @@ -588,7 +589,6 @@ packet(0x96e,clif->ackmergeitems); #if PACKETVER >= 20070227 packet(0x0288,clif->pcashshop_buy,2,4,6); packet(0x02b6,clif->pquestStateAck,2,6); - packet(0x02ba,clif->pHotkey,2,4,5,9); packet(0x02c4,clif->pPartyInvite2,2); // CZ_PARTY_JOIN_REQ packet(0x02c7,clif->pReplyPartyInvite2,2,6); packet(0x02c8,clif->pPartyTick,2); @@ -598,6 +598,10 @@ packet(0x96e,clif->ackmergeitems); packet(0x02db,clif->pBattleChat,2,4); #endif +#if PACKETVER_MAIN_NUM >= 20070618 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_AD_NUM >= 20070618 || PACKETVER_SAK_NUM >= 20070618 + packet(0x02ba,clif->pHotkey1); +#endif + //2008-01-02aSakexe #if PACKETVER >= 20080102 packet(0x01df,clif->pGMReqAccountName,2); @@ -648,6 +652,10 @@ packet(0x96e,clif->ackmergeitems); packet(0x0439,clif->pUseItem,2,4); #endif +#if PACKETVER_MAIN_NUM >= 20090406 || PACKETVER_RE_NUM >= 20090408 || PACKETVER_SAK_NUM >= 20090408 || defined(PACKETVER_ZERO) + packet(0x044a,clif->pClientVersion); +#endif + // 2009-05-20aRagexe, 2009-05-20aRagexeRE #if PACKETVER >= 20090520 // new packets @@ -718,6 +726,11 @@ packet(0x96e,clif->ackmergeitems); packet(0x0843,clif->pGMRemove2,2); #endif +// all versions +#if PACKETVER >= 20100824 + packet(0x0844,clif->pCashShopOpen1); +#endif + //2010-11-24aRagexeRE #if PACKETVER >= 20101124 packet(0x0288,clif->pcashshop_buy,4,8); @@ -768,7 +781,6 @@ packet(0x96e,clif->ackmergeitems); //2011-07-18aRagexe (Thanks to Yommy!) #if PACKETVER >= 20110718 // shuffle packets not added - packet(0x0844,clif->pCashShopOpen,2);/* tell server cashshop window is being open */ packet(0x084a,clif->pCashShopClose,2);/* tell server cashshop window is being closed */ packet(0x0846,clif->pCashShopReqTab,2); packet(0x0848,clif->pCashShopBuy,2); @@ -1659,10 +1671,8 @@ packet(0x96e,clif->ackmergeitems); // changed packet sizes #endif -// 2014-01-29bRagexeRE -#if PACKETVER >= 20140129 -// new packets - packet(0x0a01,clif->pHotkeyRowShift,2); // CZ_SHORTCUTKEYBAR_ROTATE +#if PACKETVER_MAIN_NUM >= 20140129 || PACKETVER_RE_NUM >= 20140129 || defined(PACKETVER_ZERO) + packet(0x0a01,clif->pHotkeyRowShift1); // CZ_SHORTCUTKEYBAR_ROTATE #endif // 2014-02-12aRagexeRE @@ -1772,8 +1782,10 @@ packet(0x96e,clif->ackmergeitems); #if PACKETVER >= 20151104 // new packets packet(0x0a46,clif->pReqStyleChange); + packet(0x0a48,clif->pStyleClose); #endif + // 2016-03-23aRagexeRE #if PACKETVER >= 20160323 // new packets @@ -1788,6 +1800,35 @@ packet(0x96e,clif->ackmergeitems); // changed packet sizes #endif +#if PACKETVER >= 20160302 + packet(0x0a4f,clif->plapineDdukDdak_ack); +#endif + +#if PACKETVER_MAIN_NUM >= 20160504 || PACKETVER_RE_NUM >= 20160504 || defined(PACKETVER_ZERO) + packet(0x0a70,clif->plapineDdukDdak_close); +#endif + +// all 2016-05-25 +#if PACKETVER >= 20160525 + packet(0x0a77,clif->pCameraInfo); // CZ_CAMERA_INFO +#endif + +// all 20160622+ +#if PACKETVER >= 20160622 + 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 + +#if PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) + packet(0x0ab5, clif->pLapineUpgrade_close); + packet(0x0ab6, clif->pLapineUpgrade_makeItem); +#endif + // 2017-02-28aRagexeRE #if PACKETVER >= 20170228 // new packets @@ -1875,4 +1916,85 @@ packet(0x96e,clif->ackmergeitems); packet(0x0a49,clif->pPrivateAirshipRequest); // CZ_PRIVATE_AIRSHIP_REQUEST #endif +// 2018-11-14aRagexe_zero +#if PACKETVER_ZERO_NUM >= 20181114 +// new packets +// changed packet sizes + packet(0x018e,clif->pProduceMix); // CZ_REQMAKINGITEM + packet(0x01ae,clif->pSelectArrow,2); // CZ_REQ_MAKINGARROW + packet(0x01fd,clif->pRepairItem); // CZ_REQ_ITEMREPAIR + packet(0x025b,clif->pCooking); // CZ_REQ_MAKINGITEM + packet(0x0445,clif->pDull/*,XXX*/); // CZ_SIMPLE_BUY_CASH_POINT_ITEM + packet(0x09ae,clif->pDull/*,XXX*/); // CZ_REQ_APPLY_BARGAIN_SALE_ITEM + packet(0x09b0,clif->pDull/*,XXX*/); // CZ_REQ_REMOVE_BARGAIN_SALE_ITEM + packet(0x09c3,clif->pDull/*,XXX*/); // CZ_REQ_COUNT_BARGAIN_SALE_ITEM + packet(0x0a49,clif->pPrivateAirshipRequest); // CZ_PRIVATE_AIRSHIP_REQUEST +#endif + +// 2018-11-21bRagexe +#if PACKETVER_ZERO_NUM >= 20181121 +// new packets +// changed packet sizes + packet(0x018e,clif->pProduceMix); // CZ_REQMAKINGITEM + packet(0x01ae,clif->pSelectArrow,2); // CZ_REQ_MAKINGARROW + packet(0x01fd,clif->pRepairItem); // CZ_REQ_ITEMREPAIR + packet(0x025b,clif->pCooking); // CZ_REQ_MAKINGITEM + packet(0x0445,clif->pDull/*,XXX*/); // CZ_SIMPLE_BUY_CASH_POINT_ITEM + packet(0x09ae,clif->pDull/*,XXX*/); // CZ_REQ_APPLY_BARGAIN_SALE_ITEM + packet(0x09b0,clif->pDull/*,XXX*/); // CZ_REQ_REMOVE_BARGAIN_SALE_ITEM + packet(0x09c3,clif->pDull/*,XXX*/); // CZ_REQ_COUNT_BARGAIN_SALE_ITEM + packet(0x0a49,clif->pPrivateAirshipRequest); // CZ_PRIVATE_AIRSHIP_REQUEST +#endif + +#if PACKETVER_MAIN_NUM >= 20181002 || PACKETVER_RE_NUM >= 20181002 || PACKETVER_ZERO_NUM >= 20181010 + packet(0x0b10,clif->pStartUseSkillToId); + packet(0x0b11,clif->pStopUseSkillToId); +#endif + +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 + packet(0x0b14,clif->pInventoryExpansion); + packet(0x0b16,clif->pInventoryExpansionConfirmed); + packet(0x0b19,clif->pInventoryExpansionRejected); +#endif + +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 + packet(0x0b0f,clif->pNPCBarterPurchase); + packet(0x0b12,clif->pNPCBarterClosed); +#endif + +#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220 + packet(0x0b1c,clif->pPing); +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 + packet(0x0b21,clif->pHotkey2); + packet(0x0b22,clif->pHotkeyRowShift2); // CZ_SHORTCUTKEYBAR_ROTATE +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190522 || PACKETVER_ZERO_NUM >= 20190515 + packet(0x0b28,clif->pGuildCastleTeleportRequest); + packet(0x0b2c,clif->pGuildCastleInfoRequest); +#endif + +#if PACKETVER_MAIN_NUM >= 20190703 || PACKETVER_RE_NUM >= 20190703 + packet(0x0b35,clif->pReqGearOff); +#endif + +#if PACKETVER_ZERO_NUM >= 20190709 + packet(0x0b35,clif->pReqGearOff); +#endif + +#if PACKETVER >= 20190724 + packet(0x0b4c,clif->pCashShopLimitedReq); +#endif + +#if PACKETVER_MAIN_NUM >= 20190904 || PACKETVER_RE_NUM >= 20190904 || PACKETVER_ZERO_NUM >= 20190828 + packet(0x0b57,clif->pNPCExpandedBarterPurchase); + packet(0x0b58,clif->pNPCExpandedBarterClosed); +#endif + +#if PACKETVER >= 20191224 + packet(0x0b6d,clif->pCashShopOpen2); +#endif + #endif /* MAP_PACKETS_H */ diff --git a/src/map/packets_keys_main.h b/src/map/packets_keys_main.h index 129c4eaaa..8ebbb39be 100644 --- a/src/map/packets_keys_main.h +++ b/src/map/packets_keys_main.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 +// 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, 2019-05-08cRagexe, 2019-05-08dRagexeRE, 2019-05-08eRagexeRE, 2019-05-22bRagexe, 2019-05-22bRagexeRE, 2019-05-22cRagexe, 2019-05-22cRagexeRE, 2019-05-23aRagexe, 2019-05-29aRagexe, 2019-05-29bRagexeRE, 2019-05-29cRagexe, 2019-05-29cRagexeRE, 2019-05-30aRagexe, 2019-05-30aRagexeRE, 2019-06-05JRagexeRE, 2019-06-05KRagexe, 2019-06-05LRagexeRE, 2019-06-05fRagexe, 2019-06-05hRagexeRE, 2019-06-19bRagexe, 2019-06-19cRagexeRE, 2019-06-19eRagexe, 2019-06-19hRagexe, 2019-06-26bRagexeRE, 2019-07-03aRagexe, 2019-07-03bRagexeRE, 2019-07-17aRagexe, 2019-07-17cRagexeRE, 2019-07-17dRagexe, 2019-07-17dRagexeRE, 2019-07-24aRagexe, 2019-07-24bRagexeRE, 2019-07-31bRagexe, 2019-07-31bRagexeRE, 2019-08-02aRagexe, 2019-08-02aRagexeRE, 2019-08-07aRagexe, 2019-08-07dRagexeRE, 2019-08-21aRagexe, 2019-08-21cRagexeRE, 2019-08-21dRagexeRE, 2019-08-28aRagexe, 2019-08-28aRagexeRE, 2019-09-04aRagexe, 2019-09-04bRagexe, 2019-09-04bRagexeRE, 2019-09-18bRagexe, 2019-09-18cRagexeRE, 2019-09-25aRagexe, 2019-09-25aRagexeRE, 2019-09-25bRagexe, 2019-09-25bRagexeRE, 2019-10-02bRagexeRE, 2019-10-02cRagexe, 2019-10-02dRagexe, 2019-10-02dRagexeRE, 2019-10-02dRagexeRE_2, 2019-10-16fRagexe, 2019-10-16fRagexeRE, 2019-10-16gRagexe, 2019-10-16gRagexeRE, 2019-10-18aRagexe, 2019-10-23aRagexe, 2019-10-23aRagexeRE, 2019-10-30bRagexeRE, 2019-10-30cRagexe, 2019-11-06aRagexe, 2019-11-06bRagexeRE, 2019-11-07aRagexe, 2019-11-07aRagexeRE, 2019-11-13cRagexe, 2019-11-13eRagexe, 2019-11-13eRagexeRE, 2019-11-20aRagexe, 2019-11-20cRagexeRE, 2019-11-20dRagexe, 2019-11-27aRagexe, 2019-11-27aRagexeRE, 2019-11-27bRagexe, 2019-12-04aRagexe, 2019-12-04aRagexeRE, 2019-12-04bRagexe, 2019-12-04bRagexeRE, 2019-12-04cRagexeRE, 2019-12-11aRagexe, 2019-12-11fRagexeRE, 2019-12-18bRagexe, 2019-12-18bRagexeRE, 2019-12-24aRagexe, 2019-12-24aRagexeRE, 2019-12-24bRagexe, 2019-12-24bRagexeRE, 2020-01-08aRagexe, 2020-01-08bRagexeRE, 2020-01-22cRagexe, 2020-01-22cRagexeRE, 2020-01-29bRagexe, 2020-01-30aRagexe, 2020-02-05aRagexe, 2020-02-05aRagexeRE, 2020-02-06aRagexe, 2020-02-12aRagexe, 2020-02-12aRagexeRE, 2020-02-19dRagexe, 2020-02-19eRagexeRE, 2020-03-04aRagexe, 2020-03-04aRagexeRE, 2020-03-18bRagexe, 2020-04-01bRagexe, 2020-04-14_6aRagexe, 2020-04-14eRagexe #if PACKETVER == 20101123 || \ PACKETVER == 20101124 || \ PACKETVER == 20101125 || \ @@ -126,7 +126,73 @@ PACKETVER == 20181024 || \ PACKETVER == 20181031 || \ PACKETVER == 20181107 || \ - PACKETVER >= 20181114 + PACKETVER == 20181114 || \ + PACKETVER == 20181121 || \ + PACKETVER == 20181128 || \ + PACKETVER == 20181205 || \ + PACKETVER == 20181212 || \ + PACKETVER == 20181219 || \ + PACKETVER == 20181226 || \ + PACKETVER == 20190109 || \ + PACKETVER == 20190116 || \ + PACKETVER == 20190123 || \ + PACKETVER == 20190213 || \ + PACKETVER == 20190220 || \ + PACKETVER == 20190227 || \ + PACKETVER == 20190228 || \ + PACKETVER == 20190306 || \ + PACKETVER == 20190313 || \ + PACKETVER == 20190320 || \ + PACKETVER == 20190322 || \ + PACKETVER == 20190327 || \ + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER == 20190418 || \ + PACKETVER == 20190508 || \ + PACKETVER == 20190522 || \ + PACKETVER == 20190523 || \ + PACKETVER == 20190529 || \ + PACKETVER == 20190530 || \ + PACKETVER == 20190605 || \ + PACKETVER == 20190619 || \ + PACKETVER == 20190626 || \ + PACKETVER == 20190703 || \ + PACKETVER == 20190717 || \ + PACKETVER == 20190724 || \ + PACKETVER == 20190731 || \ + PACKETVER == 20190802 || \ + PACKETVER == 20190807 || \ + PACKETVER == 20190821 || \ + PACKETVER == 20190828 || \ + PACKETVER == 20190904 || \ + PACKETVER == 20190918 || \ + PACKETVER == 20190925 || \ + PACKETVER == 20191002 || \ + PACKETVER == 20191016 || \ + PACKETVER == 20191018 || \ + PACKETVER == 20191023 || \ + PACKETVER == 20191030 || \ + PACKETVER == 20191106 || \ + PACKETVER == 20191107 || \ + PACKETVER == 20191113 || \ + PACKETVER == 20191120 || \ + PACKETVER == 20191127 || \ + PACKETVER == 20191204 || \ + PACKETVER == 20191211 || \ + PACKETVER == 20191218 || \ + PACKETVER == 20191224 || \ + PACKETVER == 20200108 || \ + PACKETVER == 20200122 || \ + PACKETVER == 20200129 || \ + PACKETVER == 20200130 || \ + PACKETVER == 20200205 || \ + PACKETVER == 20200206 || \ + PACKETVER == 20200212 || \ + PACKETVER == 20200219 || \ + PACKETVER == 20200304 || \ + PACKETVER == 20200318 || \ + PACKETVER == 20200401 || \ + PACKETVER >= 20200414 packetKeys(0x00000000,0x00000000,0x00000000); #endif diff --git a/src/map/packets_keys_zero.h b/src/map/packets_keys_zero.h index d5836bf96..904159d37 100644 --- a/src/map/packets_keys_zero.h +++ b/src/map/packets_keys_zero.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 +// 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, 2019-05-08_2aRagexe_zero, 2019-05-08aRagexe_zero, 2019-05-15aRagexe_zero, 2019-05-29aRagexe_zero, 2019-05-30aRagexe_zero, 2019-06-05_2aRagexe_zero, 2019-06-26_2aRagexe_zero, 2019-06-26_3aRagexe_zero, 2019-07-09aRagexe_zero, 2019-07-10_3aRagexe_zero, 2019-07-17aRagexe_zero, 2019-07-24aRagexe_zero, 2019-08-14_3aRagexe_zero, 2019-08-28_2aRagexe_zero, 2019-08-28_3aRagexe_zero, 2019-09-11aRagexe_zero, 2019-09-18_2aRagexe_zero, 2019-09-18aRagexe_zero, 2019-09-25_3aRagexe_zero, 2019-09-25_5aRagexe_zero, 2019-10-08_2aRagexe_zero, 2019-10-23_2aRagexe_zero, 2019-11-06aRagexe_zero, 2019-11-13aRagexe_zero, 2019-11-27_2aRagexe_zero, 2019-11-27aRagexe_zero, 2019-12-04aRagexe_zero, 2019-12-11_2aRagexe_zero, 2019-12-24_4aRagexe_zero, 2019-12-24_5aRagexe_zero, 2020-01-15_2aRagexe_zero, 2020-01-15aRagexe_zero, 2020-01-29_2aRagexe_zero, 2020-01-29aRagexe_zero, 2020-02-12aRagexe_zero, 2020-02-26aRagexe_zero, 2020-02-26bRagexe_zero, 2020-03-04aRagexe_zero, 2020-03-18_2aRagexe_zero, 2020-04-01_2aRagexe_zero, 2020-04-14bRagexe_zero #if PACKETVER == 20171018 || \ PACKETVER == 20171019 || \ PACKETVER == 20171023 || \ @@ -63,7 +63,55 @@ PACKETVER == 20180928 || \ PACKETVER == 20181010 || \ PACKETVER == 20181024 || \ - PACKETVER >= 20181114 + PACKETVER == 20181114 || \ + PACKETVER == 20181120 || \ + PACKETVER == 20181128 || \ + PACKETVER == 20181212 || \ + PACKETVER == 20181219 || \ + PACKETVER == 20181226 || \ + PACKETVER == 20190116 || \ + PACKETVER == 20190117 || \ + PACKETVER == 20190130 || \ + PACKETVER == 20190213 || \ + PACKETVER == 20190220 || \ + PACKETVER == 20190227 || \ + PACKETVER == 20190313 || \ + PACKETVER == 20190327 || \ + PACKETVER == 20190403 || \ + PACKETVER == 20190410 || \ + PACKETVER == 20190424 || \ + PACKETVER == 20190502 || \ + PACKETVER == 20190508 || \ + PACKETVER == 20190515 || \ + PACKETVER == 20190529 || \ + PACKETVER == 20190530 || \ + PACKETVER == 20190605 || \ + PACKETVER == 20190626 || \ + PACKETVER == 20190709 || \ + PACKETVER == 20190710 || \ + PACKETVER == 20190717 || \ + PACKETVER == 20190724 || \ + PACKETVER == 20190814 || \ + PACKETVER == 20190828 || \ + PACKETVER == 20190911 || \ + PACKETVER == 20190918 || \ + PACKETVER == 20190925 || \ + PACKETVER == 20191008 || \ + PACKETVER == 20191023 || \ + PACKETVER == 20191106 || \ + PACKETVER == 20191113 || \ + PACKETVER == 20191127 || \ + PACKETVER == 20191204 || \ + PACKETVER == 20191211 || \ + PACKETVER == 20191224 || \ + PACKETVER == 20200115 || \ + PACKETVER == 20200129 || \ + PACKETVER == 20200212 || \ + PACKETVER == 20200226 || \ + PACKETVER == 20200304 || \ + PACKETVER == 20200318 || \ + PACKETVER == 20200401 || \ + PACKETVER >= 20200414 packetKeys(0x00000000,0x00000000,0x00000000); #endif diff --git a/src/map/packets_shuffle_main.h b/src/map/packets_shuffle_main.h index 43be3821f..d643f3b0e 100644 --- a/src/map/packets_shuffle_main.h +++ b/src/map/packets_shuffle_main.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -3345,7 +3345,7 @@ packet(0x0969,clif->pSearchStoreInfoNextPage,0); // CZ_SEARCH_STORE_INFO_NEXT_PAGE // 2 #endif -// 2014-06-11eRagexe, 2015-02-25hRagexe, 2018-03-15aRagexe, 2018-03-21aRagexe, 2018-03-28bRagexe, 2018-04-04bRagexe, 2018-04-18aRagexe, 2018-04-25cRagexe, 2018-05-02bRagexe, 2018-05-09aRagexe, 2018-05-16cRagexe, 2018-05-23aRagexe, 2018-05-30aRagexe, 2018-06-05bRagexe, 2018-06-20cRagexe, 2018-06-20eRagexe, 2018-06-21aRagexe, 2018-07-04aRagexe, 2018-07-18bRagexe, 2018-07-18cRagexe, 2018-08-01cRagexe, 2018-08-08bRagexe, 2018-08-22cRagexe, 2018-08-29aRagexe, 2018-08-31aRagexe, 2018-09-12dRagexe, 2018-09-19aRagexe, 2018-10-02aRagexe, 2018-10-02bRagexe, 2018-10-17_02aRagexe, 2018-10-17_03aRagexe, 2018-10-17bRagexe, 2018-10-24bRagexe, 2018-10-31aRagexe, 2018-10-31bRagexe, 2018-11-07aRagexe, 2018-11-14cRagexe +// 2014-06-11eRagexe, 2015-02-25hRagexe, 2018-03-15aRagexe, 2018-03-21aRagexe, 2018-03-28bRagexe, 2018-04-04bRagexe, 2018-04-18aRagexe, 2018-04-25cRagexe, 2018-05-02bRagexe, 2018-05-09aRagexe, 2018-05-16cRagexe, 2018-05-23aRagexe, 2018-05-30aRagexe, 2018-06-05bRagexe, 2018-06-20cRagexe, 2018-06-20eRagexe, 2018-06-21aRagexe, 2018-07-04aRagexe, 2018-07-18bRagexe, 2018-07-18cRagexe, 2018-08-01cRagexe, 2018-08-08bRagexe, 2018-08-22cRagexe, 2018-08-29aRagexe, 2018-08-31aRagexe, 2018-09-12dRagexe, 2018-09-19aRagexe, 2018-10-02aRagexe, 2018-10-02bRagexe, 2018-10-17_02aRagexe, 2018-10-17_03aRagexe, 2018-10-17bRagexe, 2018-10-24bRagexe, 2018-10-31aRagexe, 2018-10-31bRagexe, 2018-11-07aRagexe, 2018-11-14cRagexe, 2018-11-14dRagexe #if PACKETVER == 20140611 || \ PACKETVER == 20150225 || \ PACKETVER == 20180315 || \ @@ -3376,7 +3376,7 @@ PACKETVER == 20181024 || \ PACKETVER == 20181031 || \ PACKETVER == 20181107 || \ - PACKETVER >= 20181114 + PACKETVER == 20181114 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 @@ -9727,5 +9727,133 @@ 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, 2019-04-17aRagexe, 2019-04-18aRagexe, 2019-05-08cRagexe, 2019-05-22bRagexe, 2019-05-22cRagexe, 2019-05-23aRagexe, 2019-05-29aRagexe, 2019-05-29cRagexe, 2019-05-30aRagexe, 2019-06-05fRagexe, 2019-06-05KRagexe, 2019-06-19bRagexe, 2019-06-19eRagexe, 2019-06-19hRagexe, 2019-07-03aRagexe, 2019-07-17aRagexe, 2019-07-17dRagexe, 2019-07-24aRagexe, 2019-07-31bRagexe, 2019-08-02aRagexe, 2019-08-07aRagexe, 2019-08-21aRagexe, 2019-08-28aRagexe +#if PACKETVER == 20181121 || \ + PACKETVER == 20181128 || \ + PACKETVER == 20181205 || \ + PACKETVER == 20181212 || \ + PACKETVER == 20181219 || \ + PACKETVER == 20181226 || \ + PACKETVER == 20190109 || \ + PACKETVER == 20190116 || \ + PACKETVER == 20190123 || \ + PACKETVER == 20190213 || \ + PACKETVER == 20190227 || \ + PACKETVER == 20190228 || \ + PACKETVER == 20190306 || \ + PACKETVER == 20190313 || \ + PACKETVER == 20190320 || \ + PACKETVER == 20190322 || \ + PACKETVER == 20190327 || \ + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER == 20190418 || \ + PACKETVER == 20190508 || \ + PACKETVER == 20190522 || \ + PACKETVER == 20190523 || \ + PACKETVER == 20190529 || \ + PACKETVER == 20190530 || \ + PACKETVER == 20190605 || \ + PACKETVER == 20190619 || \ + PACKETVER == 20190703 || \ + PACKETVER == 20190717 || \ + PACKETVER == 20190724 || \ + PACKETVER == 20190731 || \ + PACKETVER == 20190802 || \ + PACKETVER == 20190807 || \ + PACKETVER == 20190821 || \ + PACKETVER >= 20190828 + 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 + packet(0x0281,clif->pDull/*,XXX*/); // CZ_GANGSI_RANK // 4 + packet(0x02c4,clif->pPartyInvite2,2); // CZ_PARTY_JOIN_REQ // 26 + packet(0x035f,clif->pWalkToXY,2); // CZ_REQUEST_MOVE // 5 + packet(0x0360,clif->pTickSend,2); // CZ_REQUEST_TIME // 6 + packet(0x0361,clif->pChangeDir,2,4); // CZ_CHANGE_DIRECTION // 5 + packet(0x0362,clif->pTakeItem,2); // CZ_ITEM_PICKUP // 6 + packet(0x0363,clif->pDropItem,2,4); // CZ_ITEM_THROW // 6 + packet(0x0364,clif->pMoveToKafra,2,4); // CZ_MOVE_ITEM_FROM_BODY_TO_STORE // 8 + packet(0x0365,clif->pMoveFromKafra,2,4); // CZ_MOVE_ITEM_FROM_STORE_TO_BODY // 8 + packet(0x0366,clif->pUseSkillToPos,2,4,6,8); // CZ_USE_SKILL_TOGROUND // 10 + packet(0x0367,clif->pUseSkillToPosMoreInfo,2,4,6,8,10); // CZ_USE_SKILL_TOGROUND_WITHTALKBOX // 90 + packet(0x0368,clif->pGetCharNameRequest,2); // CZ_REQNAME // 6 + packet(0x0369,clif->pSolveCharName,2); // CZ_REQNAME_BYGID // 6 + packet(0x0436,clif->pWantToConnection,2,6,10,14,18); // CZ_ENTER // 19 + packet(0x0437,clif->pActionRequest,2,6); // CZ_REQUEST_ACT // 7 + packet(0x0438,clif->pUseSkillToId,2,4,6); // CZ_USE_SKILL // 10 + packet(0x07e4,clif->pItemListWindowSelected,2,4,8); // CZ_ITEMLISTWIN_RES // -1 + packet(0x07ec,clif->pDull/*,XXX*/); // CZ_JOIN_BATTLE_FIELD // 8 + packet(0x0802,clif->pPartyBookingRegisterReq,2,4); // CZ_PARTY_BOOKING_REQ_REGISTER // 18 + packet(0x0811,clif->pReqOpenBuyingStore,2,4,8,9,89); // CZ_REQ_OPEN_BUYING_STORE // -1 + packet(0x0815,clif->pReqCloseBuyingStore,0); // CZ_REQ_CLOSE_BUYING_STORE // 2 + packet(0x0817,clif->pReqClickBuyingStore,2); // CZ_REQ_CLICK_TO_BUYING_STORE // 6 + packet(0x0819,clif->pReqTradeBuyingStore,2,4,8,12); // CZ_REQ_TRADE_BUYING_STORE // -1 + packet(0x0835,clif->pSearchStoreInfo,2,4,5,9,13,14,15); // CZ_SEARCH_STORE_INFO // -1 + packet(0x0838,clif->pSearchStoreInfoNextPage,0); // CZ_SEARCH_STORE_INFO_NEXT_PAGE // 2 + packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 +#endif + +// 2019-09-04aRagexe, 2019-09-04bRagexe, 2019-09-18bRagexe, 2019-09-25aRagexe, 2019-09-25bRagexe, 2019-10-02cRagexe, 2019-10-02dRagexe, 2019-10-16fRagexe, 2019-10-16gRagexe, 2019-10-18aRagexe, 2019-10-23aRagexe, 2019-10-30cRagexe, 2019-11-06aRagexe, 2019-11-07aRagexe, 2019-11-13cRagexe, 2019-11-13eRagexe, 2019-11-20aRagexe, 2019-11-20dRagexe, 2019-11-27aRagexe, 2019-11-27bRagexe, 2019-12-04aRagexe, 2019-12-04bRagexe, 2019-12-11aRagexe, 2019-12-18bRagexe, 2019-12-24aRagexe, 2019-12-24bRagexe, 2020-01-08aRagexe, 2020-01-22cRagexe, 2020-01-29bRagexe, 2020-01-30aRagexe, 2020-02-05aRagexe, 2020-02-06aRagexe, 2020-02-12aRagexe, 2020-02-19dRagexe, 2020-03-04aRagexe, 2020-03-18bRagexe, 2020-04-01bRagexe, 2020-04-14_6aRagexe, 2020-04-14eRagexe +#if PACKETVER == 20190904 || \ + PACKETVER == 20190918 || \ + PACKETVER == 20190925 || \ + PACKETVER == 20191002 || \ + PACKETVER == 20191016 || \ + PACKETVER == 20191018 || \ + PACKETVER == 20191023 || \ + PACKETVER == 20191030 || \ + PACKETVER == 20191106 || \ + PACKETVER == 20191107 || \ + PACKETVER == 20191113 || \ + PACKETVER == 20191120 || \ + PACKETVER == 20191127 || \ + PACKETVER == 20191204 || \ + PACKETVER == 20191211 || \ + PACKETVER == 20191218 || \ + PACKETVER == 20191224 || \ + PACKETVER == 20200108 || \ + PACKETVER == 20200122 || \ + PACKETVER == 20200129 || \ + PACKETVER == 20200130 || \ + PACKETVER == 20200205 || \ + PACKETVER == 20200206 || \ + PACKETVER == 20200212 || \ + PACKETVER == 20200219 || \ + PACKETVER == 20200304 || \ + PACKETVER == 20200318 || \ + PACKETVER == 20200401 || \ + PACKETVER == 20200414 + 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 + packet(0x0281,clif->pDull/*,XXX*/); // CZ_GANGSI_RANK // 4 + packet(0x02c4,clif->pPartyInvite2,2); // CZ_PARTY_JOIN_REQ // 26 + packet(0x035f,clif->pWalkToXY,2); // CZ_REQUEST_MOVE // 5 + packet(0x0360,clif->pTickSend,2); // CZ_REQUEST_TIME // 6 + packet(0x0361,clif->pChangeDir,2,4); // CZ_CHANGE_DIRECTION // 5 + packet(0x0362,clif->pTakeItem,2); // CZ_ITEM_PICKUP // 6 + packet(0x0363,clif->pDropItem,2,4); // CZ_ITEM_THROW // 6 + packet(0x0364,clif->pMoveToKafra,2,4); // CZ_MOVE_ITEM_FROM_BODY_TO_STORE // 8 + packet(0x0365,clif->pMoveFromKafra,2,4); // CZ_MOVE_ITEM_FROM_STORE_TO_BODY // 8 + packet(0x0366,clif->pUseSkillToPos,2,4,6,8); // CZ_USE_SKILL_TOGROUND // 10 + packet(0x0367,clif->pUseSkillToPosMoreInfo,2,4,6,8,10); // CZ_USE_SKILL_TOGROUND_WITHTALKBOX // 31 + packet(0x0368,clif->pGetCharNameRequest,2); // CZ_REQNAME // 6 + packet(0x0369,clif->pSolveCharName,2); // CZ_REQNAME_BYGID // 6 + packet(0x0436,clif->pWantToConnection,2,6,10,14,18); // CZ_ENTER // 19 + packet(0x0437,clif->pActionRequest,2,6); // CZ_REQUEST_ACT // 7 + packet(0x0438,clif->pUseSkillToId,2,4,6); // CZ_USE_SKILL // 10 + packet(0x07e4,clif->pItemListWindowSelected,2,4,8); // CZ_ITEMLISTWIN_RES // -1 + packet(0x07ec,clif->pDull/*,XXX*/); // CZ_JOIN_BATTLE_FIELD // 8 + packet(0x0802,clif->pPartyBookingRegisterReq,2,4); // CZ_PARTY_BOOKING_REQ_REGISTER // 18 + packet(0x0811,clif->pReqOpenBuyingStore,2,4,8,9,89); // CZ_REQ_OPEN_BUYING_STORE // -1 + packet(0x0815,clif->pReqCloseBuyingStore,0); // CZ_REQ_CLOSE_BUYING_STORE // 2 + packet(0x0817,clif->pReqClickBuyingStore,2); // CZ_REQ_CLICK_TO_BUYING_STORE // 6 + packet(0x0819,clif->pReqTradeBuyingStore,2,4,8,12); // CZ_REQ_TRADE_BUYING_STORE // -1 + packet(0x0835,clif->pSearchStoreInfo,2,4,5,9,13,14,15); // CZ_SEARCH_STORE_INFO // -1 + packet(0x0838,clif->pSearchStoreInfoNextPage,0); // CZ_SEARCH_STORE_INFO_NEXT_PAGE // 2 + packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 +#endif + #endif /* MAP_PACKETS_SHUFFLE_MAIN_H */ diff --git a/src/map/packets_shuffle_re.h b/src/map/packets_shuffle_re.h index 429cf3012..490d517fd 100644 --- a/src/map/packets_shuffle_re.h +++ b/src/map/packets_shuffle_re.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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-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, 2019-05-08dRagexeRE, 2019-05-08eRagexeRE, 2019-05-22bRagexeRE, 2019-05-22cRagexeRE, 2019-05-29bRagexeRE, 2019-05-29cRagexeRE, 2019-05-30aRagexeRE, 2019-06-05hRagexeRE, 2019-06-05JRagexeRE, 2019-06-05LRagexeRE, 2019-06-19cRagexeRE, 2019-06-26bRagexeRE, 2019-07-03bRagexeRE, 2019-07-17cRagexeRE, 2019-07-17dRagexeRE, 2019-07-24bRagexeRE, 2019-07-31bRagexeRE, 2019-08-02aRagexeRE, 2019-08-07dRagexeRE, 2019-08-21cRagexeRE, 2019-08-21dRagexeRE, 2019-08-28aRagexeRE #if PACKETVER == 20180704 || \ PACKETVER == 20180711 || \ PACKETVER == 20180718 || \ @@ -9677,7 +9677,42 @@ PACKETVER == 20181017 || \ PACKETVER == 20181031 || \ PACKETVER == 20181107 || \ - PACKETVER >= 20181114 + PACKETVER == 20181114 || \ + PACKETVER == 20181121 || \ + PACKETVER == 20181128 || \ + PACKETVER == 20181205 || \ + PACKETVER == 20181212 || \ + PACKETVER == 20181219 || \ + PACKETVER == 20181226 || \ + PACKETVER == 20190109 || \ + PACKETVER == 20190116 || \ + PACKETVER == 20190123 || \ + PACKETVER == 20190213 || \ + PACKETVER == 20190220 || \ + PACKETVER == 20190227 || \ + PACKETVER == 20190228 || \ + PACKETVER == 20190306 || \ + PACKETVER == 20190320 || \ + PACKETVER == 20190322 || \ + PACKETVER == 20190327 || \ + PACKETVER == 20190403 || \ + PACKETVER == 20190417 || \ + PACKETVER == 20190418 || \ + PACKETVER == 20190508 || \ + PACKETVER == 20190522 || \ + PACKETVER == 20190529 || \ + PACKETVER == 20190530 || \ + PACKETVER == 20190605 || \ + PACKETVER == 20190619 || \ + PACKETVER == 20190626 || \ + PACKETVER == 20190703 || \ + PACKETVER == 20190717 || \ + PACKETVER == 20190724 || \ + PACKETVER == 20190731 || \ + PACKETVER == 20190802 || \ + PACKETVER == 20190807 || \ + PACKETVER == 20190821 || \ + PACKETVER >= 20190828 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 @@ -9709,5 +9744,59 @@ packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 #endif +// 2019-09-04bRagexeRE, 2019-09-18cRagexeRE, 2019-09-25aRagexeRE, 2019-09-25bRagexeRE, 2019-10-02bRagexeRE, 2019-10-02dRagexeRE, 2019-10-02dRagexeRE_2, 2019-10-16fRagexeRE, 2019-10-16gRagexeRE, 2019-10-23aRagexeRE, 2019-10-30bRagexeRE, 2019-11-06bRagexeRE, 2019-11-07aRagexeRE, 2019-11-13eRagexeRE, 2019-11-20cRagexeRE, 2019-11-27aRagexeRE, 2019-12-04aRagexeRE, 2019-12-04bRagexeRE, 2019-12-04cRagexeRE, 2019-12-11fRagexeRE, 2019-12-18bRagexeRE, 2019-12-24aRagexeRE, 2019-12-24bRagexeRE, 2020-01-08bRagexeRE, 2020-01-22cRagexeRE, 2020-02-05aRagexeRE, 2020-02-12aRagexeRE, 2020-02-19eRagexeRE, 2020-03-04aRagexeRE +#if PACKETVER == 20190904 || \ + PACKETVER == 20190918 || \ + PACKETVER == 20190925 || \ + PACKETVER == 20191002 || \ + PACKETVER == 20191016 || \ + PACKETVER == 20191023 || \ + PACKETVER == 20191030 || \ + PACKETVER == 20191106 || \ + PACKETVER == 20191107 || \ + PACKETVER == 20191113 || \ + PACKETVER == 20191120 || \ + PACKETVER == 20191127 || \ + PACKETVER == 20191204 || \ + PACKETVER == 20191211 || \ + PACKETVER == 20191218 || \ + PACKETVER == 20191224 || \ + PACKETVER == 20200108 || \ + PACKETVER == 20200122 || \ + PACKETVER == 20200205 || \ + PACKETVER == 20200212 || \ + PACKETVER == 20200219 || \ + PACKETVER == 20200304 + 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 + packet(0x0281,clif->pDull/*,XXX*/); // CZ_GANGSI_RANK // 4 + packet(0x02c4,clif->pPartyInvite2,2); // CZ_PARTY_JOIN_REQ // 26 + packet(0x035f,clif->pWalkToXY,2); // CZ_REQUEST_MOVE // 5 + packet(0x0360,clif->pTickSend,2); // CZ_REQUEST_TIME // 6 + packet(0x0361,clif->pChangeDir,2,4); // CZ_CHANGE_DIRECTION // 5 + packet(0x0362,clif->pTakeItem,2); // CZ_ITEM_PICKUP // 6 + packet(0x0363,clif->pDropItem,2,4); // CZ_ITEM_THROW // 6 + packet(0x0364,clif->pMoveToKafra,2,4); // CZ_MOVE_ITEM_FROM_BODY_TO_STORE // 8 + packet(0x0365,clif->pMoveFromKafra,2,4); // CZ_MOVE_ITEM_FROM_STORE_TO_BODY // 8 + packet(0x0366,clif->pUseSkillToPos,2,4,6,8); // CZ_USE_SKILL_TOGROUND // 10 + packet(0x0367,clif->pUseSkillToPosMoreInfo,2,4,6,8,10); // CZ_USE_SKILL_TOGROUND_WITHTALKBOX // 31 + packet(0x0368,clif->pGetCharNameRequest,2); // CZ_REQNAME // 6 + packet(0x0369,clif->pSolveCharName,2); // CZ_REQNAME_BYGID // 6 + packet(0x0436,clif->pWantToConnection,2,6,10,14,18); // CZ_ENTER // 19 + packet(0x0437,clif->pActionRequest,2,6); // CZ_REQUEST_ACT // 7 + packet(0x0438,clif->pUseSkillToId,2,4,6); // CZ_USE_SKILL // 10 + packet(0x07e4,clif->pItemListWindowSelected,2,4,8); // CZ_ITEMLISTWIN_RES // -1 + packet(0x07ec,clif->pDull/*,XXX*/); // CZ_JOIN_BATTLE_FIELD // 8 + packet(0x0802,clif->pPartyBookingRegisterReq,2,4); // CZ_PARTY_BOOKING_REQ_REGISTER // 18 + packet(0x0811,clif->pReqOpenBuyingStore,2,4,8,9,89); // CZ_REQ_OPEN_BUYING_STORE // -1 + packet(0x0815,clif->pReqCloseBuyingStore,0); // CZ_REQ_CLOSE_BUYING_STORE // 2 + packet(0x0817,clif->pReqClickBuyingStore,2); // CZ_REQ_CLICK_TO_BUYING_STORE // 6 + packet(0x0819,clif->pReqTradeBuyingStore,2,4,8,12); // CZ_REQ_TRADE_BUYING_STORE // -1 + packet(0x0835,clif->pSearchStoreInfo,2,4,5,9,13,14,15); // CZ_SEARCH_STORE_INFO // -1 + packet(0x0838,clif->pSearchStoreInfoNextPage,0); // CZ_SEARCH_STORE_INFO_NEXT_PAGE // 2 + packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 +#endif + #endif /* MAP_PACKETS_SHUFFLE_RE_H */ diff --git a/src/map/packets_shuffle_zero.h b/src/map/packets_shuffle_zero.h index 7b68ec472..fb12688c0 100644 --- a/src/map/packets_shuffle_zero.h +++ b/src/map/packets_shuffle_zero.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team - * Copyright (C) 2018 Andrei Karas (4144) + * Copyright (C) 2013-2020 Hercules Dev Team + * Copyright (C) 2018-2020 Andrei Karas (4144) * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -69,7 +69,7 @@ PACKETVER == 20180919 || \ PACKETVER == 20180928 || \ PACKETVER == 20181010 || \ - PACKETVER >= 20181024 + PACKETVER == 20181024 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 @@ -742,8 +742,36 @@ packet(0x0968,clif->pStoragePassword,0); // CZ_ACK_STORE_PASSWORD // 36 #endif -// 2018-11-14aRagexe_zero -#if PACKETVER == 20181114 +// 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, 2019-05-08_2aRagexe_zero, 2019-05-08aRagexe_zero, 2019-05-15aRagexe_zero, 2019-05-29aRagexe_zero, 2019-05-30aRagexe_zero, 2019-06-05_2aRagexe_zero, 2019-06-26_2aRagexe_zero, 2019-06-26_3aRagexe_zero, 2019-07-09aRagexe_zero, 2019-07-10_3aRagexe_zero, 2019-07-17aRagexe_zero, 2019-07-24aRagexe_zero, 2019-08-14_3aRagexe_zero +#if PACKETVER == 20181114 || \ + PACKETVER == 20181120 || \ + PACKETVER == 20181128 || \ + PACKETVER == 20181212 || \ + PACKETVER == 20181219 || \ + PACKETVER == 20181226 || \ + PACKETVER == 20190116 || \ + PACKETVER == 20190117 || \ + PACKETVER == 20190130 || \ + PACKETVER == 20190213 || \ + PACKETVER == 20190220 || \ + PACKETVER == 20190227 || \ + PACKETVER == 20190313 || \ + PACKETVER == 20190327 || \ + PACKETVER == 20190403 || \ + PACKETVER == 20190410 || \ + PACKETVER == 20190424 || \ + PACKETVER == 20190502 || \ + PACKETVER == 20190508 || \ + PACKETVER == 20190515 || \ + PACKETVER == 20190529 || \ + PACKETVER == 20190530 || \ + PACKETVER == 20190605 || \ + PACKETVER == 20190626 || \ + PACKETVER == 20190709 || \ + PACKETVER == 20190710 || \ + PACKETVER == 20190717 || \ + PACKETVER == 20190724 || \ + PACKETVER >= 20190814 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 @@ -775,5 +803,57 @@ packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 #endif +// 2019-08-28_2aRagexe_zero, 2019-08-28_3aRagexe_zero, 2019-09-11aRagexe_zero, 2019-09-18_2aRagexe_zero, 2019-09-18aRagexe_zero, 2019-09-25_3aRagexe_zero, 2019-09-25_5aRagexe_zero, 2019-10-08_2aRagexe_zero, 2019-10-23_2aRagexe_zero, 2019-11-06aRagexe_zero, 2019-11-13aRagexe_zero, 2019-11-27_2aRagexe_zero, 2019-11-27aRagexe_zero, 2019-12-04aRagexe_zero, 2019-12-11_2aRagexe_zero, 2019-12-24_4aRagexe_zero, 2019-12-24_5aRagexe_zero, 2020-01-15_2aRagexe_zero, 2020-01-15aRagexe_zero, 2020-01-29_2aRagexe_zero, 2020-01-29aRagexe_zero, 2020-02-12aRagexe_zero, 2020-02-26aRagexe_zero, 2020-02-26bRagexe_zero, 2020-03-04aRagexe_zero, 2020-03-18_2aRagexe_zero, 2020-04-01_2aRagexe_zero, 2020-04-14bRagexe_zero +#if PACKETVER == 20190828 || \ + PACKETVER == 20190911 || \ + PACKETVER == 20190918 || \ + PACKETVER == 20190925 || \ + PACKETVER == 20191008 || \ + PACKETVER == 20191023 || \ + PACKETVER == 20191106 || \ + PACKETVER == 20191113 || \ + PACKETVER == 20191127 || \ + PACKETVER == 20191204 || \ + PACKETVER == 20191211 || \ + PACKETVER == 20191224 || \ + PACKETVER == 20200115 || \ + PACKETVER == 20200129 || \ + PACKETVER == 20200212 || \ + PACKETVER == 20200226 || \ + PACKETVER == 20200304 || \ + PACKETVER == 20200318 || \ + PACKETVER == 20200401 || \ + PACKETVER == 20200414 + 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 + packet(0x0281,clif->pDull/*,XXX*/); // CZ_GANGSI_RANK // 4 + packet(0x02c4,clif->pPartyInvite2,2); // CZ_PARTY_JOIN_REQ // 26 + packet(0x035f,clif->pWalkToXY,2); // CZ_REQUEST_MOVE // 5 + packet(0x0360,clif->pTickSend,2); // CZ_REQUEST_TIME // 6 + packet(0x0361,clif->pChangeDir,2,4); // CZ_CHANGE_DIRECTION // 5 + packet(0x0362,clif->pTakeItem,2); // CZ_ITEM_PICKUP // 6 + packet(0x0363,clif->pDropItem,2,4); // CZ_ITEM_THROW // 6 + packet(0x0364,clif->pMoveToKafra,2,4); // CZ_MOVE_ITEM_FROM_BODY_TO_STORE // 8 + packet(0x0365,clif->pMoveFromKafra,2,4); // CZ_MOVE_ITEM_FROM_STORE_TO_BODY // 8 + packet(0x0366,clif->pUseSkillToPos,2,4,6,8); // CZ_USE_SKILL_TOGROUND // 10 + packet(0x0367,clif->pUseSkillToPosMoreInfo,2,4,6,8,10); // CZ_USE_SKILL_TOGROUND_WITHTALKBOX // 31 + packet(0x0368,clif->pGetCharNameRequest,2); // CZ_REQNAME // 6 + packet(0x0369,clif->pSolveCharName,2); // CZ_REQNAME_BYGID // 6 + packet(0x0436,clif->pWantToConnection,2,6,10,14,18); // CZ_ENTER // 19 + packet(0x0437,clif->pActionRequest,2,6); // CZ_REQUEST_ACT // 7 + packet(0x0438,clif->pUseSkillToId,2,4,6); // CZ_USE_SKILL // 10 + packet(0x07e4,clif->pItemListWindowSelected,2,4,8); // CZ_ITEMLISTWIN_RES // -1 + packet(0x07ec,clif->pDull/*,XXX*/); // CZ_JOIN_BATTLE_FIELD // 8 + packet(0x0802,clif->pPartyBookingRegisterReq,2,4); // CZ_PARTY_BOOKING_REQ_REGISTER // 18 + packet(0x0811,clif->pReqOpenBuyingStore,2,4,8,9,89); // CZ_REQ_OPEN_BUYING_STORE // -1 + packet(0x0815,clif->pReqCloseBuyingStore,0); // CZ_REQ_CLOSE_BUYING_STORE // 2 + packet(0x0817,clif->pReqClickBuyingStore,2); // CZ_REQ_CLICK_TO_BUYING_STORE // 6 + packet(0x0819,clif->pReqTradeBuyingStore,2,4,8,12); // CZ_REQ_TRADE_BUYING_STORE // -1 + packet(0x0835,clif->pSearchStoreInfo,2,4,5,9,13,14,15); // CZ_SEARCH_STORE_INFO // -1 + packet(0x0838,clif->pSearchStoreInfoNextPage,0); // CZ_SEARCH_STORE_INFO_NEXT_PAGE // 2 + packet(0x083c,clif->pSearchStoreInfoListItemClick,2,6,10); // CZ_SSILIST_ITEM_CLICK // 14 +#endif + #endif /* MAP_PACKETS_SHUFFLE_ZERO_H */ diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index cd3a3b989..3129a05d9 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2013-2018 Hercules Dev Team + * Copyright (C) 2013-2020 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 @@ -30,10 +30,6 @@ // Packet DB #define MAX_PACKET_POS 20 -#define DEFINE_PACKET_HEADER(name, id) \ - STATIC_ASSERT(sizeof(struct PACKET_##name) == PACKET_LEN_##id, "Wrong size PACKET_"#name); \ - enum { HEADER_##name = id }; - /** * **/ @@ -43,13 +39,6 @@ enum packet_headers { banking_checkType = 0x9a6, cart_additem_ackType = 0x12c, sc_notickType = 0x196, -#if PACKETVER >= 20141022 - hotkeyType = 0xa00, -#elif PACKETVER >= 20090603 - hotkeyType = 0x7d9, -#else - hotkeyType = 0x2b9, -#endif #if PACKETVER >= 20150226 cartaddType = 0xa0b, #elif PACKETVER >= 5 @@ -311,8 +300,6 @@ enum packet_headers { notifybindonequip = 0x2d3, monsterhpType = 0x977, maptypeproperty2Type = 0x99b, - npcmarketresultackType = 0x9d7, - npcmarketopenType = 0x9d5, #if PACKETVER >= 20131223 // version probably can be 20131030 [4144] wisendType = 0x9df, #else @@ -375,9 +362,6 @@ enum packet_headers { partymemberinfo = 0x01e9, partyinfo = 0x00fb, #endif -#if PACKETVER >= 20120702 - clanBasicInfo = 0x098A, ///< ZC_CLANINFO -#endif #if PACKETVER >= 20120716 clanOnlineCount = 0x0988, ///< ZC_NOTIFY_CLAN_CONNECTINFO clanLeave = 0x0989, ///< ZC_ACK_CLAN_LEAVE @@ -425,18 +409,7 @@ enum packet_headers { #else buyingStoreUpdateItemType = 0x81b, #endif -// probably can works also for < 20141223, but in 3CeaM packet size defined only for 20150513 -#if PACKETVER >= 20150513 - hominfoType = 0x9f7, -#else - hominfoType = 0x22e, -#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 @@ -454,11 +427,6 @@ enum packet_headers { #else guildLeave = 0x15a, #endif -#if PACKETVER_RE_NUM >= 20181031 - autoSpellList = 0xafb, -#else - autoSpellList = 0x1cd, -#endif }; #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute @@ -469,7 +437,7 @@ enum packet_headers { * structs for data */ struct EQUIPSLOTINFO { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 card[4]; #else uint16 card[4]; @@ -478,7 +446,7 @@ struct EQUIPSLOTINFO { struct NORMALITEM_INFO { int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ITID; #else uint16 ITID; @@ -516,7 +484,7 @@ struct ItemOptions { struct EQUIPITEM_INFO { int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ITID; #else uint16 ITID; @@ -593,7 +561,7 @@ struct packet_additem { int16 PacketType; uint16 Index; uint16 count; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 nameid; #else uint16 nameid; @@ -627,7 +595,7 @@ struct packet_additem { struct packet_dropflooritem { int16 PacketType; uint32 ITAID; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ITID; #else uint16 ITID; @@ -739,7 +707,7 @@ struct packet_spawn_unit { #else uint32 weapon; #endif -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 shield; #endif uint16 accessory; @@ -812,7 +780,7 @@ struct packet_unit_walking { #else uint32 weapon; #endif -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 shield; #endif uint16 accessory; @@ -884,7 +852,7 @@ struct packet_idle_unit { #else uint32 weapon; #endif -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 shield; #endif uint16 accessory; @@ -1047,7 +1015,7 @@ struct packet_package_item_announce { int16 PacketType; int16 PacketLength; uint8 type; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ItemID; #else uint16 ItemID; @@ -1055,7 +1023,7 @@ struct packet_package_item_announce { int8 len; char Name[NAME_LENGTH]; int8 unknown; // probably unused -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 BoxItemID; #else uint16 BoxItemID; @@ -1067,7 +1035,7 @@ struct packet_item_drop_announce { int16 PacketType; int16 PacketLength; uint8 type; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ItemID; #else uint16 ItemID; @@ -1122,7 +1090,7 @@ struct packet_roulette_open_ack { int32 Serial; int8 Step; int8 Idx; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 AdditionItemID; #else uint16 AdditionItemID; @@ -1160,7 +1128,7 @@ struct packet_roulette_generate_ack { uint8 Result; uint16 Step; uint16 Idx; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 AdditionItemID; #else uint16 AdditionItemID; @@ -1178,7 +1146,7 @@ struct packet_roulette_itemrecv_req { struct packet_roulette_itemrecv_ack { int16 PacketType; uint8 Result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 AdditionItemID; #else uint16 AdditionItemID; @@ -1215,7 +1183,8 @@ struct ZC_STORE_ITEMLIST_NORMAL { struct NORMALITEM_INFO list[MAX_ITEMLIST]; } __attribute__((packed)); -struct ZC_INVENTORY_START { +#if PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 +struct PACKET_ZC_INVENTORY_START { int16 packetType; #if PACKETVER_RE_NUM >= 20180919 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 int16 packetLength; @@ -1229,14 +1198,19 @@ struct ZC_INVENTORY_START { char name[NAME_LENGTH]; #endif } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_INVENTORY_START, 0x0b08); +#endif // PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 -struct ZC_INVENTORY_END { +#if PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 +struct PACKET_ZC_INVENTORY_END { int16 packetType; #if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 uint8 invType; #endif char flag; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_INVENTORY_END, 0x0b0b); +#endif // PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 || PACKETVER_MAIN_NUM >= 20181002 struct ZC_STORE_ITEMLIST_EQUIP { int16 PacketType; @@ -1303,6 +1277,7 @@ struct packet_viewequip_ack { int16 body2; #endif uint8 sex; + // [4144] need remove MAX_INVENTORY from here struct EQUIPITEM_INFO list[MAX_INVENTORY]; } __attribute__((packed)); @@ -1383,7 +1358,7 @@ struct packet_npc_market_purchase { int16 PacketType; int16 PacketLength; struct { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ITID; #else uint16 ITID; @@ -1392,41 +1367,29 @@ struct packet_npc_market_purchase { } list[]; // Note: We assume this should be <= MAX_INVENTORY (since you can't hold more than MAX_INVENTORY items thus cant buy that many at once). } __attribute__((packed)); -struct packet_npc_market_result_ack { - int16 PacketType; - int16 PacketLength; - uint8 result; - struct { -#if PACKETVER_RE_NUM >= 20180704 - uint32 ITID; +#if PACKETVER_MAIN_NUM >= 20131120 || PACKETVER_RE_NUM >= 20131106 || defined(PACKETVER_ZERO) +/* inner struct figured by Ind after some annoying hour of debugging (data Thanks to Yommy) */ +struct PACKET_ZC_NPC_MARKET_OPEN_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 nameid; #else - uint16 ITID; + uint16 nameid; #endif - uint16 qty; - uint32 price; - } list[MAX_INVENTORY];/* assuming MAX_INVENTORY is max since you can't hold more than MAX_INVENTORY items thus cant buy that many at once. */ + uint8 type; + uint32 price; + uint32 qty; + uint16 weight; } __attribute__((packed)); -struct packet_npc_market_open { - int16 PacketType; - int16 PacketLength; - /* inner struct figured by Ind after some annoying hour of debugging (data Thanks to Yommy) */ - struct { -#if PACKETVER_RE_NUM >= 20180704 - uint32 nameid; -#else - uint16 nameid; -#endif - uint8 type; - uint32 price; - uint32 qty; - uint16 view; - // It seems that the client doesn't have any hard-coded limit for this list - // it's possible to send up to 1890 items without dropping a packet that's - // too large [Panikon] - } list[1000];/* TODO: whats the actual max of this? */ +struct PACKET_ZC_NPC_MARKET_OPEN { + int16 packetType; + int16 packetLength; + struct PACKET_ZC_NPC_MARKET_OPEN_sub list[]; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_NPC_MARKET_OPEN, 0x09d5); +#endif + struct packet_wis_end { int16 PacketType; int8 result; @@ -1442,21 +1405,90 @@ struct packet_party_leader_changed { uint32 new_leader_aid; } __attribute__((packed)); -struct packet_hotkey { #ifdef HOTKEY_SAVING - int16 PacketType; -#if PACKETVER >= 20141022 - int8 Rotate; +struct hotkey_data { + int8 isSkill; // 0: Item, 1:Skill + uint32 id; // Item/Skill ID + int16 count; // Item Quantity/Skill Level +} __attribute__((packed)); + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 +#define MAX_HOTKEYS_PACKET 38 +struct PACKET_ZC_SHORTCUT_KEY_LIST { + int16 packetType; + int8 rotate; + int16 tab; + struct hotkey_data hotkey[MAX_HOTKEYS_PACKET]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SHORTCUT_KEY_LIST, 0x0b20); +#elif PACKETVER_MAIN_NUM >= 20141022 || PACKETVER_RE_NUM >= 20141015 || defined(PACKETVER_ZERO) +#define MAX_HOTKEYS_PACKET 38 +struct PACKET_ZC_SHORTCUT_KEY_LIST { + int16 packetType; + int8 rotate; + struct hotkey_data hotkey[MAX_HOTKEYS_PACKET]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SHORTCUT_KEY_LIST, 0x0a00); +#elif PACKETVER_MAIN_NUM >= 20090617 || PACKETVER_RE_NUM >= 20090617 || PACKETVER_SAK_NUM >= 20090617 +#define MAX_HOTKEYS_PACKET 38 +struct PACKET_ZC_SHORTCUT_KEY_LIST { + int16 packetType; + struct hotkey_data hotkey[MAX_HOTKEYS_PACKET]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SHORTCUT_KEY_LIST, 0x07d9); +#elif PACKETVER_MAIN_NUM >= 20090603 || PACKETVER_RE_NUM >= 20090603 || PACKETVER_SAK_NUM >= 20090603 +#define MAX_HOTKEYS_PACKET 36 +struct PACKET_ZC_SHORTCUT_KEY_LIST { + int16 packetType; + struct hotkey_data hotkey[MAX_HOTKEYS_PACKET]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SHORTCUT_KEY_LIST, 0x07d9); +#elif PACKETVER_MAIN_NUM >= 20070711 || PACKETVER_RE_NUM >= 20080827 || PACKETVER_AD_NUM >= 20070711 || PACKETVER_SAK_NUM >= 20070628 +#define MAX_HOTKEYS_PACKET 27 +struct PACKET_ZC_SHORTCUT_KEY_LIST { + int16 packetType; + struct hotkey_data hotkey[MAX_HOTKEYS_PACKET]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SHORTCUT_KEY_LIST, 0x02b9); #endif - struct { - int8 isSkill; // 0: Item, 1:Skill - uint32 ID; // Item/Skill ID - int16 count; // Item Quantity/Skill Level - } hotkey[MAX_HOTKEYS]; -#else // not HOTKEY_SAVING - UNAVAILABLE_STRUCT; -#endif // HOTKEY_SAVING + +#if PACKETVER_MAIN_NUM >= 20070618 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_AD_NUM >= 20070618 || PACKETVER_SAK_NUM >= 20070618 +struct PACKET_CZ_SHORTCUT_KEY_CHANGE1 { + int16 packetType; + uint16 index; + struct hotkey_data hotkey; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SHORTCUT_KEY_CHANGE1, 0x02ba); +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 +struct PACKET_CZ_SHORTCUT_KEY_CHANGE2 { + int16 packetType; + uint16 tab; + uint16 index; + struct hotkey_data hotkey; } __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SHORTCUT_KEY_CHANGE2, 0x0b21); +#endif + +#if PACKETVER_MAIN_NUM >= 20140129 || PACKETVER_RE_NUM >= 20140129 || defined(PACKETVER_ZERO) +struct PACKET_CZ_SHORTCUTKEYBAR_ROTATE1 { + int16 packetType; + uint8 rowshift; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SHORTCUTKEYBAR_ROTATE1, 0x0a01); +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190508 || PACKETVER_ZERO_NUM >= 20190605 +struct PACKET_CZ_SHORTCUTKEYBAR_ROTATE2 { + int16 packetType; + uint16 tab; + uint8 rowshift; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SHORTCUTKEYBAR_ROTATE2, 0x0b22); +#endif + +#endif // HOTKEY_SAVING /** * MISSION_HUNT_INFO (PACKETVER >= 20141022) @@ -1537,7 +1569,7 @@ struct PACKET_ZC_ADD_ITEM_TO_MAIL { int8 result; int16 index; int16 count; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -1555,7 +1587,7 @@ struct PACKET_ZC_ADD_ITEM_TO_MAIL { struct mail_item { int16 count; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ITID; #else uint16 ITID; @@ -1804,6 +1836,7 @@ struct PACKET_ZC_GROUP_LIST { struct PACKET_ZC_GROUP_LIST_SUB members[]; } __attribute__((packed)); +#if PACKETVER_MAIN_NUM >= 20130626 || PACKETVER_RE_NUM >= 20130605 || defined(PACKETVER_ZERO) struct PACKET_ZC_CLANINFO { int16 PacketType; int16 PacketLength; @@ -1814,6 +1847,8 @@ struct PACKET_ZC_CLANINFO { uint8 AllyCount; uint8 AntagonistCount; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_CLANINFO, 0x098a); +#endif struct PACKET_ZC_NOTIFY_CLAN_CONNECTINFO { int16 PacketType; @@ -1965,7 +2000,7 @@ struct PACKET_ZC_UI_ACTION { struct PACKET_CZ_PRIVATE_AIRSHIP_REQUEST { int16 PacketType; char mapName[MAP_NAME_LENGTH_EXT]; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 ItemID; #else uint16 ItemID; @@ -2011,7 +2046,7 @@ struct pet_evolution_items { struct PACKET_CZ_PET_EVOLUTION { int16 PacketType; uint16 PacketLength; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 EvolvedPetEggID; #else uint16 EvolvedPetEggID; @@ -2045,7 +2080,7 @@ struct PACKET_ZC_NOTIFY_PLAYERCHAT { struct PACKET_ZC_ITEM_ENTRY { int16 packetType; uint32 AID; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2062,7 +2097,7 @@ struct PACKET_ZC_ADD_ITEM_TO_STORE { int16 packetType; int16 index; int32 amount; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2081,7 +2116,7 @@ struct PACKET_ZC_ADD_ITEM_TO_STORE { struct PACKET_ZC_MVP_GETTING_ITEM { int16 packetType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2091,7 +2126,7 @@ struct PACKET_ZC_MVP_GETTING_ITEM { struct PACKET_ZC_ACK_TOUSESKILL { int16 packetType; uint16 skillId; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 int32 btype; uint32 itemId; #else @@ -2106,7 +2141,7 @@ struct PACKET_ZC_ADD_ITEM_TO_CART { int16 packetType; int16 index; int32 amount; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2125,7 +2160,7 @@ struct PACKET_ZC_ADD_ITEM_TO_CART { struct PACKET_CZ_REQMAKINGITEM { int16 packetType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; uint32 material[3]; #else @@ -2137,7 +2172,7 @@ struct PACKET_CZ_REQMAKINGITEM { struct PACKET_ZC_ACK_REQMAKINGITEM { int16 packetType; int16 result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2147,7 +2182,7 @@ struct PACKET_ZC_ACK_REQMAKINGITEM { struct PACKET_ZC_FEED_PET { int16 packetType; uint8 result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2157,7 +2192,7 @@ struct PACKET_ZC_FEED_PET { struct PACKET_ZC_FEED_MER { int16 packetType; uint8 result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2167,7 +2202,7 @@ struct PACKET_ZC_FEED_MER { struct PACKET_ZC_USE_ITEM_ACK { int16 packetType; int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; uint32 AID; #elif PACKETVER >= 3 @@ -2182,7 +2217,7 @@ struct PACKET_ZC_SPRITE_CHANGE { int16 packetType; uint32 AID; uint8 type; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 val; uint32 val2; #elif PACKETVER >= 4 @@ -2195,7 +2230,7 @@ struct PACKET_ZC_SPRITE_CHANGE { struct PACKET_ZC_ADD_EXCHANGE_ITEM { int16 packetType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; uint8 itemType; int32 amount; @@ -2218,7 +2253,7 @@ struct PACKET_ZC_ADD_EXCHANGE_ITEM { struct PACKET_ZC_CASH_TIME_COUNTER { int16 packetType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2229,7 +2264,7 @@ struct PACKET_ZC_CASH_TIME_COUNTER { struct PACKET_ZC_CASH_ITEM_DELETE { int16 packetType; uint16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2239,7 +2274,7 @@ struct PACKET_ZC_CASH_ITEM_DELETE { struct PACKET_ZC_ITEM_PICKUP_PARTY { int16 packetType; uint32 AID; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2254,7 +2289,7 @@ struct PACKET_ZC_ITEM_PICKUP_PARTY { struct PACKET_ZC_UPDATE_ITEM_FROM_BUYING_STORE { int16 packetType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2273,13 +2308,15 @@ struct PACKET_ZC_UPDATE_ITEM_FROM_BUYING_STORE { struct PACKET_ZC_ACK_WEAPONREFINE { int16 packetType; int32 result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; #endif } __attribute__((packed)); +#if PACKETVER_MAIN_NUM >= 20190619 || PACKETVER_RE_NUM >= 20190605 || PACKETVER_ZERO_NUM >= 20190626 +// PACKET_ZC_PROPERTY_HOMUN3 struct PACKET_ZC_PROPERTY_HOMUN { int16 packetType; char name[NAME_LENGTH]; @@ -2288,7 +2325,35 @@ struct PACKET_ZC_PROPERTY_HOMUN { uint16 level; uint16 hunger; uint16 intimacy; -#if PACKETVER_RE_NUM >= 20180704 + uint16 atk2; + uint16 matk; + uint16 hit; + uint16 crit; + uint16 def; + uint16 mdef; + uint16 flee; + uint16 amotion; + uint32 hp; + uint32 maxHp; + uint16 sp; + uint16 maxSp; + uint32 exp; + uint32 expNext; + uint16 skillPoints; + uint16 range; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PROPERTY_HOMUN, 0x0b2f); +#elif PACKETVER_MAIN_NUM >= 20131230 || PACKETVER_RE_NUM >= 20131230 || defined(PACKETVER_ZERO) +// PACKET_ZC_PROPERTY_HOMUN2 +struct PACKET_ZC_PROPERTY_HOMUN { + int16 packetType; + char name[NAME_LENGTH]; + // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) + uint8 flags; + uint16 level; + uint16 hunger; + uint16 intimacy; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2301,13 +2366,41 @@ struct PACKET_ZC_PROPERTY_HOMUN { uint16 mdef; uint16 flee; uint16 amotion; -#if PACKETVER < 20150513 - uint16 hp; - uint16 maxHp; -#else uint32 hp; uint32 maxHp; + uint16 sp; + uint16 maxSp; + uint32 exp; + uint32 expNext; + uint16 skillPoints; + uint16 range; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PROPERTY_HOMUN, 0x09f7); +#elif PACKETVER_MAIN_NUM >= 20101005 || PACKETVER_RE_NUM >= 20080827 || defined(PACKETVER_ZERO) +// PACKET_ZC_PROPERTY_HOMUN1 +struct PACKET_ZC_PROPERTY_HOMUN { + int16 packetType; + char name[NAME_LENGTH]; + // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true) + uint8 flags; + uint16 level; + uint16 hunger; + uint16 intimacy; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; #endif + uint16 atk2; + uint16 matk; + uint16 hit; + uint16 crit; + uint16 def; + uint16 mdef; + uint16 flee; + uint16 amotion; + uint16 hp; + uint16 maxHp; uint16 sp; uint16 maxSp; uint32 exp; @@ -2315,11 +2408,13 @@ struct PACKET_ZC_PROPERTY_HOMUN { uint16 skillPoints; uint16 range; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PROPERTY_HOMUN, 0x022e); +#endif struct PACKET_ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER { int16 packetType; uint16 result; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2329,7 +2424,7 @@ struct PACKET_ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER { struct PACKET_CZ_REQ_ITEMREPAIR { int16 packetType; int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2341,7 +2436,7 @@ struct PACKET_CZ_REQ_ITEMREPAIR { struct PACKET_CZ_REQ_MAKINGITEM { int16 packetType; int16 type; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2352,7 +2447,7 @@ struct PACKET_CZ_SSILIST_ITEM_CLICK { int16 packetType; uint32 AID; uint32 storeId; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2360,7 +2455,7 @@ struct PACKET_CZ_SSILIST_ITEM_CLICK { } __attribute__((packed)); struct PACKET_ZC_ACK_SCHEDULER_CASHITEM_sub { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2381,7 +2476,7 @@ struct PACKET_ZC_PC_PURCHASE_MYITEMLIST_sub { int16 index; int16 amount; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2406,7 +2501,7 @@ struct PACKET_ZC_PC_PURCHASE_ITEMLIST_sub { uint32 price; uint32 discountPrice; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2421,7 +2516,7 @@ struct PACKET_ZC_PC_PURCHASE_ITEMLIST { struct PACKET_CZ_PC_PURCHASE_ITEMLIST_sub { uint16 amount; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2435,7 +2530,7 @@ struct PACKET_CZ_PC_PURCHASE_ITEMLIST { } __attribute__((packed)); struct PACKET_CZ_REQ_OPEN_BUYING_STORE_sub { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2457,7 +2552,7 @@ struct PACKET_ZC_MYITEMLIST_BUYING_STORE_sub { uint32 price; uint16 amount; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2477,7 +2572,7 @@ struct PACKET_ZC_PC_PURCHASE_ITEMLIST_FROMMC_sub { uint16 amount; int16 index; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2510,7 +2605,7 @@ struct PACKET_ZC_ACK_ITEMLIST_BUYING_STORE_sub { uint32 price; uint16 amount; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2528,7 +2623,7 @@ struct PACKET_ZC_ACK_ITEMLIST_BUYING_STORE { struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub { int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2545,7 +2640,7 @@ struct PACKET_CZ_REQ_TRADE_BUYING_STORE { } __attribute__((packed)); struct PACKET_ZC_MAKABLEITEMLIST_sub { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; uint32 material[3]; #else @@ -2561,7 +2656,7 @@ struct PACKET_ZC_MAKABLEITEMLIST { } __attribute__((packed)); struct PACKET_ZC_MAKINGARROW_LIST_sub { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2573,10 +2668,11 @@ struct PACKET_ZC_MAKINGARROW_LIST { int16 packetLength; struct PACKET_ZC_MAKINGARROW_LIST_sub items[]; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_MAKINGARROW_LIST, 0x01ad); struct PACKET_ZC_REPAIRITEMLIST_sub { int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2593,7 +2689,7 @@ struct PACKET_ZC_REPAIRITEMLIST { struct PACKET_ZC_NOTIFY_WEAPONITEMLIST_sub { int16 index; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2609,7 +2705,7 @@ struct PACKET_ZC_NOTIFY_WEAPONITEMLIST { } __attribute__((packed)); struct PACKET_ZC_MAKINGITEM_LIST_sub { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2619,19 +2715,16 @@ struct PACKET_ZC_MAKINGITEM_LIST_sub { struct PACKET_ZC_MAKINGITEM_LIST { int16 packetType; int16 packetLength; -#if PACKETVER_RE_NUM >= 20180704 - uint32 makeItem; -#else uint16 makeItem; -#endif struct PACKET_ZC_MAKINGITEM_LIST_sub items[]; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_MAKINGITEM_LIST, 0x025a); struct PACKET_ZC_PC_CASH_POINT_ITEMLIST_sub { uint32 price; uint32 discountPrice; uint8 itemType; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2650,7 +2743,7 @@ struct PACKET_ZC_PC_CASH_POINT_ITEMLIST { struct PACKET_CZ_PC_BUY_CASH_POINT_ITEM_sub { uint16 amount; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2674,7 +2767,7 @@ struct PACKET_CZ_PC_BUY_CASH_POINT_ITEM { } __attribute__((packed)); struct PACKET_CZ_SEARCH_STORE_INFO_item { -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2699,7 +2792,7 @@ struct PACKET_ZC_SEARCH_STORE_INFO_ACK_sub { uint32 storeId; uint32 AID; char shopName[MESSAGE_SIZE]; -#if PACKETVER_RE_NUM >= 20180704 +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 uint32 itemId; #else uint16 itemId; @@ -2766,17 +2859,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; @@ -2874,6 +2996,7 @@ struct PACKET_ZC_REMOVE_EFFECT { uint32 effectId; } __attribute__((packed)); +#if PACKETVER >= 20160525 struct PACKET_ZC_CAMERA_INFO { int16 packetType; int8 action; @@ -2881,23 +3004,36 @@ struct PACKET_ZC_CAMERA_INFO { float rotation; float latitude; } __attribute__((packed)); - -#if PACKETVER_RE_NUM >= 20181031 -#define PACKET_ZC_AUTOSPELLLIST PACKET_ZC_AUTOSPELLLIST2 -#else -#define PACKET_ZC_AUTOSPELLLIST PACKET_ZC_AUTOSPELLLIST1 +DEFINE_PACKET_HEADER(ZC_CAMERA_INFO, 0x0a78); #endif -struct PACKET_ZC_AUTOSPELLLIST1 { +#if PACKETVER >= 20160525 +struct PACKET_CZ_CAMERA_INFO { int16 packetType; - int skills[7]; + int8 action; + float range; + float rotation; + float latitude; } __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_CAMERA_INFO, 0x0a77); +#endif -struct PACKET_ZC_AUTOSPELLLIST2 { +#if PACKETVER_MAIN_NUM >= 20181128 || PACKETVER_RE_NUM >= 20181031 +// PACKET_ZC_AUTOSPELLLIST2 +struct PACKET_ZC_AUTOSPELLLIST { int16 packetType; int16 packetLength; int skills[]; } __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_AUTOSPELLLIST, 0x0afb); +#elif PACKETVER_MAIN_NUM >= 20090406 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) || PACKETVER_SAK_NUM >= 20080618 +// PACKET_ZC_AUTOSPELLLIST1 +struct PACKET_ZC_AUTOSPELLLIST { + int16 packetType; + int skills[7]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_AUTOSPELLLIST, 0x01cd); +#endif #if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) #if PACKETVER_MAIN_NUM >= 20181017 || PACKETVER_RE_NUM >= 20181017 || PACKETVER_ZERO_NUM >= 20181024 @@ -2923,6 +3059,874 @@ DEFINE_PACKET_HEADER(ZC_ITEM_PREVIEW, 0x0ab9); #endif // PACKETVER_MAIN_NUM >= 20181017 || PACKETVER_RE_NUM >= 20181017 || PACKETVER_ZERO_NUM >= 20181024 #endif // PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) +#if PACKETVER_MAIN_NUM >= 20160831 || PACKETVER_RE_NUM >= 20151118 || defined(PACKETVER_ZERO) +struct PACKET_ZC_ENCHANT_EQUIPMENT { + int16 packetType; + int16 wearState; + int16 cardSlot; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + int32 itemId; +#else + int16 itemId; +#endif + int8 equipFlag; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ENCHANT_EQUIPMENT, 0x0a3f); +#endif // PACKETVER_MAIN_NUM >= 20160831 || PACKETVER_RE_NUM >= 20151118 || defined(PACKETVER_ZERO) + +#if PACKETVER_MAIN_NUM >= 20170830 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) +struct PACKET_ZC_SERVICE_MESSAGE_COLOR { + int16 packetType; + int16 packetLength; + int32 color; + char message[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SERVICE_MESSAGE_COLOR, 0x0adb); +#endif + +#if PACKETVER_MAIN_NUM >= 20181002 || PACKETVER_RE_NUM >= 20181002 || PACKETVER_ZERO_NUM >= 20181010 +struct PACKET_CZ_START_USE_SKILL { + int16 packetType; + int16 skillId; + int16 skillLv; + uint32 targetId; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_START_USE_SKILL, 0x0b10); + +struct PACKET_CZ_STOP_USE_SKILL { + int16 packetType; + int16 skillId; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_STOP_USE_SKILL, 0x0b11); +#endif + +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 +struct PACKET_ZC_INVENTORY_EXPANSION_INFO { + int16 packetType; + int16 expansionSize; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_INVENTORY_EXPANSION_INFO, 0x0b18); +#endif + +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 +struct PACKET_ZC_ACK_INVENTORY_EXPAND { + int16 packetType; + uint8 result; + uint32 itemId; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_INVENTORY_EXPAND, 0x0b15); +#endif + +#if PACKETVER_MAIN_NUM >= 20181219 || PACKETVER_RE_NUM >= 20181219 || PACKETVER_ZERO_NUM >= 20181212 +struct PACKET_ZC_ACK_INVENTORY_EXPAND_RESULT { + int16 packetType; + uint8 result; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_INVENTORY_EXPAND_RESULT, 0x0b17); +#endif + +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 +struct PACKET_CZ_INVENTORY_EXPAND { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_INVENTORY_EXPAND, 0x0b14); +#endif + +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 +struct PACKET_CZ_INVENTORY_EXPAND_CONFIRMED { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_INVENTORY_EXPAND_CONFIRMED, 0x0b16); +#endif + +#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114 +struct PACKET_CZ_INVENTORY_EXPAND_REJECTED { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_INVENTORY_EXPAND_REJECTED, 0x0b19); +#endif + +struct PACKET_CZ_REQ_REMAINTIME { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REQ_REMAINTIME, 0x01c0); + +struct PACKET_CZ_PARTY_CONFIG { + int16 packetType; + uint8 refuseInvite; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_PARTY_CONFIG, 0x02c8); + +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 +struct PACKET_ZC_NPC_BARTER_OPEN_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 nameid; +#else + uint16 nameid; +#endif + uint8 type; + uint32 amount; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 currencyNameid; +#else + uint16 currencyNameid; +#endif + uint32 currencyAmount; + uint32 weight; + uint32 index; +} __attribute__((packed)); + +struct PACKET_ZC_NPC_BARTER_OPEN { + int16 packetType; + int16 packetLength; + struct PACKET_ZC_NPC_BARTER_OPEN_sub list[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_NPC_BARTER_OPEN, 0x0b0e); +#endif + +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 +struct PACKET_CZ_NPC_BARTER_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_NPC_BARTER_CLOSE, 0x0b12); +#endif + +#if PACKETVER_MAIN_NUM >= 20190116 || PACKETVER_RE_NUM >= 20190116 || PACKETVER_ZERO_NUM >= 20181226 +struct PACKET_CZ_NPC_BARTER_PURCHASE_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + uint32 amount; + uint16 invIndex; + uint32 shopIndex; +} __attribute__((packed)); + +struct PACKET_CZ_NPC_BARTER_PURCHASE { + int16 packetType; + int16 packetLength; + struct PACKET_CZ_NPC_BARTER_PURCHASE_sub list[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_NPC_BARTER_PURCHASE, 0x0b0f); +#endif + +#if PACKETVER_MAIN_NUM >= 20181212 || PACKETVER_RE_NUM >= 20181212 || PACKETVER_ZERO_NUM >= 20190130 +struct PACKET_ZC_USESKILL_ACK { + int16 packetType; + uint32 srcId; + uint32 dstId; + uint16 x; + uint16 y; + uint16 skillId; + uint32 element; + uint32 delayTime; + uint8 disposable; + uint32 unknown; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_USESKILL_ACK, 0x0b1a); +#elif PACKETVER_MAIN_NUM >= 20091124 || PACKETVER_RE_NUM >= 20091124 || defined(PACKETVER_ZERO) +struct PACKET_ZC_USESKILL_ACK { + int16 packetType; + uint32 srcId; + uint32 dstId; + uint16 x; + uint16 y; + uint16 skillId; + uint32 element; + uint32 delayTime; + uint8 disposable; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_USESKILL_ACK, 0x07fb); +#elif PACKETVER_MAIN_NUM >= 20090406 || PACKETVER_SAK_NUM >= 20080618 || PACKETVER_RE_NUM >= 20080827 || defined(PACKETVER_ZERO) +struct PACKET_ZC_USESKILL_ACK { + int16 packetType; + uint32 srcId; + uint32 dstId; + uint16 x; + uint16 y; + uint16 skillId; + uint32 element; + uint32 delayTime; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_USESKILL_ACK, 0x013e); +#endif + +#if PACKETVER_MAIN_NUM >= 20090406 || PACKETVER_RE_NUM >= 20090408 || PACKETVER_SAK_NUM >= 20090408 || defined(PACKETVER_ZERO) +struct PACKET_CZ_CLIENT_VERSION { + int16 packetType; + uint32 clientVersion; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_CLIENT_VERSION, 0x044a); +#endif + +#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220 +struct PACKET_CZ_PING { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_PING, 0x0b1c); +#endif + +#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220 +struct PACKET_ZC_PING { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_PING, 0x0b1d); +#endif + +#if PACKETVER >= 20160622 +struct PACKET_CZ_COOLDOWN_RESET { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_COOLDOWN_RESET, 0x0a88); +#endif + +#if PACKETVER >= 20151104 +struct PACKET_CZ_STYLE_CLOSE { + int16 packetType; +} __attribute__((packed)); +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 + +struct PACKET_ZC_ACK_RANKING_name { + char name[NAME_LENGTH]; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_RANKING_points { + uint32 points; +} __attribute__((packed)); + +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190724 +struct PACKET_ZC_ACK_RANKING_sub { + char name[NAME_LENGTH]; + uint32 points; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_RANKING { + int16 packetType; + int16 rankType; + uint32 chars[10]; + uint32 points[10]; + uint32 myPoints; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_ACK_RANKING, 0x0af6); +#elif PACKETVER_MAIN_NUM >= 20130605 || PACKETVER_RE_NUM >= 20130529 || defined(PACKETVER_ZERO) +struct PACKET_ZC_ACK_RANKING_sub { + struct PACKET_ZC_ACK_RANKING_name names[10]; + struct PACKET_ZC_ACK_RANKING_points points[10]; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_RANKING { + int16 packetType; + int16 rankType; + struct PACKET_ZC_ACK_RANKING_sub ranks; + uint32 myPoints; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_ACK_RANKING, 0x097d); +#else +struct PACKET_ZC_ACK_RANKING_sub { + struct PACKET_ZC_ACK_RANKING_name names[10]; + struct PACKET_ZC_ACK_RANKING_points points[10]; +} __attribute__((packed)); +#endif + +struct PACKET_ZC_STATUS_CHANGE_ACK { + int16 packetType; + uint16 sp; + uint8 ok; + uint8 value; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_STATUS_CHANGE_ACK, 0x00bc); + +#if PACKETVER_MAIN_NUM >= 20150507 || PACKETVER_RE_NUM >= 20150429 || defined(PACKETVER_ZERO) +struct PACKET_ZC_HAT_EFFECT { + int16 packetType; + int16 packetLength; + uint32 aid; + int8 status; + uint16 effects[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_HAT_EFFECT, 0x0a3b); +#endif + +// [4144] this struct updated not in all packets in client +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 +struct SKILLDATA { + uint16 id; + int inf; + uint16 level; + uint16 sp; + uint16 range2; + uint8 upFlag; + uint16 level2; +} __attribute__((packed)); +#else +struct SKILLDATA { + uint16 id; + int inf; + uint16 level; + uint16 sp; + uint16 range2; + char name[NAME_LENGTH]; + uint8 upFlag; +} __attribute__((packed)); +#endif + +struct PACKET_ZC_ADD_SKILL { + int16 packetType; + struct SKILLDATA skill; +} __attribute__((packed)); +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 +DEFINE_PACKET_HEADER(ZC_ADD_SKILL, 0x0b31); +#else +DEFINE_PACKET_HEADER(ZC_ADD_SKILL, 0x0111); +#endif + +struct PACKET_ZC_SKILLINFO_LIST { + int16 packetType; + int16 packetLength; + struct SKILLDATA skills[]; +} __attribute__((packed)); +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 +DEFINE_PACKET_HEADER(ZC_SKILLINFO_LIST, 0x0b32); +#else +DEFINE_PACKET_HEADER(ZC_SKILLINFO_LIST, 0x010f); +#endif + +#if PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190918 +struct PACKET_ZC_SKILLINFO_UPDATE2 { + int16 packetType; + uint16 id; + int inf; + uint16 level; + uint16 sp; + uint16 range2; + uint8 upFlag; + uint16 level2; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SKILLINFO_UPDATE2, 0x0b33); +#else +struct PACKET_ZC_SKILLINFO_UPDATE2 { + int16 packetType; + uint16 id; + int inf; + uint16 level; + uint16 sp; + uint16 range2; + uint8 upFlag; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SKILLINFO_UPDATE2, 0x07e1); +#endif + +struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 ITID; +#else + uint16 ITID; +#endif + uint16 qty; + uint32 price; +} __attribute__((packed)); + +#if PACKETVER_MAIN_NUM >= 20190807 || PACKETVER_RE_NUM >= 20190807 || PACKETVER_ZERO_NUM >= 20190814 +struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT { + int16 PacketType; + int16 PacketLength; + uint16 result; + struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub list[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_NPC_MARKET_PURCHASE_RESULT, 0x0b4e); +#elif PACKETVER_MAIN_NUM >= 20131120 || PACKETVER_RE_NUM >= 20130911 || defined(PACKETVER_ZERO) +struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT { + int16 PacketType; + int16 PacketLength; + uint8 result; + struct PACKET_ZC_NPC_MARKET_PURCHASE_RESULT_sub list[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_NPC_MARKET_PURCHASE_RESULT, 0x09d7); +#endif + +struct PACKET_ZC_TALKBOX_CHATCONTENTS { + int16 PacketType; + uint32 aid; + char message[TALKBOX_MESSAGE_SIZE]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_TALKBOX_CHATCONTENTS, 0x0191); + +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 +struct PACKET_ZC_GUILD_CASTLE_LIST { + int16 packetType; + int16 packetLength; + int8 castle_list[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_GUILD_CASTLE_LIST, 0x0b27); +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190522 || PACKETVER_ZERO_NUM >= 20190515 +struct PACKET_CZ_CASTLE_TELEPORT_REQUEST { + int16 packetType; + int8 castle_id; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_CASTLE_TELEPORT_REQUEST, 0x0b28); +#endif + +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 +struct PACKET_ZC_CASTLE_TELEPORT_RESPONSE { + int16 packetType; + int16 result; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_CASTLE_TELEPORT_RESPONSE, 0x0b2e); +#endif + +#if PACKETVER_MAIN_NUM >= 20190731 || PACKETVER_RE_NUM >= 20190717 || PACKETVER_ZERO_NUM >= 20190814 +struct PACKET_ZC_CASTLE_INFO { + int16 packetType; + int8 castle_id; + int32 economy; + int32 defense; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_CASTLE_INFO, 0x0b2d); +#endif + +#if PACKETVER_MAIN_NUM >= 20190522 || PACKETVER_RE_NUM >= 20190522 || PACKETVER_ZERO_NUM >= 20190515 +struct PACKET_CZ_CASTLE_INFO_REQUEST { + int16 packetType; + int8 castle_id; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_CASTLE_INFO_REQUEST, 0x0b2c); +#endif + +#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) +struct PACKET_ZC_LAPINEDDUKDDAK_OPEN { + int16 packetType; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + int32 itemId; +#else + int16 itemId; +#endif +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_LAPINEDDUKDDAK_OPEN, 0x0a4e); +#endif // PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) + +#if PACKETVER_MAIN_NUM >= 20160504 || PACKETVER_RE_NUM >= 20160504 || defined(PACKETVER_ZERO) +struct PACKET_CZ_LAPINEDDUKDDAK_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_LAPINEDDUKDDAK_CLOSE, 0x0a70); +#endif // PACKETVER_MAIN_NUM >= 20160504 || PACKETVER_RE_NUM >= 20160504 || defined(PACKETVER_ZERO) + +#if PACKETVER >= 20160302 +struct PACKET_CZ_LAPINEDDUKDDAK_ACK_sub { + int16 index; + int16 count; +} __attribute__((packed)); + +struct PACKET_CZ_LAPINEDDUKDDAK_ACK { + int16 packetType; + int16 packetLength; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + int32 itemId; +#else + int16 itemId; +#endif + struct PACKET_CZ_LAPINEDDUKDDAK_ACK_sub items[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_LAPINEDDUKDDAK_ACK, 0x0a4f); +#endif // PACKETVER >= 20160302 + +#if PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) +struct PACKET_ZC_LAPINEDDUKDDAK_RESULT { + int16 packetType; + int16 result; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_LAPINEDDUKDDAK_RESULT, 0x0a50); +#endif // PACKETVER_MAIN_NUM >= 20160601 || PACKETVER_RE_NUM >= 20160525 || defined(PACKETVER_ZERO) + +#if PACKETVER_MAIN_NUM >= 20190703 || PACKETVER_RE_NUM >= 20190703 || PACKETVER_ZERO_NUM >= 20190709 +struct PACKET_CZ_REQ_MOUNTOFF { + int16 packetType; + uint8 action; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_REQ_MOUNTOFF, 0x0b35); +#endif + +// in 3 clients from same version +#if PACKETVER >= 20191127 +struct PACKET_ZC_NOTIFY_EFFECT3 { + int16 packetType; + uint32 aid; + uint32 effectId; + uint64 num; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_NOTIFY_EFFECT3, 0x0b69); +#elif PACKETVER_MAIN_NUM >= 20060911 || PACKETVER_AD_NUM >= 20060911 || PACKETVER_SAK_NUM >= 20060911 || defined(PACKETVER_RE) || defined(PACKETVER_ZERO) +struct PACKET_ZC_NOTIFY_EFFECT3 { + int16 packetType; + uint32 aid; + uint32 effectId; + uint32 num; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_NOTIFY_EFFECT3, 0x0284); +#endif + +#if PACKETVER >= 20100824 +struct PACKET_CZ_SE_CASHSHOP_OPEN1 { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SE_CASHSHOP_OPEN1, 0x0844); +#endif + +#if PACKETVER >= 20191224 +struct PACKET_CZ_SE_CASHSHOP_OPEN2 { + int16 packetType; + uint32 tab; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SE_CASHSHOP_OPEN2, 0x0b6d); +#endif + +#if PACKETVER >= 20190724 +struct PACKET_CZ_SE_CASHSHOP_LIMITED_REQ { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_SE_CASHSHOP_LIMITED_REQ, 0x0b4c); +#endif + +#if PACKETVER_MAIN_NUM >= 20200129 || PACKETVER_RE_NUM >= 20200205 || PACKETVER_ZERO_NUM >= 20191224 +struct PACKET_ZC_SE_CASHSHOP_OPEN { + int16 packetType; + uint32 cashPoints; + uint32 kafraPoints; + uint32 tab; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SE_CASHSHOP_OPEN, 0x0b6e); +// for ragexeRE in some version this packet unused [4144] +#elif PACKETVER_MAIN_NUM >= 20101123 || PACKETVER_RE_NUM >= 20120328 || defined(PACKETVER_ZERO) +struct PACKET_ZC_SE_CASHSHOP_OPEN { + int16 packetType; + uint32 cashPoints; + uint32 kafraPoints; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_SE_CASHSHOP_OPEN, 0x0845); +#endif + +#if PACKETVER_MAIN_NUM >= 20190904 || PACKETVER_RE_NUM >= 20190904 || PACKETVER_ZERO_NUM >= 20190828 +struct PACKET_CZ_NPC_EXPANDED_BARTER_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_NPC_EXPANDED_BARTER_CLOSE, 0x0b58); +#endif + +#if PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 || PACKETVER_ZERO_NUM >= 20191127 +struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2 { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 nameid; +#else + uint16 nameid; +#endif + uint16 refine_level; + uint32 amount; + uint16 type; +} __attribute__((packed)); + +struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 nameid; +#else + uint16 nameid; +#endif + uint16 type; + uint32 amount; + uint32 weight; + uint32 index; + uint32 zeny; + uint32 currency_count; + // Workaround for fix Visual Studio bug (error C2233). Here should be currencies[] + struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2 currencies[1]; +} __attribute__((packed)); + +// Workaround check for Visual Studio bug (error C2233) +STATIC_ASSERT(sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2[1]) == + sizeof(struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub2), + "Wrong PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub size"); + +struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN { + int16 packetType; + int16 packetLength; + int32 items_count; + struct PACKET_ZC_NPC_EXPANDED_BARTER_OPEN_sub items[]; +} __attribute__((packed)); + +DEFINE_PACKET_HEADER(ZC_NPC_EXPANDED_BARTER_OPEN, 0x0b56); +#endif + +#if PACKETVER_MAIN_NUM >= 20190904 || PACKETVER_RE_NUM >= 20190904 || PACKETVER_ZERO_NUM >= 20190828 +struct PACKET_CZ_NPC_EXPANDED_BARTER_PURCHASE_sub { +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + uint32 shopIndex; + uint32 amount; +} __attribute__((packed)); + +struct PACKET_CZ_NPC_EXPANDED_BARTER_PURCHASE { + int16 packetType; + int16 packetLength; + struct PACKET_CZ_NPC_EXPANDED_BARTER_PURCHASE_sub list[]; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_NPC_EXPANDED_BARTER_PURCHASE, 0x0b57); +#endif + +#if PACKETVER >= 7 +struct PACKET_ZC_STATE_CHANGE { + int16 packetType; + uint32 AID; + int16 bodyState; + int16 healthState; + int32 effectState; + int8 isPKModeON; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_STATE_CHANGE, 0x0229); +#else +struct PACKET_ZC_STATE_CHANGE { + int16 PacketType; + uint32 AID; + int16 bodyState; + int16 healthState; + int16 effectState; + int8 isPKModeON; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_STATE_CHANGE, 0x0119); +#endif + +struct PACKET_ZC_AUTORUN_SKILL { + int16 packetType; + uint16 skill_id; + uint32 skill_type; + uint16 skill_lv; + uint16 skill_sp; + uint16 skill_range; + char skill_name[NAME_LENGTH]; + char up_flag; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_AUTORUN_SKILL, 0x0147); + +#if PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) +struct PACKET_ZC_LAPINEUPGRADE_OPEN { + int16 packetType; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_LAPINEUPGRADE_OPEN, 0x0ab4); + +struct PACKET_ZC_LAPINEUPGRADE_RESULT { + int16 packetType; + uint16 result; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(ZC_LAPINEUPGRADE_RESULT, 0x0ab7); +#endif // PACKETVER_MAIN_NUM >= 20170726 || PACKETVER_RE_NUM >= 20170621 || defined(PACKETVER_ZERO) + +#if PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) +struct PACKET_CZ_LAPINEUPGRADE_CLOSE { + int16 packetType; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_LAPINEUPGRADE_CLOSE, 0x0ab5); + +struct PACKET_CZ_LAPINEUPGRADE_MAKE_ITEM { + int16 packetType; +#if PACKETVER_MAIN_NUM >= 20181121 || PACKETVER_RE_NUM >= 20180704 || PACKETVER_ZERO_NUM >= 20181114 + uint32 itemId; +#else + uint16 itemId; +#endif + uint16 index; +} __attribute__((packed)); +DEFINE_PACKET_HEADER(CZ_LAPINEUPGRADE_MAKE_ITEM, 0x0ab6); +#endif // PACKETVER_MAIN_NUM >= 20170111 || PACKETVER_RE_NUM >= 20170111 || defined(PACKETVER_ZERO) + #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..7d7f69620 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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; @@ -689,6 +695,7 @@ static int party_broken(int party_id) if( p->data[i].sd!=NULL ) { clif->party_withdraw(p,p->data[i].sd,p->party.member[i].account_id,p->party.member[i].name,0x10); p->data[i].sd->status.party_id=0; + clif->charnameupdate(p->data[i].sd); } } @@ -880,17 +887,20 @@ static int party_send_logout(struct map_session_data *sd) static int party_send_message(struct map_session_data *sd, const char *mes) { - int len; - nullpo_ret(sd); nullpo_ret(mes); - len = (int)strlen(mes); - if (sd->status.party_id == 0) return 0; - intif->party_message(sd->status.party_id, sd->status.account_id, mes, len); - party->recv_message(sd->status.party_id, sd->status.account_id, mes, len); + + struct party_data *p = party->search(sd->status.party_id); + + if (p == NULL) + return 0; + + int len = (int)strlen(mes); + + clif->party_message(p, sd->status.account_id, mes, len); // Chat logging type 'P' / Party Chat logs->chat(LOG_CHAT_PARTY, sd->status.party_id, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes); @@ -898,15 +908,6 @@ static int party_send_message(struct map_session_data *sd, const char *mes) return 0; } -static int party_recv_message(int party_id, int account_id, const char *mes, int len) -{ - struct party_data *p; - if( (p=party->search(party_id))==NULL) - return 0; - clif->party_message(p,account_id,mes,len); - return 0; -} - static int party_skill_check(struct map_session_data *sd, int party_id, uint16 skill_id, uint16 skill_lv) { struct party_data *p; @@ -1526,7 +1527,6 @@ void party_defaults(void) party->send_levelup = party_send_levelup; party->send_logout = party_send_logout; party->send_message = party_send_message; - party->recv_message = party_recv_message; party->skill_check = party_skill_check; party->send_xy_clear = party_send_xy_clear; party->exp_share = party_exp_share; diff --git a/src/map/party.h b/src/map/party.h index 28e16dff7..c2306b7a8 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -118,7 +118,6 @@ struct party_interface { void (*send_levelup) (struct map_session_data *sd); int (*send_logout) (struct map_session_data *sd); int (*send_message) (struct map_session_data *sd, const char *mes); - int (*recv_message) (int party_id,int account_id,const char *mes,int len); int (*skill_check) (struct map_session_data *sd, int party_id, uint16 skill_id, uint16 skill_lv); int (*send_xy_clear) (struct party_data *p); int (*exp_share) (struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny); diff --git a/src/map/path.c b/src/map/path.c index 16d9b0563..f271e8219 100644 --- a/src/map/path.c +++ b/src/map/path.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/path.h b/src/map/path.h index 7791a46cf..04072e303 100644 --- a/src/map/path.h +++ b/src/map/path.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/pc.c b/src/map/pc.c index fcd36a233..5faadf76a 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -565,83 +565,19 @@ static int pc_inventory_rental_clear(struct map_session_data *sd) /* assumes i is valid (from default areas where it is called, it is) */ static void pc_rental_expire(struct map_session_data *sd, int i) { - int nameid; - nullpo_retv(sd); - Assert_retv(i >= 0 && i < MAX_INVENTORY); - nameid = sd->status.inventory[i].nameid; - - /* Soon to be dropped, we got plans to integrate it with item db */ - switch( nameid ) { - case ITEMID_BOARDING_HALTER: - status_change_end(&sd->bl,SC_ALL_RIDING,INVALID_TIMER); - break; - case ITEMID_LOVE_ANGEL: - if( sd->status.font == 1 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_SQUIRREL: - if( sd->status.font == 2 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_GOGO: - if( sd->status.font == 3 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_PICTURE_DIARY: - if( sd->status.font == 4 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_MINI_HEART: - if( sd->status.font == 5 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_NEWCOMER: - if( sd->status.font == 6 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_KID: - if( sd->status.font == 7 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_MAGIC_CASTLE: - if( sd->status.font == 8 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - case ITEMID_BULGING_HEAD: - if( sd->status.font == 9 ) { - sd->status.font = 0; - clif->font(sd); - } - break; - } + Assert_retv(i >= 0 && i < sd->status.inventorySize); clif->rental_expired(sd->fd, i, sd->status.inventory[i].nameid); pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_RENTAL); } static void pc_inventory_rentals(struct map_session_data *sd) { - int i, c = 0; + int c = 0; int64 expire_tick, next_tick = INT64_MAX; nullpo_retv(sd); - for( i = 0; i < MAX_INVENTORY; i++ ) + for (int i = 0; i < sd->status.inventorySize; i++ ) { // Check for Rentals on Inventory if( sd->status.inventory[i].nameid == 0 ) continue; // Nothing here @@ -782,7 +718,7 @@ static int pc_equippoint(struct map_session_data *sd, int n) int ep = 0; nullpo_ret(sd); - Assert_ret(n >= 0 && n < MAX_INVENTORY); + Assert_ret(n >= 0 && n < sd->status.inventorySize); if(!sd->inventory_data[n]) return 0; @@ -838,11 +774,9 @@ static int pc_item_equippoint(struct map_session_data *sd, struct item_data *id) static int pc_setinventorydata(struct map_session_data *sd) { - int i; - nullpo_ret(sd); - for (i = 0; i < MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { int id = sd->status.inventory[i].nameid; sd->inventory_data[i] = id?itemdb->search(id):NULL; } @@ -902,7 +836,7 @@ static int pc_setequipindex(struct map_session_data *sd) for(i=0;i<EQI_MAX;i++) sd->equip_index[i] = -1; - for(i=0;i<MAX_INVENTORY;i++) { + for (i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid <= 0) continue; if(sd->status.inventory[i].equip) { @@ -1064,19 +998,23 @@ static bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data return false; // Job Change Fail } -/*================================================= - * Checks if the player can equip the item at index n in inventory. - * Returns 0 (no) or 1 (yes). - *------------------------------------------------*/ +/** + * Checks if a character can equip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @retval 0 Character can't equip the item. + * @retval 1 Character can equip the item. + * + **/ static int pc_isequip(struct map_session_data *sd, int n) { - struct item_data *item; - nullpo_ret(sd); + Assert_ret(n >= 0 && n < sd->status.inventorySize); - item = sd->inventory_data[n]; + struct item_data *item = sd->inventory_data[n]; - if(item == NULL) + if (item == NULL) return 0; #if PACKETVER <= 20100707 @@ -1084,31 +1022,34 @@ static int pc_isequip(struct map_session_data *sd, int n) return 0; #endif - if(pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT)) + if (pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT)) return 1; - if (item->elv && sd->status.base_level < item->elv) { + if (item->elv != 0 && sd->status.base_level < item->elv) { #if PACKETVER >= 20100525 clif->msgtable(sd, MSG_CANNOT_EQUIP_ITEM_LEVEL); #endif return 0; } - if (item->elvmax && sd->status.base_level > item->elvmax) { + + if (item->elvmax != 0 && sd->status.base_level > item->elvmax) { #if PACKETVER >= 20100525 clif->msgtable(sd, MSG_CANNOT_EQUIP_ITEM_LEVEL); #endif return 0; } - if(item->sex != 2 && sd->status.sex != item->sex) + + if (item->sex != SEX_SERVER && sd->status.sex != item->sex) return 0; - if ( item->equip & EQP_AMMO ) { - if (sd->state.active && !pc_iscarton(sd) && (sd->job & MAPID_THIRDMASK) == MAPID_GENETIC) { // check if sc data is already loaded. + if ((item->equip & EQP_AMMO) != 0) { + if (sd->state.active != 0 && !pc_iscarton(sd) && (sd->job & MAPID_THIRDMASK) == MAPID_GENETIC) { // Check if sc data is already loaded. #if PACKETVER_RE_NUM >= 20090529 || PACKETVER_MAIN_NUM >= 20090603 || defined(PACKETVER_ZERO) clif->msgtable(sd, MSG_USESKILL_FAIL_CART); #endif return 0; } + if (!pc_ismadogear(sd) && (sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { #if PACKETVER_RE_NUM >= 20090226 || PACKETVER_MAIN_NUM >= 20090304 || defined(PACKETVER_ZERO) clif->msgtable(sd, MSG_USESKILL_FAIL_MADOGEAR); @@ -1116,76 +1057,85 @@ static int pc_isequip(struct map_session_data *sd, int n) return 0; } } - if (sd->sc.count) { - if(item->equip & EQP_ARMS && item->type == IT_WEAPON && sd->sc.data[SC_NOEQUIPWEAPON]) // Also works with left-hand weapons [DracoRPG] + if ((battle_config.unequip_restricted_equipment & 1) != 0) { + for (int i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++) + if (map->list[sd->bl.m].zone->disabled_items[i] == item->nameid) + return 0; + } + + if ((battle_config.unequip_restricted_equipment & 2) != 0 && !itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int slot = 0; slot < item->slot; slot++) + for (int i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++) + if (map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].card[slot]) + return 0; + } + + if (sd->sc.count != 0) { + if ((item->equip & EQP_ARMS) != 0 && item->type == IT_WEAPON && sd->sc.data[SC_NOEQUIPWEAPON] != NULL) // Also works with left-hand weapons. [DracoRPG] return 0; - if(item->equip & EQP_SHIELD && item->type == IT_ARMOR && sd->sc.data[SC_NOEQUIPSHIELD]) + + if ((item->equip & EQP_SHIELD) != 0 && item->type == IT_ARMOR && sd->sc.data[SC_NOEQUIPSHIELD] != NULL) return 0; - if(item->equip & EQP_ARMOR && sd->sc.data[SC_NOEQUIPARMOR]) + + if ((item->equip & EQP_ARMOR) != 0 && sd->sc.data[SC_NOEQUIPARMOR] != NULL) return 0; - if(item->equip & EQP_HEAD_TOP && sd->sc.data[SC_NOEQUIPHELM]) + + if ((item->equip & EQP_HEAD_TOP) != 0 && sd->sc.data[SC_NOEQUIPHELM] != NULL) return 0; - if(item->equip & EQP_ACC && sd->sc.data[SC__STRIPACCESSARY]) + + if ((item->equip & EQP_ACC) != 0 && sd->sc.data[SC__STRIPACCESSARY] != NULL) return 0; - if(item->equip && sd->sc.data[SC_KYOUGAKU]) + + if (item->equip != 0 && sd->sc.data[SC_KYOUGAKU] != NULL) return 0; - if (sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_SUPERNOVICE) { - //Spirit of Super Novice equip bonuses. [Skotlex] - if (sd->status.base_level > 90 && item->equip & EQP_HELM) - return 1; //Can equip all helms - - if (sd->status.base_level > 96 && item->equip & EQP_ARMS && item->type == IT_WEAPON) - switch (item->subtype) { //In weapons, the look determines type of weapon. - case W_DAGGER: //Level 4 Knives are equippable.. this means all knives, I'd guess? - case W_1HSWORD: //All 1H swords - case W_1HAXE: //All 1H Axes - case W_MACE: //All 1H Maces - case W_STAFF: //All 1H Staves + if (sd->sc.data[SC_SOULLINK] != NULL && sd->sc.data[SC_SOULLINK]->val2 == SL_SUPERNOVICE) { // Spirit of Super Novice equip bonuses. [Skotlex] + if (sd->status.base_level > 90 && (item->equip & EQP_HELM) != 0) + return 1; // Can equip all helms. + + if (sd->status.base_level > 96 && (item->equip & EQP_ARMS) != 0 && item->type == IT_WEAPON) { + switch (item->subtype) { // In weapons, the look determines type of weapon. + case W_DAGGER: // Level 4 Knives are equippable.. this means all knives, I'd guess? + case W_1HSWORD: // All 1H swords. + case W_1HAXE: // All 1H axes. + case W_MACE: // All 1H maces. + case W_STAFF: // All 1H staffs. return 1; } + } } } - //Not equipable by class. [Skotlex] - if (((1ULL<<(sd->job & MAPID_BASEMASK)) & item->class_base[(sd->job & JOBL_2_1) != 0 ? 1 : ((sd->job & JOBL_2_2) != 0 ? 2 : 0)]) == 0) + + uint64 mask_job = 1ULL << (sd->job & MAPID_BASEMASK); + uint64 mask_item = item->class_base[((sd->job & JOBL_2_1) != 0) ? 1 : (((sd->job & JOBL_2_2) != 0) ? 2 : 0)]; + + if ((mask_job & mask_item) == 0) // Not equipable by class. [Skotlex] return 0; - //Not usable by upper class. [Inkfish] - while( 1 ) { + + // Not usable by upper class. [Inkfish] + while (1) { if ((item->class_upper & ITEMUPPER_NORMAL) != 0) { - if ((sd->job & (JOBL_UPPER|JOBL_THIRD|JOBL_BABY)) == 0) + if ((sd->job & (JOBL_UPPER | JOBL_THIRD | JOBL_BABY)) == 0) break; } + if ((item->class_upper & ITEMUPPER_UPPER) != 0) { - if ((sd->job & (JOBL_UPPER|JOBL_THIRD)) != 0) + if ((sd->job & (JOBL_UPPER | JOBL_THIRD)) != 0) break; } + if ((item->class_upper & ITEMUPPER_BABY) != 0) { if ((sd->job & JOBL_BABY) != 0) break; } + if ((item->class_upper & ITEMUPPER_THIRD) != 0) { if ((sd->job & JOBL_THIRD) != 0) break; } - return 0; - } - if ( battle_config.unequip_restricted_equipment & 1 ) { - int i; - for ( i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++ ) - if ( map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].nameid ) - return 0; - } - - if ( battle_config.unequip_restricted_equipment & 2 ) { - if ( !itemdb_isspecial( sd->status.inventory[n].card[0] ) ) { - int i, slot; - for ( slot = 0; slot < MAX_SLOTS; slot++ ) - for ( i = 0; i < map->list[sd->bl.m].zone->disabled_items_count; i++ ) - if ( map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].card[slot] ) - return 0; - } + return 0; } return 1; @@ -1379,6 +1329,7 @@ static bool pc_authok(struct map_session_data *sd, int login_id2, time_t expirat pc->setpos(sd,sd->status.last_point.map,0,0,CLR_OUTSIGHT); } + clif->inventoryExpansionInfo(sd); clif->overweight_percent(sd); clif->authok(sd); @@ -1536,17 +1487,16 @@ static int pc_reg_received(struct map_session_data *sd) if (sd->status.guild_id) guild->member_joined(sd); - // pet - if (sd->status.pet_id > 0) - intif->request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); - - // Homunculus [albator] - if( sd->status.hom_id > 0 ) - intif->homunculus_requestload(sd->status.account_id, sd->status.hom_id); - if( sd->status.mer_id > 0 ) - intif->mercenary_request(sd->status.mer_id, sd->status.char_id); - if( sd->status.ele_id > 0 ) - intif->elemental_request(sd->status.ele_id, sd->status.char_id); + if (sd->state.standalone == 0 && sd->state.autotrade == 0) { // prevents loading pets, homunculi, mercenaries or elementals if the character doesn't have a client attached + if (sd->status.pet_id != 0) + intif->request_petdata(sd->status.account_id, sd->status.char_id, sd->status.pet_id); + if (sd->status.hom_id != 0) + intif->homunculus_requestload(sd->status.account_id, sd->status.hom_id); + if (sd->status.mer_id != 0) + intif->mercenary_request(sd->status.mer_id, sd->status.char_id); + if (sd->status.ele_id != 0) + intif->elemental_request(sd->status.ele_id, sd->status.char_id); + } map->addiddb(&sd->bl); map->delnickdb(sd->status.char_id, sd->status.name); @@ -1655,58 +1605,56 @@ static void pc_calc_skilltree_clear(struct map_session_data *sd) *------------------------------------------*/ static int pc_calc_skilltree(struct map_session_data *sd) { - int i,id=0,flag; - int class = 0, classidx = 0; - nullpo_ret(sd); - i = pc->calc_skilltree_normalize_job(sd); - class = pc->mapid2jobid(i, sd->status.sex); + uint32 job = pc->calc_skilltree_normalize_job(sd); + int class = pc->mapid2jobid(job, sd->status.sex); if (class == -1) { //Unable to normalize job?? - ShowError("pc_calc_skilltree: Unable to normalize job %d for character %s (%d:%d)\n", i, sd->status.name, sd->status.account_id, sd->status.char_id); + ShowError("pc_calc_skilltree: Unable to normalize job %u for character %s (%d:%d)\n", job, sd->status.name, sd->status.account_id, sd->status.char_id); return 1; } - classidx = pc->class2idx(class); + int classidx = pc->class2idx(class); pc->calc_skilltree_clear(sd); - for (i = 0; i < MAX_SKILL_DB; i++) { - if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED ) - { // Restore original level of skills after deleting earned skills. + for (int i = 0; i < MAX_SKILL_DB; i++) { + if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { + // Restore original level of skills after deleting earned skills. sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; } - if( sd->sc.count && sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_BARDDANCER && skill->dbs->db[i].nameid >= DC_HUMMING && skill->dbs->db[i].nameid <= DC_SERVICEFORYOU ) - { //Enable Bard/Dancer spirit linked skills. - if (sd->status.sex) { - // Link dancer skills to bard. - if (i < 8) { - Assert_report(i >= 8); - continue; - } - if (sd->status.skill[i-8].lv < 10) - continue; - sd->status.skill[i].id = skill->dbs->db[i].nameid; - sd->status.skill[i].lv = sd->status.skill[i-8].lv; // Set the level to the same as the linking skill - sd->status.skill[i].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill + if (sd->sc.count && sd->sc.data[SC_SOULLINK] && sd->sc.data[SC_SOULLINK]->val2 == SL_BARDDANCER + && ((skill->dbs->db[i].nameid >= BA_WHISTLE && skill->dbs->db[i].nameid <= BA_APPLEIDUN) + || (skill->dbs->db[i].nameid >= DC_HUMMING && skill->dbs->db[i].nameid <= DC_SERVICEFORYOU)) + ) { + //Enable Bard/Dancer spirit linked skills. + int linked_nameid = skill->get_linked_song_dance_id(skill->dbs->db[i].nameid); + if (linked_nameid == 0) { + Assert_report("Linked bard/dance skill not found"); + continue; + } + int copy_from_index; + int copy_to_index; + if (sd->status.sex == SEX_MALE && skill->dbs->db[i].nameid >= BA_WHISTLE && skill->dbs->db[i].nameid <= BA_APPLEIDUN) { + copy_from_index = i; + copy_to_index = skill->get_index(linked_nameid); } else { - // Link bard skills to dancer. - if (i < 8) { - Assert_report(i >= 8); - continue; - } - if (sd->status.skill[i].lv < 10) - continue; - sd->status.skill[i-8].id = skill->dbs->db[i-8].nameid; - sd->status.skill[i-8].lv = sd->status.skill[i].lv; // Set the level to the same as the linking skill - sd->status.skill[i-8].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill + copy_from_index = skill->get_index(linked_nameid); + copy_to_index = i; } + if (copy_from_index < copy_to_index) + continue; // Copy only after the source skill has been filled into the tree + if (sd->status.skill[copy_from_index].lv < 10) + continue; // Copy only if the linked skill has been mastered + sd->status.skill[copy_to_index].id = skill->dbs->db[copy_to_index].nameid; + sd->status.skill[copy_to_index].lv = sd->status.skill[copy_from_index].lv; // Set the level to the same as the linking skill + sd->status.skill[copy_to_index].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill } } if( pc_has_permission(sd, PC_PERM_ALL_SKILL) ) { - for (i = 0; i < MAX_SKILL_DB; i++) { + for (int i = 0; i < MAX_SKILL_DB; i++) { switch(skill->dbs->db[i].nameid) { /** * Dummy skills must be added here otherwise they'll be displayed in the, @@ -1738,9 +1686,11 @@ static int pc_calc_skilltree(struct map_session_data *sd) return 0; } + bool changed = false; do { - flag = 0; - for (i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[classidx][i].id) > 0; i++) { + changed = false; + int id; + for (int i = 0; i < MAX_SKILL_TREE && (id = pc->skill_tree[classidx][i].id) > 0; i++) { int idx = pc->skill_tree[classidx][i].idx; bool satisfied = true; if (sd->status.skill[idx].id > 0) @@ -1790,10 +1740,10 @@ static int pc_calc_skilltree(struct map_session_data *sd) sd->status.skill[idx].lv = 1; // need to manually specify a skill level sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill. } - flag = 1; // skill list has changed, perform another pass + changed = true; // skill list has changed, perform another pass } } - } while(flag); + } while (changed); pc->calc_skilltree_bonus(sd, classidx); @@ -2221,11 +2171,12 @@ static int pc_bonus_addeff_onskill(struct s_addeffectonskill *effect, int max, e return 1; } -static int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id, short group, int race_mask, int rate) +static int pc_bonus_item_drop(struct s_add_drop *drop, const short max, int id, bool is_group, int race_mask, int rate) { int i; nullpo_ret(drop); + Assert_ret(is_group || id > 0); //Apply config rate adjustment settings. if (rate >= 0) { //Absolute drop. if (battle_config.item_rate_adddrop != 100) @@ -2240,17 +2191,14 @@ static int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id if (rate > -1) rate = -1; } - for(i = 0; i < max && (drop[i].id || drop[i].group); i++) { - if (((id && drop[i].id == id) || (group && drop[i].group == group)) - && race_mask != RCMASK_NONE - ) { + for (i = 0; i < max && (drop[i].id != 0 || drop[i].is_group); i++) { + if (drop[i].id == id && race_mask != RCMASK_NONE) { drop[i].race |= race_mask; if (drop[i].rate > 0 && rate > 0) { //Both are absolute rates. if (drop[i].rate < rate) drop[i].rate = rate; - } else - if(drop[i].rate < 0 && rate < 0) { + } else if (drop[i].rate < 0 && rate < 0) { //Both are relative rates. if (drop[i].rate > rate) drop[i].rate = rate; @@ -2264,13 +2212,13 @@ static int pc_bonus_item_drop(struct s_add_drop *drop, const short max, short id return 0; } drop[i].id = id; - drop[i].group = group; + drop[i].is_group = is_group; drop[i].race |= race_mask; drop[i].rate = rate; return 1; } -static int pc_addautobonus(struct s_autobonus *bonus, char max, const char *bonus_script, short rate, unsigned int dur, short flag, const char *other_script, unsigned short pos, bool onskill) +static int pc_addautobonus(struct s_autobonus *bonus, char max, const char *bonus_script, short rate, unsigned int dur, short flag, const char *other_script, unsigned int pos, bool onskill) { int i; @@ -2906,7 +2854,7 @@ static int pc_bonus(struct map_session_data *sd, int type, int val) case SP_INTRAVISION: // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG] if(sd->state.lr_flag != 2) { sd->special_state.intravision = 1; - clif->status_change(&sd->bl, SI_CLAIRVOYANCE, 1, 0, 0, 0, 0); + clif->status_change(&sd->bl, status->get_sc_icon(SC_CLAIRVOYANCE), status->get_sc_relevant_bl_types(SC_CLAIRVOYANCE), 1, 0, 0, 0, 0); } break; case SP_NO_KNOCKBACK: @@ -3111,7 +3059,7 @@ static int pc_bonus(struct map_session_data *sd, int type, int val) #endif case SP_ADD_MONSTER_DROP_CHAINITEM: if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), 0, val, map->race_id2mask(RC_ALL), 10000); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), val, true, map->race_id2mask(RC_ALL), 10000); break; case SP_ADDMAXWEIGHT: if (sd->state.lr_flag != 2) @@ -3686,7 +3634,7 @@ static int pc_bonus2(struct map_session_data *sd, int type, int type2, int val) break; case SP_ADD_MONSTER_DROP_ITEM: if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, map->race_id2mask(RC_ALL), val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, map->race_id2mask(RC_ALL), val); break; case SP_SP_LOSS_RATE: if(sd->state.lr_flag != 2) { @@ -3892,7 +3840,7 @@ static int pc_bonus2(struct map_session_data *sd, int type, int type2, int val) break; } if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), 0, type2, race_mask, 10000); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, true, race_mask, 10000); } break; #ifdef RENEWAL @@ -3932,12 +3880,12 @@ static int pc_bonus3(struct map_session_data *sd, int type, int type2, int type3 break; } if (sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, race_mask, val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, race_mask, val); } break; case SP_ADD_CLASS_DROP_ITEM: if(sd->state.lr_flag != 2) - pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, 0, -type3, val); + pc->bonus_item_drop(sd->add_drop, ARRAYLENGTH(sd->add_drop), type2, false, -type3, val); break; case SP_AUTOSPELL: if(sd->state.lr_flag != 2) @@ -4258,7 +4206,7 @@ static int pc_skill(struct map_session_data *sd, int id, int level, int flag) if( sd->status.skill[index].id == id ) { if( sd->status.skill[index].lv >= level ) return 0; - if( sd->status.skill[index].flag == SKILL_FLAG_PERMANENT ) //Non-granted skill, store it's level. + if (sd->status.skill[index].flag == SKILL_FLAG_PERMANENT) // Non-granted skill, store its level. sd->status.skill[index].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[index].lv; } else { sd->status.skill[index].id = id; @@ -4308,7 +4256,7 @@ static bool pc_can_insert_card_into(struct map_session_data *sd, int idx_card, i nullpo_ret(sd); - if (idx_equip < 0 || idx_equip >= MAX_INVENTORY || sd->inventory_data[idx_equip] == NULL) + if (idx_equip < 0 || idx_equip >= sd->status.inventorySize || sd->inventory_data[idx_equip] == NULL) return false; //Invalid item index. if (sd->status.inventory[idx_equip].nameid <= 0 || sd->status.inventory[idx_equip].amount < 1) return false; // target item missing @@ -4342,7 +4290,7 @@ static bool pc_can_insert_card(struct map_session_data *sd, int idx_card) { nullpo_ret(sd); - if (idx_card < 0 || idx_card >= MAX_INVENTORY || sd->inventory_data[idx_card] == NULL) + if (idx_card < 0 || idx_card >= sd->status.inventorySize || sd->inventory_data[idx_card] == NULL) return false; //Invalid card index. if (sd->status.inventory[idx_card].nameid <= 0 || sd->status.inventory[idx_card].amount < 1) return false; // target card missing @@ -4438,7 +4386,6 @@ static int pc_modifysellvalue(struct map_session_data *sd, int orig_value) *------------------------------------------*/ static int pc_checkadditem(struct map_session_data *sd, int nameid, int amount) { - int i; struct item_data* data; nullpo_ret(sd); @@ -4454,7 +4401,7 @@ static int pc_checkadditem(struct map_session_data *sd, int nameid, int amount) if( data->stack.inventory && amount > data->stack.amount ) return ADDITEM_OVERAMOUNT; - for(i=0;i<MAX_INVENTORY;i++){ + for(int i = 0; i < sd->status.inventorySize; i++) { // FIXME: This does not consider the checked item's cards, thus could check a wrong slot for stackability. if(sd->status.inventory[i].nameid==nameid){ if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) @@ -4472,11 +4419,10 @@ static int pc_checkadditem(struct map_session_data *sd, int nameid, int amount) *------------------------------------------*/ static int pc_inventoryblank(struct map_session_data *sd) { - int i,b; - nullpo_ret(sd); + int b = 0; - for(i=0,b=0;i<MAX_INVENTORY;i++){ + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid==0) b++; } @@ -4510,7 +4456,7 @@ static int pc_payzeny(struct map_session_data *sd, int zeny, enum e_log_pick_typ if (sd->state.showzeny) { char output[255]; - sprintf(output, "Removed %dz.", zeny); + sprintf(output, msg_sd(sd, 885), zeny); // Removed %dz. clif_disp_onlyself(sd, output); } } @@ -4649,7 +4595,7 @@ static int pc_getzeny(struct map_session_data *sd, int zeny, enum e_log_pick_typ if (sd->state.showzeny) { char output[255]; - sprintf(output, "Gained %dz.", zeny); + sprintf(output, msg_sd(sd, 886), zeny); // Gained %dz. clif_disp_onlyself(sd, output); } } @@ -4673,8 +4619,8 @@ static int pc_search_inventory(struct map_session_data *sd, int item_id) int i; nullpo_retr(INDEX_NOT_FOUND, sd); - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == item_id && (sd->status.inventory[i].amount > 0 || item_id == 0) ); - return ( i < MAX_INVENTORY ) ? i : INDEX_NOT_FOUND; + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].nameid == item_id && (sd->status.inventory[i].amount > 0 || item_id == 0)); + return (i < sd->status.inventorySize) ? i : INDEX_NOT_FOUND; } /*========================================== @@ -4736,11 +4682,11 @@ static int pc_additem(struct map_session_data *sd, const struct item *item_data, } } - i = MAX_INVENTORY; + i = sd->status.inventorySize; // Stackable | Non Rental if( itemdb->isstackable2(data) && item_data->expire_time == 0 ) { - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++) { if( sd->status.inventory[i].nameid == item_data->nameid && sd->status.inventory[i].bound == item_data->bound && sd->status.inventory[i].expire_time == 0 && @@ -4755,7 +4701,7 @@ static int pc_additem(struct map_session_data *sd, const struct item *item_data, } } - if ( i >= MAX_INVENTORY ) { + if (i >= sd->status.inventorySize) { i = pc->search_inventory(sd,0); if (i == INDEX_NOT_FOUND) return 4; @@ -4782,18 +4728,27 @@ static int pc_additem(struct map_session_data *sd, const struct item *item_data, sd->weight += w; clif->updatestatus(sd,SP_WEIGHT); + + // auto-favorite + if (data->flag.auto_favorite > 0) { + sd->status.inventory[i].favorite = 1; + clif->favorite_item(sd, i); + } + //Auto-equip if(data->flag.autoequip) pc->equipitem(sd, i, data->equip); /* rental item check */ - if( item_data->expire_time ) { - if( time(NULL) > item_data->expire_time ) { - pc->rental_expire(sd,i); + if (item_data->expire_time > 0) { + if (time(NULL) > item_data->expire_time) { + pc->rental_expire(sd, i); } else { - int seconds = (int)( item_data->expire_time - time(NULL) ); + int seconds = (int)(item_data->expire_time - time(NULL)); clif->rental_time(sd->fd, sd->status.inventory[i].nameid, seconds); pc->inventory_rental_add(sd, seconds); + if (data->rental_start_script != NULL) + script->run_item_rental_start_script(sd, data, 0); } } quest->questinfo_refresh(sd); @@ -4815,7 +4770,7 @@ static int pc_additem(struct map_session_data *sd, const struct item *item_data, static int pc_delitem(struct map_session_data *sd, int n, int amount, int type, short reason, e_log_pick_type log_type) { nullpo_retr(1, sd); - Assert_retr(1, n >= 0 && n < MAX_INVENTORY); + Assert_retr(1, n >= 0 && n < sd->status.inventorySize); if(sd->status.inventory[n].nameid==0 || amount <= 0 || sd->status.inventory[n].amount<amount || sd->inventory_data[n] == NULL) return 1; @@ -4824,12 +4779,21 @@ static int pc_delitem(struct map_session_data *sd, int n, int amount, int type, sd->status.inventory[n].amount -= amount; sd->weight -= sd->inventory_data[n]->weight*amount ; + + // It's here because the data would most likely get zeroed in following if [Hemagx] + struct item_data *itd = sd->inventory_data[n]; + bool is_rental = (sd->status.inventory[n].expire_time > 0) ? true : false; + if( sd->status.inventory[n].amount <= 0 ){ if(sd->status.inventory[n].equip) pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); memset(&sd->status.inventory[n],0,sizeof(sd->status.inventory[0])); sd->inventory_data[n] = NULL; } + + if (is_rental && itd->rental_end_script != NULL) + script->run_item_rental_end_script(sd, itd, 0); + if(!(type&1)) clif->delitem(sd,n,amount,reason); if(!(type&2)) @@ -4849,7 +4813,7 @@ static int pc_dropitem(struct map_session_data *sd, int n, int amount) { nullpo_retr(1, sd); - if(n < 0 || n >= MAX_INVENTORY) + if(n < 0 || n >= sd->status.inventorySize) return 0; if(amount <= 0) @@ -4858,7 +4822,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; @@ -4960,7 +4924,7 @@ static int pc_isUseitem(struct map_session_data *sd, int n) int nameid; nullpo_ret(sd); - Assert_ret(n >= 0 && n < MAX_INVENTORY); + Assert_ret(n >= 0 && n < sd->status.inventorySize); item = sd->inventory_data[n]; nameid = sd->status.inventory[n].nameid; @@ -5196,7 +5160,7 @@ static int pc_useitem(struct map_session_data *sd, int n) bool removeItem = false; nullpo_ret(sd); - Assert_ret(n >= 0 && n < MAX_INVENTORY); + Assert_ret(n >= 0 && n < sd->status.inventorySize); if (sd->npc_id || sd->state.workinprogress & 1) { #if PACKETVER >= 20110308 @@ -5308,6 +5272,14 @@ static int pc_useitem(struct map_session_data *sd, int n) #endif if( battle_config.item_restricted_consumption_type && sd->status.inventory[n].expire_time == 0 ) { clif->useitemack(sd,n,sd->status.inventory[n].amount-1,true); + + // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, there is a chance to don't consume the scroll. [Kenpachi] + if ((nameid == ITEMID_EARTH_SCROLL_1_3 || nameid == ITEMID_EARTH_SCROLL_1_5) + && sd->sc.count > 0 && sd->sc.data[SC_EARTHSCROLL] != NULL + && rnd() % 100 > sd->sc.data[SC_EARTHSCROLL]->val2) { + return 0; + } + pc->delitem(sd, n, 1, 1, DELITEM_NORMAL, LOG_TYPE_CONSUME); } return 0; @@ -5349,11 +5321,38 @@ static int pc_useitem(struct map_session_data *sd, int n) script->run_use_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); script->potion_flag = 0; + // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, there is a chance to don't consume the scroll. [Kenpachi] + if ((nameid == ITEMID_EARTH_SCROLL_1_3 || nameid == ITEMID_EARTH_SCROLL_1_5) && sd->sc.count > 0 + && sd->sc.data[SC_EARTHSCROLL] != NULL && rnd() % 100 > sd->sc.data[SC_EARTHSCROLL]->val2) { + removeItem = false; + } + if (removeItem) pc->delitem(sd, n, 1, 1, DELITEM_NORMAL, LOG_TYPE_CONSUME); return 1; } +/** + * Unsets a character's auto-cast related data. + * + * @param sd The character's session data. + * @return 0 if parameter sd is NULL, otherwise 1. + */ +static int pc_autocast_clear(struct map_session_data *sd) +{ + nullpo_ret(sd); + + sd->autocast.type = AUTOCAST_NONE; + sd->autocast.skill_id = 0; + sd->autocast.skill_lv = 0; + sd->autocast.itemskill_conditions_checked = false; + sd->autocast.itemskill_check_conditions = false; + sd->autocast.itemskill_instant_cast = false; + sd->autocast.itemskill_cast_on_self = false; + + return 1; +} + /*========================================== * Add item on cart for given index. * Return: @@ -5433,7 +5432,7 @@ static int pc_cart_delitem(struct map_session_data *sd, int n, int amount, int t { struct item_data * data; nullpo_retr(1, sd); - Assert_retr(1, n >= 0 && n < MAX_INVENTORY); + Assert_retr(1, n >= 0 && n < MAX_CART); if( sd->status.cart[n].nameid == 0 || sd->status.cart[n].amount < amount || !(data = itemdb->exists(sd->status.cart[n].nameid)) ) return 1; @@ -5467,12 +5466,12 @@ static int pc_putitemtocart(struct map_session_data *sd, int idx, int amount) nullpo_ret(sd); - if (idx < 0 || idx >= MAX_INVENTORY) //Invalid index check [Skotlex] + if (idx < 0 || idx >= sd->status.inventorySize) //Invalid index check [Skotlex] return 1; 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 +5518,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; @@ -5537,7 +5536,7 @@ static void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type t /* both restricted to inventory */ case IBT_PARTY: case IBT_CHARACTER: - for( i = 0; i < MAX_INVENTORY; i++ ){ + for (i = 0; i < sd->status.inventorySize; i++ ) { if( sd->status.inventory[i].bound == type ) { pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_SKILLUSE, LOG_TYPE_OTHER); // FIXME: is this the correct reason flag? } @@ -5549,7 +5548,7 @@ static void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type t case IBT_GUILD: { struct guild_storage *gstor = idb_get(gstorage->db,sd->status.guild_id); - for( i = 0; i < MAX_INVENTORY; i++ ){ + for (i = 0; i < sd->status.inventorySize; i++ ) { if(sd->status.inventory[i].bound == type) { if( gstor ) gstorage->additem(sd,gstor,&sd->status.inventory[i],sd->status.inventory[i].amount); @@ -5582,9 +5581,9 @@ static int pc_show_steal(struct block_list *bl, va_list ap) nullpo_ret(sd); if((item=itemdb->exists(itemid))==NULL) - sprintf(output,"%s stole an Unknown Item (id: %i).",sd->status.name, itemid); + sprintf(output, msg_sd(sd, 887), sd->status.name, itemid); // %s stole an Unknown Item (id: %i). else - sprintf(output,"%s stole %s.",sd->status.name,item->jname); + sprintf(output, msg_sd(sd, 888), sd->status.name, item->jname); // %s stole %s. clif->message(tsd->fd, output); return 0; @@ -5630,15 +5629,17 @@ static int pc_steal_item(struct map_session_data *sd, struct block_list *bl, uin // Try dropping one item, in the order from first to last possible slot. // Droprate is affected by the skill success rate. - for (i = 0; i < MAX_STEAL_DROP; i++) { + for (i = 0; i < MAX_MOB_DROP; i++) { if (md->db->dropitem[i].nameid == 0) continue; if ((data = itemdb->exists(md->db->dropitem[i].nameid)) == NULL) continue; + if (data->type == IT_CARD) + continue; if (rnd() % 10000 < apply_percentrate(md->db->dropitem[i].p, rate, 100)) break; } - if (i == MAX_STEAL_DROP) + if (i == MAX_MOB_DROP) return 0; itemid = md->db->dropitem[i].nameid; @@ -5699,242 +5700,295 @@ static int pc_steal_coin(struct map_session_data *sd, struct block_list *target, return 0; } -/*========================================== - * Set's a player position. - * Return values: - * 0 - Success. - * 1 - Invalid map index. - * 2 - Map not in this map-server, and failed to locate alternate map-server. - *------------------------------------------*/ -static int pc_setpos(struct map_session_data *sd, unsigned short map_index, int x, int y, clr_type clrtype) +/** + * Sets a character's position. + * + * @param sd The related character. + * @param map_index The target map's index. + * @param x The target x-coordinate. + * @param y The target y-coordinate. + * @param clrtype The unit clear type, which should be used. + * @retval 0 Success. + * @retval 1 Invalid map index. + * @retval 2 Map not in this map-server, and failed to locate alternative map-server. + * @retval 3 No character data. (Parameter sd is a NULL pointer.) + * @retval 4 Character is jailed. + * + **/ +static int pc_setpos(struct map_session_data *sd, unsigned short map_index, int x, int y, enum clr_type clrtype) { - int16 m; + nullpo_retr(3, sd); - nullpo_ret(sd); + int map_id = map->mapindex2mapid(map_index); - if( !map_index || !mapindex_id2name(map_index) || ( m = map->mapindex2mapid(map_index) ) == -1 ) { - ShowDebug("pc_setpos: Passed mapindex(%d) is invalid!\n", map_index); + if (map_index == 0 || !mapindex_id2name(map_index) || map_id == INDEX_NOT_FOUND) { + ShowDebug("pc_setpos: Passed mapindex %d is invalid!\n", map_index); return 1; } - if( pc_isdead(sd) ) { //Revive dead people before warping them + if (pc_isdead(sd)) { // Revive dead character before warping. pc->setstand(sd); - pc->setrestartvalue(sd,1); + pc->setrestartvalue(sd, 1); } - if( map->list[m].flag.src4instance ) { - struct party_data *p; + if (map->list[map_id].flag.src4instance != 0) { bool stop = false; - int i = 0, j = 0; - if( sd->instances ) { - for( i = 0; i < sd->instances; i++ ) { - if( sd->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[sd->instance[i]].num_map, j, map->list[instance->list[sd->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[sd->instance[i]].map[j]].custom_name); - if( j != instance->list[sd->instance[i]].num_map ) + if (sd->instances != 0) { + int i, j = 0; + + for (i = 0; i < sd->instances; i++) { + if (sd->instance[i] >= 0) { + ARR_FIND(0, instance->list[sd->instance[i]].num_map, j, + map->list[instance->list[sd->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[sd->instance[i]].map[j]].custom_name); + + if (j != instance->list[sd->instance[i]].num_map) break; } } - if( i != sd->instances ) { - m = instance->list[sd->instance[i]].map[j]; - map_index = map_id2index(m); + + if (i != sd->instances) { + map_id = instance->list[sd->instance[i]].map[j]; + map_index = map_id2index(map_id); stop = true; } } - if ( !stop && sd->status.party_id && (p = party->search(sd->status.party_id)) != NULL && p->instances ) { - for( i = 0; i < p->instances; i++ ) { - if( p->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[p->instance[i]].num_map, j, map->list[instance->list[p->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[p->instance[i]].map[j]].custom_name); - if( j != instance->list[p->instance[i]].num_map ) + + struct party_data *p = party->search(sd->status.party_id); + + if (!stop && sd->status.party_id != 0 && p != NULL && p->instances != 0) { + int i, j = 0; + + for (i = 0; i < p->instances; i++) { + if (p->instance[i] >= 0) { + ARR_FIND(0, instance->list[p->instance[i]].num_map, j, + map->list[instance->list[p->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[p->instance[i]].map[j]].custom_name); + + if (j != instance->list[p->instance[i]].num_map) break; } } - if( i != p->instances ) { - m = instance->list[p->instance[i]].map[j]; - map_index = map_id2index(m); + + if (i != p->instances) { + map_id = instance->list[p->instance[i]].map[j]; + map_index = map_id2index(map_id); stop = true; } } - if ( !stop && sd->status.guild_id && sd->guild && sd->guild->instances ) { - for( i = 0; i < sd->guild->instances; i++ ) { - if( sd->guild->instance[i] >= 0 ) { - ARR_FIND(0, instance->list[sd->guild->instance[i]].num_map, j, map->list[instance->list[sd->guild->instance[i]].map[j]].instance_src_map == m && !map->list[instance->list[sd->guild->instance[i]].map[j]].custom_name); - if( j != instance->list[sd->guild->instance[i]].num_map ) + + if (!stop && sd->status.guild_id != 0 && sd->guild != NULL && sd->guild->instances != 0) { + int i, j = 0; + + for (i = 0; i < sd->guild->instances; i++) { + if (sd->guild->instance[i] >= 0) { + ARR_FIND(0, instance->list[sd->guild->instance[i]].num_map, j, + map->list[instance->list[sd->guild->instance[i]].map[j]].instance_src_map == map_id + && !map->list[instance->list[sd->guild->instance[i]].map[j]].custom_name); + + if (j != instance->list[sd->guild->instance[i]].num_map) break; } } - if( i != sd->guild->instances ) { - m = instance->list[sd->guild->instance[i]].map[j]; - map_index = map_id2index(m); - //stop = true; Uncomment if adding new checks + + if (i != sd->guild->instances) { + map_id = instance->list[sd->guild->instance[i]].map[j]; + map_index = map_id2index(map_id); + //stop = true; Uncomment when adding new checks. } } - /* we hit a instance, if empty we populate the spawn data */ - if( map->list[m].instance_id >= 0 && instance->list[map->list[m].instance_id].respawn.map == 0 && - instance->list[map->list[m].instance_id].respawn.x == 0 && - instance->list[map->list[m].instance_id].respawn.y == 0) { - instance->list[map->list[m].instance_id].respawn.map = map_index; - instance->list[map->list[m].instance_id].respawn.x = x; - instance->list[map->list[m].instance_id].respawn.y = y; + // We hit an instance. If empty we populate the spawn data. + if (map->list[map_id].instance_id >= 0 && instance->list[map->list[map_id].instance_id].respawn.map == 0 + && instance->list[map->list[map_id].instance_id].respawn.x == 0 + && instance->list[map->list[map_id].instance_id].respawn.y == 0) { + instance->list[map->list[map_id].instance_id].respawn.map = map_index; + instance->list[map->list[map_id].instance_id].respawn.x = x; + instance->list[map->list[map_id].instance_id].respawn.y = y; } } - sd->state.changemap = (sd->mapindex != map_index); + sd->state.changemap = (sd->mapindex != map_index) ? 1 : 0; sd->state.warping = 1; sd->state.workinprogress = 0; - if( sd->state.changemap ) { // Misc map-changing settings - int i; + + if (sd->state.changemap != 0) { // Miscellaneous map-changing settings. sd->state.pmap = sd->bl.m; - for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { + for (int i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); - if (queue && queue->event_mapchange[0] != '\0') { - pc->setregstr(sd, script->add_variable("@Queue_Destination_Map$"), map->list[m].name); + + if (queue != NULL && queue->event_mapchange[0] != '\0') { + pc->setregstr(sd, script->add_variable("@Queue_Destination_Map$"), map->list[map_id].name); npc->event(sd, queue->event_mapchange, 0); } } - if( map->list[m].cell == (struct mapcell *)0xdeadbeaf ) - map->cellfromcache(&map->list[m]); - if (sd->sc.count) { // Cancel some map related stuff. - if (sd->sc.data[SC_JAILED]) - return 1; //You may not get out! + if (map->list[map_id].cell == (struct mapcell *)0xdeadbeaf) + map->cellfromcache(&map->list[map_id]); + + if (sd->sc.count != 0) { // Cancel some map related stuff. + if (sd->sc.data[SC_JAILED] != NULL) + return 4; // You may not get out! + status_change_end(&sd->bl, SC_CASH_BOSS_ALARM, INVALID_TIMER); status_change_end(&sd->bl, SC_WARM, INVALID_TIMER); status_change_end(&sd->bl, SC_SUN_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_MOON_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_STAR_COMFORT, INVALID_TIMER); status_change_end(&sd->bl, SC_MIRACLE, INVALID_TIMER); - status_change_end(&sd->bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER);//Will later check if this is needed. [Rytech] + status_change_end(&sd->bl, SC_NEUTRALBARRIER_MASTER, INVALID_TIMER); // Will later check if this is needed. [Rytech] status_change_end(&sd->bl, SC_NEUTRALBARRIER, INVALID_TIMER); status_change_end(&sd->bl, SC_STEALTHFIELD_MASTER, INVALID_TIMER); status_change_end(&sd->bl, SC_STEALTHFIELD, INVALID_TIMER); - if (sd->sc.data[SC_KNOWLEDGE]) { + + if (sd->sc.data[SC_KNOWLEDGE] != NULL) { struct status_change_entry *sce = sd->sc.data[SC_KNOWLEDGE]; + if (sce->timer != INVALID_TIMER) timer->delete(sce->timer, status->change_timer); - sce->timer = timer->add(timer->gettick() + skill->get_time(SG_KNOWLEDGE, sce->val1), status->change_timer, sd->bl.id, SC_KNOWLEDGE); + + sce->timer = timer->add(timer->gettick() + skill->get_time(SG_KNOWLEDGE, sce->val1), + status->change_timer, sd->bl.id, SC_KNOWLEDGE); } + status_change_end(&sd->bl, SC_PROPERTYWALK, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKING, INVALID_TIMER); status_change_end(&sd->bl, SC_CLOAKINGEXCEED, INVALID_TIMER); } - for( i = 0; i < EQI_MAX; i++ ) { - if( sd->equip_index[ i ] >= 0 ) - if( !pc->isequip( sd , sd->equip_index[ i ] ) ) - pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_FORCE); + + for (int i = 0; i < EQI_MAX; i++) { + if (sd->equip_index[i] >= 0 && pc->isequip(sd , sd->equip_index[i]) == 0) + pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_FORCE); } - if (battle_config.clear_unit_onwarp&BL_PC) + + if ((battle_config.clear_unit_onwarp & BL_PC) != 0) skill->clear_unitgroup(&sd->bl); - party->send_dot_remove(sd); //minimap dot fix [Kevin] + + party->send_dot_remove(sd); // Minimap dot fix. [Kevin] guild->send_dot_remove(sd); bg->send_dot_remove(sd); - if (sd->regen.state.gc) + + if (sd->regen.state.gc != 0) sd->regen.state.gc = 0; - // make sure vending is allowed here - if (sd->state.vending && map->list[m].flag.novending) { - clif->message (sd->fd, msg_sd(sd,276)); // "You can't open a shop on this map" + + // Make sure that vending is allowed here. + if (sd->state.vending != 0 && map->list[map_id].flag.novending != 0) { + clif->message(sd->fd, msg_sd(sd, 276)); // "You can't open a shop on this map" vending->close(sd); } - if (sd->mapindex != 0) { - // Only if the character is already on a map - if (map->list[sd->bl.m].channel) { - channel->leave(map->list[sd->bl.m].channel,sd); - } - } + if (sd->mapindex != 0 && map->list[sd->bl.m].channel != NULL) // Only if the character is already on a map. + channel->leave(map->list[sd->bl.m].channel, sd); } - if( m < 0 ) { + if (map_id < 0) { uint32 ip; uint16 port; - //if can't find any map-servers, just abort setting position. - if(!sd->mapindex || map->mapname2ipport(map_index,&ip,&port)) + + // If can't find any map-servers, just abort setting position. + if (sd->mapindex == 0 || map->mapname2ipport(map_index, &ip, &port) != 0) return 2; - if (sd->npc_id) + if (sd->npc_id != 0) npc->event_dequeue(sd); + npc->script_event(sd, NPCE_LOGOUT); - //remove from map, THEN change x/y coordinates - unit->remove_map_pc(sd,clrtype); + + // Remove from map, THEN change x/y coordinates. + unit->remove_map_pc(sd, clrtype); + if (battle_config.player_warp_keep_direction == 0) - sd->ud.dir = 0; // makes character face north + sd->ud.dir = 0; /// Make character facing north. + sd->mapindex = map_index; - sd->bl.x=x; - sd->bl.y=y; + sd->bl.x= x; + sd->bl.y= y; pc->clean_skilltree(sd); - chrif->save(sd,2); - chrif->changemapserver(sd, ip, (short)port); + chrif->save(sd, 2); + chrif->changemapserver(sd, ip, port); - //Free session data from this map server [Kevin] + // Free session data from this map server. [Kevin] unit->free_pc(sd); return 0; } - if( x < 0 || x >= map->list[m].xs || y < 0 || y >= map->list[m].ys ) { - ShowError("pc_setpos: attempt to place player %s (%d:%d) on invalid coordinates (%s-%d,%d)\n", sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(map_index),x,y); - x = y = 0; // make it random + if (x < 0 || x >= map->list[map_id].xs || y < 0 || y >= map->list[map_id].ys) { // Invalid coordinates. Randomize them. + ShowError("pc_setpos: Attempt to place player %s (%d:%d) on invalid coordinates (%s-%d,%d)!\n", + sd->status.name, sd->status.account_id, sd->status.char_id, + mapindex_id2name(map_index), x, y); + x = 0; + y = 0; } - if( x == 0 && y == 0 ) {// pick a random walkable cell + if (x == 0 && y == 0) { // Pick a random walkable cell. do { - x=rnd()%(map->list[m].xs-2)+1; - y=rnd()%(map->list[m].ys-2)+1; - } while(map->getcell(m, &sd->bl, x, y, CELL_CHKNOPASS)); + x = rnd() % (map->list[map_id].xs - 2) + 1; + y = rnd() % (map->list[map_id].ys - 2) + 1; + } while(map->getcell(map_id, &sd->bl, x, y, CELL_CHKNOPASS) != 0); } - if (sd->state.vending && map->getcell(m, &sd->bl, x, y, CELL_CHKNOVENDING)) { - clif->message (sd->fd, msg_sd(sd,204)); // "You can't open a shop on this cell." + if (sd->state.vending != 0 && map->getcell(map_id, &sd->bl, x, y, CELL_CHKNOVENDING) != 0) { + clif->message(sd->fd, msg_sd(sd, 204)); // "You can't open a shop on this cell." vending->close(sd); } if (battle_config.player_warp_keep_direction == 0) - sd->ud.dir = 0; // makes character face north + sd->ud.dir = 0; // Make character facing north. - if(sd->bl.prev != NULL){ - unit->remove_map_pc(sd,clrtype); - clif->changemap(sd,m,x,y); // [MouseJstr] - } else if(sd->state.active) - //Tag player for rewarping after map-loading is done. [Skotlex] - sd->state.rewarp = 1; + if (sd->bl.prev != NULL) { + unit->remove_map_pc(sd, clrtype); + clif->changemap(sd, map_id, x, y); // [MouseJstr] + } else if (sd->state.active != 0) { + sd->state.rewarp = 1; // Tag character for re-warping after map-loading is done. [Skotlex] + } sd->mapindex = map_index; - sd->bl.m = m; - sd->bl.x = sd->ud.to_x = x; - sd->bl.y = sd->ud.to_y = y; + sd->bl.m = map_id; + sd->bl.x = x; + sd->bl.y = y; + sd->ud.to_x = x; + sd->ud.to_y = y; - if( sd->status.guild_id > 0 && map->list[m].flag.gvg_castle ) { // Increased guild castle regen [Valaris] + if (sd->status.guild_id > 0 && map->list[map_id].flag.gvg_castle != 0) { // Double regeneration in guild castle. [Valaris] struct guild_castle *gc = guild->mapindex2gc(sd->mapindex); - if(gc && gc->guild_id == sd->status.guild_id) + + if (gc != NULL && gc->guild_id == sd->status.guild_id) sd->regen.state.gc = 1; } - if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) { - sd->pd->bl.m = m; - sd->pd->bl.x = sd->pd->ud.to_x = x; - sd->pd->bl.y = sd->pd->ud.to_y = y; + if (sd->status.pet_id > 0 && sd->pd != NULL && sd->pd->pet.intimate > PET_INTIMACY_NONE) { + sd->pd->bl.m = map_id; + sd->pd->bl.x = x; + sd->pd->bl.y = y; + sd->pd->ud.to_x = x; + sd->pd->ud.to_y = y; sd->pd->ud.dir = sd->ud.dir; } - if( homun_alive(sd->hd) ) { - sd->hd->bl.m = m; - sd->hd->bl.x = sd->hd->ud.to_x = x; - sd->hd->bl.y = sd->hd->ud.to_y = y; + if (homun_alive(sd->hd)) { + sd->hd->bl.m = map_id; + sd->hd->bl.x = x; + sd->hd->bl.y = y; + sd->hd->ud.to_x = x; + sd->hd->ud.to_y = y; sd->hd->ud.dir = sd->ud.dir; } - if( sd->md ) { - sd->md->bl.m = m; - sd->md->bl.x = sd->md->ud.to_x = x; - sd->md->bl.y = sd->md->ud.to_y = y; + if (sd->md != NULL) { + sd->md->bl.m = map_id; + sd->md->bl.x = x; + sd->md->bl.y = y; + sd->md->ud.to_x = x; + sd->md->ud.to_y = y; sd->md->ud.dir = sd->ud.dir; } - /* given autotrades have no clients you have to trigger this manually otherwise they get stuck in memory limbo bugreport:7495 */ - if( sd->state.autotrade ) - clif->pLoadEndAck(0,sd); + // Given autotrades have no clients. You have to trigger this manually, otherwise they get stuck in memory limbo. (bugreport:7495) + if (sd->state.autotrade != 0) + clif->pLoadEndAck(0, sd); return 0; } @@ -5946,7 +6000,7 @@ static int pc_setpos(struct map_session_data *sd, unsigned short map_index, int * 0 = fail or FIXME success (from pc->setpos) * x(1|2) = fail *------------------------------------------*/ -static int pc_randomwarp(struct map_session_data *sd, clr_type type) +static int pc_randomwarp(struct map_session_data *sd, enum clr_type type) { int x,y,i=0; int16 m; @@ -6147,7 +6201,7 @@ static int pc_checkequip(struct map_session_data *sd, int pos) * Convert's from the client's lame Job ID system * to the map server's 'makes sense' system. [Skotlex] *------------------------------------------*/ -static int pc_jobid2mapid(int16 class) +static int pc_jobid2mapid(int class) { switch (class) { //Novice And 1-1 Jobs @@ -6289,9 +6343,9 @@ static int pc_jobid2mapid(int16 class) } //Reverts the map-style class id to the client-style one. -static int pc_mapid2jobid(uint16 job, int sex) +static int pc_mapid2jobid(unsigned int class, int sex) { - switch (job) { + switch (class) { //Novice And 1-1 Jobs case MAPID_NOVICE: return JOB_NOVICE; case MAPID_SWORDMAN: return JOB_SWORDMAN; @@ -6963,7 +7017,7 @@ static int pc_checkjoblevelup(struct map_session_data *sd) status_calc_pc(sd,SCO_FORCE); clif->misceffect(&sd->bl,1); if (pc->checkskill(sd, SG_DEVIL) && !pc->nextjobexp(sd)) - clif->status_change(&sd->bl,SI_DEVIL1, 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. + clif->status_change(&sd->bl, status->get_sc_icon(SC_DEVIL1), status->get_sc_relevant_bl_types(SC_DEVIL1), 1, 0, 0, 0, 1); //Permanent blind effect from SG_DEVIL. npc->script_event(sd, NPCE_JOBLVUP); @@ -7131,11 +7185,16 @@ static bool pc_gainexp(struct map_session_data *sd, struct block_list *src, uint if(sd->state.showexp) { char output[256]; sprintf(output, - "Experience Gained Base:%"PRIu64" (%.2f%%) Job:%"PRIu64" (%.2f%%)", + msg_sd(sd, 889), // Experience Gained Base:%"PRIu64" (%.2f%%) Job:%"PRIu64" (%.2f%%) base_exp, nextbp * (float)100, job_exp, nextjp * (float)100); clif_disp_onlyself(sd, output); } + // Share master EXP to homunculus + if (sd->hd != NULL && battle_config.hom_bonus_exp_from_master > 0) { + homun->gainexp(sd->hd, apply_percentrate((int)base_exp, battle_config.hom_bonus_exp_from_master, 100)); + } + return true; } @@ -7352,34 +7411,33 @@ static int pc_maxparameterincrease(struct map_session_data *sd, int type) */ static bool pc_statusup(struct map_session_data *sd, int type, int increase) { - int max_increase = 0, current = 0, needed_points = 0, final_value = 0; - nullpo_ret(sd); + int realIncrease = increase; // check conditions - if (type < SP_STR || type > SP_LUK || increase <= 0) { - clif->statusupack(sd, type, 0, 0); + if (type < SP_STR || type > SP_LUK || realIncrease <= 0) { + clif->statusupack(sd, type, 0, increase); return false; } // check limits - current = pc->getstat(sd, type); - max_increase = pc->maxparameterincrease(sd, type); - increase = cap_value(increase, 0, max_increase); // cap to the maximum status points available - if (increase <= 0 || current + increase > pc_maxparameter(sd)) { - clif->statusupack(sd, type, 0, 0); + int current = pc->getstat(sd, type); + int max_increase = pc->maxparameterincrease(sd, type); + realIncrease = cap_value(realIncrease, 0, max_increase); // cap to the maximum status points available + if (realIncrease <= 0 || current + realIncrease > pc_maxparameter(sd)) { + clif->statusupack(sd, type, 0, increase); return false; } // check status points - needed_points = pc->need_status_point(sd, type, increase); + int needed_points = pc->need_status_point(sd, type, realIncrease); if (needed_points < 0 || needed_points > sd->status.status_point) { // Sanity check - clif->statusupack(sd, type, 0, 0); + clif->statusupack(sd, type, 0, increase); return false; } // set new values - final_value = pc->setstat(sd, type, current + increase); + int final_value = pc->setstat(sd, type, current + realIncrease); sd->status.status_point -= needed_points; status_calc_pc(sd, SCO_NONE); @@ -7510,7 +7568,7 @@ static int pc_allskillup(struct map_session_data *sd) nullpo_ret(sd); for (i = 0; i < MAX_SKILL_DB; i++) { - if (sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED) { + if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0) { sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0; sd->status.skill[i].flag = SKILL_FLAG_PERMANENT; if (sd->status.skill[i].lv == 0) @@ -7729,7 +7787,7 @@ static int pc_resetskill(struct map_session_data *sd, int flag) return 0; if( pc->checkskill(sd, SG_DEVIL) && !pc->nextjobexp(sd) ) //Remove perma blindness due to skill-reset. [Skotlex] - clif->sc_end(&sd->bl, sd->bl.id, SELF, SI_DEVIL1); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_DEVIL1)); i = sd->sc.option; if( i&OPTION_RIDING && pc->checkskill(sd, KN_RIDING) ) i &= ~OPTION_RIDING; @@ -7754,7 +7812,7 @@ static int pc_resetskill(struct map_session_data *sd, int flag) pc->setoption(sd, i); if( homun_alive(sd->hd) && pc->checkskill(sd, AM_CALLHOMUN) ) - homun->vaporize(sd, HOM_ST_REST); + homun->vaporize(sd, HOM_ST_REST, true); if ((sd->sc.data[SC_SPRITEMABLE] && pc->checkskill(sd, SU_SPRITEMABLE))) status_change_end(&sd->bl, SC_SPRITEMABLE, INVALID_TIMER); @@ -7945,7 +8003,7 @@ static int pc_skillheal2_bonus(struct map_session_data *sd, uint16 skill_id) return bonus; } -static void pc_respawn(struct map_session_data *sd, clr_type clrtype) +static void pc_respawn(struct map_session_data *sd, enum clr_type clrtype) { if( !pc_isdead(sd) ) return; // not applicable @@ -7995,7 +8053,7 @@ static void pc_damage(struct map_session_data *sd, struct block_list *src, unsig if( sd->status.pet_id > 0 && sd->pd && battle_config.pet_damage_support ) pet->target_check(sd,src,1); - if( sd->status.ele_id > 0 ) + if (sd->status.ele_id != 0 && sd->ed != NULL) elemental->set_target(sd,src); if (battle_config.prevent_logout_trigger & PLT_DAMAGE) @@ -8010,163 +8068,177 @@ static void pc_damage(struct map_session_data *sd, struct block_list *src, unsig } } -/*========================================== - * Invoked when a player has negative current hp - *------------------------------------------*/ +/** + * Invoked when a character died. + * + * @param sd The died character. + * @param src The unit which caused the death. + * @retval 0 Death canceled. + * @retval 1 Standard death. + * @retval 9 Died in PVP/GVG/BG. + * + **/ static int pc_dead(struct map_session_data *sd, struct block_list *src) { - int i=0,j=0; - int64 tick = timer->gettick(); + nullpo_ret(sd); - nullpo_retr(0, sd); + for (int i = 0; i < MAX_PC_DEVOTION; i++) { + if (sd->devotion[i] != 0) { + struct map_session_data *devsd = map->id2sd(sd->devotion[i]); - for (j = 0; j < MAX_PC_DEVOTION; j++) { - if (sd->devotion[j]) { - struct map_session_data *devsd = map->id2sd(sd->devotion[j]); - if (devsd) + if (devsd != NULL) status_change_end(&devsd->bl, SC_DEVOTION, INVALID_TIMER); - sd->devotion[j] = 0; + + sd->devotion[i] = 0; } } - if(sd->status.pet_id > 0 && sd->pd) { + if (sd->status.pet_id > 0 && sd->pd != NULL) { struct pet_data *pd = sd->pd; - if( !map->list[sd->bl.m].flag.noexppenalty ) { + + if (map->list[sd->bl.m].flag.noexppenalty == 0) { pet->set_intimate(pd, pd->pet.intimate - pd->petDB->die); - if( pd->pet.intimate < 0 ) - pd->pet.intimate = 0; - clif->send_petdata(sd,sd->pd,1,pd->pet.intimate); + clif->send_petdata(sd, sd->pd, 1, pd->pet.intimate); } - if( sd->pd->target_id ) // Unlock all targets... + + if (sd->pd->target_id != 0) // Unlock all targets. pet->unlocktarget(sd->pd); } - if (sd->status.hom_id > 0){ - if(battle_config.homunculus_auto_vapor && sd->hd) - homun->vaporize(sd, HOM_ST_REST); - } + if (sd->status.hom_id > 0 && sd->hd != NULL && battle_config.homunculus_auto_vapor != 0) + homun->vaporize(sd, HOM_ST_REST, true); - if( sd->md ) - mercenary->delete(sd->md, 3); // Your mercenary soldier has ran away. + if (sd->md != NULL) + mercenary->delete(sd->md, 3); // Your mercenary soldier ran away. - if( sd->ed ) + if (sd->ed != NULL) elemental->delete(sd->ed, 0); - // Leave duel if you die [LuzZza] - if(battle_config.duel_autoleave_when_die) { - if(sd->duel_group > 0) + if (battle_config.duel_autoleave_when_die != 0) { // Leave duel if character died. [LuzZza] + if (sd->duel_group > 0) duel->leave(sd->duel_group, sd); - if(sd->duel_invite > 0) + if (sd->duel_invite > 0) duel->reject(sd->duel_invite, sd); } - if (sd->npc_id && sd->st && sd->st->state != RUN) + if (sd->npc_id != 0 && sd->st != NULL && sd->st->state != RUN) npc->event_dequeue(sd); - pc_setglobalreg(sd,script->add_variable("PC_DIE_COUNTER"),sd->die_counter+1); - pc->setparam(sd, SP_KILLERRID, src?src->id:0); + pc_setglobalreg(sd, script->add_variable("PC_DIE_COUNTER"), sd->die_counter + 1); + pc->setparam(sd, SP_KILLERRID, (src != NULL) ? src->id : 0); + + if (sd->bg_id != 0) { //TODO: Purge when bgqueue is deemed ok. + struct battleground_data *bgd = bg->team_search(sd->bg_id); - if( sd->bg_id ) {/* TODO: purge when bgqueue is deemed ok */ - struct battleground_data *bgd; - if( (bgd = bg->team_search(sd->bg_id)) != NULL && bgd->die_event[0] ) + if (bgd != NULL && bgd->die_event[0] != '\0') npc->event(sd, bgd->die_event, 0); } - for (i = 0; i < VECTOR_LENGTH(sd->script_queues); i++ ) { + for (int i = 0; i < VECTOR_LENGTH(sd->script_queues); i++) { struct script_queue *queue = script->queue(VECTOR_INDEX(sd->script_queues, i)); - if (queue && queue->event_death[0] != '\0') + + if (queue != NULL && queue->event_death[0] != '\0') npc->event(sd, queue->event_death, 0); } - npc->script_event(sd,NPCE_DIE); + npc->script_event(sd, NPCE_DIE); - // Clear anything NPC-related when you die and was interacting with one. - if ( (sd->npc_id || sd->npc_shopid) && sd->state.dialog) { - if (sd->state.using_fake_npc) { + // Clear anything NPC-related if character died while interacting with one. + if ((sd->npc_id != 0 || sd->npc_shopid != 0) && sd->state.dialog != 0) { + if (sd->state.using_fake_npc != 0) { clif->clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd); sd->state.using_fake_npc = 0; } - if (sd->state.menu_or_input) + + if (sd->state.menu_or_input != 0) sd->state.menu_or_input = 0; - if (sd->npc_menu) + + if (sd->npc_menu != 0) sd->npc_menu = 0; sd->npc_id = 0; sd->npc_shopid = 0; - if (sd->st && sd->st->state != END) + + if (sd->st != NULL && sd->st->state != END) sd->st->state = END; } - /* e.g. not killed through pc->damage */ - if( pc_issit(sd) ) { - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); - } + // E.g. not killed through pc->damage(). + if (pc_issit(sd)) + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT)); pc_setdead(sd); - clif->party_dead_notification(sd); - //Reset menu skills/item skills - if (sd->skillitem) - sd->skillitem = sd->skillitemlv = 0; - if (sd->menuskill_id) - sd->menuskill_id = sd->menuskill_val = 0; - //Reset ticks. - sd->hp_loss.tick = sd->sp_loss.tick = sd->hp_regen.tick = sd->sp_regen.tick = 0; + pc->autocast_clear(sd); // Unset auto-cast data. - if ( sd->spiritball ) + if (sd->menuskill_id != 0) { // Reset menu skills. + sd->menuskill_id = 0; + sd->menuskill_val = 0; + } + + // Reset ticks. + sd->hp_loss.tick = 0; + sd->sp_loss.tick = 0; + sd->hp_regen.tick = 0; + sd->sp_regen.tick = 0; + + if (sd->spiritball != 0) pc->delspiritball(sd, sd->spiritball, 0); + if (sd->charm_type != CHARM_TYPE_NONE && sd->charm_count > 0) pc->del_charm(sd, sd->charm_count, sd->charm_type); + int64 tick = timer->gettick(); + if (src != NULL) { switch (src->type) { - case BL_MOB: - { - struct mob_data *md = BL_UCAST(BL_MOB, src); - if (md->target_id==sd->bl.id) - mob->unlocktarget(md,tick); - if (battle_config.mobs_level_up && md->status.hp - && md->level < pc->maxbaselv(sd) - && !md->guardian_data && md->special_state.ai == AI_NONE// Guardians/summons should not level. [Skotlex] - ) { - // monster level up [Valaris] - clif->misceffect(&md->bl,0); - md->level++; - status_calc_mob(md, SCO_NONE); - status_percent_heal(src,10,0); - - if( battle_config.show_mob_info&4 ) - {// update name with new level - clif->charnameack(0, &md->bl); - } - } - src = battle->get_master(src); // Maybe Player Summon + case BL_MOB: { + struct mob_data *md = BL_UCAST(BL_MOB, src); + + if (md->target_id == sd->bl.id) + mob->unlocktarget(md, tick); + + if (battle_config.mobs_level_up != 0 && md->status.hp != 0 && md->level < pc->maxbaselv(sd) + && md->guardian_data == NULL && md->special_state.ai == AI_NONE) { // Guardians/summons should not level up. [Skotlex] + /// Monster level up. [Valaris] + clif->misceffect(&md->bl, 0); + md->level++; + status_calc_mob(md, SCO_NONE); + status_percent_heal(src, 10, 0); + + if ((battle_config.show_mob_info & 4) != 0) + clif->blname_ack(0, &md->bl); // Update name with new level. } + + src = battle->get_master(src); // Maybe character summon. break; - case BL_PET: //Pass on to master... - src = &BL_UCAST(BL_PET, src)->msd->bl; + } + case BL_PET: + src = &BL_UCAST(BL_PET, src)->msd->bl; // Pass on to master. break; - case BL_HOM: - src = &BL_UCAST(BL_HOM, src)->master->bl; + case BL_HOM: + src = &BL_UCAST(BL_HOM, src)->master->bl; // Pass on to master. break; - case BL_MER: - src = &BL_UCAST(BL_MER, src)->master->bl; + case BL_MER: + src = &BL_UCAST(BL_MER, src)->master->bl; // Pass on to master. break; } } if (src != NULL && src->type == BL_PC) { struct map_session_data *ssd = BL_UCAST(BL_PC, src); + pc->setparam(ssd, SP_KILLEDRID, sd->bl.id); npc->script_event(ssd, NPCE_KILLPC); + achievement->validate_pc_kill(ssd, sd); - achievement->validate_pc_kill(ssd, sd); // Achievements [Smokexyz/Hercules] - - if (battle_config.pk_mode&2) { + if ((battle_config.pk_mode & 2) != 0) { ssd->status.manner -= 5; - if(ssd->status.manner < 0) - sc_start(NULL,src,SC_NOCHAT,100,0,0); + + if (ssd->status.manner < 0) + sc_start(NULL, src, SC_NOCHAT, 100, 0, 0); + #if 0 // PK/Karma system code (not enabled yet) [celest] // originally from Kade Online, so i don't know if any of these is correct ^^; @@ -8178,14 +8250,15 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) // If player killed was more evil sd->status.karma--; ssd->status.karma--; - } - else if (sd->status.karma < ssd->status.karma) // If player killed was more good + } else if (sd->status.karma < ssd->status.karma) { // If player killed was more good ssd->status.karma++; + } // or the PK System way... if (sd->status.karma > 0) // player killed is dishonourable? ssd->status.karma--; // honour points earned + sd->status.karma++; // honour points lost // To-do: Receive exp on certain occasions @@ -8193,136 +8266,156 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) } } - if( battle_config.bone_drop==2 - || (battle_config.bone_drop==1 && map->list[sd->bl.m].flag.pvp) - ) { + if (battle_config.bone_drop == 2 || (battle_config.bone_drop == 1 && map->list[sd->bl.m].flag.pvp != 0)) { struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=ITEMID_SKULL_; - item_tmp.identify=1; - item_tmp.card[0]=CARD0_CREATE; - item_tmp.card[1]=0; - item_tmp.card[2]=GetWord(sd->status.char_id,0); // CharId - item_tmp.card[3]=GetWord(sd->status.char_id,1); + + memset(&item_tmp, 0, sizeof(item_tmp)); + item_tmp.nameid = ITEMID_SKULL_; + item_tmp.identify = 1; + item_tmp.card[0] = CARD0_CREATE; + item_tmp.card[1] = 0; + item_tmp.card[2] = GetWord(sd->status.char_id, 0); + item_tmp.card[3] = GetWord(sd->status.char_id, 1); map->addflooritem(&sd->bl, &item_tmp, 1, sd->bl.m, sd->bl.x, sd->bl.y, 0, 0, 0, 0, false); } - // activate Steel body if a super novice dies at 99+% exp [celest] - if ((sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && !sd->state.snovice_dead_flag) { + // Activate Steel Body if a Super Novice dies at 99+% EXP. [celest] + if ((sd->job & MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->state.snovice_dead_flag == 0) { uint64 next = pc->nextbaseexp(sd); - if( next == 0 ) next = pc->thisbaseexp(sd); + + if (next == 0) + next = pc->thisbaseexp(sd); + if (get_percentage64(sd->status.base_exp, next) >= 99) { sd->state.snovice_dead_flag = 1; pc->setstand(sd); status_percent_heal(&sd->bl, 100, 100); clif->resurrection(&sd->bl, 1); - if(battle_config.pc_invincible_time) + + if (battle_config.pc_invincible_time != 0) pc->setinvincibletimer(sd, battle_config.pc_invincible_time); - sc_start(NULL,&sd->bl,status->skill2sc(MO_STEELBODY),100,1,skill->get_time(MO_STEELBODY,1)); - if(map_flag_gvg2(sd->bl.m)) + + sc_start(NULL, &sd->bl, status->skill2sc(MO_STEELBODY), 100, 1, skill->get_time(MO_STEELBODY, 1)); + + if (map_flag_gvg2(sd->bl.m)) pc->respawn_timer(INVALID_TIMER, timer->gettick(), sd->bl.id, 0); + return 0; } } - // changed penalty options, added death by player if pk_mode [Valaris] - if (battle_config.death_penalty_type - && pc->isDeathPenaltyJob(sd->job) - && !map->list[sd->bl.m].flag.noexppenalty && !map_flag_gvg2(sd->bl.m) - && !sd->sc.data[SC_BABY] && !sd->sc.data[SC_CASH_DEATHPENALTY] - ) { + if (battle_config.death_penalty_type != 0 && pc->isDeathPenaltyJob(sd->job) && !map_flag_gvg2(sd->bl.m) + && map->list[sd->bl.m].flag.noexppenalty == 0 && sd->sc.data[SC_BABY] == NULL + && sd->sc.data[SC_CASH_DEATHPENALTY] == NULL && !pc->auto_exp_insurance(sd)) { if (battle_config.death_penalty_base > 0) { unsigned int base_penalty = 0; + int rate = battle_config.death_penalty_base; + switch (battle_config.death_penalty_type) { - case 1: - base_penalty = (unsigned int) apply_percentrate64(pc->nextbaseexp(sd), battle_config.death_penalty_base, 10000); - break; - case 2: - base_penalty = (unsigned int) apply_percentrate64(sd->status.base_exp, battle_config.death_penalty_base, 10000); - break; + case 1: + base_penalty = (unsigned int)apply_percentrate64(pc->nextbaseexp(sd), rate, 10000); + break; + case 2: + base_penalty = (unsigned int)apply_percentrate64(sd->status.base_exp, rate, 10000); + break; } if (base_penalty != 0) { - if (battle_config.pk_mode && src && src->type==BL_PC) - base_penalty*=2; - if( sd->status.mod_death != 100 ) + if (battle_config.pk_mode != 0 && src != NULL && src->type == BL_PC) + base_penalty *= 2; + + if (sd->status.mod_death != 100) base_penalty = base_penalty * sd->status.mod_death / 100; + sd->status.base_exp -= min(sd->status.base_exp, base_penalty); - clif->updatestatus(sd,SP_BASEEXP); + clif->updatestatus(sd, SP_BASEEXP); } } - if(battle_config.death_penalty_job > 0) { + if (battle_config.death_penalty_job > 0) { unsigned int job_penalty = 0; + int rate = battle_config.death_penalty_job; switch (battle_config.death_penalty_type) { - case 1: - job_penalty = (unsigned int) apply_percentrate64(pc->nextjobexp(sd), battle_config.death_penalty_job, 10000); - break; - case 2: - job_penalty = (unsigned int) apply_percentrate64(sd->status.job_exp, battle_config.death_penalty_job, 10000); - break; + case 1: + job_penalty = (unsigned int)apply_percentrate64(pc->nextjobexp(sd), rate, 10000); + break; + case 2: + job_penalty = (unsigned int)apply_percentrate64(sd->status.job_exp, rate, 10000); + break; } if (job_penalty != 0) { - if (battle_config.pk_mode && src && src->type==BL_PC) - job_penalty*=2; - if( sd->status.mod_death != 100 ) + if (battle_config.pk_mode != 0 && src != NULL && src->type == BL_PC) + job_penalty *= 2; + + if (sd->status.mod_death != 100) job_penalty = job_penalty * sd->status.mod_death / 100; + sd->status.job_exp -= min(sd->status.job_exp, job_penalty); - clif->updatestatus(sd,SP_JOBEXP); + clif->updatestatus(sd, SP_JOBEXP); } } - if (battle_config.zeny_penalty > 0 && !map->list[sd->bl.m].flag.nozenypenalty) { + if (battle_config.zeny_penalty > 0 && map->list[sd->bl.m].flag.nozenypenalty == 0) { int zeny_penalty = apply_percentrate(sd->status.zeny, battle_config.zeny_penalty, 10000); + if (zeny_penalty != 0) pc->payzeny(sd, zeny_penalty, LOG_TYPE_PICKDROP_PLAYER, NULL); } } - if(map->list[sd->bl.m].flag.pvp_nightmaredrop) { - // Moved this outside so it works when PVP isn't enabled and during pk mode [Ancyker] - for(j=0;j<map->list[sd->bl.m].drop_list_count;j++){ - int id = map->list[sd->bl.m].drop_list[j].drop_id; - int type = map->list[sd->bl.m].drop_list[j].drop_type; - int per = map->list[sd->bl.m].drop_list[j].drop_per; - if(id == 0) + if (map->list[sd->bl.m].flag.pvp_nightmaredrop != 0) { + // Moved this outside so it works when PVP isn't enabled and during pk mode. [Ancyker] + for (int i = 0; i < map->list[sd->bl.m].drop_list_count; i++) { + int id = map->list[sd->bl.m].drop_list[i].drop_id; + int type = map->list[sd->bl.m].drop_list[i].drop_type; + int per = map->list[sd->bl.m].drop_list[i].drop_per; + + if (id == 0) continue; - if(id == -1){ - int eq_num=0,eq_n[MAX_INVENTORY],k; - memset(eq_n,0,sizeof(eq_n)); - for(i=0;i<MAX_INVENTORY;i++){ - if( (type == 1 && !sd->status.inventory[i].equip) - || (type == 2 && sd->status.inventory[i].equip) - || type == 3) - { - ARR_FIND( 0, MAX_INVENTORY, k, eq_n[k] <= 0 ); - if( k < MAX_INVENTORY ) - eq_n[k] = i; + + if (id == -1) { + int eq_num = 0; + int eq_n[MAX_INVENTORY]; + + memset(eq_n, 0, sizeof(eq_n)); + + for (int j = 0; j < sd->status.inventorySize; j++) { + bool is_equipped = (sd->status.inventory[j].equip != 0); + + if ((type == 1 && !is_equipped) || (type == 2 && is_equipped) || type == 3) { + int k; + + ARR_FIND(0, sd->status.inventorySize, k, eq_n[k] <= 0); + + if (k < sd->status.inventorySize) + eq_n[k] = j; eq_num++; } } - if(eq_num > 0){ - int n = eq_n[rnd()%eq_num]; - if(rnd()%10000 < per){ - if(sd->status.inventory[n].equip) + + if (eq_num > 0) { + int n = eq_n[rnd() % eq_num]; + + if (rnd() % 10000 < per) { + if (sd->status.inventory[n].equip != 0) pc->unequipitem(sd, n, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); - pc->dropitem(sd,n,1); + + pc->dropitem(sd, n, 1); } } - } - else if(id > 0){ - for(i=0;i<MAX_INVENTORY;i++){ - if(sd->status.inventory[i].nameid == id - && rnd()%10000 < per - && ((type == 1 && !sd->status.inventory[i].equip) - || (type == 2 && sd->status.inventory[i].equip) - || type == 3) ){ - if(sd->status.inventory[i].equip) - pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); - pc->dropitem(sd,i,1); + } else if (id > 0) { + for (int j = 0; j < sd->status.inventorySize; j++) { + bool is_equipped = (sd->status.inventory[j].equip != 0); + + if (((type == 1 && !is_equipped) || (type == 2 && is_equipped) || type == 3) + && sd->status.inventory[j].nameid == id && rnd() % 10000 < per) { + if (is_equipped) + pc->unequipitem(sd, j, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); + + pc->dropitem(sd, j, 1); break; } } @@ -8330,46 +8423,51 @@ static int pc_dead(struct map_session_data *sd, struct block_list *src) } } - // Remove autotrade to prevent autotrading from save point - if( (sd->state.standalone || sd->state.autotrade) - && (map->list[sd->bl.m].flag.pvp || map->list[sd->bl.m].flag.gvg) - ) { + // Remove autotrade to prevent autotrading from save point. + if ((map->list[sd->bl.m].flag.pvp != 0 || map->list[sd->bl.m].flag.gvg != 0) + && (sd->state.standalone != 0 || sd->state.autotrade != 0)) { sd->state.autotrade = 0; sd->state.standalone = 0; - pc->autotrade_update(sd,PAUC_REMOVE); + pc->autotrade_update(sd, PAUC_REMOVE); map->quit(sd); } - // pvp - // disable certain pvp functions on pk_mode [Valaris] - if( map->list[sd->bl.m].flag.pvp && !battle_config.pk_mode && !map->list[sd->bl.m].flag.pvp_nocalcrank ) { + // Disable certain PVP functions on pk_mode. [Valaris] + if (map->list[sd->bl.m].flag.pvp != 0 && battle_config.pk_mode == 0 + && map->list[sd->bl.m].flag.pvp_nocalcrank == 0) { sd->pvp_point -= 5; sd->pvp_lost++; + if (src != NULL && src->type == BL_PC) { struct map_session_data *ssd = BL_UCAST(BL_PC, src); + ssd->pvp_point++; ssd->pvp_won++; } - if( sd->pvp_point < 0 ) - { - timer->add(tick+1, pc->respawn_timer,sd->bl.id,0); + + if (sd->pvp_point < 0) { + timer->add(tick + 1, pc->respawn_timer, sd->bl.id, 0); return 1|8; } } - //GvG - if( map_flag_gvg2(sd->bl.m) ) { - timer->add(tick+1, pc->respawn_timer, sd->bl.id, 0); + + // GVG + if (map_flag_gvg2(sd->bl.m)) { + timer->add(tick + 1, pc->respawn_timer, sd->bl.id, 0); return 1|8; - } else if( sd->bg_id ) { + } + + if (sd->bg_id != 0) { struct battleground_data *bgd = bg->team_search(sd->bg_id); - if( bgd && bgd->mapindex > 0 ) { // Respawn by BG - timer->add(tick+1000, pc->respawn_timer, sd->bl.id, 0); + + if (bgd != NULL && bgd->mapindex > 0) { // Respawn by BG. + timer->add(tick + 1000, pc->respawn_timer, sd->bl.id, 0); return 1|8; } } - //Reset "can log out" tick. - if( battle_config.prevent_logout ) + // Reset "can log out" tick. + if (battle_config.prevent_logout != 0) sd->canlog_tick = timer->gettick() - battle_config.prevent_logout; return 1; @@ -8457,8 +8555,12 @@ static int64 pc_readparam(const struct map_session_data *sd, int type) case SP_FLEE1: val = sd->battle_status.flee; break; case SP_FLEE2: val = sd->battle_status.flee2; break; case SP_DEFELE: val = sd->battle_status.def_ele; break; -#ifndef RENEWAL_CAST case SP_VARCASTRATE: +#ifdef RENEWAL_CAST + val = sd->bonus.varcastrate; + break; +#else + FALLTHROUGH #endif case SP_CASTRATE: val = sd->castrate; @@ -8544,7 +8646,6 @@ static int64 pc_readparam(const struct map_session_data *sd, int type) case SP_FIXCASTRATE: val = sd->bonus.fixcastrate; break; case SP_ADD_FIXEDCAST: val = sd->bonus.add_fixcast; break; #ifdef RENEWAL_CAST - case SP_VARCASTRATE: val = sd->bonus.varcastrate; break; case SP_ADD_VARIABLECAST:val = sd->bonus.add_varcast; break; #endif } @@ -8789,6 +8890,10 @@ static int pc_itemheal(struct map_session_data *sd, int itemid, int hp, int sp) // 2014 Halloween Event : Pumpkin Bonus if ( sd->sc.data[SC_MTF_PUMPKIN] && itemid == ITEMID_PUMPKIN ) hp += (int)(hp * sd->sc.data[SC_MTF_PUMPKIN]->val1/100); + + // Activation Potion + if (sd->sc.data[SC_VITALIZE_POTION] != NULL) + hp += hp * sd->sc.data[SC_VITALIZE_POTION]->val3 / 100; } if(sp) { bonus = 100 + (sd->battle_status.int_<<1) @@ -9008,6 +9113,13 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) if (sd->disguise != -1) pc->disguise(sd, -1); + // Fix atcommand @jobchange when the player changing from 3rd job having alternate body style into non-3rd job, crashing the client + if (pc->has_second_costume(sd) == false) { + sd->status.body = 0; + sd->vd.body_style = 0; + clif->changelook(&sd->bl, LOOK_BODY2, sd->vd.body_style); + } + status->set_viewdata(&sd->bl, class); clif->changelook(&sd->bl, LOOK_BASE, sd->vd.class); // move sprite update to prevent client crashes with incompatible equipment [Valaris] if(sd->vd.cloth_color) @@ -9051,7 +9163,7 @@ static int pc_jobchange(struct map_session_data *sd, int class, int upper) pc->setoption(sd, i); if(homun_alive(sd->hd) && !pc->checkskill(sd, AM_CALLHOMUN)) - homun->vaporize(sd, HOM_ST_REST); + homun->vaporize(sd, HOM_ST_REST, true); if ((sd->sc.data[SC_SPRITEMABLE] && pc->checkskill(sd, SU_SPRITEMABLE))) status_change_end(&sd->bl, SC_SPRITEMABLE, INVALID_TIMER); @@ -9176,6 +9288,72 @@ static int pc_changelook(struct map_session_data *sd, int type, int val) return 0; } +/** + * Hides a character. + * + * @param sd The character to hide. + * @param show_msg Whether to show message to the character or not. + * + **/ +static void pc_hide(struct map_session_data *sd, bool show_msg) +{ + nullpo_retv(sd); + + clif->clearunit_area(&sd->bl, CLR_OUTSIGHT); + sd->sc.option |= OPTION_INVISIBLE; + sd->vd.class = INVISIBLE_CLASS; + + if (show_msg) + clif->message(sd->fd, atcommand->msgsd(sd, 11)); // Invisible: On + + // Decrement the number of pvp players on the map. + map->list[sd->bl.m].users_pvp--; + + if (map->list[sd->bl.m].flag.pvp != 0 && map->list[sd->bl.m].flag.pvp_nocalcrank == 0 + && sd->pvp_timer != INVALID_TIMER) { // Unregister the player for ranking. + timer->delete(sd->pvp_timer, pc->calc_pvprank_timer); + sd->pvp_timer = INVALID_TIMER; + } + + clif->changeoption(&sd->bl); +} + +/** + * Unhides a character. + * + * @param sd The character to unhide. + * @param show_msg Whether to show message to the character or not. + * + **/ +static void pc_unhide(struct map_session_data *sd, bool show_msg) +{ + nullpo_retv(sd); + + sd->sc.option &= ~OPTION_INVISIBLE; + + if (sd->disguise != -1) + status->set_viewdata(&sd->bl, sd->disguise); + else + status->set_viewdata(&sd->bl, sd->status.class); + + if (show_msg) + clif->message(sd->fd, atcommand->msgsd(sd, 10)); // Invisible: Off + + // Increment the number of pvp players on the map. + map->list[sd->bl.m].users_pvp++; + + if (map->list[sd->bl.m].flag.pvp != 0 && map->list[sd->bl.m].flag.pvp_nocalcrank == 0) // Register the player for ranking. + sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0); + + // bugreport:2266 + map->foreachinmovearea(clif->insight, &sd->bl, AREA_SIZE, sd->bl.x, sd->bl.y, BL_ALL, &sd->bl); + + if (sd->disguise != -1) + clif->spawn_unit(&sd->bl, AREA_WOS); + + clif->changeoption(&sd->bl); +} + /*========================================== * Give an option (type) to player (sd) and display it to client *------------------------------------------*/ @@ -9187,15 +9365,21 @@ static int pc_setoption(struct map_session_data *sd, int type) //Option has to be changed client-side before the class sprite or it won't always work (eg: Wedding sprite) [Skotlex] sd->sc.option=type; - clif->changeoption(&sd->bl); + + if ((p_type & OPTION_INVISIBLE) != 0 && (type & OPTION_INVISIBLE) == 0) // Unhide character. + pc->unhide(sd, false); + else if ((p_type & OPTION_INVISIBLE) == 0 && (type & OPTION_INVISIBLE) != 0) // Hide character. + pc->hide(sd, false); + else + clif->changeoption(&sd->bl); if( (type&OPTION_RIDING && !(p_type&OPTION_RIDING)) || (type&OPTION_DRAGON && !(p_type&OPTION_DRAGON) && pc->checkskill(sd,RK_DRAGONTRAINING) > 0) ) { // Mounting - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_RIDING, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_RIDING), 0, 0, 0); status_calc_pc(sd,SCO_NONE); } else if( (!(type&OPTION_RIDING) && p_type&OPTION_RIDING) || (!(type&OPTION_DRAGON) && p_type&OPTION_DRAGON) ) { // Dismount - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_RIDING); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_RIDING)); status_calc_pc(sd,SCO_NONE); } @@ -9215,15 +9399,15 @@ static int pc_setoption(struct map_session_data *sd, int type) #endif if (type&OPTION_FALCON && !(p_type&OPTION_FALCON)) //Falcon ON - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_FALCON, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_FALCON), 0, 0, 0); else if (!(type&OPTION_FALCON) && p_type&OPTION_FALCON) //Falcon OFF - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_FALCON); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_FALCON)); if( type&OPTION_WUGRIDER && !(p_type&OPTION_WUGRIDER) ) { // Mounting - clif->sc_load(&sd->bl,sd->bl.id,AREA,SI_WUGRIDER, 0, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_WUGRIDER), 0, 0, 0); status_calc_pc(sd,SCO_NONE); } else if( !(type&OPTION_WUGRIDER) && p_type&OPTION_WUGRIDER ) { // Dismount - clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_WUGRIDER); + clif->sc_end(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_WUGRIDER)); status_calc_pc(sd,SCO_NONE); } @@ -9311,7 +9495,7 @@ static int pc_setcart(struct map_session_data *sd, int type) clif->cartList(sd); clif->updatestatus(sd, SP_CARTINFO); sc_start(NULL,&sd->bl, SC_PUSH_CART, 100, type, 0); - clif->sc_load(&sd->bl, sd->bl.id, AREA, SI_ON_PUSH_CART, type, 0, 0); + clif->sc_load(&sd->bl, sd->bl.id, AREA, status->get_sc_icon(SC_ON_PUSH_CART), type, 0, 0); if( sd->sc.data[SC_PUSH_CART] )/* forcefully update */ sd->sc.data[SC_PUSH_CART]->val1 = type; break; @@ -9378,15 +9562,23 @@ static void pc_setridingpeco(struct map_session_data *sd, bool flag) * * @param sd Target player. * @param flag New state. + * @param mtype Type of the mado gear. **/ -static void pc_setmadogear(struct map_session_data *sd, bool flag) +static void pc_setmadogear(struct map_session_data *sd, bool flag, enum mado_type mtype) { nullpo_retv(sd); + Assert_retv(mtype >= MADO_ROBOT && mtype < MADO_MAX); + if (flag) { - if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) + if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { pc->setoption(sd, sd->sc.option|OPTION_MADOGEAR); +#if PACKETVER_MAIN_NUM >= 20191120 || PACKETVER_RE_NUM >= 20191106 + sc_start(&sd->bl, &sd->bl, SC_MADOGEAR, 100, (int)mtype, INFINITE_DURATION); +#endif + } } else if (pc_ismadogear(sd)) { pc->setoption(sd, sd->sc.option&~OPTION_MADOGEAR); + // pc->setoption resets status effects when changing mado, no need to re do it here. } } @@ -10132,141 +10324,163 @@ static void pc_equipitem_pos(struct map_session_data *sd, struct item_data *id, } } -/*========================================== - * Equip item on player sd at req_pos from inventory index n - * Return: - * 0 = fail - * 1 = success - *------------------------------------------*/ +/** + * Attempts to equip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @param req_pos The equipment slot, where the item should be equipped. (See enum equip_pos.) + * @return 0 on failure, 1 on success. + * + **/ static int pc_equipitem(struct map_session_data *sd, int n, int req_pos) { - int i,pos,flag=0,iflag; - struct item_data *id; - nullpo_ret(sd); - if( n < 0 || n >= MAX_INVENTORY ) { - clif->equipitemack(sd,0,0,EIA_FAIL); + if (n < 0 || n >= sd->status.inventorySize) { + clif->equipitemack(sd, 0, 0, EIA_FAIL); return 0; } - if( DIFF_TICK(sd->canequip_tick,timer->gettick()) > 0 ) - { - clif->equipitemack(sd,n,0,EIA_FAIL); + // If the character is in berserk mode, the item can't be equipped. + if (sd->sc.count != 0 && (sd->sc.data[SC_BERSERK] != NULL || sd->sc.data[SC_NO_SWITCH_EQUIP] != NULL)) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; } - id = sd->inventory_data[n]; - pos = pc->equippoint(sd,n); //With a few exceptions, item should go in all specified slots. + if (battle_config.battle_log != 0) + ShowInfo("equip %d(%d) %x:%x\n", sd->status.inventory[n].nameid, n, sd->status.inventory[n].equip, + (unsigned int)req_pos); - if(battle_config.battle_log) - ShowInfo("equip %d(%d) %x:%x\n", sd->status.inventory[n].nameid, n, (unsigned int)(id ? id->equip : 0), (unsigned int)req_pos); - if(!pc->isequip(sd,n) || !(pos&req_pos) || sd->status.inventory[n].equip != 0 || (sd->status.inventory[n].attribute & ATTR_BROKEN) != 0 ) { // [Valaris] - // FIXME: pc->isequip: equip level failure uses 2 instead of 0 - clif->equipitemack(sd,n,0,EIA_FAIL); // fail + if (DIFF_TICK(sd->canequip_tick, timer->gettick()) > 0) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; } - if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_NO_SWITCH_EQUIP]) - { - clif->equipitemack(sd,n,0,EIA_FAIL); // fail + int pos = pc->equippoint(sd, n); // With a few exceptions, item should go in all specified slots. + + if (pc->isequip(sd,n) == 0 || (pos & req_pos) == 0 || sd->status.inventory[n].equip != 0 + || (sd->status.inventory[n].attribute & ATTR_BROKEN) != 0) { + clif->equipitemack(sd, n, 0, EIA_FAIL); return 0; + } + + if (sd->inventory_data[n]->flag.bindonequip != 0 && sd->status.inventory[n].bound == 0) { + sd->status.inventory[n].bound = IBT_CHARACTER; + clif->notify_bounditem(sd, n); + } + + if (pos == EQP_ACC) { // Accesories should only go in one of the two. + pos = req_pos & EQP_ACC; + + if (pos == EQP_ACC) // User specified both slots. + pos = (sd->equip_index[EQI_ACC_R] >= 0) ? EQP_ACC_L : EQP_ACC_R; + } else if (pos == EQP_ARMS && sd->inventory_data[n]->equip == EQP_HAND_R) { // Dual wield capable weapon. + pos = req_pos & EQP_ARMS; + + if (pos == EQP_ARMS) // User specified both slots, pick one for them. + pos = (sd->equip_index[EQI_HAND_R] >= 0) ? EQP_HAND_L : EQP_HAND_R; + } else if (pos == EQP_SHADOW_ACC) { // Accesories should only go in one of the two, + pos = req_pos & EQP_SHADOW_ACC; + + if (pos == EQP_SHADOW_ACC) // User specified both slots. + pos = (sd->equip_index[EQI_SHADOW_ACC_R] >= 0) ? EQP_SHADOW_ACC_L : EQP_SHADOW_ACC_R; + } else if (pos == EQP_SHADOW_ARMS && sd->inventory_data[n]->equip == EQP_SHADOW_WEAPON) { // Dual wield capable weapon. + pos = req_pos & EQP_SHADOW_ARMS; + + if (pos == EQP_SHADOW_ARMS) // User specified both slots, pick one for them. + pos = (sd->equip_index[EQI_SHADOW_WEAPON] >= 0) ? EQP_SHADOW_SHIELD : EQP_SHADOW_WEAPON; } - /* won't fail from this point onwards */ - if( id->flag.bindonequip && !sd->status.inventory[n].bound ) { - sd->status.inventory[n].bound = (unsigned char)IBT_CHARACTER; - clif->notify_bounditem(sd,n); - } - - if(pos == EQP_ACC) { //Accesories should only go in one of the two, - pos = req_pos&EQP_ACC; - if (pos == EQP_ACC) //User specified both slots.. - pos = sd->equip_index[EQI_ACC_R] >= 0 ? EQP_ACC_L : EQP_ACC_R; - } else if(pos == EQP_ARMS && id->equip == EQP_HAND_R) { //Dual wield capable weapon. - pos = (req_pos&EQP_ARMS); - if (pos == EQP_ARMS) //User specified both slots, pick one for them. - pos = sd->equip_index[EQI_HAND_R] >= 0 ? EQP_HAND_L : EQP_HAND_R; - } else if(pos == EQP_SHADOW_ACC) { //Accesories should only go in one of the two, - pos = req_pos&EQP_SHADOW_ACC; - if (pos == EQP_SHADOW_ACC) //User specified both slots.. - pos = sd->equip_index[EQI_SHADOW_ACC_R] >= 0 ? EQP_SHADOW_ACC_L : EQP_SHADOW_ACC_R; - } else if( pos == EQP_SHADOW_ARMS && id->equip == EQP_SHADOW_WEAPON) { //Dual wield capable weapon. - pos = (req_pos&EQP_SHADOW_ARMS); - if (pos == EQP_SHADOW_ARMS) //User specified both slots, pick one for them. - pos = sd->equip_index[EQI_SHADOW_WEAPON] >= 0 ? EQP_SHADOW_SHIELD : EQP_SHADOW_WEAPON; - } - - if (pos&EQP_HAND_R && battle_config.use_weapon_skill_range&BL_PC) { - //Update skill-block range database when weapon range changes. [Skotlex] - i = sd->equip_index[EQI_HAND_R]; - if (i < 0 || !sd->inventory_data[i]) //No data, or no weapon equipped + int flag = 0; + + // Update skill-block range database when weapon range changes. [Skotlex] + if ((pos & EQP_HAND_R) != 0 && (battle_config.use_weapon_skill_range & BL_PC) != 0) { + int idx = sd->equip_index[EQI_HAND_R]; + + if (idx < 0 || sd->inventory_data[idx] == NULL) // No data, or no weapon equipped. flag = 1; else - flag = id->range != sd->inventory_data[i]->range; + flag = (sd->inventory_data[n]->range != sd->inventory_data[idx]->range) ? 1 : 0; } - for(i=0;i<EQI_MAX;i++) { - if(pos & pc->equip_pos[i]) { - if(sd->equip_index[i] >= 0) //Slot taken, remove item from there. + for (int i = 0; i < EQI_MAX; i++) { + if ((pos & pc->equip_pos[i]) != 0) { + if (sd->equip_index[i] >= 0) // Slot taken, remove item from there. pc->unequipitem(sd, sd->equip_index[i], PCUNEQUIPITEM_FORCE); sd->equip_index[i] = n; } } - if(pos==EQP_AMMO){ - clif->arrowequip(sd,n); - clif->arrow_fail(sd,3); + if (pos == EQP_AMMO) { + clif->arrowequip(sd, n); + clif->arrow_fail(sd, 3); + } else { + clif->equipitemack(sd, n, pos, EIA_SUCCESS); } - else - clif->equipitemack(sd,n,pos,EIA_SUCCESS); - sd->status.inventory[n].equip=pos; + sd->status.inventory[n].equip = pos; + pc->equipitem_pos(sd, sd->inventory_data[n], n, pos); + pc->checkallowskill(sd); // Check if status changes should be halted. - pc->equipitem_pos(sd, id, n, pos); + int iflag = sd->npc_item_flag; - pc->checkallowskill(sd); //Check if status changes should be halted. - iflag = sd->npc_item_flag; + // Check for combos. (MUST be done before status->calc_pc()!) + if (sd->inventory_data[n]->combos_count != 0) + pc->checkcombo(sd, sd->inventory_data[n]); - /* check for combos (MUST be before status_calc_pc) */ - if( id->combos_count ) - pc->checkcombo(sd,id); - if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < id->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + if (!itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int i = 0; i < sd->inventory_data[n]->slot; i++) { + if (sd->status.inventory[n].card[i] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if( data->combos_count ) - pc->checkcombo(sd,data); - } + + struct item_data *data = itemdb->exists(sd->status.inventory[n].card[i]); + + if (data != NULL && data->combos_count != 0) + pc->checkcombo(sd, data); } } - status_calc_pc(sd,SCO_NONE); - if (flag) //Update skill data + status_calc_pc(sd, SCO_NONE); + + if (flag != 0) // Update skill data. clif->skillinfoblock(sd); + + // Execute equip script. [Skotlex] + struct item_data *equip_data = sd->inventory_data[n]; + struct map_zone_data *zone = map->list[sd->bl.m].zone; + int dis_items_cnt = zone->disabled_items_count; - //OnEquip script [Skotlex] - if (id->equip_script) - script->run_item_equip_script(sd, id, npc->fake_nd->bl.id); + if (equip_data->equip_script != NULL) { + int idx; - if(itemdb_isspecial(sd->status.inventory[n].card[0])) - ; //No cards - else { - for( i = 0; i < id->slot; i++ ) { - struct item_data *data; - if (!sd->status.inventory[n].card[i]) + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == equip_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_equip_script(sd, equip_data, npc->fake_nd->bl.id); + } + + struct item *equip = &sd->status.inventory[n]; + + if (!itemdb_isspecial(equip->card[0])) { + for (int slot = 0; slot < equip_data->slot; slot++) { + if (equip->card[slot] == 0) continue; - if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) { - if (data->equip_script) - script->run_item_equip_script(sd, data, npc->fake_nd->bl.id); + + struct item_data *card_data = itemdb->exists(equip->card[slot]); + + if (card_data != NULL && card_data->equip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == card_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_equip_script(sd, card_data, npc->fake_nd->bl.id); } } } + sd->npc_item_flag = iflag; return 1; @@ -10338,147 +10552,146 @@ static void pc_unequipitem_pos(struct map_session_data *sd, int n, int pos) } } -/*========================================== - * Called when attemting to unequip an item from player - * type: @see enum pc_unequipitem_flag - * Return: - * 0 = fail - * 1 = success - *------------------------------------------*/ +/** + * Attempts to unequip an item. + * + * @param sd The related character. + * @param n The item's inventory index. + * @param flag Modifier for additional actions. (See enum pc_unequipitem_flag.) + * @return 0 on failure, 1 on success. + * + **/ static int pc_unequipitem(struct map_session_data *sd, int n, int flag) { - int i, iflag; - bool status_calc = false; - int pos; - nullpo_ret(sd); - if (n < 0 || n >= MAX_INVENTORY) { + if (n < 0 || n >= sd->status.inventorySize) { clif->unequipitemack(sd, 0, 0, UIA_FAIL); return 0; } - // if player is berserk then cannot unequip - if (!(flag & PCUNEQUIPITEM_FORCE) && sd->sc.count && (sd->sc.data[SC_BERSERK] || sd->sc.data[SC_NO_SWITCH_EQUIP])) { + // If the character is in berserk mode, the item can't be unequipped. + if (sd->sc.count != 0 && (sd->sc.data[SC_BERSERK] != NULL || sd->sc.data[SC_NO_SWITCH_EQUIP] != NULL) + && (flag & PCUNEQUIPITEM_FORCE) == 0) { clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - if (!(flag & PCUNEQUIPITEM_FORCE) && sd->sc.count && sd->sc.data[SC_KYOUGAKU]) { + if ((flag & PCUNEQUIPITEM_FORCE) == 0 && sd->sc.count != 0 && sd->sc.data[SC_KYOUGAKU] != NULL) { clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - if (battle_config.battle_log) + if (battle_config.battle_log != 0) ShowInfo("unequip %d %x:%x\n", n, (unsigned int)(pc->equippoint(sd, n)), sd->status.inventory[n].equip); - if (sd->status.inventory[n].equip == 0) { //Nothing to unequip + if (sd->status.inventory[n].equip == 0) { // Nothing to unequip. clif->unequipitemack(sd, n, 0, UIA_FAIL); return 0; } - for (i = 0; i < EQI_MAX; i++) { - if (sd->status.inventory[n].equip & pc->equip_pos[i]) + for (int i = 0; i < EQI_MAX; i++) { + if ((sd->status.inventory[n].equip & pc->equip_pos[i]) != 0) sd->equip_index[i] = -1; } - pos = sd->status.inventory[n].equip; - pc->unequipitem_pos(sd, n, pos); + int pos = sd->status.inventory[n].equip; + pc->unequipitem_pos(sd, n, pos); clif->unequipitemack(sd, n, pos, UIA_SUCCESS); - if ((pos & EQP_ARMS) && - sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST && (sd->sc.data[SC_TK_SEVENWIND] == NULL || sd->sc.data[SC_ASPERSIO] != NULL)) //Check for seven wind (but not level seven!) + if ((pos & EQP_ARMS) != 0 && sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST + && (sd->sc.data[SC_TK_SEVENWIND] == NULL || sd->sc.data[SC_ASPERSIO] != NULL)) { // Check for Seven Wind. (But not level seven!) skill->enchant_elemental_end(&sd->bl, -1); + } - if (pos & EQP_ARMOR) { - // On Armor Change... + if ((pos & EQP_ARMOR) != 0) { status_change_end(&sd->bl, SC_BENEDICTIO, INVALID_TIMER); status_change_end(&sd->bl, SC_ARMOR_RESIST, INVALID_TIMER); } #ifdef RENEWAL - if (battle->bc->bow_unequip_arrow && pos&EQP_ARMS && sd->equip_index[EQI_AMMO] > 0) + if (battle->bc->bow_unequip_arrow != 0 && (pos & EQP_ARMS) != 0 && sd->equip_index[EQI_AMMO] > 0) pc->unequipitem(sd, sd->equip_index[EQI_AMMO], PCUNEQUIPITEM_FORCE); #endif - if( sd->state.autobonus&pos ) - sd->state.autobonus &= ~sd->status.inventory[n].equip; //Check for activated autobonus [Inkfish] + if ((sd->state.autobonus & pos) != 0) // Check for activated autobonus. [Inkfish] + sd->state.autobonus &= ~sd->status.inventory[n].equip; sd->status.inventory[n].equip = 0; - iflag = sd->npc_item_flag; - /* check for combos (MUST be before status_calc_pc) */ + bool status_calc = false; + int iflag = sd->npc_item_flag; + + // Check for combos. (MUST be done before status->calc_pc()!) if (sd->inventory_data[n] != NULL) { - if (sd->inventory_data[n]->combos_count) { - if (pc->removecombo(sd, sd->inventory_data[n])) - status_calc = true; - } - if (itemdb_isspecial(sd->status.inventory[n].card[0]) == false) { - for (i = 0; i < sd->inventory_data[n]->slot; i++) { - struct item_data *data; + if (sd->inventory_data[n]->combos_count != 0 && pc->removecombo(sd, sd->inventory_data[n]) != 0) + status_calc = true; + + if (!itemdb_isspecial(sd->status.inventory[n].card[0])) { + for (int i = 0; i < sd->inventory_data[n]->slot; i++) { if (sd->status.inventory[n].card[i] == 0) continue; - if ((data = itemdb->exists(sd->status.inventory[n].card[i])) != NULL) { - if (data->combos_count) { - if (pc->removecombo(sd, data)) - status_calc = true; - } - } + + struct item_data *data = itemdb->exists(sd->status.inventory[n].card[i]); + + if (data != NULL && data->combos_count != 0 && pc->removecombo(sd, data) != 0) + status_calc = true; } } - /* Item Options checking */ - for (i = 0; i < MAX_ITEM_OPTIONS; i++) { - struct itemdb_option *ito = NULL; - int16 item_option = sd->status.inventory[n].option[i].index; - if (item_option <= 0) + // Check item options. + for (int i = 0; i < MAX_ITEM_OPTIONS; i++) { + if (sd->status.inventory[n].option[i].index <= 0) continue; - if ((ito = itemdb->option_exists(sd->status.inventory[n].option[i].index)) == NULL) + + if (itemdb->option_exists(sd->status.inventory[n].option[i].index) == NULL) continue; status_calc = true; } } - if (flag & PCUNEQUIPITEM_RECALC || status_calc) { + if ((flag & PCUNEQUIPITEM_RECALC) != 0 || status_calc) { pc->checkallowskill(sd); status_calc_pc(sd, SCO_NONE); } - if (sd->sc.data[SC_CRUCIS] && battle->check_undead(sd->battle_status.race, sd->battle_status.def_ele) == false) + if (sd->sc.data[SC_CRUCIS] != NULL && !battle->check_undead(sd->battle_status.race, sd->battle_status.def_ele)) status_change_end(&sd->bl, SC_CRUCIS, INVALID_TIMER); - //OnUnEquip script [Skotlex] + // Execute unequip script. [Skotlex] if (sd->inventory_data[n] != NULL) { - if (sd->inventory_data[n]->unequip_script != NULL) { - if (battle_config.unequip_restricted_equipment & 1) { - ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, i, map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].nameid); - if (i == map->list[sd->bl.m].zone->disabled_items_count) - script->run_item_unequip_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); - } - else - script->run_item_unequip_script(sd, sd->inventory_data[n], npc->fake_nd->bl.id); + struct item_data *equip_data = sd->inventory_data[n]; + struct map_zone_data *zone = map->list[sd->bl.m].zone; + int dis_items_cnt = zone->disabled_items_count; + + if (equip_data->unequip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == equip_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_unequip_script(sd, equip_data, npc->fake_nd->bl.id); } - if (itemdb_isspecial(sd->status.inventory[n].card[0]) == false) { - for (i = 0; i < sd->inventory_data[n]->slot; i++) { - struct item_data *data = NULL; - if (sd->status.inventory[n].card[i] == 0) + + struct item *equip = &sd->status.inventory[n]; + + if (!itemdb_isspecial(equip->card[0])) { + for (int slot = 0; slot < equip_data->slot; slot++) { + if (equip->card[slot] == 0) continue; - if ((data = itemdb->exists(sd->status.inventory[n].card[i])) != NULL) { - if (data->unequip_script) { - if (battle_config.unequip_restricted_equipment & 2) { - int j; - ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, j, map->list[sd->bl.m].zone->disabled_items[j] == sd->status.inventory[n].card[i]); - if (j == map->list[sd->bl.m].zone->disabled_items_count) - script->run_item_unequip_script(sd, data, npc->fake_nd->bl.id); - } else { - script->run_item_unequip_script(sd, data, npc->fake_nd->bl.id); - } - } - } + struct item_data *card_data = itemdb->exists(equip->card[slot]); + if (card_data != NULL && card_data->unequip_script != NULL) { + int idx; + + ARR_FIND(0, dis_items_cnt, idx, zone->disabled_items[idx] == card_data->nameid); + + if (idx == dis_items_cnt) + script->run_item_unequip_script(sd, card_data, npc->fake_nd->bl.id); + } } } } @@ -10505,7 +10718,7 @@ static int pc_checkitem(struct map_session_data *sd) int id = 0; if (sd->itemcheck & PCCHECKITEM_INVENTORY) { - for (i = 0; i < MAX_INVENTORY; i++) { + for (i = 0; i < sd->status.inventorySize; i++) { if ((id = sd->status.inventory[i].nameid) == 0) continue; @@ -10585,7 +10798,7 @@ static int pc_checkitem(struct map_session_data *sd) } } - for (i = 0; i < MAX_INVENTORY; i++) { + for (i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].nameid == 0) continue; @@ -10759,11 +10972,14 @@ static int pc_divorce(struct map_session_data *sd) // Both players online, lets do the divorce manually sd->status.partner_id = 0; p_sd->status.partner_id = 0; - for( i = 0; i < MAX_INVENTORY; i++ ) + for (i = 0; i < sd->status.inventorySize; i++) { - if( sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F ) + if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); - if( p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F ) + } + for (i = 0; i < p_sd->status.inventorySize; i++) + { + if (p_sd->status.inventory[i].nameid == WEDDING_RING_M || p_sd->status.inventory[i].nameid == WEDDING_RING_F) pc->delitem(p_sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_DIVORCE); } @@ -10946,7 +11162,7 @@ static int pc_daynight_timer_sub(struct map_session_data *sd, va_list ap) { nullpo_ret(sd); if (sd->state.night != map->night_flag && map->list[sd->bl.m].flag.nightenabled) { //Night/day state does not match. - clif->status_change(&sd->bl, SI_SKE, map->night_flag, 0, 0, 0, 0); //New night effect by dynamix [Skotlex] + clif->status_change(&sd->bl, status->get_sc_icon(SC_SKE), status->get_sc_relevant_bl_types(SC_SKE), map->night_flag, 0, 0, 0, 0); //New night effect by dynamix [Skotlex] sd->state.night = map->night_flag; return 1; } @@ -10969,7 +11185,7 @@ static int map_day_timer(int tid, int64 tick, int id, intptr_t data) map->night_flag = 0; // 0=day, 1=night [Yor] map->foreachpc(pc->daynight_timer_sub); safestrncpy(tmp_soutput, (data == 0) ? msg_txt(502) : msg_txt(60), sizeof(tmp_soutput)); // The day has arrived! - intif->broadcast(tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT); + clif->broadcast(NULL, tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT, ALL_CLIENT); return 0; } @@ -10990,7 +11206,7 @@ static int map_night_timer(int tid, int64 tick, int id, intptr_t data) map->night_flag = 1; // 0=day, 1=night [Yor] map->foreachpc(pc->daynight_timer_sub); safestrncpy(tmp_soutput, (data == 0) ? msg_txt(503) : msg_txt(59), sizeof(tmp_soutput)); // The night has fallen... - intif->broadcast(tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT); + clif->broadcast(NULL, tmp_soutput, (int)strlen(tmp_soutput) + 1, BC_DEFAULT, ALL_CLIENT); return 0; } @@ -10999,7 +11215,7 @@ static void pc_setstand(struct map_session_data *sd) nullpo_retv(sd); status_change_end(&sd->bl, SC_TENSIONRELAX, INVALID_TIMER); - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT)); //Reset sitting tick. sd->ssregen.tick.hp = sd->ssregen.tick.sp = 0; if (pc_isdead(sd)) { @@ -11112,7 +11328,7 @@ static int pc_charm_timer(int tid, int64 tick, int id, intptr_t data) * @param max Maximum amount of charms to add. * @param type Charm type (@see spirit_charm_types) */ -static void pc_add_charm(struct map_session_data *sd, int interval, int max, int type) +static void pc_add_charm(struct map_session_data *sd, int interval, int max, enum spirit_charm_types type) { int tid, i; @@ -11154,7 +11370,7 @@ static void pc_add_charm(struct map_session_data *sd, int interval, int max, int * @param count Amount of charms to remove. * @param type Type of charm to remove. */ -static void pc_del_charm(struct map_session_data *sd, int count, int type) +static void pc_del_charm(struct map_session_data *sd, int count, enum spirit_charm_types type) { int i; @@ -11566,12 +11782,9 @@ static bool pc_read_exp_db(void) struct config_t exp_db_conf; struct config_setting_t *edb = NULL; int entry_count = 0; - -#ifdef RENEWAL - const char *config_filename = "db/re/exp_group_db.conf"; -#else - const char *config_filename = "db/pre-re/exp_group_db.conf"; -#endif + char config_filename[256]; + + libconfig->format_db_path(DBPATH"exp_group_db.conf", config_filename, sizeof(config_filename)); if (!libconfig->load_file(&exp_db_conf, config_filename)) return false; @@ -12194,6 +12407,29 @@ static int pc_have_magnifier(struct map_session_data *sd) } /** + * checks if player have any item that listed in item chain + * @param sd map_session_data of Player + * @param chain_cache_id cache id of item chain + * @return index of inventory, INDEX_NOT_FOUND if it is not found + */ +static int pc_have_item_chain(struct map_session_data *sd, enum e_chain_cache chain_cache_id) +{ + nullpo_retr(INDEX_NOT_FOUND, sd); + Assert_retr(INDEX_NOT_FOUND, chain_cache_id >= ECC_ORE && chain_cache_id < ECC_MAX); + + int chain_id = itemdb->chain_cache[chain_cache_id]; + + for (int n = 0; n < itemdb->chains[chain_id].qty; n++) { + struct item_chain_entry *entry = &itemdb->chains[chain_id].items[n]; + int index = pc->search_inventory(sd, entry->id); + if (index != INDEX_NOT_FOUND) + return index; + } + + return INDEX_NOT_FOUND; +} + +/** * Checks if player have basic skills learned. * @param sd Player Data * @param level Required Level of Novice Skill @@ -12290,6 +12526,54 @@ static void pc_check_supernovice_call(struct map_session_data *sd, const char *m } } +/** + * Sends a message t all online GMs having the specified permission. + * + * @param sender_name Sender character name. + * @param permission The required permission to receive this message. + * @param message The message body. + * + * @return The amount of characters the message was delivered to. + */ +// The transmission of GM only Wisp/Page from server to inter-server +static int pc_wis_message_to_gm(const char *sender_name, int permission, const char *message) +{ + nullpo_ret(sender_name); + nullpo_ret(message); + int mes_len = (int)strlen(message) + 1; // + null + int count = 0; + + // information is sent to all online GM + map->foreachpc(pc->wis_message_to_gm_sub, permission, sender_name, message, mes_len, &count); + + return count; +} + +/** + * Helper function for pc_wis_message_to_gm(). + */ +static int pc_wis_message_to_gm_sub(struct map_session_data *sd, va_list va) +{ + nullpo_ret(sd); + + int permission = va_arg(va, int); + if (!pc_has_permission(sd, permission)) + return 0; + + const char *sender_name = va_arg(va, const char *); + const char *message = va_arg(va, const char *); + int len = va_arg(va, int); + int *count = va_arg(va, int *); + + nullpo_ret(sender_name); + nullpo_ret(message); + nullpo_ret(count); + + clif->wis_message(sd->fd, sender_name, message, len); + ++*count; + return 1; +} + static void pc_update_job_and_level(struct map_session_data *sd) { nullpo_retv(sd); @@ -12332,11 +12616,44 @@ static bool pc_has_second_costume(struct map_session_data *sd) { nullpo_retr(false, sd); - if ((sd->job & JOBL_THIRD) != 0) +// FIXME: JOB_SUPER_NOVICE_E(4190) is not supposed to be 3rd Job. (Issue#2383) + if ((sd->job & JOBL_THIRD) != 0 && (sd->job & MAPID_BASEMASK) != MAPID_NOVICE) return true; return false; } +static bool pc_expandInventory(struct map_session_data *sd, int adjustSize) +{ + nullpo_retr(false, sd); + const int invSize = sd->status.inventorySize; + if (adjustSize > MAX_INVENTORY || invSize + adjustSize <= FIXED_INVENTORY_SIZE || invSize + adjustSize > MAX_INVENTORY) { + clif->inventoryExpandResult(sd, EXPAND_INVENTORY_RESULT_MAX_SIZE); + return false; + } + 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; + } + sd->status.inventorySize += adjustSize; + clif->inventoryExpansionInfo(sd); + return true; +} + +static bool pc_auto_exp_insurance(struct map_session_data *sd) +{ + nullpo_retr(false, sd); + + int item_position = pc->have_item_chain(sd, ECC_NEO_INSURANCE); + if (item_position == INDEX_NOT_FOUND) + return false; + + pc->delitem(sd, item_position, 1, 0, DELITEM_SKILLUSE, LOG_TYPE_CONSUME); +#if PACKETVER >= 20100914 + clif->msgtable(sd, MSG_NOTIFY_NEO_INSURANCE_ITEM_USE); +#endif + return true; +} + static void do_final_pc(void) { @@ -12566,6 +12883,7 @@ void pc_defaults(void) pc->unequipitem_pos = pc_unequipitem_pos; pc->checkitem = pc_checkitem; pc->useitem = pc_useitem; + pc->autocast_clear = pc_autocast_clear; pc->skillatk_bonus = pc_skillatk_bonus; pc->skillheal_bonus = pc_skillheal_bonus; @@ -12578,6 +12896,8 @@ void pc_defaults(void) pc->itemheal = pc_itemheal; pc->percentheal = pc_percentheal; pc->jobchange = pc_jobchange; + pc->hide = pc_hide; + pc->unhide = pc_unhide; pc->setoption = pc_setoption; pc->setcart = pc_setcart; pc->setfalcon = pc_setfalcon; @@ -12719,6 +13039,8 @@ void pc_defaults(void) pc->check_supernovice_call = pc_check_supernovice_call; pc->process_chat_message = pc_process_chat_message; + pc->wis_message_to_gm = pc_wis_message_to_gm; + pc->wis_message_to_gm_sub = pc_wis_message_to_gm_sub; /** * Autotrade persistency [Ind/Hercules <3] @@ -12734,9 +13056,12 @@ void pc_defaults(void) pc->update_idle_time = pc_update_idle_time; pc->have_magnifier = pc_have_magnifier; + pc->have_item_chain = pc_have_item_chain; pc->check_basicskill = pc_check_basicskill; pc->isDeathPenaltyJob = pc_isDeathPenaltyJob; pc->has_second_costume = pc_has_second_costume; + pc->expandInventory = pc_expandInventory; + pc->auto_exp_insurance = pc_auto_exp_insurance; } diff --git a/src/map/pc.h b/src/map/pc.h index 8302279b1..e560df549 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -117,22 +117,22 @@ BEGIN_ZEROED_BLOCK; // all the variables within this block get zero'ed in each c int addrace2[RC2_MAX]; int addsize[3]; struct drain_data { - short rate; - short per; - short value; + int rate; + int per; + int value; unsigned type:1; } hp_drain[RC_MAX], sp_drain[RC_MAX]; struct { - short class_, rate; + int class_, rate; } add_dmg[MAX_PC_BONUS]; struct { - short flag, rate; + int flag, rate; unsigned char ele; } addele2[MAX_PC_BONUS]; END_ZEROED_BLOCK; }; struct s_autospell { - short id, lv, rate, flag; + int id, lv, rate, flag; int card_id; bool lock; // bAutoSpellOnSkill: blocks autospell from triggering again, while being executed }; @@ -147,20 +147,20 @@ struct s_addeffect { }; struct s_addeffectonskill { enum sc_type id; - short rate, skill; + int rate, skill; unsigned char target; }; struct s_add_drop { + bool is_group; int id; - short group; int race, rate; }; struct s_autobonus { - short rate,atk_type; + int rate,atk_type; unsigned int duration; char *bonus_script, *other_script; int active; - unsigned short pos; + unsigned int pos; }; enum npc_timeout_type { NPCT_INPUT = 0, @@ -173,6 +173,17 @@ struct pc_combos { int id; /* this combo id */ }; +/** Auto-cast related data. **/ +struct autocast_data { + enum autocast_type type; // The auto-cast type. + int skill_id; // The auto-cast skill ID. + int skill_lv; // The auto-cast skill level. + bool itemskill_conditions_checked; // Used by itemskill() script command, to prevent second check of conditions after target was selected. + bool itemskill_check_conditions; // Used by itemskill() script command, to check skill conditions and consume them. + bool itemskill_instant_cast; // Used by itemskill() script command, to cast skill instantaneously. + bool itemskill_cast_on_self; // Used by itemskill() script command, to forcefully cast skill on invoking character. +}; + struct map_session_data { struct block_list bl; struct unit_data ud; @@ -181,6 +192,7 @@ struct map_session_data { struct status_change sc; struct regen_data regen; struct regen_data_sub sregen, ssregen; + struct autocast_data autocast; //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in //status_calc_pc, while special_state is recalculated in each call. [Skotlex] struct { @@ -194,8 +206,6 @@ struct map_session_data { unsigned int rest : 1; unsigned int storage_flag : 2; // @see enum storage_flag unsigned int snovice_dead_flag : 1; //Explosion spirits on death: 0 off, 1 used. - unsigned int abra_flag : 2; // Abracadabra bugfix by Aru - unsigned int autocast : 1; // Autospell flag [Inkfish] unsigned int autotrade : 2; //By Fantik unsigned int showdelay :1; unsigned int showexp :1; @@ -219,11 +229,11 @@ struct map_session_data { unsigned int changemap : 1; unsigned int callshop : 1; // flag to indicate that a script used callshop; on a shop short pmap; // Previous map on Map Change - unsigned short autoloot; + unsigned int autoloot; int autolootid[AUTOLOOTITEM_SIZE]; // [Zephyrus] - unsigned short autoloottype; + unsigned int autoloottype; unsigned int autolooting : 1; //performance-saver, autolooting state for @alootid - unsigned short autobonus; //flag to indicate if an autobonus is activated. [Inkfish] + unsigned int autobonus; //flag to indicate if an autobonus is activated. [Inkfish] unsigned int gmaster_flag : 1; unsigned int prevend : 1;//used to flag wheather you've spent 40sp to open the vending or not. unsigned int warping : 1;//states whether you're in the middle of a warp processing @@ -237,6 +247,9 @@ 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; + unsigned int npc_unloaded : 1; ///< The player is talking with an unloaded NPCs (respawned tombstones) + unsigned int lapine_ui : 1; } state; struct { unsigned char no_weapon_damage, no_magic_damage, no_misc_damage; @@ -273,6 +286,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] @@ -288,7 +304,6 @@ struct map_session_data { int followtimer; // [MouseJstr] int followtarget; time_t emotionlasttime; // to limit flood with emotion packets - short skillitem,skillitemlv; uint16 skill_id_old,skill_lv_old; uint16 skill_id_dance,skill_lv_dance; short cook_mastery; // range: [0,1999] [Inkfish] @@ -312,7 +327,7 @@ struct map_session_data { int16 weapontype; ///< Weapon type considering both hands (@see enum weapon_type). int16 weapontype1; ///< Weapon type in the right/primary hand (@see enum weapon_type). int16 weapontype2; ///< Weapon type in the left/secondary hand (@see enum weapon_type). - short disguise; // [Valaris] + int disguise; // [Valaris] struct weapon_data right_weapon, left_weapon; BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of status_calc_pc() @@ -337,9 +352,9 @@ BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of st int expaddrace[RC_MAX]; int ignore_mdef[RC_MAX]; int ignore_def[RC_MAX]; - short sp_gain_race[RC_MAX]; - short sp_gain_race_attack[RC_MAX]; - short hp_gain_race_attack[RC_MAX]; + int sp_gain_race[RC_MAX]; + int sp_gain_race_attack[RC_MAX]; + int hp_gain_race_attack[RC_MAX]; #ifdef RENEWAL int race_tolerance[RC_MAX]; #endif @@ -347,16 +362,16 @@ BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of st struct s_addeffect addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS]; struct s_addeffectonskill addeff3[MAX_PC_BONUS]; struct { //skillatk raises bonus dmg% of skills, skillheal increases heal%, skillblown increases bonus blewcount for some skills. - unsigned short id; - short val; + unsigned int id; + int val; } skillatk[MAX_PC_BONUS], skillusesprate[MAX_PC_BONUS], skillusesp[MAX_PC_BONUS], skillheal[5], skillheal2[5], skillblown[MAX_PC_BONUS], skillcast[MAX_PC_BONUS], skillcooldown[MAX_PC_BONUS], skillfixcast[MAX_PC_BONUS], skillvarcast[MAX_PC_BONUS], skillfixcastrate[MAX_PC_BONUS]; struct { - short value; + int value; int rate; int tick; } hp_loss, sp_loss, hp_regen, sp_regen; struct { - short class_, rate; + int class_, rate; } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS]; struct s_add_drop add_drop[MAX_PC_BONUS]; struct { @@ -364,11 +379,11 @@ BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of st int rate; } itemhealrate[MAX_PC_BONUS]; struct { - short flag, rate; + int flag, rate; unsigned char ele; } subele2[MAX_PC_BONUS]; struct { - short value; + int value; int rate, tick; } def_set_race[RC_MAX], mdef_set_race[RC_MAX]; struct { @@ -394,17 +409,17 @@ BEGIN_ZEROED_BLOCK; // this block will be globally zeroed at the beginning of st int itemhealrate2; // [Epoque] Increase heal rate of all healing items. int shieldmdef;//royal guard's unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex] - short splash_range, splash_add_range; - short add_steal_rate; - short add_heal_rate, add_heal2_rate; - short sp_gain_value, hp_gain_value, magic_sp_gain_value, magic_hp_gain_value; - short hp_vanish_rate; - short hp_vanish_per, hp_vanish_trigger; - short sp_vanish_rate; - short sp_vanish_per, sp_vanish_trigger; - unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest] - unsigned short unbreakable_equip; //100% break resistance on certain equipment - unsigned short unstripable_equip; + int splash_range, splash_add_range; + int add_steal_rate; + int add_heal_rate, add_heal2_rate; + int sp_gain_value, hp_gain_value, magic_sp_gain_value, magic_hp_gain_value; + int hp_vanish_rate; + int hp_vanish_per, hp_vanish_trigger; + int sp_vanish_rate; + int sp_vanish_per, sp_vanish_trigger; + unsigned int unbreakable; // chance to prevent ANY equipment breaking [celest] + unsigned int unbreakable_equip; //100% break resistance on certain equipment + unsigned int unstripable_equip; int fixcastrate,varcastrate; int add_fixcast,add_varcast; int ematk; // matk bonus from equipment @@ -419,22 +434,22 @@ END_ZEROED_BLOCK; int matk_rate; int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate; int itemid; - short itemindex; //Used item's index in sd->inventory [Skotlex] - short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo] - short spiritball, spiritball_old; + int itemindex; //Used item's index in sd->inventory [Skotlex] + int catch_target_class; + int spiritball, spiritball_old; int spirit_timer[MAX_SPIRITBALL]; - short charm_count; - int charm_type; + int charm_count; + enum spirit_charm_types charm_type; int charm_timer[MAX_SPIRITCHARM]; unsigned char potion_success_counter; //Potion successes in row counter unsigned char mission_count; //Stores the bounty kill count for TK_MISSION - short mission_mobid; //Stores the target mob_id for TK_MISSION + int mission_mobid; //Stores the target mob_id for TK_MISSION int die_counter; //Total number of times you've died int devotion[MAX_PC_DEVOTION]; //Stores the account IDs of chars devoted to. int trade_partner; struct { struct { - short index, amount; + int index, amount; } item[10]; int zeny, weight; } deal; @@ -465,23 +480,24 @@ END_ZEROED_BLOCK; struct { int m; //-1 - none, other: map index corresponding to map name. - unsigned short index; //map index + unsigned int index; //map index } feel_map[MAX_PC_FEELHATE];// 0 - Sun; 1 - Moon; 2 - Stars - short hate_mob[MAX_PC_FEELHATE]; + int hate_mob[MAX_PC_FEELHATE]; int pvp_timer; - short pvp_point; - unsigned short pvp_rank, pvp_lastusers; - unsigned short pvp_won, pvp_lost; + int pvp_point; + unsigned int pvp_rank, pvp_lastusers; + unsigned int pvp_won, pvp_lost; char eventqueue[MAX_EVENTQUEUE][EVENT_NAME_LENGTH]; int eventtimer[MAX_EVENTTIMER]; - unsigned short eventcount; // [celest] + unsigned int eventcount; // [celest] int change_level_2nd; // job level when changing from 1st to 2nd class [jobchange_level in global_reg_value] int change_level_3rd; // job level when changing from 2nd to 3rd class [jobchange_level_3rd in global_reg_value] char fakename[NAME_LENGTH]; // fake names [Valaris] + int fakename_options; // Fake name display options. int duel_group; // duel vars [LuzZza] int duel_invite; @@ -629,6 +645,7 @@ END_ZEROED_BLOCK; unsigned immune : 1; unsigned sitstand : 1; unsigned commands : 1; + unsigned npc : 1; } block_action; /* Achievement System */ @@ -660,17 +677,19 @@ 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 || (sd)->state.lapine_ui == 1) +#define pc_cant_act_except_lapine(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 || (sd)->state.lapine_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) ) #define pc_ishiding(sd) ( (sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) ) #define pc_iscloaking(sd) ( !((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK) ) #define pc_ischasewalk(sd) ( (sd)->sc.option&OPTION_CHASEWALK ) -#define pc_ismuted(sc,type) ( (sc)->data[SC_NOCHAT] && (sc)->data[SC_NOCHAT]->val1&(type) ) +#define pc_ismuted(sc, type) ( (sc)->data[SC_NOCHAT] != NULL && (battle_config.manner_system & (type)) != 0 ) +#define pc_isvending(sd) ((sd)->state.vending || (sd)->state.prevend || (sd)->state.buyingstore) #ifdef NEW_CARTS #define pc_iscarton(sd) ( (sd)->sc.data[SC_PUSH_CART] ) @@ -769,24 +788,24 @@ END_ZEROED_BLOCK; #define pc_can_give_bound_items(sd) ( pc_has_permission((sd),PC_PERM_TRADE_BOUND) ) struct skill_tree_requirement { - short id; + int id; unsigned short idx; unsigned char lv; }; struct skill_tree_entry { - short id; + int id; unsigned short idx; unsigned char max; unsigned char joblv; - short inherited; + int inherited; VECTOR_DECL(struct skill_tree_requirement) need; }; // Celest struct sg_data { - short anger_id; - short bless_id; - short comfort_id; + int anger_id; + int bless_id; + int comfort_id; char feel_var[NAME_LENGTH]; char hate_var[NAME_LENGTH]; bool (*day_func)(void); @@ -905,7 +924,7 @@ END_ZEROED_BLOCK; /* End */ int (*setrestartvalue) (struct map_session_data *sd,int type); int (*makesavestatus) (struct map_session_data *sd); - void (*respawn) (struct map_session_data* sd, clr_type clrtype); + void (*respawn) (struct map_session_data* sd, enum clr_type clrtype); int (*setnewpc) (struct map_session_data *sd, int account_id, int char_id, int login_id1, unsigned int client_tick, int sex, int fd); bool (*authok) (struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, const struct mmo_charstatus *st, bool changing_mapservers); void (*authfail) (struct map_session_data *sd); @@ -927,9 +946,9 @@ END_ZEROED_BLOCK; /* End */ int (*calc_skilltree_normalize_job) (struct map_session_data *sd); int (*clean_skilltree) (struct map_session_data *sd); - int (*setpos) (struct map_session_data* sd, unsigned short map_index, int x, int y, clr_type clrtype); + int (*setpos) (struct map_session_data* sd, unsigned short map_index, int x, int y, enum clr_type clrtype); int (*setsavepoint) (struct map_session_data *sd, short map_index, int x, int y); - int (*randomwarp) (struct map_session_data *sd,clr_type type); + int (*randomwarp) (struct map_session_data *sd, enum clr_type type); int (*memo) (struct map_session_data* sd, int pos); int (*checkadditem) (struct map_session_data *sd,int nameid,int amount); @@ -959,7 +978,7 @@ END_ZEROED_BLOCK; /* End */ int (*updateweightstatus) (struct map_session_data *sd); - int (*addautobonus) (struct s_autobonus *bonus,char max,const char *bonus_script,short rate,unsigned int dur,short atk_type,const char *o_script,unsigned short pos,bool onskill); + int (*addautobonus) (struct s_autobonus *bonus,char max,const char *bonus_script,short rate,unsigned int dur,short atk_type,const char *o_script,unsigned int pos,bool onskill); int (*exeautobonus) (struct map_session_data* sd,struct s_autobonus *bonus); int (*endautobonus) (int tid, int64 tick, int id, intptr_t data); int (*delautobonus) (struct map_session_data* sd,struct s_autobonus *bonus,char max,bool restore); @@ -1013,6 +1032,7 @@ END_ZEROED_BLOCK; /* End */ void (*unequipitem_pos) (struct map_session_data *sd, int n, int pos); int (*checkitem) (struct map_session_data *sd); int (*useitem) (struct map_session_data *sd,int n); + int (*autocast_clear) (struct map_session_data *sd); int (*skillatk_bonus) (struct map_session_data *sd, uint16 skill_id); int (*skillheal_bonus) (struct map_session_data *sd, uint16 skill_id); @@ -1025,11 +1045,13 @@ END_ZEROED_BLOCK; /* End */ int (*itemheal) (struct map_session_data *sd,int itemid, int hp,int sp); int (*percentheal) (struct map_session_data *sd,int hp,int sp); int (*jobchange) (struct map_session_data *sd, int class, int upper); + void (*hide) (struct map_session_data *sd, bool show_msg); + void (*unhide) (struct map_session_data *sd, bool show_msg); int (*setoption) (struct map_session_data *sd,int type); int (*setcart) (struct map_session_data* sd, int type); void (*setfalcon) (struct map_session_data *sd, bool flag); void (*setridingpeco) (struct map_session_data *sd, bool flag); - void (*setmadogear) (struct map_session_data *sd, bool flag); + void (*setmadogear) (struct map_session_data *sd, bool flag, enum mado_type mtype); void (*setridingdragon) (struct map_session_data *sd, unsigned int type); void (*setridingwug) (struct map_session_data *sd, bool flag); int (*changelook) (struct map_session_data *sd,int type,int val); @@ -1068,8 +1090,8 @@ END_ZEROED_BLOCK; /* End */ void (*setstand) (struct map_session_data *sd); int (*candrop) (struct map_session_data *sd,struct item *item); - int (*jobid2mapid) (int16 class); // Skotlex - int (*mapid2jobid) (unsigned short class_, int sex); // Skotlex + int (*jobid2mapid) (int class); // Skotlex + int (*mapid2jobid) (unsigned int class_, int sex); // Skotlex const char * (*job_name) (int class); @@ -1108,8 +1130,8 @@ END_ZEROED_BLOCK; /* End */ int (*load_combo) (struct map_session_data *sd); - void (*add_charm) (struct map_session_data *sd, int interval, int max, int type); - void (*del_charm) (struct map_session_data *sd, int count, int type); + void (*add_charm) (struct map_session_data *sd, int interval, int max, enum spirit_charm_types type); + void (*del_charm) (struct map_session_data *sd, int count, enum spirit_charm_types type); void (*baselevelchanged) (struct map_session_data *sd); int (*level_penalty_mod) (int diff, unsigned char race, uint32 mode, int type); @@ -1124,7 +1146,7 @@ END_ZEROED_BLOCK; /* End */ int (*bonus_autospell_onskill) (struct s_autospell *spell, int max, short src_skill, short id, short lv, short rate, int card_id); int (*bonus_addeff) (struct s_addeffect* effect, int max, enum sc_type id, int16 rate, int16 arrow_rate, uint8 flag, uint16 duration); int (*bonus_addeff_onskill) (struct s_addeffectonskill* effect, int max, enum sc_type id, short rate, short skill_id, unsigned char target); - int (*bonus_item_drop) (struct s_add_drop *drop, const short max, short id, short group, int race, int rate); + int (*bonus_item_drop) (struct s_add_drop *drop, const short max, int id, bool is_group, int race, int rate); void (*calcexp) (struct map_session_data *sd, uint64 *base_exp, uint64 *job_exp, struct block_list *src); int (*respawn_timer) (int tid, int64 tick, int id, intptr_t data); int (*jobchange_killclone) (struct block_list *bl, va_list ap); @@ -1179,12 +1201,17 @@ END_ZEROED_BLOCK; /* End */ void (*update_idle_time) (struct map_session_data* sd, enum e_battle_config_idletime type); int (*have_magnifier) (struct map_session_data *sd); + int (*have_item_chain) (struct map_session_data *sd, enum e_chain_cache chain_cache_id); bool (*process_chat_message) (struct map_session_data *sd, const char *message); + int (*wis_message_to_gm) (const char *sender_name, int permission, const char *message); + int (*wis_message_to_gm_sub) (struct map_session_data *sd, va_list va); void (*check_supernovice_call) (struct map_session_data *sd, const char *message); bool (*check_basicskill) (struct map_session_data *sd, int level); bool (*isDeathPenaltyJob) (uint16 job); bool (*has_second_costume) (struct map_session_data *sd); + bool (*expandInventory) (struct map_session_data *sd, int adjustSize); + bool (*auto_exp_insurance) (struct map_session_data *sd); }; #ifdef HERCULES_CORE diff --git a/src/map/pc_groups.c b/src/map/pc_groups.c index 887c946e3..9149aa6e4 100644 --- a/src/map/pc_groups.c +++ b/src/map/pc_groups.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -449,6 +449,7 @@ static void do_init_pc_groups(void) { "disable_store", PC_PERM_DISABLE_STORE }, { "disable_exp", PC_PERM_DISABLE_EXP }, { "disable_skill_usage", PC_PERM_DISABLE_SKILL_USAGE }, + { "bypass_nostorage", PC_PERM_BYPASS_NOSTORAGE }, }; unsigned char i, len = ARRAYLENGTH(pc_g_defaults); diff --git a/src/map/pc_groups.h b/src/map/pc_groups.h index 6070809e0..0a8cfce86 100644 --- a/src/map/pc_groups.h +++ b/src/map/pc_groups.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,6 +57,7 @@ enum e_pc_permission { PC_PERM_DISABLE_STORE = 0x1000000, PC_PERM_DISABLE_EXP = 0x2000000, PC_PERM_DISABLE_SKILL_USAGE = 0x4000000, + PC_PERM_BYPASS_NOSTORAGE = 0x8000000, }; // Cached config settings for quick lookup diff --git a/src/map/pet.c b/src/map/pet.c index c32a92b78..299de42c7 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -60,62 +60,101 @@ struct pet_interface *pet; #define MIN_PETTHINKTIME 100 +/** + * Gets a pet's hunger value, depending it's hunger level. + * This value is only used in clif_parse_LoadEndAck() when calling clif_pet_emotion(). + * + * @param pd The pet. + * @return The pet's hunger value. + * + **/ static int pet_hungry_val(struct pet_data *pd) { nullpo_ret(pd); - if(pd->pet.hungry > 90) + if (pd->pet.hungry > PET_HUNGER_SATISFIED) return 4; - else if(pd->pet.hungry > 75) + else if (pd->pet.hungry > PET_HUNGER_NEUTRAL) return 3; - else if(pd->pet.hungry > 25) + else if (pd->pet.hungry > PET_HUNGER_HUNGRY) return 2; - else if(pd->pet.hungry > 10) + else if (pd->pet.hungry > PET_HUNGER_VERY_HUNGRY) return 1; else return 0; } -static void pet_set_intimate(struct pet_data *pd, int value) +/** + * Sets a pet's hunger value. + * + * @param pd The pet. + * @param value The pet's new hunger value. + * + **/ +static void pet_set_hunger(struct pet_data *pd, int value) { - int intimate; - struct map_session_data *sd; + nullpo_retv(pd); + pd->pet.hungry = cap_value(value, PET_HUNGER_STARVING, PET_HUNGER_STUFFED); +} + +/** + * Sets a pet's intimacy value. + * Deletes the pet if its intimacy value reaches PET_INTIMACY_NONE (0). + * + * @param pd The pet. + * @param value The pet's new intimacy value. + * + **/ +static void pet_set_intimate(struct pet_data *pd, int value) +{ nullpo_retv(pd); - intimate = pd->pet.intimate; - sd = pd->msd; + nullpo_retv(pd->msd); + + pd->pet.intimate = cap_value(value, PET_INTIMACY_NONE, PET_INTIMACY_MAX); - pd->pet.intimate = value; + struct map_session_data *sd = pd->msd; - if( (intimate >= battle_config.pet_equip_min_friendly && pd->pet.intimate < battle_config.pet_equip_min_friendly) || (intimate < battle_config.pet_equip_min_friendly && pd->pet.intimate >= battle_config.pet_equip_min_friendly) ) - status_calc_pc(sd,SCO_NONE); + status_calc_pc(sd, SCO_NONE); - /* Pet is lost, delete the egg */ - if (value <= 0) { + if (pd->pet.intimate == PET_INTIMACY_NONE) { /// Pet is lost. Delete the egg. int i; - ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET && - pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])); + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].card[0] == CARD0_PET + && pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], + sd->status.inventory[i].card[2])); - if (i != MAX_INVENTORY) { + if (i != sd->status.inventorySize) pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_EGG); - } } } +/** + * Creates a pet egg. + * + * @param sd The character who tries to create the pet egg. + * @param item_id The pet egg's item ID. + * @return 0 on failure, 1 on success. + * + **/ static int pet_create_egg(struct map_session_data *sd, int item_id) { - int pet_id = pet->search_petDB_index(item_id, PET_EGG); nullpo_ret(sd); - if (pet_id < 0) return 0; //No pet egg here. - if (!pc->inventoryblank(sd)) return 0; // Inventory full + + int pet_id = pet->search_petDB_index(item_id, PET_EGG); + + if (pet_id == INDEX_NOT_FOUND) // No pet egg here. + return 0; + + if (pc->inventoryblank(sd) == 0) // Inventory full. + return 0; + sd->catch_target_class = pet->db[pet_id].class_; - intif->create_pet(sd->status.account_id, sd->status.char_id, - (short)pet->db[pet_id].class_, - (short)mob->db(pet->db[pet_id].class_)->lv, - pet->db[pet_id].EggID, 0, - (short)pet->db[pet_id].intimate, - 100, 0, 1, pet->db[pet_id].jname); + intif->create_pet(sd->status.account_id, sd->status.char_id, pet->db[pet_id].class_, + mob->db(pet->db[pet_id].class_)->lv, pet->db[pet_id].EggID, 0, + (short)pet->db[pet_id].intimate, PET_HUNGER_STUFFED, + 0, 1, pet->db[pet_id].jname); + return 1; } @@ -162,48 +201,42 @@ static int pet_attackskill(struct pet_data *pd, int target_id) return 0; } +/** + * Checks if a pet can attack a target. + * + * @param sd The pet's master. + * @param bl The pet's target. + * @param type 0 - Support master when attacking. Not 0 - Support master when being attacked. + * @return 0 on failure, 1 on success. + * + **/ static int pet_target_check(struct map_session_data *sd, struct block_list *bl, int type) { - struct pet_data *pd; - int rate; - nullpo_ret(sd); - pd = sd->pd; + nullpo_ret(sd->pd); + nullpo_ret(bl); + Assert_ret(sd->pd->msd == NULL || sd->pd->msd->pd == sd->pd); - Assert_ret(pd->msd == 0 || pd->msd->pd == pd); + struct pet_data *pd = sd->pd; - if( bl == NULL || bl->type != BL_MOB || bl->prev == NULL - || pd->pet.intimate < battle_config.pet_support_min_friendly - || pd->pet.hungry < 1 - || pd->pet.class_ == status->get_class(bl)) - return 0; + if ((type == 0 && pd->petDB->attack_rate == 0) || (type != 0 && pd->petDB->defence_attack_rate == 0)) + return 0; // If base rate is 0, there's nothing to do. - if( pd->bl.m != bl->m - || !check_distance_bl(&pd->bl, bl, pd->db->range2)) + if (bl->type != BL_MOB || bl->prev == NULL || pd->pet.intimate < battle_config.pet_support_min_friendly + || pd->pet.hungry <= PET_HUNGER_STARVING || pd->pet.class_ == status->get_class(bl) + || pd->bl.m != bl->m || !check_distance_bl(&pd->bl, bl, pd->db->range2) + || status->check_skilluse(&pd->bl, bl, 0, 0) == 0) { return 0; + } - if (!status->check_skilluse(&pd->bl, bl, 0, 0)) - return 0; + int rate = ((type == 0) ? pd->petDB->attack_rate : pd->petDB->defence_attack_rate) * pd->rate_fix / 1000; - if(!type) { - rate = pd->petDB->attack_rate; - rate = rate * pd->rate_fix/1000; - if(pd->petDB->attack_rate > 0 && rate <= 0) - rate = 1; - } else { - rate = pd->petDB->defence_attack_rate; - rate = rate * pd->rate_fix/1000; - if(pd->petDB->defence_attack_rate > 0 && rate <= 0) - rate = 1; - } - if(rnd()%10000 < rate) - { - if(pd->target_id == 0 || rnd()%10000 < pd->petDB->change_target_rate) - pd->target_id = bl->id; - } + if (rnd() % 10000 < max(rate, 1) && (pd->target_id == 0 || rnd() % 10000 < pd->petDB->change_target_rate)) + pd->target_id = bl->id; return 0; } + /*========================================== * Pet SC Check [Skotlex] *------------------------------------------*/ @@ -226,59 +259,72 @@ static int pet_sc_check(struct map_session_data *sd, int type) return 0; } +/** + * Updates a pet's hunger value and timer and updates the pet's intimacy value if starving. + * + * @param tid The timer ID. + * @param tick The base amount of ticks to add to the pet's hunger timer. (The timer's current ticks when calling this fuction.) + * @param id The pet master's account ID. + * @param data Unused. + * @return 1 on failure, 0 on success. + * + **/ static int pet_hungry(int tid, int64 tick, int id, intptr_t data) { - struct map_session_data *sd; - struct pet_data *pd; - int interval; + struct map_session_data *sd = map->id2sd(id); - sd=map->id2sd(id); - if(!sd) + if (sd == NULL || sd->status.pet_id == 0 || sd->pd == NULL) return 1; - if(!sd->status.pet_id || !sd->pd) - return 1; + struct pet_data *pd = sd->pd; - pd = sd->pd; - if(pd->pet_hungry_timer != tid){ - ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid); + /** + * If HungerDelay is 0, there's nothing to do. + * Actually this shouldn't happen, since the timer wasn't added in pet_data_init(), but just to be sure... + * + **/ + if (pd->petDB->hungry_delay == 0) { + pet->hungry_timer_delete(pd); return 0; } + + if (pd->pet_hungry_timer != tid) { + ShowError("pet_hungry: pet_hungry_timer %d != %d\n", pd->pet_hungry_timer, tid); + return 1; + } + pd->pet_hungry_timer = INVALID_TIMER; - if (pd->pet.intimate <= 0) - return 1; //You lost the pet already, the rest is irrelevant. + if (pd->pet.intimate <= PET_INTIMACY_NONE) + return 1; // You lost the pet already, the rest is irrelevant. - pd->pet.hungry--; - /* Pet Autofeed */ - if (battle_config.feature_enable_homun_autofeed != 0) { - if (pd->petDB->autofeed == 1 && pd->pet.autofeed == 1 && pd->pet.hungry <= 25) { + pet->set_hunger(pd, pd->pet.hungry - pd->petDB->hunger_decrement); + + // Pet auto-feed. + if (battle_config.feature_enable_pet_autofeed == 1) { + if (pd->petDB->autofeed == 1 && pd->pet.autofeed == 1 && pd->pet.hungry <= PET_HUNGER_HUNGRY) pet->food(sd, pd); - } } - if( pd->pet.hungry < 0 ) - { + int interval = pd->petDB->hungry_delay; + + if (pd->pet.hungry == PET_HUNGER_STARVING) { pet_stop_attack(pd); - pd->pet.hungry = 0; - pet->set_intimate(pd, pd->pet.intimate - battle_config.pet_hungry_friendly_decrease); - if( pd->pet.intimate <= 0 ) - { - pd->pet.intimate = 0; + pet->set_intimate(pd, pd->pet.intimate - pd->petDB->starving_decrement); + + if (pd->pet.intimate == PET_INTIMACY_NONE) pd->status.speed = pd->db->status.speed; - } + status_calc_pet(pd, SCO_NONE); - clif->send_petdata(sd,pd,1,pd->pet.intimate); + clif->send_petdata(sd, pd, 1, pd->pet.intimate); + + if (pd->petDB->starving_delay > 0) + interval = pd->petDB->starving_delay; } - clif->send_petdata(sd,pd,2,pd->pet.hungry); - if(battle_config.pet_hungry_delay_rate != 100) - interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; - else - interval = pd->petDB->hungry_delay; - if(interval <= 0) - interval = 1; - pd->pet_hungry_timer = timer->add(tick+interval,pet->hungry,sd->bl.id,0); + clif->send_petdata(sd, pd, 2, pd->pet.hungry); + interval = interval * battle_config.pet_hungry_delay_rate / 100; + pd->pet_hungry_timer = timer->add(tick + max(interval, 1), pet->hungry, sd->bl.id, 0); return 0; } @@ -315,21 +361,31 @@ static int pet_hungry_timer_delete(struct pet_data *pd) return 1; } +/** + * Makes a pet start performing/dancing. + * + * @param sd Unused. + * @param pd The pet. + * @return 0 on failure, 1 on success. + * + **/ static int pet_performance(struct map_session_data *sd, struct pet_data *pd) { + nullpo_ret(pd); + int val; - nullpo_retr(1, pd); - if (pd->pet.intimate > 900) - val = (pd->petDB->s_perfor > 0)? 4:3; - else if(pd->pet.intimate > 750) //TODO: this is way too high + if (pd->pet.intimate > PET_INTIMACY_LOYAL) + val = (pd->petDB->s_perfor > 0) ? 4 : 3; + else if (pd->pet.intimate > PET_INTIMACY_CORDIAL) //TODO: This is way too high. val = 2; else val = 1; - pet_stop_walking(pd,STOPWALKING_FLAG_NONE | (2000<<8)); // Stop walking for 2000ms - clif->send_petdata(NULL, pd, 4, rnd()%val + 1); - pet->lootitem_drop(pd,NULL); + pet_stop_walking(pd, STOPWALKING_FLAG_NONE | (2000 << 8)); // Stop walking for 2 seconds. + clif->send_petdata(NULL, pd, 4, rnd() % val + 1); + pet->lootitem_drop(pd, NULL); + return 1; } @@ -342,12 +398,27 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) pet->lootitem_drop(pd,sd); // Pet Evolution - ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET && + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].card[0] == CARD0_PET && pd->pet.pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])); - if (i != MAX_INVENTORY) { + 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); @@ -362,78 +433,80 @@ static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) return 1; } +/** + * Initializes a pet. + * + * @param sd The pet's master. + * @param petinfo The pet's status data. + * @return 1 on failure, 0 on success. + * + **/ static int pet_data_init(struct map_session_data *sd, struct s_pet *petinfo) { - struct pet_data *pd; - int i=0,interval=0; - nullpo_retr(1, sd); nullpo_retr(1, petinfo); - Assert_retr(1, sd->status.pet_id == 0 || sd->pd == 0 || sd->pd->msd == sd); + Assert_retr(1, sd->status.pet_id == 0 || sd->pd == NULL || sd->pd->msd == sd); - if(sd->status.account_id != petinfo->account_id || sd->status.char_id != petinfo->char_id) { + if (sd->status.account_id != petinfo->account_id || sd->status.char_id != petinfo->char_id) { sd->status.pet_id = 0; return 1; } + if (sd->status.pet_id != petinfo->pet_id) { - if (sd->status.pet_id) { - //Wrong pet?? Set incubate to no and send it back for saving. + if (sd->status.pet_id != 0) { // Wrong pet? Set incubate to no and send it back for saving. petinfo->incubate = 1; - intif->save_petdata(sd->status.account_id,petinfo); + intif->save_petdata(sd->status.account_id, petinfo); sd->status.pet_id = 0; return 1; } - //The pet_id value was lost? odd... restore it. - sd->status.pet_id = petinfo->pet_id; + + sd->status.pet_id = petinfo->pet_id; // The pet_id value was lost? Odd... Restore it. } - i = pet->search_petDB_index(petinfo->class_,PET_CLASS); - if(i < 0) { + int i = pet->search_petDB_index(petinfo->class_, PET_CLASS); + + if (i == INDEX_NOT_FOUND) { sd->status.pet_id = 0; return 1; } + + struct pet_data *pd; + CREATE(pd, struct pet_data, 1); - pd->bl.type = BL_PET; - pd->bl.id = npc->get_new_npc_id(); + memcpy(&pd->pet, petinfo, sizeof(struct s_pet)); sd->pd = pd; - pd->msd = sd; pd->petDB = &pet->db[i]; - pd->db = mob->db(petinfo->class_); - memcpy(&pd->pet, petinfo, sizeof(struct s_pet)); - status->set_viewdata(&pd->bl, petinfo->class_); + pd->db = mob->db(pd->petDB->class_); + pd->bl.type = BL_PET; + pd->bl.id = npc->get_new_npc_id(); + status->set_viewdata(&pd->bl, pd->petDB->class_); unit->dataset(&pd->bl); pd->ud.dir = sd->ud.dir; - pd->bl.m = sd->bl.m; pd->bl.x = sd->bl.x; pd->bl.y = sd->bl.y; unit->calc_pos(&pd->bl, sd->bl.x, sd->bl.y, sd->ud.dir); pd->bl.x = pd->ud.to_x; pd->bl.y = pd->ud.to_y; - map->addiddb(&pd->bl); - status_calc_pet(pd,SCO_FIRST); - + status_calc_pet(pd, SCO_FIRST); pd->last_thinktime = timer->gettick(); pd->state.skillbonus = 0; - if( battle_config.pet_status_support ) - script->run_pet(pet->db[i].pet_script,0,sd->bl.id,0); + if (pd->petDB->pet_script != NULL && battle_config.pet_status_support == 1) + script->run_pet(pd->petDB->pet_script, 0, sd->bl.id, 0); - if( pd->petDB ) { - if( pd->petDB->equip_script ) - status_calc_pc(sd,SCO_NONE); + if (pd->petDB->equip_script != NULL) + status_calc_pc(sd, SCO_NONE); - if( battle_config.pet_hungry_delay_rate != 100 ) - interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; - else - interval = pd->petDB->hungry_delay; + pd->pet_hungry_timer = INVALID_TIMER; + + if (pd->petDB->hungry_delay > 0) { + int interval = pd->petDB->hungry_delay * battle_config.pet_hungry_delay_rate / 100; + pd->pet_hungry_timer = timer->add(timer->gettick() + max(interval, 1), pet->hungry, sd->bl.id, 0); } - if( interval <= 0 ) - interval = 1; - pd->pet_hungry_timer = timer->add(timer->gettick() + interval, pet->hungry, sd->bl.id, 0); return 0; } @@ -492,10 +565,10 @@ static int pet_recv_petdata(int account_id, struct s_pet *p, int flag) if(p->incubate == 1) { int i; // Get Egg Index - ARR_FIND(0, MAX_INVENTORY, i, sd->status.inventory[i].card[0] == CARD0_PET && + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].card[0] == CARD0_PET && p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])); - if(i == MAX_INVENTORY) { + if(i == sd->status.inventorySize) { ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name); sd->status.pet_id = 0; return 1; @@ -527,7 +600,7 @@ static int pet_select_egg(struct map_session_data *sd, int egg_index) { nullpo_ret(sd); - if(egg_index < 0 || egg_index >= MAX_INVENTORY) + if (egg_index < 0 || egg_index >= sd->status.inventorySize) return 0; //Forged packet! if(sd->status.inventory[egg_index].card[0] == CARD0_PET) @@ -548,57 +621,66 @@ static int pet_catch_process1(struct map_session_data *sd, int target_class) return 0; } +/** + * Begins the actual process of catching a monster. + * + * @param sd The character who tries to catch the monster. + * @param target_id The monster ID of the pet, which the character tries to catch. + * @return 1 on failure, 0 on success. + * + **/ static int pet_catch_process2(struct map_session_data *sd, int target_id) { - struct mob_data *md = NULL; - struct block_list *bl = NULL; - int i = 0, pet_catch_rate = 0; - nullpo_retr(1, sd); - bl = map->id2bl(target_id); // TODO: Why does this not use map->id2md? - md = BL_CAST(BL_MOB, bl); - if (md == NULL || md->bl.prev == NULL) { - // Invalid inputs/state, abort capture. - clif->pet_roulette(sd,0); + struct mob_data *md = BL_CAST(BL_MOB, map->id2bl(target_id)); //TODO: Why does this not use map->id2md? + + if (md == NULL || md->bl.prev == NULL) { // Invalid inputs/state, abort capture. + clif->pet_roulette(sd, 0); sd->catch_target_class = -1; sd->itemid = -1; sd->itemindex = -1; return 1; } - //FIXME: delete taming item here, if this was an item-invoked capture and the item was flagged as delay-consume [ultramage] + //FIXME: Delete taming item here, if this was an item-invoked capture and the item was flagged as delay-consume. [ultramage] - i = pet->search_petDB_index(md->class_,PET_CLASS); - //catch_target_class == 0 is used for universal lures (except bosses for now). [Skotlex] - if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS)) + // catch_target_class == 0 is used for universal lures (except bosses for now). [Skotlex] + if (sd->catch_target_class == 0 && (md->status.mode & MD_BOSS) == 0) sd->catch_target_class = md->class_; - if(i < 0 || sd->catch_target_class != md->class_) { - clif->emotion(&md->bl, E_AG); //mob will do /ag if wrong lure is used on them. - clif->pet_roulette(sd,0); + + int i = pet->search_petDB_index(md->class_, PET_CLASS); + + if (i == INDEX_NOT_FOUND || sd->catch_target_class != md->class_) { + clif->emotion(&md->bl, E_AG); // Mob will do /ag if wrong lure is used on it. + clif->pet_roulette(sd, 0); sd->catch_target_class = -1; return 1; } - pet_catch_rate = (pet->db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - get_percentage(md->status.hp, md->status.max_hp))/100; + int pet_catch_rate; + int capture = pet->db[i].capture; + int mob_hp_perc = get_percentage(md->status.hp, md->status.max_hp); - if(pet_catch_rate < 1) pet_catch_rate = 1; - if(battle->bc->pet_catch_rate != 100) - pet_catch_rate = (pet_catch_rate*battle->bc->pet_catch_rate)/100; + if (battle_config.pet_catch_rate_official_formula == 1) { + pet_catch_rate = capture * (100 - mob_hp_perc) / 100 + capture; + } else { + int lvl_diff_mod = (sd->status.base_level - md->level) * 30; + int char_luk_mod = sd->battle_status.luk * 20; + pet_catch_rate = (capture + lvl_diff_mod + char_luk_mod) * (200 - mob_hp_perc) / 100; + } - if(rnd()%10000 < pet_catch_rate) - { - unit->remove_map(&md->bl,CLR_OUTSIGHT,ALC_MARK); - status_kill(&md->bl); - clif->pet_roulette(sd,1); - intif->create_pet(sd->status.account_id,sd->status.char_id,pet->db[i].class_,mob->db(pet->db[i].class_)->lv, - pet->db[i].EggID,0,pet->db[i].intimate,100,0,1,pet->db[i].jname); + pet_catch_rate = cap_value(pet_catch_rate, 1, 10000) * battle_config.pet_catch_rate / 100; + if (rnd() % 10000 < pet_catch_rate) { + unit->remove_map(&md->bl, CLR_OUTSIGHT, ALC_MARK); + status_kill(&md->bl); + clif->pet_roulette(sd, 1); + intif->create_pet(sd->status.account_id, sd->status.char_id, pet->db[i].class_, mob->db(pet->db[i].class_)->lv, + pet->db[i].EggID, 0, pet->db[i].intimate, PET_HUNGER_STUFFED, 0, 1, pet->db[i].jname); achievement->validate_taming(sd, pet->db[i].class_); - } - else - { - clif->pet_roulette(sd,0); + } else { + clif->pet_roulette(sd, 0); sd->catch_target_class = -1; } @@ -614,7 +696,7 @@ static int pet_catch_process2(struct map_session_data *sd, int target_id) * pet_id - Should contain pet id otherwise means failure * returns true on success **/ -static bool pet_get_egg(int account_id, short pet_class, int pet_id) +static bool pet_get_egg(int account_id, int pet_class, int pet_id) { struct map_session_data *sd; struct item tmp_item; @@ -655,42 +737,52 @@ static bool pet_get_egg(int account_id, short pet_class, int pet_id) return true; } +/** + * Performs selected pet menu option. + * + * @param sd The pet's master. + * @param menunum The selected menu option. + * @return 1 on failure, 0 on success. + * + **/ static int pet_menu(struct map_session_data *sd, int menunum) { - struct item_data *egg_id; - nullpo_ret(sd); - if (sd->pd == NULL) - return 1; + nullpo_retr(1, sd); + nullpo_retr(1, sd->pd); - //You lost the pet already. - if(!sd->status.pet_id || sd->pd->pet.intimate <= 0 || sd->pd->pet.incubate) - return 1; + if (sd->status.pet_id == 0 || sd->pd->pet.intimate <= PET_INTIMACY_NONE || sd->pd->pet.incubate != 0) + return 1; // You lost the pet already. + + struct item_data *egg_id = itemdb->exists(sd->pd->petDB->EggID); - egg_id = itemdb->exists(sd->pd->petDB->EggID); - if (egg_id) { - if ((egg_id->flag.trade_restriction&ITR_NODROP) && !pc->inventoryblank(sd)) { - clif->message(sd->fd, msg_sd(sd,451)); // You can't return your pet because your inventory is full. + if (egg_id != NULL) { + if ((egg_id->flag.trade_restriction & ITR_NODROP) != 0 && pc->inventoryblank(sd) == 0) { + clif->message(sd->fd, msg_sd(sd, 451)); // You can't return your pet because your inventory is full. return 1; } } - switch(menunum) { - case 0: - clif->send_petstatus(sd); - break; - case 1: - pet->food(sd, sd->pd); - break; - case 2: - pet->performance(sd, sd->pd); - break; - case 3: - pet->return_egg(sd, sd->pd); - break; - case 4: - pet->unequipitem(sd, sd->pd); - break; + switch (menunum) { + case 0: + clif->send_petstatus(sd); + break; + case 1: + pet->food(sd, sd->pd); + break; + case 2: + pet->performance(sd, sd->pd); + break; + case 3: + pet->return_egg(sd, sd->pd); + break; + case 4: + pet->unequipitem(sd, sd->pd); + break; + default: ; + ShowError("pet_menu: Unexpected menu option: %d\n", menunum); + return 1; } + return 0; } @@ -733,7 +825,7 @@ static int pet_change_name_ack(struct map_session_data *sd, const char *name, in } safestrncpy(pd->pet.name, newname, NAME_LENGTH); aFree(newname); - clif->charnameack (0,&pd->bl); + clif->blname_ack(0,&pd->bl); pd->pet.rename_flag = 1; clif->send_petdata(NULL, sd->pd, 3, sd->pd->vd.head_bottom); clif->send_petstatus(sd); @@ -815,49 +907,56 @@ static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) return 0; } +/** + * Feeds a pet and updates its intimacy value. + * + * @param sd The pet's master. + * @param pd The pet. + * @return 1 on failure, 0 on success. + * + **/ static int pet_food(struct map_session_data *sd, struct pet_data *pd) { - int i, food_id; - + nullpo_retr(1, sd); nullpo_retr(1, pd); - food_id = pd->petDB->FoodID; - i = pc->search_inventory(sd, food_id); - if(i == INDEX_NOT_FOUND) { - clif->pet_food(sd, food_id, 0); + + int i = pc->search_inventory(sd, pd->petDB->FoodID); + + if (i == INDEX_NOT_FOUND) { + clif->pet_food(sd, pd->petDB->FoodID, 0); return 1; } + pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME); - if (pd->pet.hungry > 90) { - pet->set_intimate(pd, pd->pet.intimate - pd->petDB->r_full); - } else { - int add_intimate = 0; - if (battle_config.pet_friendly_rate != 100) - add_intimate = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; - else - add_intimate = pd->petDB->r_hungry; - if (pd->pet.hungry > 75) { - add_intimate = add_intimate >> 1; - if (add_intimate <= 0) - add_intimate = 1; - } - pet->set_intimate(pd, pd->pet.intimate + add_intimate); - } - if (pd->pet.intimate <= 0) { - pd->pet.intimate = 0; + int intimacy = 0; + + if (pd->pet.hungry >= PET_HUNGER_STUFFED) + intimacy -= pd->petDB->r_full; // Decrease intimacy by OverFeedDecrement. + else if (pd->pet.hungry > PET_HUNGER_SATISFIED) + intimacy -= pd->petDB->r_full / 2; // Decrease intimacy by 50% of OverFeedDecrement. + else if (pd->pet.hungry > PET_HUNGER_NEUTRAL) + intimacy -= pd->petDB->r_full * 5 / 100; // Decrease intimacy by 5% of OverFeedDecrement. + else if (pd->pet.hungry > PET_HUNGER_HUNGRY) + intimacy += pd->petDB->r_hungry * 75 / 100; // Increase intimacy by 75% of FeedIncrement. + else if (pd->pet.hungry > PET_HUNGER_VERY_HUNGRY) + intimacy += pd->petDB->r_hungry; // Increase intimacy by FeedIncrement. + else + intimacy += pd->petDB->r_hungry / 2; // Increase intimacy by 50% of FeedIncrement. + + intimacy = intimacy * battle_config.pet_friendly_rate / 100; + pet->set_intimate(pd, pd->pet.intimate + intimacy); + + if (pd->pet.intimate == PET_INTIMACY_NONE) { pet_stop_attack(pd); pd->status.speed = pd->db->status.speed; - } else if (pd->pet.intimate > 1000) { - pd->pet.intimate = 1000; } - status_calc_pet(pd, SCO_NONE); - pd->pet.hungry += pd->petDB->fullness; - if( pd->pet.hungry > 100 ) - pd->pet.hungry = 100; - clif->send_petdata(sd,pd,2,pd->pet.hungry); - clif->send_petdata(sd,pd,1,pd->pet.intimate); - clif->pet_food(sd,pd->petDB->FoodID,1); + status_calc_pet(pd, SCO_NONE); + pet->set_hunger(pd, pd->pet.hungry + pd->petDB->fullness); + clif->send_petdata(sd, pd, 2, pd->pet.hungry); + clif->send_petdata(sd, pd, 1, pd->pet.intimate); + clif->pet_food(sd, pd->petDB->FoodID, 1); return 0; } @@ -876,7 +975,8 @@ static int pet_randomwalk(struct pet_data *pd, int64 tick) int r=rnd(); int x=pd->bl.x+r%(d*2+1)-d; int y=pd->bl.y+r/(d*2+1)%(d*2+1)-d; - if(map->getcell (pd->bl.m, &pd->bl, x, y, CELL_CHKPASS) && unit->walktoxy(&pd->bl, x, y, 0)) { + if (map->getcell(pd->bl.m, &pd->bl, x, y, CELL_CHKPASS) != 0 + && unit->walk_toxy(&pd->bl, x, y, 0) == 0) { pd->move_fail_count=0; break; } @@ -903,126 +1003,147 @@ static int pet_randomwalk(struct pet_data *pd, int64 tick) return 0; } +/** + * Performs pet's AI actions. (Moving, attacking, etc.) + * + * @param pd The pet. + * @param sd The pet's master. + * @param tick Timestamp of last support. + * @return Always 0. + * + **/ static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, int64 tick) { - struct block_list *target = NULL; nullpo_ret(pd); + nullpo_ret(pd->bl.prev); + nullpo_ret(sd); + nullpo_ret(sd->bl.prev); - if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL) + if (DIFF_TICK(tick, pd->last_thinktime) < MIN_PETTHINKTIME) return 0; - if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME) - return 0; - pd->last_thinktime=tick; + pd->last_thinktime = tick; - if(pd->ud.attacktimer != INVALID_TIMER || pd->ud.skilltimer != INVALID_TIMER || pd->bl.m != sd->bl.m) + if (pd->ud.attacktimer != INVALID_TIMER || pd->ud.skilltimer != INVALID_TIMER || pd->bl.m != sd->bl.m) return 0; - if(pd->ud.walktimer != INVALID_TIMER && pd->ud.walkpath.path_pos <= 2) - return 0; //No thinking when you just started to walk. + if (pd->ud.walktimer != INVALID_TIMER && pd->ud.walkpath.path_pos <= 2) + return 0; // No thinking when you just started to walk. - if(pd->pet.intimate <= 0) { - //Pet should just... well, random walk. - pet->randomwalk(pd,tick); + if (pd->pet.intimate <= PET_INTIMACY_NONE) { + pet->randomwalk(pd, tick); // Pet should just... well, random walk. return 0; } - if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) { - //Master too far, chase. - if(pd->target_id) + if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) { // Master too far away. Chase him. + if (pd->target_id != 0) pet->unlocktarget(pd); - if(pd->ud.walktimer != INVALID_TIMER && pd->ud.target == sd->bl.id) - return 0; //Already walking to him + + if (pd->ud.walktimer != INVALID_TIMER && pd->ud.target == sd->bl.id) + return 0; // Already walking to him. + if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0) - return 0; //Can't move yet. - pd->status.speed = (sd->battle_status.speed>>1); - if(pd->status.speed <= 0) - pd->status.speed = 1; - if (!unit->walktobl(&pd->bl, &sd->bl, 3, 0)) - pet->randomwalk(pd,tick); + return 0; // Can't move yet. + + pd->status.speed = max(sd->battle_status.speed / 2, MIN_WALK_SPEED); + + if (unit->walktobl(&pd->bl, &sd->bl, 3, 0) == 0) + pet->randomwalk(pd, tick); + return 0; } - //Return speed to normal. - if (pd->status.speed != pd->petDB->speed) { + if (pd->status.speed != pd->petDB->speed) { // Reset speed to normal. if (pd->ud.walktimer != INVALID_TIMER) - return 0; //Wait until the pet finishes walking back to master. + return 0; // Wait until the pet finishes walking back to master. + pd->status.speed = pd->petDB->speed; - pd->ud.state.change_walk_target = pd->ud.state.speed_changed = 1; + pd->ud.state.speed_changed = 1; + pd->ud.state.change_walk_target = 1; } - if (pd->target_id) { - target= map->id2bl(pd->target_id); - if (!target || pd->bl.m != target->m || status->isdead(target) - || !check_distance_bl(&pd->bl, target, pd->db->range3) - ) { + struct block_list *target = NULL; + + if (pd->target_id != 0) { + target = map->id2bl(pd->target_id); + + if (target == NULL || pd->bl.m != target->m || status->isdead(target) == 1 + || !check_distance_bl(&pd->bl, target, pd->db->range3)) { target = NULL; pet->unlocktarget(pd); } } - if(!target && pd->loot && pd->msd && pc_has_permission(pd->msd, PC_PERM_TRADE) && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) { - //Use half the pet's range of sight. - map->foreachinrange(pet->ai_sub_hard_lootsearch,&pd->bl, - pd->db->range2/2, BL_ITEM,pd,&target); + if (target == NULL && pd->loot != NULL && pd->msd != NULL && pc_has_permission(pd->msd, PC_PERM_TRADE) + && pd->loot->count < pd->loot->max && DIFF_TICK(tick, pd->ud.canact_tick) > 0) { // Use half the pet's range of sight. + map->foreachinrange(pet->ai_sub_hard_lootsearch, &pd->bl, pd->db->range2 / 2, BL_ITEM, pd, &target); } - if (!target) { - //Just walk around. + if (target == NULL) { // Just walk around. if (check_distance_bl(&sd->bl, &pd->bl, 3)) - return 0; //Already next to master. + return 0; // Already next to master. - if(pd->ud.walktimer != INVALID_TIMER && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3)) - return 0; //Already walking to him + if (pd->ud.walktimer != INVALID_TIMER && check_distance_blxy(&sd->bl, pd->ud.to_x, pd->ud.to_y, 3)) + return 0; // Already walking to him. unit->calc_pos(&pd->bl, sd->bl.x, sd->bl.y, sd->ud.dir); - if(!unit->walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0)) - pet->randomwalk(pd,tick); + + if (unit->walk_toxy(&pd->bl, pd->ud.to_x, pd->ud.to_y, 0) != 0) + pet->randomwalk(pd, tick); return 0; } - if(pd->ud.target == target->id && - (pd->ud.attacktimer != INVALID_TIMER || pd->ud.walktimer != INVALID_TIMER)) - return 0; //Target already locked. + if (pd->ud.target == target->id && (pd->ud.attacktimer != INVALID_TIMER || pd->ud.walktimer != INVALID_TIMER)) + return 0; // Target already locked. + + if (target->type != BL_ITEM) { // Target is enemy. Chase or attack it. + if (!battle->check_range(&pd->bl, target, pd->status.rhw.range)) { // Chase enemy. + if (unit->walktobl(&pd->bl, target, pd->status.rhw.range, 2) == 0) // Enemy is unreachable. + pet->unlocktarget(pd); - if (target->type != BL_ITEM) - { //enemy targetted - if(!battle->check_range(&pd->bl,target,pd->status.rhw.range)) { - //Chase - if(!unit->walktobl(&pd->bl, target, pd->status.rhw.range, 2)) - pet->unlocktarget(pd); //Unreachable target. return 0; } - //Continuous attack. - unit->attack(&pd->bl, pd->target_id, 1); - } else { - //Item Targeted, attempt loot - if (!check_distance_bl(&pd->bl, target, 1)) { - //Out of range - if(!unit->walktobl(&pd->bl, target, 1, 1)) //Unreachable target. + + unit->attack(&pd->bl, pd->target_id, 1); // Start/continue attacking. + } else { // Target is item. Attempt looting. + if (!check_distance_bl(&pd->bl, target, 1)) { // Item is out of range. + if (unit->walktobl(&pd->bl, target, 1, 1) == 0) // Item is unreachable. pet->unlocktarget(pd); + return 0; - } else{ + } + + if (pd->loot->count < pd->loot->max) { struct flooritem_data *fitem = BL_UCAST(BL_ITEM, target); - if(pd->loot->count < pd->loot->max){ - memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0])); - pd->loot->weight += itemdb_weight(fitem->item_data.nameid)*fitem->item_data.amount; - map->clearflooritem(target); - } - //Target is unlocked regardless of whether it was picked or not. - pet->unlocktarget(pd); + + memcpy(&pd->loot->item[pd->loot->count++], &fitem->item_data, sizeof(pd->loot->item[0])); + pd->loot->weight += itemdb_weight(fitem->item_data.nameid) * fitem->item_data.amount; + map->clearflooritem(target); } + + pet->unlocktarget(pd); // Target is unlocked regardless of whether the item was picked or not. } + return 0; } +/** + * Calls pet_ai_sub_hard() for a character's pet if conditions are fulfilled. + * + * @param sd The character. + * @param ap Additional arguments. In this case only the time stamp of pet AI timer execution. + * @return Always 0. + * + **/ static int pet_ai_sub_foreachclient(struct map_session_data *sd, va_list ap) { - int64 tick = va_arg(ap,int64); nullpo_ret(sd); - if(sd->status.pet_id && sd->pd) - pet->ai_sub_hard(sd->pd,sd,tick); + + int64 tick = va_arg(ap, int64); + + if (sd->bl.prev != NULL && sd->status.pet_id != 0 && sd->pd != NULL && sd->pd->bl.prev != NULL) + pet->ai_sub_hard(sd->pd, sd, tick); return 0; } @@ -1127,45 +1248,54 @@ static int pet_lootitem_drop(struct pet_data *pd, struct map_session_data *sd) return 1; } -/*========================================== - * pet bonus giving skills [Valaris] / Rewritten by [Skotlex] - *------------------------------------------*/ +/** + * Applies pet's stat bonuses to its master. (See petskillbonus() script command.) + * + * @param tid The timer ID + * @param tick The base amount of ticks to add to the pet's bonus timer. (The timer's current ticks when calling this fuction.) + * @param id The pet's master's account ID. + * @param data Unused. + * @return 1 on failure, 0 on success. + * + **/ static int pet_skill_bonus_timer(int tid, int64 tick, int id, intptr_t data) { - struct map_session_data *sd=map->id2sd(id); - struct pet_data *pd; - int bonus; - int duration = 0; + struct map_session_data *sd = map->id2sd(id); - if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL) + if (sd == NULL || sd->pd == NULL || sd->pd->bonus == NULL) return 1; - pd=sd->pd; + struct pet_data *pd = sd->pd; - if(pd->bonus->timer != tid) { - ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid); + if (pd->bonus->timer != tid) { + ShowError("pet_skill_bonus_timer %d != %d\n", pd->bonus->timer, tid); pd->bonus->timer = INVALID_TIMER; - return 0; + return 1; } - // determine the time for the next timer - if (pd->state.skillbonus && pd->bonus->delay > 0) { + int bonus; + int duration; + + // Determine the time for the next timer. + if (pd->state.skillbonus == 1 && pd->bonus->delay > 0) { bonus = 0; - duration = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again - } else if (pd->pet.intimate) { + duration = pd->bonus->delay * 1000; // The duration until pet bonuses will be reactivated again. + } else if (pd->pet.intimate > PET_INTIMACY_NONE) { bonus = 1; - duration = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect - } else { //Lost pet... + duration = pd->bonus->duration * 1000; // The duration for pet bonuses to be in effect. + } else { // Lost pet... pd->bonus->timer = INVALID_TIMER; - return 0; + return 1; } if (pd->state.skillbonus != bonus) { pd->state.skillbonus = bonus; status_calc_pc(sd, SCO_NONE); } - // wait for the next timer - pd->bonus->timer=timer->add(tick+duration,pet->skill_bonus_timer,sd->bl.id,0); + + // Wait for the next timer. + pd->bonus->timer = timer->add(tick + duration, pet->skill_bonus_timer, sd->bl.id, 0); + return 0; } @@ -1306,120 +1436,160 @@ static int pet_read_db_libconfig(const char *filename, bool ignore_missing) count++; } libconfig->destroy(&pet_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); return count; } +/** + * Reads a single pet from DB. + * + * @param it The libconfig settings block, which contains the pet's data. + * @param n The pet's index in pet->db[]. + * @param source The pet DB's file name. + * @return 0 on failure, the pet's ID on success. + * + **/ static int pet_read_db_sub(struct config_setting_t *it, int n, const char *source) { - struct config_setting_t *t = NULL; - struct item_data *data = NULL; - const char *str = NULL; - int i32 = 0; - nullpo_ret(it); nullpo_ret(source); Assert_ret(n >= 0 && n < MAX_PET_DB); - if (!libconfig->setting_lookup_int(it, "Id", &i32)) { + int i32 = 0; + + if (libconfig->setting_lookup_int(it, "Id", &i32) == CONFIG_FALSE) { ShowWarning("pet_read_db_sub: Missing Id in \"%s\", entry #%d, skipping.\n", source, n); return 0; } - pet->db[n].class_ = i32; - if (!libconfig->setting_lookup_string(it, "SpriteName", &str) || !*str ) { - ShowWarning("pet_read_db_sub: Missing SpriteName in pet %d of \"%s\", skipping.\n", pet->db[n].class_, source); + if (mob->db_checkid(i32) == 0) { + ShowWarning("pet_read_db_sub: Invalid Id in \"%s\", entry #%d, skipping.\n", source, n); return 0; } - safestrncpy(pet->db[n].name, str, sizeof(pet->db[n].name)); - if (!libconfig->setting_lookup_string(it, "Name", &str) || !*str) { - ShowWarning("pet_read_db_sub: Missing Name in pet %d of \"%s\", skipping.\n", pet->db[n].class_, source); + pet->db[n].class_ = i32; + safestrncpy(pet->db[n].name, mob->db(i32)->sprite, sizeof(pet->db[n].name)); + + const char *str; + + if (libconfig->setting_lookup_string(it, "Name", &str) == CONFIG_FALSE || *str == '\0') { + ShowWarning("pet_read_db_sub: Missing Name in pet %d of \"%s\", skipping.\n", + pet->db[n].class_, source); return 0; } + safestrncpy(pet->db[n].jname, str, sizeof(pet->db[n].jname)); - if (libconfig->setting_lookup_string(it, "TamingItem", &str)) { - if (!(data = itemdb->name2id(str))) { - ShowWarning("pet_read_db_sub: Invalid item '%s' in pet %d of \"%s\", defaulting to 0.\n", str, pet->db[n].class_, source); - } else { - pet->db[n].itemID = data->nameid; - } + if (libconfig->setting_lookup_string(it, "EggItem", &str) == CONFIG_FALSE || *str == '\0') { + ShowWarning("pet_read_db_sub: Missing EggItem in pet %d of \"%s\", skipping.\n", + pet->db[n].class_, source); + return 0; } - if (libconfig->setting_lookup_string(it, "EggItem", &str)) { - if (!(data = itemdb->name2id(str))) { - ShowWarning("pet_read_db_sub: Invalid item '%s' in pet %d of \"%s\", defaulting to 0.\n", str, pet->db[n].class_, source); - } else { - pet->db[n].EggID = data->nameid; - } + struct item_data *data; + + if ((data = itemdb->name2id(str)) == NULL) { + ShowWarning("pet_read_db_sub: Invalid EggItem '%s' in pet %d of \"%s\", skipping.\n", + str, pet->db[n].class_, source); + return 0; } - if (libconfig->setting_lookup_string(it, "AccessoryItem", &str)) { - if (!(data = itemdb->name2id(str))) { - ShowWarning("pet_read_db_sub: Invalid item '%s' in pet %d of \"%s\", defaulting to 0.\n", str, pet->db[n].class_, source); - } else { - pet->db[n].AcceID = data->nameid; - } + pet->db[n].EggID = data->nameid; + + if (libconfig->setting_lookup_string(it, "TamingItem", &str) == CONFIG_TRUE) { + if ((data = itemdb->name2id(str)) == NULL) + ShowWarning("pet_read_db_sub: Invalid TamingItem '%s' in pet %d of \"%s\", defaulting to 0.\n", + str, pet->db[n].class_, source); + else + pet->db[n].itemID = data->nameid; } - if (libconfig->setting_lookup_string(it, "FoodItem", &str)) { - if (!(data = itemdb->name2id(str))) { - ShowWarning("pet_read_db_sub: Invalid item '%s' in pet %d of \"%s\", defaulting to 0.\n", str, pet->db[n].class_, source); - } else { + pet->db[n].FoodID = 537; + + if (libconfig->setting_lookup_string(it, "FoodItem", &str) == CONFIG_TRUE) { + if ((data = itemdb->name2id(str)) == NULL) + ShowWarning("pet_read_db_sub: Invalid FoodItem '%s' in pet %d of \"%s\", defaulting to Pet_Food (ID=537).\n", + str, pet->db[n].class_, source); + else pet->db[n].FoodID = data->nameid; - } } - if (libconfig->setting_lookup_int(it, "FoodEffectiveness", &i32)) - pet->db[n].fullness = i32; + if (libconfig->setting_lookup_string(it, "AccessoryItem", &str) == CONFIG_TRUE) { + if ((data = itemdb->name2id(str)) == NULL) + ShowWarning("pet_read_db_sub: Invalid AccessoryItem '%s' in pet %d of \"%s\", defaulting to 0.\n", + str, pet->db[n].class_, source); + else + pet->db[n].AcceID = data->nameid; + } - if (libconfig->setting_lookup_int(it, "HungerDelay", &i32)) - pet->db[n].hungry_delay = i32 * 1000; + int ret = libconfig->setting_lookup_int(it, "FoodEffectiveness", &i32); + pet->db[n].fullness = (ret == CONFIG_FALSE) ? 80 : cap_value(i32, 1, PET_HUNGER_STUFFED); - if ((t = libconfig->setting_get_member(it, "Intimacy"))) { - if (config_setting_is_group(t)) { - pet->read_db_sub_intimacy(n, t); - } - } - if (pet->db[n].r_hungry <= 0) - pet->db[n].r_hungry = 1; + ret = libconfig->setting_lookup_int(it, "HungerDelay", &i32); + pet->db[n].hungry_delay = (ret == CONFIG_FALSE) ? 60000 : cap_value(1000 * i32, 0, INT_MAX); - if (libconfig->setting_lookup_int(it, "CaptureRate", &i32)) - pet->db[n].capture = i32; + ret = libconfig->setting_lookup_int(it, "HungerDecrement", &i32); + pet->db[n].hunger_decrement = (ret == CONFIG_FALSE) ? 1 : cap_value(i32, PET_HUNGER_STARVING, PET_HUNGER_STUFFED - 1); - if (libconfig->setting_lookup_int(it, "Speed", &i32)) - pet->db[n].speed = i32; + if (pet->db[n].hunger_decrement == PET_HUNGER_STARVING) + pet->db[n].hungry_delay = 0; - if ((t = libconfig->setting_get_member(it, "SpecialPerformance")) && (i32 = libconfig->setting_get_bool(t))) - pet->db[n].s_perfor = (char)i32; + /** + * Preventively set default intimacy values here, just in case that 'Intimacy' block is not defined, + * or pet_read_db_sub_intimacy() fails execution. + * + **/ + pet->db[n].intimate = PET_INTIMACY_NEUTRAL; + pet->db[n].r_hungry = 10; + pet->db[n].r_full = 100; + pet->db[n].die = 20; + pet->db[n].starving_delay = min(20000, pet->db[n].hungry_delay); + pet->db[n].starving_decrement = 20; - if ((t = libconfig->setting_get_member(it, "TalkWithEmotes")) && (i32 = libconfig->setting_get_bool(t))) - pet->db[n].talk_convert_class = i32; + struct config_setting_t *t; - if (libconfig->setting_lookup_int(it, "AttackRate", &i32)) - pet->db[n].attack_rate = i32; + if ((t = libconfig->setting_get_member(it, "Intimacy")) != NULL && config_setting_is_group(t)) + pet->read_db_sub_intimacy(n, t); - if (libconfig->setting_lookup_int(it, "DefendRate", &i32)) - pet->db[n].defence_attack_rate = i32; + ret = libconfig->setting_lookup_int(it, "CaptureRate", &i32); + pet->db[n].capture = (ret == CONFIG_FALSE) ? 1000 : cap_value(i32, 1, 10000); - if (libconfig->setting_lookup_int(it, "ChangeTargetRate", &i32)) - pet->db[n].change_target_rate = i32; + ret = libconfig->setting_lookup_int(it, "Speed", &i32); + pet->db[n].speed = (ret == CONFIG_FALSE) ? DEFAULT_WALK_SPEED : cap_value(i32, MIN_WALK_SPEED, MAX_WALK_SPEED); - // Pet Evolution - if ((t = libconfig->setting_get_member(it, "Evolve")) && config_setting_is_group(t)) { - pet->read_db_sub_evolution(t, n); + if ((t = libconfig->setting_get_member(it, "SpecialPerformance")) != NULL + && (i32 = libconfig->setting_get_bool(t)) != 0) { + pet->db[n].s_perfor = (char)i32; } - if ((t = libconfig->setting_get_member(it, "AutoFeed")) && (i32 = libconfig->setting_get_bool(t))) + if ((t = libconfig->setting_get_member(it, "TalkWithEmotes")) != NULL + && (i32 = libconfig->setting_get_bool(t)) != 0) { + pet->db[n].talk_convert_class = i32; + } + + ret = libconfig->setting_lookup_int(it, "AttackRate", &i32); + pet->db[n].attack_rate = (ret == CONFIG_FALSE) ? 300 : cap_value(i32, 0, 10000); + + ret = libconfig->setting_lookup_int(it, "DefendRate", &i32); + pet->db[n].defence_attack_rate = (ret == CONFIG_FALSE) ? 300 : cap_value(i32, 0, 10000); + + ret = libconfig->setting_lookup_int(it, "ChangeTargetRate", &i32); + pet->db[n].change_target_rate = (ret == CONFIG_FALSE) ? 800 : cap_value(i32, 0, 10000); + + if ((t = libconfig->setting_get_member(it, "AutoFeed")) != NULL && (i32 = libconfig->setting_get_bool(t)) != 0) pet->db[n].autofeed = i32; - if (libconfig->setting_lookup_string(it, "PetScript", &str)) - pet->db[n].pet_script = *str ? script->parse(str, source, -pet->db[n].class_, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + pet->db[n].pet_script = NULL; + if (libconfig->setting_lookup_string(it, "PetScript", &str) == CONFIG_TRUE && *str != '\0') + pet->db[n].pet_script = script->parse(str, source, -pet->db[n].class_, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL); + + pet->db[n].equip_script = NULL; + if (libconfig->setting_lookup_string(it, "EquipScript", &str) == CONFIG_TRUE && *str != '\0') + pet->db[n].equip_script = script->parse(str, source, -pet->db[n].class_, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL); - if (libconfig->setting_lookup_string(it, "EquipScript", &str)) - pet->db[n].equip_script = *str ? script->parse(str, source, -pet->db[n].class_, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL; + if ((t = libconfig->setting_get_member(it, "Evolve")) != NULL && config_setting_is_group(t)) + pet->read_db_sub_evolution(t, n); return pet->db[n].class_; } @@ -1499,24 +1669,41 @@ static void pet_read_db_sub_evolution(struct config_setting_t *t, int n) } } +/** + * Reads a pet's intimacy data from DB. + * + * @param idx The pet's index in pet->db[]. + * @param t The libconfig settings block, which contains the pet's intimacy data. + * @return false on failure, true on success. + * + **/ static bool pet_read_db_sub_intimacy(int idx, struct config_setting_t *t) { + nullpo_retr(false, t); + Assert_retr(false, idx >= 0 && idx < MAX_PET_DB); + int i32 = 0; - nullpo_retr(false, t); - Assert_ret(idx >= 0 && idx < MAX_PET_DB); + if (libconfig->setting_lookup_int(t, "Initial", &i32) == CONFIG_TRUE) + pet->db[idx].intimate = cap_value(i32, PET_INTIMACY_AWKWARD, PET_INTIMACY_MAX); + + if (libconfig->setting_lookup_int(t, "FeedIncrement", &i32) == CONFIG_TRUE) + pet->db[idx].r_hungry = cap_value(i32, PET_INTIMACY_AWKWARD, PET_INTIMACY_MAX); + + if (libconfig->setting_lookup_int(t, "OverFeedDecrement", &i32) == CONFIG_TRUE) + pet->db[idx].r_full = cap_value(i32, PET_INTIMACY_NONE, PET_INTIMACY_MAX); - if (libconfig->setting_lookup_int(t, "Initial", &i32)) - pet->db[idx].intimate = i32; + if (libconfig->setting_lookup_int(t, "OwnerDeathDecrement", &i32) == CONFIG_TRUE) + pet->db[idx].die = cap_value(i32, PET_INTIMACY_NONE, PET_INTIMACY_MAX); - if (libconfig->setting_lookup_int(t, "FeedIncrement", &i32)) - pet->db[idx].r_hungry = i32; + if (libconfig->setting_lookup_int(t, "StarvingDelay", &i32) == CONFIG_TRUE) + pet->db[idx].starving_delay = cap_value(1000 * i32, 0, pet->db[idx].hungry_delay); - if (libconfig->setting_lookup_int(t, "OverFeedDecrement", &i32)) - pet->db[idx].r_full = i32; + if (libconfig->setting_lookup_int(t, "StarvingDecrement", &i32) == CONFIG_TRUE) + pet->db[idx].starving_decrement = cap_value(i32, PET_INTIMACY_NONE, PET_INTIMACY_MAX); - if (libconfig->setting_lookup_int(t, "OwnerDeathDecrement", &i32)) - pet->db[idx].die = i32; + if (pet->db[idx].starving_decrement == PET_INTIMACY_NONE) + pet->db[idx].starving_delay = 0; return true; } @@ -1610,6 +1797,7 @@ void pet_defaults(void) pet->final = do_final_pet; pet->hungry_val = pet_hungry_val; + pet->set_hunger = pet_set_hunger; pet->set_intimate = pet_set_intimate; pet->create_egg = pet_create_egg; pet->unlocktarget = pet_unlocktarget; diff --git a/src/map/pet.h b/src/map/pet.h index 049816804..fa37e896a 100644 --- a/src/map/pet.h +++ b/src/map/pet.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ struct pet_evolve_data { }; struct s_pet_db { - short class_; + int class_; char name[NAME_LENGTH],jname[NAME_LENGTH]; int itemID; int EggID; @@ -57,6 +57,9 @@ struct s_pet_db { int defence_attack_rate; int change_target_rate; int autofeed; + int hunger_decrement; + int starving_delay; + int starving_decrement; struct script_code *equip_script; struct script_code *pet_script; @@ -143,6 +146,7 @@ struct pet_interface { int (*final) (void); /* */ int (*hungry_val) (struct pet_data *pd); + void (*set_hunger) (struct pet_data *pd, int value); void (*set_intimate) (struct pet_data *pd, int value); int (*create_egg) (struct map_session_data *sd, int item_id); int (*unlocktarget) (struct pet_data *pd); @@ -160,7 +164,7 @@ struct pet_interface { int (*select_egg) (struct map_session_data *sd, int egg_index); int (*catch_process1) (struct map_session_data *sd, int target_class); int (*catch_process2) (struct map_session_data *sd, int target_id); - bool (*get_egg) (int account_id, short pet_class, int pet_id ); + bool (*get_egg) (int account_id, int pet_class, int pet_id ); int (*unequipitem) (struct map_session_data *sd, struct pet_data *pd); int (*food) (struct map_session_data *sd, struct pet_data *pd); int (*ai_sub_hard_lootsearch) (struct block_list *bl, va_list ap); diff --git a/src/map/quest.c b/src/map/quest.c index f10d6847f..217acfa19 100644 --- a/src/map/quest.c +++ b/src/map/quest.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -585,7 +585,7 @@ static int quest_read_db(void) count++; } libconfig->destroy(&quest_db_conf); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filename); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); return count; } @@ -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); } } } @@ -814,7 +815,7 @@ static bool quest_questinfo_validate_items(struct map_session_data *sd, struct q for (int i = 0; i < VECTOR_LENGTH(qi->items); i++) { struct questinfo_itemreq *item = &VECTOR_INDEX(qi->items, i); int count = 0; - for (int j = 0; j < MAX_INVENTORY; j++) { + for (int j = 0; j < sd->status.inventorySize; j++) { if (sd->status.inventory[j].nameid == item->nameid) count += sd->status.inventory[j].amount; } @@ -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..f83bb7d73 100644 --- a/src/map/quest.h +++ b/src/map/quest.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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..f6c855cea --- /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-2020 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, filepath); + + 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..bb3790a45 --- /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-2020 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..5dbed2afc --- /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-2020 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 602754d42..1ebed0623 100644 --- a/src/map/rodex.c +++ b/src/map/rodex.c @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2017 Hercules Dev Team + * Copyright (C) 2017-2020 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 @@ -83,7 +83,7 @@ static void rodex_add_item(struct map_session_data *sd, int16 idx, int16 amount) nullpo_retv(sd); - if (idx < 0 || idx >= MAX_INVENTORY) { + if (idx < 0 || idx >= sd->status.inventorySize) { clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR); return; } @@ -160,7 +160,7 @@ static void rodex_remove_item(struct map_session_data *sd, int16 idx, int16 amou struct item_data *itd; nullpo_retv(sd); - Assert_retv(idx >= 0 && idx < MAX_INVENTORY); + Assert_retv(idx >= 0 && idx < sd->status.inventorySize); for (i = 0; i < RODEX_MAX_ITEM; ++i) { if (sd->rodex.tmp.items[i].idx == idx) @@ -204,7 +204,7 @@ static void rodex_remove_item(struct map_session_data *sd, int16 idx, int16 amou /// @param base_level : Reference to return the character base level, if he exists /// @param char_id : Reference to return the character id, if he exists /// @param class : Reference to return the character class id, if he exists -static void rodex_check_player(struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class) +static void rodex_check_player(struct map_session_data *sd, const char *name, int *base_level, int *char_id, int *class) { intif->rodex_checkname(sd, name); } @@ -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 < MAX_INVENTORY; ++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 ddf7cb32b..6a1621657 100644 --- a/src/map/rodex.h +++ b/src/map/rodex.h @@ -2,7 +2,7 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2017 Hercules Dev Team + * Copyright (C) 2017-2020 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 @@ -65,7 +65,7 @@ struct rodex_interface { void (*refresh) (struct map_session_data *sd, int8 open_type, int64 first_mail_id); void (*add_item) (struct map_session_data *sd, int16 idx, int16 amount); void (*remove_item) (struct map_session_data *sd, int16 idx, int16 amount); - void (*check_player) (struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class); + void (*check_player) (struct map_session_data *sd, const char *name, int *base_level, int *char_id, int *class); int (*send_mail) (struct map_session_data *sd, const char *receiver_name, const char *body, const char *title, int64 zeny); void (*send_mail_result) (struct map_session_data *ssd, struct map_session_data *rsd, bool result); struct rodex_message *(*get_mail) (struct map_session_data *sd, int64 mail_id); @@ -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 41f21cd72..45c954dc0 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -104,7 +105,7 @@ static inline void SETVALUE(struct script_buf *buf, int i, int n) VECTOR_INDEX(*buf, i+2) = GetByte(n, 2); } -const char *script_op2name(int op) +static const char *script_op2name(int op) { #define RETURN_OP_NAME(type) case type: return #type switch( op ) { @@ -2812,7 +2813,7 @@ static struct map_session_data *script_charid2sd(struct script_state *st, int ch static struct map_session_data *script_nick2sd(struct script_state *st, const char *name) { struct map_session_data *sd; - if ((sd = map->nick2sd(name)) == NULL) { + if ((sd = map->nick2sd(name, false)) == NULL) { ShowWarning("script_nick2sd: Player name '%s' not found!\n", name); script->reportfunc(st); script->reportsrc(st); @@ -4621,6 +4622,9 @@ static void run_script_main(struct script_state *st) nullpo_retv(st); script->attach_state(st); + if (st->state != END && Assert_chk(st->state == RUN || st->state == STOP || st->state == RERUNLINE)) { + st->state = END; + } nd = map->id2nd(st->oid); if( nd && nd->bl.m >= 0 ) @@ -5146,7 +5150,8 @@ static uint8 script_add_language(const char *name) static void script_load_translations(void) { struct config_t translations_conf; - const char *config_filename = "db/translations.conf"; // FIXME hardcoded name + char config_filename[256]; + libconfig->format_db_path("translations.conf", config_filename, sizeof(config_filename)); struct config_setting_t *translations = NULL; int i, size; int total = 0; @@ -5185,8 +5190,8 @@ static void script_load_translations(void) size = libconfig->setting_length(translations); for(i = 0; i < size; i++) { - const char *translation_file = libconfig->setting_get_string_elem(translations, i); - total += script->load_translation(translation_file, ++lang_id); + const char *translation_dir = libconfig->setting_get_string_elem(translations, i); + total += script->load_translation(translation_dir, ++lang_id); } libconfig->destroy(&translations_conf); @@ -5223,39 +5228,39 @@ static void script_load_translations(void) } /** - * Generates a language name from a translation filename. + * Generates a language name from a translation directory name. * - * @param file The filename. + * @param directory The directory name. * @return The corresponding translation name. */ -static const char *script_get_translation_file_name(const char *file) +static const char *script_get_translation_dir_name(const char *directory) { const char *basename = NULL, *last_dot = NULL; - nullpo_retr("Unknown", file); + nullpo_retr("Unknown", directory); - basename = strrchr(file, '/');; + basename = strrchr(directory, '/'); #ifdef WIN32 { - const char *basename_windows = strrchr(file, '\\'); + const char *basename_windows = strrchr(directory, '\\'); if (basename_windows > basename) basename = basename_windows; } #endif // WIN32 if (basename == NULL) - basename = file; + basename = directory; else basename++; // Skip slash Assert_retr("Unknown", *basename != '\0'); last_dot = strrchr(basename, '.'); if (last_dot != NULL) { - static char file_name[200]; + static char dir_name[200]; if (last_dot == basename) return basename + 1; - safestrncpy(file_name, basename, last_dot - basename + 1); - return file_name; + safestrncpy(dir_name, basename, last_dot - basename + 1); + return dir_name; } return basename; @@ -5336,18 +5341,19 @@ static bool script_load_translation_addstring(const char *file, uint8 lang_id, c /** * Parses an individual translation file. * - * @param file The filename to parse. + * @param directory The directory structure to read. * @param lang_id The language identifier. * @return The amount of strings loaded. */ -static int script_load_translation(const char *file, uint8 lang_id) +static int script_load_translation_file(const char *file, uint8 lang_id) { - int translations = 0; char line[1024]; - char msgctxt[NAME_LENGTH*2+1] = { 0 }; - FILE *fp; - int lineno = 0; + char msgctxt[NAME_LENGTH*2+1] = ""; struct script_string_buf msgid, msgstr; + struct script_string_buf *msg_ptr; + int translations = 0; + int lineno = 0; + FILE *fp; nullpo_ret(file); @@ -5359,46 +5365,53 @@ static int script_load_translation(const char *file, uint8 lang_id) VECTOR_INIT(msgid); VECTOR_INIT(msgstr); - script->add_language(script->get_translation_file_name(file)); - if (lang_id >= atcommand->max_message_table) - atcommand->expand_message_table(); - while (fgets(line, sizeof(line), fp) != NULL) { int len = (int)strlen(line); int i; lineno++; - if(len <= 1) + if (len <= 1) { + if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + // Add string + if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) + translations++; + + msgctxt[0] = '\0'; + VECTOR_TRUNCATE(msgid); + VECTOR_TRUNCATE(msgstr); + } continue; + } if (line[0] == '#') continue; - if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + if (VECTOR_LENGTH(msgid) > 0) { + if (VECTOR_LENGTH(msgstr) > 0) { + msg_ptr = &msgstr; + } else { + msg_ptr = &msgid; + } if (line[0] == '"') { // Continuation line - (void)VECTOR_POP(msgstr); // Pop final '\0' - for (i = 8; i < len - 2; i++) { - VECTOR_ENSURE(msgstr, 1, 512); + (void)VECTOR_POP(*msg_ptr); // Pop final '\0' + for (i = 1; i < len - 2; i++) { + VECTOR_ENSURE(*msg_ptr, 1, 512); if (line[i] == '\\' && line[i+1] == '"') { - VECTOR_PUSH(msgstr, '"'); + VECTOR_PUSH(*msg_ptr, '"'); + i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(*msg_ptr, '\r'); i++; } else { - VECTOR_PUSH(msgstr, line[i]); + VECTOR_PUSH(*msg_ptr, line[i]); } } - VECTOR_ENSURE(msgstr, 1, 512); - VECTOR_PUSH(msgstr, '\0'); + VECTOR_ENSURE(*msg_ptr, 1, 512); + VECTOR_PUSH(*msg_ptr, '\0'); continue; } - // Add string - if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) - translations++; - - msgctxt[0] = '\0'; - VECTOR_TRUNCATE(msgid); - VECTOR_TRUNCATE(msgstr); } if (strncasecmp(line,"msgctxt \"", 9) == 0) { @@ -5408,6 +5421,9 @@ static int script_load_translation(const char *file, uint8 lang_id) if (line[i] == '\\' && line[i+1] == '"') { msgctxt[cursor] = '"'; i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + msgctxt[cursor] = '\r'; + i++; } else { msgctxt[cursor] = line[i]; } @@ -5429,6 +5445,9 @@ static int script_load_translation(const char *file, uint8 lang_id) if (line[i] == '\\' && line[i+1] == '"') { VECTOR_PUSH(msgid, '"'); i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(msgid, '\r'); + i++; } else { VECTOR_PUSH(msgid, line[i]); } @@ -5448,6 +5467,9 @@ static int script_load_translation(const char *file, uint8 lang_id) if (line[i] == '\\' && line[i+1] == '"') { VECTOR_PUSH(msgstr, '"'); i++; + } else if (line[i] == '\\' && line[i+1] == 'r') { + VECTOR_PUSH(msgstr, '\r'); + i++; } else { VECTOR_PUSH(msgstr, line[i]); } @@ -5473,10 +5495,47 @@ static int script_load_translation(const char *file, uint8 lang_id) VECTOR_CLEAR(msgid); VECTOR_CLEAR(msgstr); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", translations, file); return translations; } +struct load_translation_data { + uint8 lang_id; + int translation_count; +}; + +static void script_load_translation_sub(const char *filename, void *context) +{ + nullpo_retv(context); + + struct load_translation_data *data = context; + + data->translation_count += script->load_translation_file(filename, data->lang_id); +} + +/** + * Loads a translations directory + * + * @param directory The directory structure to read. + * @param lang_id The language identifier. + * @return The amount of strings loaded. + */ +static int script_load_translation(const char *directory, uint8 lang_id) +{ + struct load_translation_data data = { 0 }; + data.lang_id = lang_id; + + nullpo_ret(directory); + + script->add_language(script->get_translation_dir_name(directory)); + if (lang_id >= atcommand->max_message_table) + atcommand->expand_message_table(); + + findfile(directory, ".po", script_load_translation_sub, &data); + + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' translations in '"CL_WHITE"%s"CL_RESET"'.\n", data.translation_count, directory); + return data.translation_count; +} + /** * **/ @@ -5958,6 +6017,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. /// @@ -6680,169 +6752,205 @@ static BUILDIN(warpchar) return true; } -/*========================================== - * Warpparty - [Fredzilla] [Paradox924X] [Jedzkie] [Dastgir] - * Syntax: warpparty("<to_mapname>", <x>, <y>, <party_id>, "<from_mapname>", <include_leader>) - * If 'from_mapname' is specified, only the party members on that map will be warped - * If 'include_leader' option is set to false, the leader will be warped too. - *------------------------------------------*/ + +/** + * Warps a party to a specific/random map or save point. + * + * @code{.herc} + * warpparty("<to map name>", <x>, <y>, <party id>{{, <ignore mapflags>}, "<from map name>"{, <include leader>}}); + * @endcode + * + **/ static BUILDIN(warpparty) { - struct map_session_data *sd = NULL; - struct map_session_data *pl_sd; - struct party_data* p; - int type; - int map_index; - int i; - bool include_leader = true; + const int p_id = script_getnum(st, 5); + struct party_data *p = party->search(p_id); + + if (p == NULL) { + ShowError("script:%s: Party not found! (%d)\n", script->getfuncname(st), p_id); + script_pushint(st, 0); + return false; + } + + const char *m_name_to = script_getstr(st, 2); + const int type = (strcmp(m_name_to, "Random") == 0) ? 0 : + (strcmp(m_name_to, "SavePointAll") == 0) ? 1 : + (strcmp(m_name_to, "SavePoint") == 0) ? 2 : + (strcmp(m_name_to, "Leader") == 0) ? 3 : 4; + int map_index = 0; + + if (type == 4 && (map_index = script->mapindexname2id(st, m_name_to)) == 0) { + ShowError("script:%s: Target map not found! (%s)\n", script->getfuncname(st), m_name_to); + script_pushint(st, 0); + return false; + } - const char* str = script_getstr(st, 2); int x = script_getnum(st, 3); int y = script_getnum(st, 4); - int p_id = script_getnum(st, 5); - const char* str2 = NULL; + struct map_session_data *p_sd; - if (script_hasdata(st, 6)) - str2 = script_getstr(st, 6); - if (script_hasdata(st, 7)) - include_leader = script_getnum(st, 7); + if (type == 3) { + int idx; - p = party->search(p_id); + ARR_FIND(0, MAX_PARTY, idx, p->party.member[idx].leader == 1); - if (p == NULL) - return true; + if (idx == MAX_PARTY || (p_sd = p->data[idx].sd) == NULL) { + ShowError("script:%s: Party leader not found!\n", script->getfuncname(st)); + script_pushint(st, 0); + return false; + } - type = (strcmp(str, "Random") == 0) ? 0 - : (strcmp(str, "SavePointAll") == 0) ? 1 - : (strcmp(str, "SavePoint") == 0) ? 2 - : (strcmp(str, "Leader") == 0) ? 3 - : 4; + map_index = p_sd->mapindex; + x = p_sd->bl.x; + y = p_sd->bl.y; + } else if (type == 2) { + struct map_session_data *sd = script->rid2sd(st); - switch (type) { - case 3: - ARR_FIND(0, MAX_PARTY, i, p->party.member[i].leader); - if (i == MAX_PARTY || !p->data[i].sd) // Leader not found / not online - return true; - pl_sd = p->data[i].sd; - map_index = pl_sd->mapindex; - x = pl_sd->bl.x; - y = pl_sd->bl.y; - break; - case 4: - map_index = script->mapindexname2id(st, str); - break; - case 2: - // "SavePoint" uses save point of the currently attached player - if ((sd = script->rid2sd(st)) == NULL) - return true; - /* Fall through */ - default: - map_index = 0; - break; + if (sd == NULL) { + ShowError("script:%s: No character attached for warp to save point!\n", + script->getfuncname(st)); + script_pushint(st, 0); + return false; + } + + map_index = sd->status.save_point.map; + x = sd->status.save_point.x; + y = sd->status.save_point.y; } - for (i = 0; i < MAX_PARTY; i++) { - if (!(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id) - continue; + const int offset = (script_hasdata(st, 6) && script_isinttype(st, 6)) ? 1 : 0; + const bool ignore_mapflags = (offset == 1 && script_getnum(st, 6) != 0); + const char *m_name_from = script_hasdata(st, 6 + offset) ? script_getstr(st, 6 + offset) : NULL; + const bool include_leader = script_hasdata(st, 7 + offset) ? script_getnum(st, 7 + offset) : true; - if (str2 && strcmp(str2, map->list[pl_sd->bl.m].name) != 0) + if (m_name_from != NULL && script->mapindexname2id(st, m_name_from) == 0) { + ShowError("script:%s: Source map not found! (%s)\n", script->getfuncname(st), m_name_from); + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_PARTY; i++) { + if ((p_sd = p->data[i].sd) == NULL || p_sd->status.party_id != p_id || pc_isdead(p_sd)) continue; - if (pc_isdead(pl_sd)) + if (p->party.member[i].online == 0 || (!include_leader && p->party.member[i].leader == 1)) continue; - if (include_leader == false && p->party.member[i].leader) + if (m_name_from != NULL && strcmp(m_name_from, map->list[p_sd->bl.m].name) != 0) continue; - switch( type ) { - case 0: // Random - if (!map->list[pl_sd->bl.m].flag.nowarp) - pc->randomwarp(pl_sd, CLR_TELEPORT); - break; - case 1: // SavePointAll - if (!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd, pl_sd->status.save_point.map, pl_sd->status.save_point.x, pl_sd->status.save_point.y, CLR_TELEPORT); - break; - case 2: // SavePoint - if (!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); - break; - case 3: // Leader - case 4: // m,x,y - if (!map->list[pl_sd->bl.m].flag.noreturn && !map->list[pl_sd->bl.m].flag.nowarp) - pc->setpos(pl_sd, map_index, x, y, CLR_TELEPORT); - break; + if (!ignore_mapflags) { + if (((type == 0 || type > 2) && map->list[p_sd->bl.m].flag.nowarp == 1) || + (type > 0 && map->list[p_sd->bl.m].flag.noreturn == 1)) + continue; } + + if (type == 1) { + map_index = p_sd->status.save_point.map; + x = p_sd->status.save_point.x; + y = p_sd->status.save_point.y; + } + + if (type > 0) + pc->setpos(p_sd, map_index, x, y, CLR_TELEPORT); + else + pc->randomwarp(p_sd, CLR_TELEPORT); } + script_pushint(st, 1); return true; } -/*========================================== - * Warpguild - [Fredzilla] - * Syntax: warpguild "mapname",x,y,Guild_ID,{"from_mapname"}; - *------------------------------------------*/ + +/** + * Warps a guild to a specific/random map or save point. + * + * @code{.herc} + * warpguild("<to map name>", <x>, <y>, <guild id>{{, <ignore mapflags>}, "<from map name>"}); + * @endcode + * + **/ static BUILDIN(warpguild) { - struct map_session_data *sd = NULL; - struct guild* g; - int type; - int i; - int16 map_id = -1; + const int g_id = script_getnum(st, 5); + struct guild *g = guild->search(g_id); - const char *str = script_getstr(st, 2); - int x = script_getnum(st, 3); - int y = script_getnum(st, 4); - int gid = script_getnum(st, 5); + if (g == NULL) { + ShowError("script:%s: Guild not found! (%d)\n", script->getfuncname(st), g_id); + script_pushint(st, 0); + return false; + } - if (script_hasdata(st, 6)) { - map_id = map->mapname2mapid(script_getstr(st, 6)); + const char *m_name_to = script_getstr(st, 2); + const int type = (strcmp(m_name_to, "Random") == 0) ? 0 : + (strcmp(m_name_to, "SavePointAll") == 0) ? 1 : + (strcmp(m_name_to, "SavePoint") == 0) ? 2 : 3; + int map_index = 0; + + if (type == 3 && (map_index = script->mapindexname2id(st, m_name_to)) == 0) { + ShowError("script:%s: Target map not found! (%s)\n", script->getfuncname(st), m_name_to); + script_pushint(st, 0); + return false; } - g = guild->search(gid); - if (g == NULL) - return true; + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); - type = (strcmp(str, "Random") == 0) ? 0 - : (strcmp(str, "SavePointAll") == 0) ? 1 - : (strcmp(str, "SavePoint") == 0) ? 2 - : 3; + if (type == 2) { + struct map_session_data *sd = script->rid2sd(st); - if (type == 2 && (sd = script->rid2sd(st)) == NULL) - {// "SavePoint" uses save point of the currently attached player - return true; + if (sd == NULL) { + ShowError("script:%s: No character attached for warp to save point!\n", + script->getfuncname(st)); + script_pushint(st, 0); + return false; + } + + map_index = sd->status.save_point.map; + x = sd->status.save_point.x; + y = sd->status.save_point.y; } - for (i = 0; i < MAX_GUILD; i++) { - if (g->member[i].online && g->member[i].sd != NULL) { - struct map_session_data *pl_sd = g->member[i].sd; + const int offset = (script_hasdata(st, 6) && script_isinttype(st, 6)) ? 1 : 0; + const bool ignore_mapflags = (offset == 1 && script_getnum(st, 6) != 0); + const char *m_name_from = script_hasdata(st, 6 + offset) ? script_getstr(st, 6 + offset) : NULL; - if (map_id >= 0 && map_id != pl_sd->bl.m) + if (m_name_from != NULL && script->mapindexname2id(st, m_name_from) == 0) { + ShowError("script:%s: Source map not found! (%s)\n", script->getfuncname(st), m_name_from); + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_GUILD; i++) { + struct map_session_data *g_sd = g->member[i].sd; + + if (g->member[i].online == 0 || g_sd == NULL || g_sd->status.guild_id != g_id || pc_isdead(g_sd)) + continue; + + if (m_name_from != NULL && strcmp(m_name_from, map->list[g_sd->bl.m].name) != 0) + continue; + + if (!ignore_mapflags) { + if (((type == 0 || type > 2) && map->list[g_sd->bl.m].flag.nowarp == 1) || + (type > 0 && map->list[g_sd->bl.m].flag.noreturn == 1)) continue; + } - switch (type) - { - case 0: // Random - if (!map->list[pl_sd->bl.m].flag.nowarp) - pc->randomwarp(pl_sd, CLR_TELEPORT); - break; - case 1: // SavePointAll - if (!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd, pl_sd->status.save_point.map, pl_sd->status.save_point.x, pl_sd->status.save_point.y, CLR_TELEPORT); - break; - case 2: // SavePoint - if (!map->list[pl_sd->bl.m].flag.noreturn) - pc->setpos(pl_sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT); - break; - case 3: // m,x,y - if (!map->list[pl_sd->bl.m].flag.noreturn && !map->list[pl_sd->bl.m].flag.nowarp) - pc->setpos(pl_sd, script->mapindexname2id(st, str), x, y, CLR_TELEPORT); - break; - } + if (type == 1) { + map_index = g_sd->status.save_point.map; + x = g_sd->status.save_point.x; + y = g_sd->status.save_point.y; } + + if (type > 0) + pc->setpos(g_sd, map_index, x, y, CLR_TELEPORT); + else + pc->randomwarp(g_sd, CLR_TELEPORT); } + script_pushint(st, 1); return true; } + /*========================================== * Force Heal a player (hp and sp) *------------------------------------------*/ @@ -6944,59 +7052,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; } @@ -7553,7 +7662,6 @@ static BUILDIN(viewpoint) *------------------------------------------*/ static BUILDIN(countitem) { - int nameid, i; int count = 0; struct item_data* id = NULL; @@ -7575,11 +7683,12 @@ static BUILDIN(countitem) return false; } - nameid = id->nameid; + int nameid = id->nameid; - for(i = 0; i < MAX_INVENTORY; i++) - if(sd->status.inventory[i].nameid == nameid) + for (int i = 0; i < sd->status.inventorySize; i++) { + if (sd->status.inventory[i].nameid == nameid) count += sd->status.inventory[i].amount; + } script_pushint(st,count); return true; @@ -7593,7 +7702,6 @@ static BUILDIN(countitem2) { int nameid, iden, ref, attr, c1, c2, c3, c4; int count = 0; - int i; struct item_data* id = NULL; struct map_session_data *sd = script->rid2sd(st); @@ -7623,7 +7731,7 @@ static BUILDIN(countitem2) c3 = script_getnum(st,8); c4 = script_getnum(st,9); - for(i = 0; i < MAX_INVENTORY; i++) + for (int i = 0; i < sd->status.inventorySize; i++) if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL && sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref && @@ -7638,6 +7746,65 @@ static BUILDIN(countitem2) } /*========================================== + * countnameditem(item ID, { <Char Name / ID> }) + * returns number of named items. + *------------------------------------------*/ +static BUILDIN(countnameditem) +{ + int count = 0; + struct item_data* id = NULL; + struct map_session_data *sd; + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + // Character name was given + sd = script->nick2sd(st, script_getstr(st, 3)); + } else { + // Character ID was given + sd = script->charid2sd(st, script_getnum(st, 3)); + } + } else { + // Use RID by default if no name was provided + sd = script->rid2sd(st); + } + + // Player not attached + if (sd == NULL) { + return true; + } + + if (script_isstringtype(st, 2)) { + // Get item from DB via item name + id = itemdb->search_name(script_getstr(st, 2)); + } else { + // Get item from DB via item ID + id = itemdb->exists(script_getnum(st, 2)); + } + + if (id == NULL) { + ShowError("buildin_countnameditem: Invalid item '%s'.\n", script_getstr(st, 2)); // returns string, regardless of what it was + script_pushint(st, 0); + return false; + } + + for (int i = 0; i < MAX_INVENTORY; i++) { + if (sd->status.inventory[i].nameid > 0 && + sd->inventory_data[i] != NULL && + sd->status.inventory[i].amount > 0 && + sd->status.inventory[i].nameid == id->nameid && + sd->status.inventory[i].card[0] == CARD0_CREATE && + sd->status.inventory[i].card[2] == sd->status.char_id && + sd->status.inventory[i].card[3] == sd->status.char_id >> 16) + { + count += sd->status.inventory[i].amount; + } + } + + script_pushint(st, count); + return true; +} + +/*========================================== * Check if item with this amount can fit in inventory * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY) * Return @@ -8116,12 +8283,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 @@ -8555,6 +8722,48 @@ static BUILDIN(delitem2) return false; } +/** + * Deletes item at given index. + * delitem(<index>{, <amount{, <account id>}}); + */ +static BUILDIN(delitemidx) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 4)) { + if ((sd = script->id2sd(st, script_getnum(st, 4))) == NULL) { + st->state = END; + return true; + } + } else { + if ((sd = script->rid2sd(st)) == NULL) + return true; + } + + int i = script_getnum(st, 2); + if (i < 0 || i >= sd->status.inventorySize) { + ShowError("buildin_delitemidx: Index (%d) should be from 0-%d.\n", i, sd->status.inventorySize - 1); + st->state = END; + return false; + } + + if (itemdb->exists(sd->status.inventory[i].nameid) == NULL) + ShowWarning("buildin_delitemidx: Deleting invalid Item ID (%d).\n", sd->status.inventory[i].nameid); + + int amount = 0; + if (script_hasdata(st, 3)) { + if ((amount = script_getnum(st, 3)) > sd->status.inventory[i].amount) + amount = sd->status.inventory[i].amount; + } else { + amount = sd->status.inventory[i].amount; + } + + if (amount > 0) + script->buildin_delitem_delete(sd, i, &amount, true); + + return true; +} + /*========================================== * Enables/Disables use of items while in an NPC [Skotlex] *------------------------------------------*/ @@ -8662,7 +8871,7 @@ static BUILDIN(getcharid) struct map_session_data *sd; if (script_hasdata(st, 3)) - sd = map->nick2sd(script_getstr(st, 3)); + sd = map->nick2sd(script_getstr(st, 3), false); else sd = script->rid2sd(st); @@ -8820,6 +9029,93 @@ static BUILDIN(getpartyleader) return true; } +enum guildinfo_type { + GUILDINFO_NAME, + GUILDINFO_ID, + GUILDINFO_LEVEL, + GUILDINFO_ONLINE, + GUILDINFO_AV_LEVEL, + GUILDINFO_MAX_MEMBERS, + GUILDINFO_EXP, + GUILDINFO_NEXT_EXP, + GUILDINFO_SKILL_POINTS, + GUILDINFO_MASTER_NAME, + GUILDINFO_MASTER_CID, +}; + +static BUILDIN(getguildinfo) +{ + struct guild *g = NULL; + + if (script_hasdata(st, 3)) { + if (script_isstringtype(st, 3)) { + const char *guild_name = script_getstr(st, 3); + g = guild->searchname(guild_name); + } else { + int guild_id = script_getnum(st, 3); + g = guild->search(guild_id); + } + } else { + struct map_session_data *sd = script->rid2sd(st); + g = sd ? sd->guild : NULL; + } + + enum guildinfo_type type = script_getnum(st, 2); + + if (g == NULL) { + // guild does not exist + switch (type) { + case GUILDINFO_NAME: + case GUILDINFO_MASTER_NAME: + script_pushconststr(st, ""); + break; + default: + script_pushint(st, -1); + } + } else { + switch (type) { + case GUILDINFO_NAME: + script_pushstrcopy(st, g->name); + break; + case GUILDINFO_ID: + script_pushint(st, g->guild_id); + break; + case GUILDINFO_LEVEL: + script_pushint(st, g->guild_lv); + break; + case GUILDINFO_ONLINE: + script_pushint(st, g->connect_member); + break; + case GUILDINFO_AV_LEVEL: + script_pushint(st, g->average_lv); + break; + case GUILDINFO_MAX_MEMBERS: + script_pushint(st, g->max_member); + break; + case GUILDINFO_EXP: + script_pushint(st, g->exp); + break; + case GUILDINFO_NEXT_EXP: + script_pushint(st, g->next_exp); + break; + case GUILDINFO_SKILL_POINTS: + script_pushint(st, g->skill_point); + break; + case GUILDINFO_MASTER_NAME: + script_pushstrcopy(st, g->member[0].name); + break; + case GUILDINFO_MASTER_CID: + script_pushint(st, g->member[0].char_id); + break; + default: + ShowError("script:getguildinfo: unknown info type!\n"); + st->state = END; + return false; + } + } + return true; +} + /*========================================== * Return the name of the @guild_id * null if not found @@ -8925,6 +9221,63 @@ static BUILDIN(getguildmember) return true; } +/** + * getguildonline(<Guild ID>{, type}) + * Returns amount of guild members online. +**/ + +enum script_getguildonline_types { + GUILD_ONLINE_ALL = 0, + GUILD_ONLINE_VENDOR, + GUILD_ONLINE_NO_VENDOR +}; + +BUILDIN(getguildonline) +{ + struct guild *g; + int guild_id = script_getnum(st, 2); + int type = GUILD_ONLINE_ALL, j = 0; + + if ((g = guild->search(guild_id)) == NULL) { + script_pushint(st, -1); + return true; + } + + if (script_hasdata(st, 3)) { + type = script_getnum(st, 3); + + if (type < GUILD_ONLINE_ALL || type > GUILD_ONLINE_NO_VENDOR) { + ShowWarning("buildin_getguildonline: Invalid type specified. Defaulting to GUILD_ONLINE_ALL.\n"); + type = GUILD_ONLINE_ALL; + } + } + + struct map_session_data *sd; + for (int i = 0; i < MAX_GUILD; i++) { + if (g->member[i].online && (sd = g->member[i].sd) != NULL) { + switch (type) { + case GUILD_ONLINE_VENDOR: + if (sd->state.vending > 0) + j++; + break; + + case GUILD_ONLINE_NO_VENDOR: + if (sd->state.vending == 0) + j++; + break; + + default: + j++; + break; + } + } + } + + script_pushint(st, j); + + return true; +} + /*========================================== * Get char string information by type : * Return by @type : @@ -9148,13 +9501,13 @@ static BUILDIN(getequipname) *------------------------------------------*/ static BUILDIN(getbrokenid) { - int i,num,id=0,brokencounter=0; + int num,id=0,brokencounter=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].card[0] == CARD0_PET) continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { @@ -9181,7 +9534,7 @@ static BUILDIN(getbrokencount) if (sd == NULL) return true; - for (i = 0; i < MAX_INVENTORY; i++) { + for (i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].card[0] == CARD0_PET) continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) @@ -9198,14 +9551,13 @@ static BUILDIN(getbrokencount) *------------------------------------------*/ static BUILDIN(repair) { - int i,num; int repaircounter=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - num=script_getnum(st,2); - for(i=0; i<MAX_INVENTORY; i++) { + int num = script_getnum(st, 2); + for(int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].card[0] == CARD0_PET) continue; if ((sd->status.inventory[i].attribute & ATTR_BROKEN) != 0) { @@ -9229,12 +9581,12 @@ static BUILDIN(repair) *------------------------------------------*/ static BUILDIN(repairall) { - int i, repaircounter = 0; + int repaircounter = 0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - for(i = 0; i < MAX_INVENTORY; i++) + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].card[0] == CARD0_PET) continue; @@ -9362,27 +9714,29 @@ static BUILDIN(getequipisidentify) } /*========================================== - * Get the item refined value at pos - * return (npc) - * x : refine amount - * 0 : false (not refined) + * Get the total refine amount of equip at given pos + * return total refine amount *------------------------------------------*/ static BUILDIN(getequiprefinerycnt) { - int i = -1,num; - struct map_session_data *sd; + int total_refine = 0; + struct map_session_data* sd = script->rid2sd(st); - num = script_getnum(st,2); - sd = script->rid2sd(st); - if( sd == NULL ) - return true; + if (sd != NULL) + { + int count = script_lastdata(st); + int script_equip_size = ARRAYLENGTH(script->equip); + for (int n = 2; n <= count; n++) { + int i = -1; + int num = script_getnum(st, n); + if (num > 0 && num <= script_equip_size) + i = pc->checkequip(sd, script->equip[num - 1]); - if (num > 0 && num <= ARRAYLENGTH(script->equip)) - i=pc->checkequip(sd,script->equip[num-1]); - if(i >= 0) - script_pushint(st,sd->status.inventory[i].refine); - else - script_pushint(st,0); + if (i >= 0) + total_refine += sd->status.inventory[i].refine; + } + } + script_pushint(st, total_refine); return true; } @@ -9445,7 +9799,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); @@ -9657,6 +10011,25 @@ static BUILDIN(statusup2) return true; } + +/*========================================== +* Returns the number of stat points needed to change the specified stat by val. +* needed_status_point(<type>,<val>); [secretdataz] +*------------------------------------------*/ +static BUILDIN(needed_status_point) +{ + int type = script_getnum(st, 2); + int val = script_getnum(st, 3);; + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) + script_pushint(st, 0); + else + script_pushint(st, pc->need_status_point(sd, type, val)); + + return true; +} + /// See 'doc/item_bonus.txt' /// /// bonus <bonus type>,<val1>; @@ -10321,6 +10694,7 @@ static BUILDIN(checkmount) /** * Mounts or dismounts a combat mount. * + * setmount <flag>, <mtype>; * setmount <flag>; * setmount; * @@ -10339,6 +10713,8 @@ static BUILDIN(checkmount) * If an invalid value or no flag is specified, the appropriate mount is * auto-detected. As a result of this, there is no need to specify a flag at * all, unless it is a dragon color other than green. + * + * In newer clients you can specify the mado gear type though the mtype argument. */ static BUILDIN(setmount) { @@ -10351,6 +10727,12 @@ static BUILDIN(setmount) if (script_hasdata(st,2)) flag = script_getnum(st,2); + enum mado_type mtype = script_hasdata(st, 3) ? script_getnum(st, 3) : MADO_ROBOT; + if (mtype < MADO_ROBOT || mtype >= MADO_MAX) { + ShowError("script_setmount: Invalid mado type has been passed (%d).\n", flag); + return false; + } + // Color variants for Rune Knight dragon mounts. if (flag != SETMOUNT_TYPE_NONE) { if (flag < SETMOUNT_TYPE_AUTODETECT || flag >= SETMOUNT_TYPE_MAX) { @@ -10377,7 +10759,7 @@ static BUILDIN(setmount) } else if ((sd->job & MAPID_THIRDMASK) == MAPID_MECHANIC) { // Mechanic (Mado Gear) if (pc->checkskill(sd, NC_MADOLICENCE)) - pc->setmadogear(sd, true); + pc->setmadogear(sd, true, mtype); } else { // Knight / Crusader (Peco Peco) if (pc->checkskill(sd, KN_RIDING)) @@ -10391,7 +10773,7 @@ static BUILDIN(setmount) pc->setridingwug(sd, false); } if (pc_ismadogear(sd)) { - pc->setmadogear(sd, false); + pc->setmadogear(sd, false, mtype); } if (pc_isridingpeco(sd)) { pc->setridingpeco(sd, false); @@ -10525,24 +10907,36 @@ static BUILDIN(gettime) return true; } -/*========================================== +/* * GetTimeStr("TimeFMT", Length); - *------------------------------------------*/ + */ static BUILDIN(gettimestr) { char *tmpstr; const char *fmtstr; int maxlen; - time_t now = time(NULL); + time_t now; + + fmtstr = script_getstr(st, 2); + maxlen = script_getnum(st, 3); + + if (script_hasdata(st, 4)) { + int timestamp = script_getnum(st, 4); + if (timestamp < 0) { + ShowWarning("buildin_gettimestr: UNIX timestamp must be in positive value.\n"); + return false; + } - fmtstr=script_getstr(st,2); - maxlen=script_getnum(st,3); + now = (time_t)timestamp; + } else { + now = time(NULL); + } - tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char)); - strftime(tmpstr,maxlen,fmtstr,localtime(&now)); - tmpstr[maxlen]='\0'; + tmpstr = (char *)aMalloc((maxlen +1)*sizeof(char)); + strftime(tmpstr, maxlen, fmtstr, localtime(&now)); + tmpstr[maxlen] = '\0'; - script_pushstr(st,tmpstr); + script_pushstr(st, tmpstr); return true; } @@ -10561,6 +10955,12 @@ static BUILDIN(openstorage) return false; } + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nostorage & 2)) { + script_pushint(st, 0); + return true; + } + storage->open(sd); script_pushint(st, 1); // success flag. @@ -10574,38 +10974,60 @@ static BUILDIN(guildopenstorage) if (sd == NULL) return true; + // Mapflag preventing from openstorage here + if (!pc_has_permission(sd, PC_PERM_BYPASS_NOSTORAGE) && (map->list[sd->bl.m].flag.nogstorage & 2)) { + script_pushint(st, 1); + return true; + } + ret = gstorage->open(sd); script_pushint(st,ret); return true; } -/*========================================== - * Make player use a skill trought item usage - *------------------------------------------*/ -/// itemskill <skill id>,<level>{,flag -/// itemskill "<skill name>",<level>{,flag +/** + * Makes the attached character use a skill by using an item. + * + * @code{.herc} + * itemskill(<skill id>, <skill level>{, <flag>}); + * itemskill("<skill name>", <skill level>{, <flag>}); + * @endcode + * + */ static BUILDIN(itemskill) { - int id; - int lv; struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL || sd->ud.skilltimer != INVALID_TIMER) return true; - id = ( script_isstringtype(st,2) ? skill->name2id(script_getstr(st,2)) : script_getnum(st,2) ); - lv = script_getnum(st,3); -/* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ -#if 0 - if( !script_hasdata(st, 4) ) { - if( !skill->check_condition_castbegin(sd,id,lv) || !skill->check_condition_castend(sd,id,lv) ) + pc->autocast_clear(sd); + sd->autocast.type = AUTOCAST_ITEM; + sd->autocast.skill_id = script_isstringtype(st, 2) ? skill->name2id(script_getstr(st, 2)) : script_getnum(st, 2); + sd->autocast.skill_lv = script_getnum(st, 3); + + int flag = script_hasdata(st, 4) ? script_getnum(st, 4) : ISF_NONE; + + sd->autocast.itemskill_check_conditions = ((flag & ISF_CHECKCONDITIONS) == ISF_CHECKCONDITIONS); + + if (sd->autocast.itemskill_check_conditions) { + if (skill->check_condition_castbegin(sd, sd->autocast.skill_id, sd->autocast.skill_lv) == 0 + || skill->check_condition_castend(sd, sd->autocast.skill_id, sd->autocast.skill_lv) == 0) { + pc->autocast_clear(sd); return true; + } + + sd->autocast.itemskill_conditions_checked = true; } -#endif - sd->skillitem=id; - sd->skillitemlv=lv; - clif->item_skill(sd,id,lv); + + sd->autocast.itemskill_instant_cast = ((flag & ISF_INSTANTCAST) == ISF_INSTANTCAST); + sd->autocast.itemskill_cast_on_self = ((flag & ISF_CASTONSELF) == ISF_CASTONSELF); + + clif->item_skill(sd, sd->autocast.skill_id, sd->autocast.skill_lv); + return true; } + /*========================================== * Attempt to create an item *------------------------------------------*/ @@ -10654,7 +11076,7 @@ static BUILDIN(makepet) if (pet_id >= 0 && sd) { sd->catch_target_class = pet->db[pet_id].class_; intif->create_pet(sd->status.account_id, sd->status.char_id, - (short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv, + pet->db[pet_id].class_, mob->db(pet->db[pet_id].class_)->lv, pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate, 100, 0, 1, pet->db[pet_id].jname); } @@ -10952,10 +11374,16 @@ static BUILDIN(killmonster) int16 m,allflag=0; mapname=script_getstr(st,2); event=script_getstr(st,3); - if(strcmp(event,"All")==0) + + if (strcmpi(event, "all") == 0) { + if (strcmp(event, "all") != 0) { + ShowWarning("buildin_killmonster: \"%s\" deprecated! Please use \"all\" instead.\n", event); + script->reportsrc(st); + } allflag = 1; - else + } else { script->check_event(st, event); + } if( (m=map->mapname2mapid(mapname))<0 ) return true; @@ -11016,6 +11444,21 @@ static BUILDIN(killmonsterall) return true; } +static BUILDIN(killmonstergid) +{ + int mobgid = script_getnum(st, 2); + struct mob_data *md = map->id2md(mobgid); + + if (md == NULL) { + ShowWarning("buildin_killmonstergid: Error in finding monster GID '%d' or the target is not a monster.\n", mobgid); + return false; + } + + md->state.npc_killmonster = 1; + status_kill(&md->bl); + return true; +} + /*========================================== * Creates a clone of a player. * clone map, x, y, event, char_id, master_id, mode, flag, duration @@ -11357,6 +11800,13 @@ static BUILDIN(getunits) const char *mapname = script_getstr(st, 5); int16 m = map->mapname2mapid(mapname); + if (m == -1) { + ShowError("script:getunits: Invalid map(%s) provided.\n", mapname); + script->reportdata(data); + st->state = END; + return false; + } + if (script_hasdata(st, 9)) { int16 x1 = script_getnum(st, 6); int16 y1 = script_getnum(st, 7); @@ -11651,6 +12101,18 @@ static BUILDIN(playerattached) } /*========================================== + * Used by OnTouchNPC: label to return monster GID + *------------------------------------------*/ +static BUILDIN(mobattached) +{ + if (st->rid == 0 || map->id2md(st->rid) == NULL) + script_pushint(st, 0); + else + script_pushint(st, st->rid); + return true; +} + +/*========================================== *------------------------------------------*/ static BUILDIN(announce) { @@ -11662,12 +12124,12 @@ static BUILDIN(announce) int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY size_t len = strlen(mes); + send_target target = ALL_CLIENT; + struct block_list *bl = NULL; Assert_retr(false, len < INT_MAX); if( flag&(BC_TARGET_MASK|BC_SOURCE_MASK) ) { // Broadcast source or broadcast region defined - send_target target; - struct block_list *bl = NULL; if (flag&BC_NPC) { // If bc_npc flag is set, use NPC as broadcast source bl = map->id2bl(st->oid); @@ -11685,17 +12147,13 @@ static BUILDIN(announce) case BC_SELF: target = SELF; break; default: target = ALL_CLIENT; break; // BC_ALL } - - if (fontColor) - clif->broadcast2(bl, mes, (int)len+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target); - else - clif->broadcast(bl, mes, (int)len+1, flag&BC_COLOR_MASK, target); - } else { - if (fontColor) - intif->broadcast2(mes, (int)len+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY); - else - intif->broadcast(mes, (int)len+1, flag&BC_COLOR_MASK); } + + if (fontColor) + clif->broadcast2(bl, mes, (int)len+1, (unsigned int)strtoul(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target); + else + clif->broadcast(bl, mes, (int)len+1, flag&BC_COLOR_MASK, target); + return true; } /*========================================== @@ -12063,6 +12521,56 @@ static BUILDIN(hideonnpc) npc->enable(str,4); return true; } +/*========================================== + *------------------------------------------*/ +static BUILDIN(cloakonnpc) +{ + struct npc_data *nd = npc->name2id(script_getstr(st, 2)); + if (nd == NULL) { + ShowError("buildin_cloakonnpc: invalid npc name '%s'.\n", script_getstr(st, 2)); + return false; + } + + if (script_hasdata(st, 3)) { + struct map_session_data *sd = map->id2sd(script_getnum(st, 3)); + if (sd == NULL) + return false; + + uint32 val = nd->option; + nd->option |= OPTION_CLOAK; + clif->changeoption_target(&nd->bl, &sd->bl, SELF); + nd->option = val; + } else { + nd->option |= OPTION_CLOAK; + clif->changeoption(&nd->bl); + } + return true; +} +/*========================================== + *------------------------------------------*/ +static BUILDIN(cloakoffnpc) +{ + struct npc_data *nd = npc->name2id(script_getstr(st, 2)); + if (nd == NULL) { + ShowError("buildin_cloakoffnpc: invalid npc name '%s'.\n", script_getstr(st, 2)); + return false; + } + + if (script_hasdata(st, 3)) { + struct map_session_data *sd = map->id2sd(script_getnum(st, 3)); + if (sd == NULL) + return false; + + uint32 val = nd->option; + nd->option &= ~OPTION_CLOAK; + clif->changeoption_target(&nd->bl, &sd->bl, SELF); + nd->option = val; + } else { + nd->option &= ~OPTION_CLOAK; + clif->changeoption(&nd->bl); + } + return true; +} /* Starts a status effect on the target unit or on the attached player. * @@ -12381,7 +12889,7 @@ static BUILDIN(homunculus_morphembryo) clif->additem(sd, 0, 0, i); clif->emotion(&sd->hd->bl, E_SWT); } else { - homun->vaporize(sd, HOM_ST_MORPH); + homun->vaporize(sd, HOM_ST_MORPH, true); success = true; } } else { @@ -12933,7 +13441,8 @@ enum mapinfo_info { MAPINFO_ID, MAPINFO_SIZE_X, MAPINFO_SIZE_Y, - MAPINFO_ZONE + MAPINFO_ZONE, + MAPINFO_NPC_COUNT }; static BUILDIN(getmapinfo) @@ -12958,7 +13467,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; } @@ -12989,8 +13498,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; } @@ -13064,6 +13576,8 @@ static BUILDIN(getmapflag) case MF_NOVIEWID: script_pushint(st, map->list[m].flag.noviewid); break; case MF_PAIRSHIP_STARTABLE: script_pushint(st, map->list[m].flag.pairship_startable); break; case MF_PAIRSHIP_ENDABLE: script_pushint(st, map->list[m].flag.pairship_endable); break; + case MF_NOSTORAGE: script_pushint(st, map->list[m].flag.nostorage); break; + case MF_NOGSTORAGE: script_pushint(st, map->list[m].flag.nogstorage); break; } } @@ -13194,6 +13708,8 @@ static BUILDIN(setmapflag) case MF_NOVIEWID: map->list[m].flag.noviewid = (val <= 0) ? EQP_NONE : val; break; case MF_PAIRSHIP_STARTABLE: map->list[m].flag.pairship_startable = 1; break; case MF_PAIRSHIP_ENDABLE: map->list[m].flag.pairship_endable = 1; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = cap_value(val, 0, 3); break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = cap_value(val, 0, 3); break; } } @@ -13285,6 +13801,8 @@ static BUILDIN(removemapflag) case MF_NOCASHSHOP: map->list[m].flag.nocashshop = 0; break; case MF_NOAUTOLOOT: map->list[m].flag.noautoloot = 0; break; case MF_NOVIEWID: map->list[m].flag.noviewid = EQP_NONE; break; + case MF_NOSTORAGE: map->list[m].flag.nostorage = 0; break; + case MF_NOGSTORAGE: map->list[m].flag.nogstorage = 0; break; } } @@ -13796,7 +14314,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; @@ -14089,47 +14608,53 @@ static BUILDIN(strmobinfo) return true; } -/*========================================== - * Summon guardians [Valaris] - * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id> - *------------------------------------------*/ +/** + * Summons a castle guardian mob. + * + * @code{.herc} + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, <guardian index>}); + * guardian("<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"{, <guardian index>}}); + * @endcode + * + * @author Valaris + * + **/ static BUILDIN(guardian) { - int class_ = 0, x = 0, y = 0, guardian = 0; - const char *str, *mapname, *evt=""; bool has_index = false; + int guardian = 0; + const char *event = ""; - mapname = script_getstr(st,2); - x = script_getnum(st,3); - y = script_getnum(st,4); - str = script_getstr(st,5); - class_ = script_getnum(st,6); - - if( script_hasdata(st,8) ) - {// "<event label>",<guardian index> - evt=script_getstr(st,7); - guardian=script_getnum(st,8); + if (script_hasdata(st, 8)) { /// "<event label>", <guardian index> + event = script_getstr(st, 7); + script->check_event(st, event); + guardian = script_getnum(st, 8); has_index = true; - } else if( script_hasdata(st,7) ) { - struct script_data *data = script_getdata(st,7); - script->get_val(st,data); // Dereference if it's a variable - if( data_isstring(data) ) { - // "<event label>" - evt=script_getstr(st,7); - } else if( data_isint(data) ) { - // <guardian index> - guardian=script_getnum(st,7); + } else if (script_hasdata(st, 7)) { + struct script_data *data = script_getdata(st, 7); + + script->get_val(st, data); /// Dereference if it's a variable. + + if (data_isstring(data)) { /// "<event label>" + event = script_getstr(st, 7); + script->check_event(st, event); + } else if (data_isint(data)) { /// <guardian index> + guardian = script_getnum(st, 7); has_index = true; } else { - ShowError("script:guardian: invalid data type for argument #6 (from 1)\n"); + ShowError("script:guardian: Invalid data type for argument #6!\n"); script->reportdata(data); return false; } } - script->check_event(st, evt); - script_pushint(st, mob->spawn_guardian(mapname,x,y,str,class_,evt,guardian,has_index)); + const char *mapname = script_getstr(st, 2); + const char *name = script_getstr(st, 5); + const int x = script_getnum(st, 3); + const int y = script_getnum(st, 4); + const int mob_id = script_getnum(st, 6); + script_pushint(st, mob->spawn_guardian(mapname, x, y, name, mob_id, event, guardian, has_index, st->oid)); return true; } /*========================================== @@ -14253,24 +14778,34 @@ static BUILDIN(getitemslots) return true; } -// TODO: add matk here if needed - -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - *------------------------------------------*/ +/** + * Returns various information about an item. + * + * @code{.herc} + * getiteminfo(<item ID>, <type>); + * getiteminfo("<item name>", <type>); + * @endcode + * + **/ static BUILDIN(getiteminfo) { - int item_id = script_getnum(st, 2); - int n = script_getnum(st, 3); - struct item_data *it = itemdb->exists(item_id); + struct item_data *it; + + if (script_isstringtype(st, 2)) { /// Item name. + const char *name = script_getstr(st, 2); + it = itemdb->search_name(name); + } else { /// Item ID. + it = itemdb->exists(script_getnum(st, 2)); + } if (it == NULL) { script_pushint(st, -1); return true; } - switch (n) { + int type = script_getnum(st, 3); + + switch (type) { case ITEMINFO_BUYPRICE: script_pushint(st, it->value_buy); break; @@ -14325,11 +14860,105 @@ static BUILDIN(getiteminfo) case ITEMINFO_TRADE: script_pushint(st, it->flag.trade_restriction); break; + case ITEMINFO_ELV_MAX: + script_pushint(st, it->elvmax); + break; + case ITEMINFO_DROPEFFECT_MODE: + script_pushint(st, it->dropeffectmode); + break; + case ITEMINFO_DELAY: + script_pushint(st, it->delay); + break; + case ITEMINFO_CLASS_BASE_1: + script_pushint(st, it->class_base[0]); + break; + case ITEMINFO_CLASS_BASE_2: + script_pushint(st, it->class_base[1]); + break; + case ITEMINFO_CLASS_BASE_3: + script_pushint(st, it->class_base[2]); + break; + case ITEMINFO_CLASS_UPPER: + script_pushint(st, it->class_upper); + break; + case ITEMINFO_FLAG_NO_REFINE: + script_pushint(st, it->flag.no_refine); + break; + case ITEMINFO_FLAG_DELAY_CONSUME: + script_pushint(st, it->flag.delay_consume); + break; + case ITEMINFO_FLAG_AUTOEQUIP: + script_pushint(st, it->flag.autoequip); + break; + case ITEMINFO_FLAG_AUTO_FAVORITE: + script_pushint(st, it->flag.auto_favorite); + break; + case ITEMINFO_FLAG_BUYINGSTORE: + script_pushint(st, it->flag.buyingstore); + break; + case ITEMINFO_FLAG_BINDONEQUIP: + script_pushint(st, it->flag.bindonequip); + break; + case ITEMINFO_FLAG_KEEPAFTERUSE: + script_pushint(st, it->flag.keepafteruse); + break; + case ITEMINFO_FLAG_FORCE_SERIAL: + script_pushint(st, it->flag.force_serial); + break; + case ITEMINFO_FLAG_NO_OPTIONS: + script_pushint(st, it->flag.no_options); + break; + case ITEMINFO_FLAG_DROP_ANNOUNCE: + script_pushint(st, it->flag.drop_announce); + break; + case ITEMINFO_FLAG_SHOWDROPEFFECT: + script_pushint(st, it->flag.showdropeffect); + break; + case ITEMINFO_STACK_AMOUNT: + script_pushint(st, it->stack.amount); + break; + case ITEMINFO_STACK_FLAG: { + int stack_flag = 0; + + if (it->stack.inventory != 0) + stack_flag |= 1; + + if (it->stack.cart != 0) + stack_flag |= 2; + + if (it->stack.storage != 0) + stack_flag |= 4; + + if (it->stack.guildstorage != 0) + stack_flag |= 8; + + script_pushint(st, stack_flag); + break; + } + case ITEMINFO_ITEM_USAGE_FLAG: + script_pushint(st, it->item_usage.flag); + break; + case ITEMINFO_ITEM_USAGE_OVERRIDE: + script_pushint(st, it->item_usage.override); + break; + case ITEMINFO_GM_LV_TRADE_OVERRIDE: + script_pushint(st, it->gm_lv_trade_override); + break; + case ITEMINFO_ID: + script_pushint(st, it->nameid); + break; + case ITEMINFO_AEGISNAME: + script_pushstrcopy(st, it->name); + break; + case ITEMINFO_NAME: + script_pushstrcopy(st, it->jname); + break; default: - ShowError("buildin_getiteminfo: Invalid item type %d.\n", n); - script_pushint(st,-1); + ShowError("buildin_getiteminfo: Invalid item info type %d.\n", type); + script_pushint(st, -1); return false; } + return true; } @@ -14594,6 +15223,78 @@ static BUILDIN(setiteminfo) case ITEMINFO_TRADE: it->flag.trade_restriction = value; break; + case ITEMINFO_ELV_MAX: + it->elvmax = cap_value(value, 0, MAX_LEVEL); + break; + case ITEMINFO_DROPEFFECT_MODE: + it->dropeffectmode = value; + break; + case ITEMINFO_DELAY: + it->delay = value; + break; + case ITEMINFO_CLASS_BASE_1: + it->class_base[0] = value; + break; + case ITEMINFO_CLASS_BASE_2: + it->class_base[1] = value; + break; + case ITEMINFO_CLASS_BASE_3: + it->class_base[2] = value; + break; + case ITEMINFO_CLASS_UPPER: + it->class_upper = value; + break; + case ITEMINFO_FLAG_NO_REFINE: + it->flag.no_refine = cap_value(value, 0, MAX_REFINE); + break; + case ITEMINFO_FLAG_DELAY_CONSUME: + it->flag.delay_consume = value; + break; + case ITEMINFO_FLAG_AUTOEQUIP: + it->flag.autoequip = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_AUTO_FAVORITE: + it->flag.auto_favorite = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_BUYINGSTORE: + it->flag.buyingstore = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_BINDONEQUIP: + it->flag.bindonequip = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_KEEPAFTERUSE: + it->flag.keepafteruse = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_FORCE_SERIAL: + it->flag.force_serial = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_NO_OPTIONS: + it->flag.no_options = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_DROP_ANNOUNCE: + it->flag.drop_announce = cap_value(value, 0, 1); + break; + case ITEMINFO_FLAG_SHOWDROPEFFECT: + it->flag.showdropeffect = cap_value(value, 0, 1); + break; + case ITEMINFO_STACK_AMOUNT: + it->stack.amount = cap_value(value, 0, USHRT_MAX); + break; + case ITEMINFO_STACK_FLAG: + it->stack.inventory = ((value & 1) != 0); + it->stack.cart = ((value & 2) != 0); + it->stack.storage = ((value & 4) != 0); + it->stack.guildstorage = ((value & 8) != 0); + break; + case ITEMINFO_ITEM_USAGE_FLAG: + it->item_usage.flag = cap_value(value, 0, 1); + break; + case ITEMINFO_ITEM_USAGE_OVERRIDE: + it->item_usage.override = value; + break; + case ITEMINFO_GM_LV_TRADE_OVERRIDE: + it->gm_lv_trade_override = value; + break; default: ShowError("buildin_setiteminfo: invalid type %d.\n", n); script_pushint(st,-1); @@ -14714,47 +15415,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 i,j=0,k; - if(!sd) return true; + if (sd == NULL) + return true; - for(i=0;i<MAX_INVENTORY;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; } @@ -14816,10 +15525,9 @@ static BUILDIN(getskilllist) static BUILDIN(clearitem) { struct map_session_data *sd = script->rid2sd(st); - int i; if (sd == NULL) return true; - for (i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if (sd->status.inventory[i].amount) { pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_SCRIPT); } @@ -15259,6 +15967,56 @@ static BUILDIN(specialeffect) return true; } +/*========================================== + * Special effects with num [4144] + *------------------------------------------*/ +static BUILDIN(specialeffectnum) +{ + struct block_list *bl = NULL; + int type = script_getnum(st, 2); + int num = script_getnum(st, 3); + int num2 = script_getnum(st, 4); + enum send_target target = AREA; + + if (script_hasdata(st, 5)) { + target = script_getnum(st, 5); + } + + if (script_hasdata(st, 6)) { + if (script_isstringtype(st, 6)) { + struct npc_data *nd = npc->name2id(script_getstr(st, 6)); + if (nd != NULL) { + bl = &nd->bl; + } + } else { + bl = map->id2bl(script_getnum(st, 6)); + } + } else { + bl = map->id2bl(st->oid); + } + + if (bl == NULL) { + return true; + } + + uint64 bigNum = ((uint64)num2) * 0xffffffff + num; + if (target == SELF) { + struct map_session_data *sd; + if (script_hasdata(st, 7)) { + sd = map->id2sd(script_getnum(st, 7)); + } else { + sd = script->rid2sd(st); + } + if (sd != NULL) { + clif->specialeffect_value_single(bl, type, bigNum, sd->fd); + } + } else { + clif->specialeffect_value(bl, type, bigNum, target); + } + + return true; +} + static BUILDIN(specialeffect2) { struct map_session_data *sd; @@ -15353,7 +16111,6 @@ static BUILDIN(atcommand) struct map_session_data *sd, *dummy_sd = NULL; int fd; const char* cmd; - bool ret = true; cmd = script_getstr(st,2); @@ -15376,11 +16133,12 @@ static BUILDIN(atcommand) if (!atcommand->exec(fd, sd, cmd, false)) { ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd); - script->reportsrc(st); - ret = false; + if (dummy_sd != NULL) + aFree(dummy_sd); + return false; } if (dummy_sd) aFree(dummy_sd); - return ret; + return true; } /** @@ -15468,36 +16226,74 @@ static BUILDIN(recovery) return true; } -/*========================================== - * Get your pet info: getpetinfo(n) - * n -> 0:pet_id 1:pet_class 2:pet_name - * 3:friendly 4:hungry, 5: rename flag. - *------------------------------------------*/ +/* + * Get your current pet information + */ static BUILDIN(getpetinfo) { struct map_session_data *sd = script->rid2sd(st); - struct pet_data *pd; - int type=script_getnum(st,2); + if (sd == NULL) + return true; - if (sd == NULL || sd->pd == NULL) { - if (type == 2) - script_pushconststr(st,"null"); + struct pet_data *pd = sd->pd; + int type = script_getnum(st, 2); + if (pd == NULL) { + if (type == PETINFO_NAME) + script_pushconststr(st, "null"); else - script_pushint(st,0); + script_pushint(st, 0); return true; } - pd = sd->pd; + switch(type) { - case 0: script_pushint(st,pd->pet.pet_id); break; - case 1: script_pushint(st,pd->pet.class_); break; - case 2: script_pushstrcopy(st,pd->pet.name); break; - case 3: script_pushint(st,pd->pet.intimate); break; - case 4: script_pushint(st,pd->pet.hungry); break; - case 5: script_pushint(st,pd->pet.rename_flag); break; - default: - script_pushint(st,0); - break; + case PETINFO_ID: + script_pushint(st, pd->pet.pet_id); + break; + case PETINFO_CLASS: + script_pushint(st, pd->pet.class_); + break; + case PETINFO_NAME: + script_pushstrcopy(st, pd->pet.name); + break; + case PETINFO_INTIMACY: + script_pushint(st, pd->pet.intimate); + break; + case PETINFO_HUNGRY: + script_pushint(st, pd->pet.hungry); + break; + case PETINFO_RENAME: + script_pushint(st, pd->pet.rename_flag); + break; + case PETINFO_GID: + script_pushint(st, pd->bl.id); + break; + case PETINFO_EGGITEM: + script_pushint(st, pd->pet.egg_id); + break; + case PETINFO_FOODITEM: + script_pushint(st, pd->petDB->FoodID); + break; + case PETINFO_ACCESSORYITEM: + script_pushint(st, pd->petDB->AcceID); + break; + case PETINFO_ACCESSORYFLAG: + script_pushint(st, (pd->pet.equip != 0)? 1:0); + break; + case PETINFO_EVO_EGGID: + if (VECTOR_DATA(pd->petDB->evolve_data) != NULL) + script_pushint(st, VECTOR_DATA(pd->petDB->evolve_data)->petEggId); + else + script_pushint(st, 0); + break; + case PETINFO_AUTOFEED: + script_pushint(st, pd->pet.autofeed); + break; + default: + ShowWarning("buildin_getpetinfo: Invalid type %d.\n", type); + script_pushint(st, 0); + return false; } + return true; } @@ -15535,20 +16331,15 @@ static BUILDIN(gethominfo) return true; } -/// Retrieves information about character's mercenary -/// getmercinfo <type>[,<char id>]; +/* + * Retrieves information about character's mercenary + * getmercinfo <type>{, <char id> }; + */ static BUILDIN(getmercinfo) { - int type; - struct map_session_data* sd; - struct mercenary_data* md; - - type = script_getnum(st,2); - - if (script_hasdata(st,3)) { - int char_id = script_getnum(st,3); - - if ((sd = script->charid2sd(st, char_id)) == NULL) { + struct map_session_data *sd; + if (script_hasdata(st, 3)) { + if ((sd = script->charid2sd(st, script_getnum(st, 3))) == NULL) { script_pushnil(st); return true; } @@ -15557,27 +16348,48 @@ static BUILDIN(getmercinfo) return true; } - md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL; + struct mercenary_data *md = (sd->status.mer_id && sd->md)? sd->md : NULL; + int type = script_getnum(st, 2); + if (md == NULL) { + if (type == MERCINFO_NAME) + script_pushconststr(st, ""); + else + script_pushint(st, 0); + return true; + } - switch( type ) - { - case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break; - case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break; - case 2: - if( md ) - script_pushstrcopy(st,md->db->name); - else - script_pushconststr(st,""); - break; - case 3: script_pushint(st,md ? mercenary->get_faith(md) : 0); break; - case 4: script_pushint(st,md ? mercenary->get_calls(md) : 0); break; - case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break; - case 6: script_pushint(st,md ? mercenary->get_lifetime(md) : 0); break; - case 7: script_pushint(st,md ? md->db->lv : 0); break; - default: - ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id); - script_pushnil(st); - return false; + switch (type) { + case MERCINFO_ID: + script_pushint(st, md->mercenary.mercenary_id); + break; + case MERCINFO_CLASS: + script_pushint(st, md->mercenary.class_); + break; + case MERCINFO_NAME: + script_pushstrcopy(st, md->db->name); + break; + case MERCINFO_FAITH: + script_pushint(st, mercenary->get_faith(md)); + break; + case MERCINFO_CALLS: + script_pushint(st, mercenary->get_calls(md)); + break; + case MERCINFO_KILLCOUNT: + script_pushint(st, md->mercenary.kill_count); + break; + case MERCINFO_LIFETIME: + script_pushint(st, mercenary->get_lifetime(md)); + break; + case MERCINFO_LEVEL: + script_pushint(st, md->db->lv); + break; + case MERCINFO_GID: + script_pushint(st, md->bl.id); + break; + default: + ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id); + script_pushnil(st); + return false; } return true; @@ -15590,19 +16402,18 @@ static BUILDIN(getmercinfo) *------------------------------------------*/ static BUILDIN(checkequipedcard) { - int n,i,c=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) return true; - c = script_getnum(st,2); + int c = script_getnum(st,2); - for( i=0; i<MAX_INVENTORY; i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]) { if (itemdb_isspecial(sd->status.inventory[i].card[0])) continue; - for(n=0;n<sd->inventory_data[i]->slot;n++) { + for (int n = 0; n < sd->inventory_data[i]->slot; n++) { if(sd->status.inventory[i].card[n]==c) { script_pushint(st,1); return true; @@ -15678,6 +16489,29 @@ static BUILDIN(message) return true; } +static BUILDIN(servicemessage) +{ + struct map_session_data *sd = NULL; + + if (script_hasdata(st, 4)) { + if (script_isstringtype(st, 4)) + sd = script->nick2sd(st, script_getstr(st, 4)); + else + sd = script->id2sd(st, script_getnum(st, 4)); + } else { + sd = script->rid2sd(st); + } + + if (sd == NULL) + return true; + + const char *message = script_getstr(st, 2); + const int color = script_getnum(st, 3); + clif->serviceMessageColor(sd, color, message); + + return true; +} + /*========================================== * npctalk (sends message to surrounding area) * usage: npctalk("<message>"{, "<npc name>"{, <show_name>}}); @@ -15716,37 +16550,41 @@ static BUILDIN(npctalk) // change npc walkspeed [Valaris] static BUILDIN(npcspeed) { - struct npc_data* nd; - int speed; - - speed = script_getnum(st,2); - nd = map->id2nd(st->oid); + struct npc_data *nd = map->id2nd(st->oid); + int speed = script_getnum(st, 2); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcspeed: floating NPC don't have unit data.\n"); + return false; + } nd->speed = speed; nd->ud->state.speed_changed = 1; } return true; } + // make an npc walk to a position [Valaris] static BUILDIN(npcwalkto) { struct npc_data *nd = map->id2nd(st->oid); - int x=0,y=0; - - x=script_getnum(st,2); - y=script_getnum(st,3); + int x = script_getnum(st, 2); + int y = script_getnum(st, 3); if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcwalkto: floating NPC don't have unit data.\n"); + return false; + } if (!nd->status.hp) { status_calc_npc(nd, SCO_FIRST); } else { status_calc_npc(nd, SCO_NONE); } - unit->walktoxy(&nd->bl,x,y,0); + unit->walk_toxy(&nd->bl, x, y, 0); } return true; @@ -15758,6 +16596,10 @@ static BUILDIN(npcstop) if (nd != NULL) { unit->bl2ud2(&nd->bl); // ensure nd->ud is safe to edit + if (nd->ud == NULL) { + ShowWarning("buildin_npcstop: floating NPC don't have unit data.\n"); + return false; + } unit->stop_walking(&nd->bl, STOPWALKING_FLAG_FIXPOS|STOPWALKING_FLAG_NEXTCELL); } @@ -15990,7 +16832,7 @@ static BUILDIN(getmapxy) case 0: //Get Character Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); else sd = map->id2sd(script_getnum(st,6)); } else { @@ -16017,7 +16859,7 @@ static BUILDIN(getmapxy) case 2: //Get Pet Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); else { bl = map->id2bl(script_getnum(st,6)); break; @@ -16039,7 +16881,7 @@ static BUILDIN(getmapxy) case 4: //Get Homun Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -16054,7 +16896,7 @@ static BUILDIN(getmapxy) case 5: //Get Mercenary Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -16069,7 +16911,7 @@ static BUILDIN(getmapxy) case 6: //Get Elemental Position if (script_hasdata(st,6)) { if (script_isstringtype(st,6)) { - sd = map->nick2sd(script_getstr(st,6)); + sd = map->nick2sd(script_getstr(st,6), false); } else { bl = map->id2bl(script_getnum(st,6)); break; @@ -16168,38 +17010,54 @@ static BUILDIN(logmes) return true; } +/** + * Summons a mob which will act as a slave for the invoking character. + * + * @code{.herc} + * summon("mob name", <mob id>{, <timeout>{, "event label"}}); + * @endcode + * + * @author Celest + * + **/ static BUILDIN(summon) { - int class_, timeout=0; - const char *str,*event=""; - struct mob_data *md; - int64 tick = timer->gettick(); struct map_session_data *sd = script->rid2sd(st); + if (sd == NULL) return true; - str = script_getstr(st,2); - class_ = script_getnum(st,3); - if( script_hasdata(st,4) ) - timeout=script_getnum(st,4); - if( script_hasdata(st,5) ) { - event=script_getstr(st,5); + const int64 tick = timer->gettick(); + + clif->skill_poseffect(&sd->bl, AM_CALLHOMUN, 1, sd->bl.x, sd->bl.y, tick); + + const char *event = ""; + + if (script_hasdata(st, 5)) { + event = script_getstr(st, 5); script->check_event(st, event); } - clif->skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); + const char *name = script_getstr(st, 2); + const int mob_id = script_getnum(st, 3); + struct mob_data *md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, name, mob_id, event, + SZ_SMALL, AI_NONE, 0); - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, class_, event, SZ_SMALL, AI_NONE); - if (md) { - md->master_id=sd->bl.id; + if (md != NULL) { + md->master_id = sd->bl.id; md->special_state.ai = AI_ATTACK; - if( md->deletetimer != INVALID_TIMER ) + + if (md->deletetimer != INVALID_TIMER) timer->delete(md->deletetimer, mob->timer_delete); - md->deletetimer = timer->add(tick+(timeout>0?timeout*1000:60000),mob->timer_delete,md->bl.id,0); - mob->spawn (md); //Now it is ready for spawning. - clif->specialeffect(&md->bl,344,AREA); + + const int timeout = script_hasdata(st, 4) ? script_getnum(st, 4) * 1000 : 60000; + + md->deletetimer = timer->add(tick + ((timeout == 0) ? 60000 : timeout), mob->timer_delete, md->bl.id, 0); + mob->spawn(md); + clif->specialeffect(&md->bl, 344, AREA); sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); } + return true; } @@ -16237,6 +17095,8 @@ static BUILDIN(isequippedcnt) if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; + if(j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index) continue; + if(j == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index)) continue; if(!sd->inventory_data[index]) continue; @@ -16290,6 +17150,8 @@ static BUILDIN(isequipped) if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue; if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue; if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue; + if(j == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index) continue; + if(j == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index)) continue; if(!sd->inventory_data[index]) continue; @@ -16447,9 +17309,9 @@ static BUILDIN(equip) ShowError("wrong item ID : equipitem(%d)\n",nameid); return false; } - ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].equip == 0 ); - if( i < MAX_INVENTORY ) - pc->equipitem(sd,i,item_data->equip); + ARR_FIND(0, sd->status.inventorySize, i, sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].equip == 0); + if (i < sd->status.inventorySize) + pc->equipitem(sd, i, item_data->equip); return true; } @@ -16507,21 +17369,21 @@ static BUILDIN(equip2) c2 = script_getnum(st, 7); c3 = script_getnum(st, 8); - ARR_FIND( 0, MAX_INVENTORY, i,( sd->status.inventory[i].equip == 0 && + ARR_FIND(0, sd->status.inventorySize, i, (sd->status.inventory[i].equip == 0 && sd->status.inventory[i].nameid == nameid && sd->status.inventory[i].refine == ref && sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c0 && sd->status.inventory[i].card[1] == c1 && sd->status.inventory[i].card[2] == c2 && - sd->status.inventory[i].card[3] == c3 ) ); + sd->status.inventory[i].card[3] == c3)); - if( i < MAX_INVENTORY ) { + if (i < sd->status.inventorySize) { script_pushint(st,1); pc->equipitem(sd,i,item_data->equip); - } - else + } else { script_pushint(st,0); + } return true; } @@ -17576,6 +18438,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; @@ -17981,10 +18854,12 @@ static BUILDIN(npcshopdelitem) unsigned int nameid = script_getnum(st,i); ARR_FIND(0, size, n, nd->u.shop.shop_item[n].nameid == nameid); - if (n < size) { - memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n)); - size--; + if (n == size) { + continue; + } else if (n < size - 1) { + memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0]) * (size - n - 1)); } + size--; } RECREATE(nd->u.shop.shop_item, struct npc_item_list, size); @@ -18217,6 +19092,7 @@ static BUILDIN(getmonsterinfo) case 20: script_pushint(st,monster->status.def_ele); break; case 21: script_pushint(st,monster->status.mode); break; case 22: script_pushint(st,monster->mexp); break; + case 23: script_pushint(st, monster->dmg_taken_rate); break; default: script_pushint(st,-1); //wrong Index } return true; @@ -18290,7 +19166,7 @@ static BUILDIN(searchitem) if ((items[0] = itemdb->exists(atoi(itemname)))) { count = 1; } else { - count = itemdb->search_name_array(items, ARRAYLENGTH(items), itemname, 0); + count = itemdb->search_name_array(items, ARRAYLENGTH(items), itemname, IT_SEARCH_NAME_PARTIAL); if (count > MAX_SEARCH) count = MAX_SEARCH; } @@ -18387,12 +19263,14 @@ static BUILDIN(pcblockmove) static BUILDIN(setpcblock) { - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd = script_hasdata(st, 4) ? script->id2sd(st, script_getnum(st, 4)) : script->rid2sd(st); enum pcblock_action_flag type = script_getnum(st, 2); int state = (script_getnum(st, 3) > 0) ? 1 : 0; - if (sd == NULL) + if (sd == NULL) { + script_pushint(st, 0); return true; + } if ((type & PCBLOCK_MOVE) != 0) sd->block_action.move = state; @@ -18418,12 +19296,16 @@ static BUILDIN(setpcblock) if ((type & PCBLOCK_COMMANDS) != 0) sd->block_action.commands = state; + if ((type & PCBLOCK_NPC) != 0) + sd->block_action.npc = state; + + script_pushint(st, 1); return true; } static BUILDIN(checkpcblock) { - struct map_session_data *sd = script->rid2sd(st); + struct map_session_data *sd = script_hasdata(st, 2) ? script->id2sd(st, script_getnum(st, 2)) : script->rid2sd(st); int retval = PCBLOCK_NONE; if (sd == NULL) { @@ -18455,6 +19337,9 @@ static BUILDIN(checkpcblock) if (sd->block_action.commands != 0) retval |= PCBLOCK_COMMANDS; + if (sd->block_action.npc != 0) + retval |= PCBLOCK_NPC; + script_pushint(st, retval); return true; } @@ -18529,22 +19414,25 @@ static BUILDIN(getunittype) /** * Sets real-time unit data for a game object. - * Setunitdata <GUID>,<DataType>,<Val1>{,<Val2>,<Val3>} + * + * @code{.herc} + * setunitdata <GUID>, <DataType>, <Val1>{, <Val2>, <Val3>} + * @endcode + * * @param1 GUID GID of the unit. * @param2 DataType Type of Data to be set for the unit. * @param3 Value#1 Value to be passed as change in data. * @param4 Value#2 Optional int value to be passed for certain data types. * @param5 Value#3 Optional int value to be passed for certain data types. * @return 1 on success, 0 on failure. - */ + * + * Note: Please make this script command only modify ONE INTEGER value. + * If need to modify string type data, or having multiple arguments, please introduce a new script command. + * + **/ static BUILDIN(setunitdata) { - struct block_list *bl = NULL; - const char *mapname = NULL, *udtype = NULL; - int type = 0, val = 0, val2 = 0, val3 = 0; - struct map_session_data *tsd = NULL; - - bl = map->id2bl(script_getnum(st, 2)); + struct block_list *bl = map->id2bl(script_getnum(st, 2)); if (bl == NULL) { ShowWarning("buildin_setunitdata: Error in finding object with given GID %d!\n", script_getnum(st, 2)); @@ -18552,22 +19440,26 @@ static BUILDIN(setunitdata) return false; } - type = script_getnum(st, 3); + int type = script_getnum(st, 3); - /* type bounds */ + // Type bounds. if (type < UDT_SIZE || type >= UDT_MAX) { // Note: UDT_TYPE is not valid here ShowError("buildin_setunitdata: Invalid unit data type %d provided.\n", type); script_pushint(st, 0); return false; } - /* Mandatory Argument 3 */ + const char *mapname = NULL; + int val = 0; + + // Mandatory argument #3. Subject to deprecate. if (type == UDT_MAPIDXY) { if (!script_isstringtype(st, 4)) { ShowError("buildin_setunitdata: Invalid data type for argument #3.\n"); script_pushint(st, 0); return false; } + mapname = script_getstr(st, 4); } else { if (script_isstringtype(st, 4)) { @@ -18575,68 +19467,87 @@ static BUILDIN(setunitdata) script_pushint(st, 0); return false; } + val = script_getnum(st, 4); } -/* checks if value is out of bounds. */ + +/**************************************************************************************************** + * Define temporary macros. [BEGIN] + ****************************************************************************************************/ + +// Checks if value is out of bounds. #define setunitdata_check_bounds(arg, min, max) \ do { \ if (script_getnum(st, (arg)) < (min) || script_getnum(st, (arg)) > (max)) { \ - ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d, max: %d)\n", script_getnum(st, (arg)), (arg)-1, (min), (max)); \ + ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d, max: %d)\n", \ + script_getnum(st, (arg)), (arg) - 1, (min), (max)); \ script_pushint(st, 0); \ return false; \ } \ } while(0); -/* checks if value is out of bounds. */ + +// Checks if value is too low. #define setunitdata_check_min(arg, min) \ do { \ if (script_getnum(st, (arg)) < (min)) { \ - ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d)\n", script_getnum(st, (arg)), (arg)-1, (min)); \ + ShowError("buildin_setunitdata: Invalid value %d for argument #%d. (min: %d)\n", \ + script_getnum(st, (arg)), (arg) - 1, (min)); \ script_pushint(st, 0); \ return false; \ } \ } while(0); -/* checks if the argument doesn't exist, if required. - * also checks if the argument exists, if not required. */ + +// Checks if the argument doesn't exist, if required. Also checks if the argument exists, if not required. #define setunitdata_assert_arg(arg, required) \ do { \ if (required && !script_hasdata(st, (arg))) { \ - ShowError("buildin_setunitdata: Type %d reqires argument #%d.\n", type, (arg)-1); \ + ShowError("buildin_setunitdata: Type %d reqires argument #%d.\n", type, (arg) - 1); \ script_pushint(st, 0); \ return false; \ } else if (!required && script_hasdata(st, arg)) { \ - ShowError("buildin_setunitdata: Argument %d is not required for type %d.\n", (arg)-1, type); \ + ShowError("buildin_setunitdata: Argument %d is not required for type %d.\n", (arg) - 1, type); \ script_pushint(st, 0); \ return false; \ } \ } while (0); -/* checks if the data is an integer. */ + +// Checks if the data is an integer. #define setunitdata_check_int(arg) \ do { \ setunitdata_assert_arg((arg), true); \ if (script_isstringtype(st, (arg))) { \ - ShowError("buildin_setunitdata: Argument #%d expects integer, string given.\n", (arg)-1); \ + ShowError("buildin_setunitdata: Argument #%d expects integer, string given.\n", (arg) - 1); \ script_pushint(st, 0); \ return false; \ } \ } while(0); -/* checks if the data is a string. */ + +// Checks if the data is a string. #define setunitdata_check_string(arg) \ do { \ setunitdata_assert_arg((arg), true); \ if (script_isinttype(st, (arg))) { \ - ShowError("buildin_setunitdata: Argument #%d expects string, integer given.\n", (arg)-1); \ + ShowError("buildin_setunitdata: Argument #%d expects string, integer given.\n", (arg) - 1); \ script_pushint(st, 0); \ return false; \ } \ } while(0); +/**************************************************************************************************** + * Define temporary macros. [END] + ****************************************************************************************************/ + if (type != UDT_MAPIDXY && type != UDT_WALKTOXY) { setunitdata_assert_arg(5, false); setunitdata_assert_arg(6, false); } - switch (type) - { + int val2 = 0; + int val3 = 0; + + struct map_session_data *tsd = NULL; + + switch (type) { case UDT_SIZE: setunitdata_check_bounds(4, SZ_SMALL, SZ_BIG); break; @@ -18662,30 +19573,36 @@ static BUILDIN(setunitdata) case UDT_MASTERAID: setunitdata_check_min(4, 0); tsd = map->id2sd(val); + if (tsd == NULL) { - ShowWarning("buildin_setunitdata: Account ID %d not found for master change!\n",val); + ShowWarning("buildin_setunitdata: Account ID %d not found for master change!\n", val); script_pushint(st, 0); return false; } + break; case UDT_MASTERCID: setunitdata_check_min(4, 0); tsd = map->charid2sd(val); + if (tsd == NULL) { - ShowWarning("buildin_setunitdata: Character ID %d not found for master change!\n",val); + ShowWarning("buildin_setunitdata: Character ID %d not found for master change!\n", val); script_pushint(st, 0); return false; } + break; case UDT_MAPIDXY: - if ((val = map->mapname2mapid(mapname)) == -1) { + if ((val = map->mapname2mapid(mapname)) == INDEX_NOT_FOUND) { ShowError("buildin_setunitdata: Non-existent map %s provided.\n", mapname); + script_pushint(st, 0); return false; } + setunitdata_check_int(5); setunitdata_check_int(6); - setunitdata_check_bounds(5, 0, MAX_MAP_SIZE/2); - setunitdata_check_bounds(6, 0, MAX_MAP_SIZE/2); + setunitdata_check_bounds(5, 0, MAX_MAP_SIZE / 2); + setunitdata_check_bounds(6, 0, MAX_MAP_SIZE / 2); val2 = script_getnum(st, 5); val3 = script_getnum(st, 6); break; @@ -18693,8 +19610,8 @@ static BUILDIN(setunitdata) setunitdata_assert_arg(6, false); setunitdata_check_int(5); val2 = script_getnum(st, 5); - setunitdata_check_bounds(4, 0, MAX_MAP_SIZE/2); - setunitdata_check_bounds(5, 0, MAX_MAP_SIZE/2); + setunitdata_check_bounds(4, 0, MAX_MAP_SIZE / 2); + setunitdata_check_bounds(5, 0, MAX_MAP_SIZE / 2); break; case UDT_SPEED: setunitdata_check_bounds(4, 0, MAX_WALK_SPEED); @@ -18749,76 +19666,110 @@ static BUILDIN(setunitdata) setunitdata_check_bounds(4, 0, SHRT_MAX); break; case UDT_HUNGER: - setunitdata_check_bounds(4, 0, 99); + setunitdata_check_bounds(4, PET_HUNGER_STARVING, PET_HUNGER_STUFFED); // Pets and Homunculi have the same hunger value bounds. break; case UDT_RACE: case UDT_ELETYPE: 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; + case UDT_DAMAGE_TAKEN_RATE: + setunitdata_check_bounds(4, 1, INT_MAX); + break; default: break; } +/**************************************************************************************************** + * Undefine temporary macros. [BEGIN] + ****************************************************************************************************/ + #undef setunitdata_check_bounds +#undef setunitdata_check_min #undef setunitdata_assert_arg #undef setunitdata_check_int #undef setunitdata_check_string - /* Set the values */ +/**************************************************************************************************** + * Undefine temporary macros. [END] + ****************************************************************************************************/ + + // Set the values. switch (bl->type) { - case BL_MOB: - { + case BL_MOB: { struct mob_data *md = BL_UCAST(BL_MOB, bl); - nullpo_retr(false, md); - switch (type) - { + if (md == NULL) { + ShowError("buildin_setunitdata: Can't find monster for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } + + switch (type) { case UDT_SIZE: - md->status.size = (unsigned char) val; + md->status.size = (unsigned char)val; break; case UDT_LEVEL: md->level = val; + + if ((battle_config.show_mob_info & 4) != 0) + clif->blname_ack(0, &md->bl); + break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); - clif->charnameack(0, &md->bl); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); + clif->blname_ack(0, &md->bl); break; case UDT_MAXHP: - md->status.max_hp = (unsigned int) val; - clif->charnameack(0, &md->bl); + md->status.max_hp = (unsigned int)val; + clif->blname_ack(0, &md->bl); break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: - md->status.max_sp = (unsigned int) val; + md->status.max_sp = (unsigned int)val; break; case UDT_MASTERAID: md->master_id = val; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_SPEED: - md->status.speed = (unsigned short) val; + md->status.speed = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_MODE: - md->status.mode = (enum e_mode) val; + md->status.mode = (enum e_mode)val; break; case UDT_AI: - md->special_state.ai = (enum ai) val; + md->special_state.ai = (enum ai)val; break; case UDT_SCOPTION: - md->sc.option = (unsigned int) val; + md->sc.option = (unsigned int)val; break; case UDT_SEX: - md->vd->sex = (char) val; + md->vd->sex = (char)val; break; case UDT_CLASS: mob->class_change(md, val); @@ -18848,115 +19799,121 @@ static BUILDIN(setunitdata) clif->changelook(bl, LOOK_WEAPON, val); break; case UDT_LOOKDIR: - unit->setdir(bl, (uint8) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_CANMOVETICK: md->ud.canmove_tick = val; break; case UDT_STR: - md->status.str = (unsigned short) val; + md->status.str = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_AGI: - md->status.agi = (unsigned short) val; + md->status.agi = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_VIT: - md->status.vit = (unsigned short) val; + md->status.vit = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_INT: - md->status.int_ = (unsigned short) val; + md->status.int_ = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_DEX: - md->status.dex = (unsigned short) val; + md->status.dex = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_LUK: - md->status.luk = (unsigned short) val; + md->status.luk = (unsigned short)val; status->calc_misc(bl, &md->status, md->level); break; case UDT_ATKRANGE: - md->status.rhw.range = (unsigned short) val; + md->status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - md->status.rhw.atk = (unsigned short) val; + md->status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - md->status.rhw.atk2 = (unsigned short) val; + md->status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - md->status.matk_min = (unsigned short) val; + md->status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - md->status.matk_max = (unsigned short) val; + md->status.matk_max = (unsigned short)val; break; case UDT_DEF: - md->status.def = (defType) val; + md->status.def = (defType)val; break; case UDT_MDEF: - md->status.mdef = (defType) val; + md->status.mdef = (defType)val; break; case UDT_HIT: - md->status.hit = (short) val; + md->status.hit = (short)val; break; case UDT_FLEE: - md->status.flee = (short) val; + md->status.flee = (short)val; break; case UDT_PDODGE: - md->status.flee2 = (short) val; + md->status.flee2 = (short)val; break; case UDT_CRIT: - md->status.cri = (short) val; + md->status.cri = (short)val; break; case UDT_RACE: - md->status.race = (unsigned char) val; + md->status.race = (unsigned char)val; break; case UDT_ELETYPE: - md->status.def_ele = (unsigned char) val; + md->status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - md->status.ele_lv = (unsigned char) val; + md->status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - md->status.amotion = (unsigned short) val; + md->status.amotion = (unsigned short)val; break; case UDT_ADELAY: - md->status.adelay = (unsigned short) val; + md->status.adelay = (unsigned short)val; break; case UDT_DMOTION: - md->status.dmotion = (unsigned short) val; + md->status.dmotion = (unsigned short)val; + break; + case UDT_DAMAGE_TAKEN_RATE: + md->dmg_taken_rate = (int)val; break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for mob unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for mob unit.\n", type); script_pushint(st, 0); return false; } - } + break; - case BL_HOM: - { + } + case BL_HOM: { struct homun_data *hd = BL_UCAST(BL_HOM, bl); - nullpo_retr(false, hd); + if (hd == NULL) { + ShowError("buildin_setunitdata: Can't find Homunculus for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } - switch (type) - { + switch (type) { case UDT_SIZE: - hd->base_status.size = (unsigned char) val; + hd->base_status.size = (unsigned char)val; break; case UDT_LEVEL: - hd->homunculus.level = (short) val; + hd->homunculus.level = (short)val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: hd->homunculus.max_hp = val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: hd->homunculus.max_sp = val; @@ -18966,634 +19923,645 @@ static BUILDIN(setunitdata) hd->master = tsd; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_SPEED: - hd->base_status.speed = (unsigned short) val; + hd->base_status.speed = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_LOOKDIR: - unit->setdir(bl, (unsigned char) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_CANMOVETICK: hd->ud.canmove_tick = val; break; case UDT_STR: - hd->base_status.str = (unsigned short) val; + hd->base_status.str = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_AGI: - hd->base_status.agi = (unsigned short) val; + hd->base_status.agi = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_VIT: - hd->base_status.vit = (unsigned short) val; + hd->base_status.vit = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_INT: - hd->base_status.int_ = (unsigned short) val; + hd->base_status.int_ = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_DEX: - hd->base_status.dex = (unsigned short) val; + hd->base_status.dex = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_LUK: - hd->base_status.luk = (unsigned short) val; + hd->base_status.luk = (unsigned short)val; status->calc_misc(bl, &hd->base_status, hd->homunculus.level); break; case UDT_ATKRANGE: - hd->base_status.rhw.range = (unsigned short) val; + hd->base_status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - hd->base_status.rhw.atk = (unsigned short) val; + hd->base_status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - hd->base_status.rhw.atk2 = (unsigned short) val; + hd->base_status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - hd->base_status.matk_min = (unsigned short) val; + hd->base_status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - hd->base_status.matk_max = (unsigned short) val; + hd->base_status.matk_max = (unsigned short)val; break; case UDT_DEF: - hd->base_status.def = (defType) val; + hd->base_status.def = (defType)val; break; case UDT_MDEF: - hd->base_status.mdef = (defType) val; + hd->base_status.mdef = (defType)val; break; case UDT_HIT: - hd->base_status.hit = (short) val; + hd->base_status.hit = (short)val; break; case UDT_FLEE: - hd->base_status.flee = (short) val; + hd->base_status.flee = (short)val; break; case UDT_PDODGE: - hd->base_status.flee2 = (short) val; + hd->base_status.flee2 = (short)val; break; case UDT_CRIT: - hd->base_status.cri = (short) val; + hd->base_status.cri = (short)val; break; case UDT_RACE: - hd->base_status.race = (unsigned char) val; + hd->base_status.race = (unsigned char)val; break; case UDT_ELETYPE: - hd->base_status.def_ele = (unsigned char) val; + hd->base_status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - hd->base_status.ele_lv = (unsigned char) val; + hd->base_status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - hd->base_status.amotion = (unsigned short) val; + hd->base_status.amotion = (unsigned short)val; break; case UDT_ADELAY: - hd->base_status.adelay = (unsigned short) val; + hd->base_status.adelay = (unsigned short)val; break; case UDT_DMOTION: - hd->base_status.dmotion = (unsigned short) val; + hd->base_status.dmotion = (unsigned short)val; break; case UDT_HUNGER: - hd->homunculus.hunger = (short) val; + hd->homunculus.hunger = (short)val; clif->send_homdata(hd->master, SP_HUNGRY, hd->homunculus.hunger); break; case UDT_INTIMACY: - homun->add_intimacy(hd, (unsigned int) val); + homun->add_intimacy(hd, (unsigned int)val); clif->send_homdata(hd->master, SP_INTIMATE, hd->homunculus.intimacy / 100); break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for homunculus unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for homunculus unit.\n", type); script_pushint(st, 0); return false; } - clif->send_homdata(hd->master, SP_ACK, 0); // send homun data - } + clif->send_homdata(hd->master, SP_ACK, 0); // Send Homunculus data. break; - case BL_PET: - { + } + case BL_PET: { struct pet_data *pd = BL_UCAST(BL_PET, bl); - nullpo_retr(false, pd); + if (pd == NULL) { + ShowError("buildin_setunitdata: Can't find pet for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } - switch (type) - { + switch (type) { case UDT_SIZE: - pd->status.size = (unsigned char) val; + pd->status.size = (unsigned char)val; break; case UDT_LEVEL: - pd->pet.level = (short) val; + pd->pet.level = (short)val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: - pd->status.max_hp = (unsigned int) val; + pd->status.max_hp = (unsigned int)val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: - pd->status.max_sp = (unsigned int) val; + pd->status.max_sp = (unsigned int)val; break; case UDT_MASTERAID: pd->pet.account_id = val; pd->msd = tsd; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_SPEED: - pd->status.speed = (unsigned short) val; + pd->status.speed = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_LOOKDIR: - unit->setdir(bl, (unsigned char) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_CANMOVETICK: pd->ud.canmove_tick = val; break; case UDT_STR: - pd->status.str = (unsigned short) val; + pd->status.str = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_AGI: - pd->status.agi = (unsigned short) val; + pd->status.agi = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_VIT: - pd->status.vit = (unsigned short) val; + pd->status.vit = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_INT: - pd->status.int_ = (unsigned short) val; + pd->status.int_ = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_DEX: - pd->status.dex = (unsigned short) val; + pd->status.dex = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_LUK: - pd->status.luk = (unsigned short) val; + pd->status.luk = (unsigned short)val; status->calc_misc(bl, &pd->status, pd->pet.level); break; case UDT_ATKRANGE: - pd->status.rhw.range = (unsigned short) val; + pd->status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - pd->status.rhw.atk = (unsigned short) val; + pd->status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - pd->status.rhw.atk2 = (unsigned short) val; + pd->status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - pd->status.matk_min = (unsigned short) val; + pd->status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - pd->status.matk_max = (unsigned short) val; + pd->status.matk_max = (unsigned short)val; break; case UDT_DEF: - pd->status.def = (defType) val; + pd->status.def = (defType)val; break; case UDT_MDEF: - pd->status.mdef = (defType) val; + pd->status.mdef = (defType)val; break; case UDT_HIT: - pd->status.hit = (short) val; + pd->status.hit = (short)val; break; case UDT_FLEE: - pd->status.flee = (short) val; + pd->status.flee = (short)val; break; case UDT_PDODGE: - pd->status.flee2 = (short) val; + pd->status.flee2 = (short)val; break; case UDT_CRIT: - pd->status.cri = (short) val; + pd->status.cri = (short)val; break; case UDT_RACE: - pd->status.race = (unsigned char) val; + pd->status.race = (unsigned char)val; break; case UDT_ELETYPE: - pd->status.def_ele = (unsigned char) val; + pd->status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - pd->status.ele_lv = (unsigned char) val; + pd->status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - pd->status.amotion = (unsigned short) val; + pd->status.amotion = (unsigned short)val; break; case UDT_ADELAY: - pd->status.adelay = (unsigned short) val; + pd->status.adelay = (unsigned short)val; break; case UDT_DMOTION: - pd->status.dmotion = (unsigned short) val; + pd->status.dmotion = (unsigned short)val; break; case UDT_INTIMACY: pet->set_intimate(pd, val); clif->send_petdata(pd->msd, pd, 1, pd->pet.intimate); break; case UDT_HUNGER: - pd->pet.hungry = (short) val; + pet->set_hunger(pd, val); break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for pet unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for pet unit.\n", type); script_pushint(st, 0); return false; } - clif->send_petstatus(pd->msd); // send pet data - } + + clif->send_petstatus(pd->msd); // Send pet data. break; - case BL_MER: - { + } + case BL_MER: { struct mercenary_data *mc = BL_UCAST(BL_MER, bl); - nullpo_retr(false, mc); + if (mc == NULL) { + ShowError("buildin_setunitdata: Can't find mercenary for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } - switch (type) - { + switch (type) { case UDT_SIZE: - mc->base_status.size = (unsigned char) val; + mc->base_status.size = (unsigned char)val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: - mc->base_status.max_hp = (unsigned int) val; + mc->base_status.max_hp = (unsigned int)val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: - mc->base_status.max_sp = (unsigned int) val; + mc->base_status.max_sp = (unsigned int)val; break; case UDT_MASTERCID: mc->mercenary.char_id = val; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_SPEED: - mc->base_status.size = (unsigned char) val; + mc->base_status.size = (unsigned char)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_LOOKDIR: - unit->setdir(bl, (unsigned char) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_CANMOVETICK: mc->ud.canmove_tick = val; break; case UDT_STR: - mc->base_status.str = (unsigned short) val; + mc->base_status.str = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_AGI: - mc->base_status.agi = (unsigned short) val; + mc->base_status.agi = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_VIT: - mc->base_status.vit = (unsigned short) val; + mc->base_status.vit = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_INT: - mc->base_status.int_ = (unsigned short) val; + mc->base_status.int_ = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_DEX: - mc->base_status.dex = (unsigned short) val; + mc->base_status.dex = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_LUK: - mc->base_status.luk = (unsigned short) val; + mc->base_status.luk = (unsigned short)val; status->calc_misc(bl, &mc->base_status, mc->db->lv); break; case UDT_ATKRANGE: - mc->base_status.rhw.range = (unsigned short) val; + mc->base_status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - mc->base_status.rhw.atk = (unsigned short) val; + mc->base_status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - mc->base_status.rhw.atk2 = (unsigned short) val; + mc->base_status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - mc->base_status.matk_min = (unsigned short) val; + mc->base_status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - mc->base_status.matk_max = (unsigned short) val; + mc->base_status.matk_max = (unsigned short)val; break; case UDT_DEF: - mc->base_status.def = (defType) val; + mc->base_status.def = (defType)val; break; case UDT_MDEF: - mc->base_status.mdef = (defType) val; + mc->base_status.mdef = (defType)val; break; case UDT_HIT: - mc->base_status.hit = (short) val; + mc->base_status.hit = (short)val; break; case UDT_FLEE: - mc->base_status.flee = (short) val; + mc->base_status.flee = (short)val; break; case UDT_PDODGE: - mc->base_status.flee2 = (short) val; + mc->base_status.flee2 = (short)val; break; case UDT_CRIT: - mc->base_status.cri = (short) val; + mc->base_status.cri = (short)val; break; case UDT_RACE: - mc->base_status.race = (unsigned char) val; + mc->base_status.race = (unsigned char)val; break; case UDT_ELETYPE: - mc->base_status.def_ele = (unsigned char) val; + mc->base_status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - mc->base_status.ele_lv = (unsigned char) val; + mc->base_status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - mc->base_status.amotion = (unsigned short) val; + mc->base_status.amotion = (unsigned short)val; break; case UDT_ADELAY: - mc->base_status.adelay = (unsigned short) val; + mc->base_status.adelay = (unsigned short)val; break; case UDT_DMOTION: - mc->base_status.dmotion = (unsigned short) val; + mc->base_status.dmotion = (unsigned short)val; break; case UDT_MERC_KILLCOUNT: - mc->mercenary.kill_count = (unsigned int) val; + mc->mercenary.kill_count = (unsigned int)val; break; case UDT_LIFETIME: - mc->mercenary.life_time = (unsigned int) val; + mc->mercenary.life_time = (unsigned int)val; break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for mercenary unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for mercenary unit.\n", type); script_pushint(st, 0); return false; } + // Send mercenary data. clif->mercenary_info(map->charid2sd(mc->mercenary.char_id)); clif->mercenary_skillblock(map->charid2sd(mc->mercenary.char_id)); - } break; - case BL_ELEM: - { + } + case BL_ELEM: { struct elemental_data *ed = BL_UCAST(BL_ELEM, bl); - nullpo_retr(false, ed); + if (ed == NULL) { + ShowError("buildin_setunitdata: Can't find Elemental for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } - switch (type) - { + switch (type) { case UDT_SIZE: - ed->base_status.size = (unsigned char) val; + ed->base_status.size = (unsigned char)val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: - ed->base_status.max_hp = (unsigned int) val; + ed->base_status.max_hp = (unsigned int)val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: - ed->base_status.max_sp = (unsigned int) val; + ed->base_status.max_sp = (unsigned int)val; break; case UDT_MASTERCID: ed->elemental.char_id = val; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_SPEED: - ed->base_status.speed = (unsigned short) val; + ed->base_status.speed = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_LOOKDIR: - unit->setdir(bl, (unsigned char) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_CANMOVETICK: ed->ud.canmove_tick = val; break; case UDT_STR: - ed->base_status.str = (unsigned short) val; + ed->base_status.str = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_AGI: - ed->base_status.agi = (unsigned short) val; + ed->base_status.agi = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_VIT: - ed->base_status.vit = (unsigned short) val; + ed->base_status.vit = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_INT: - ed->base_status.int_ = (unsigned short) val; + ed->base_status.int_ = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_DEX: - ed->base_status.dex = (unsigned short) val; + ed->base_status.dex = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_LUK: - ed->base_status.luk = (unsigned short) val; + ed->base_status.luk = (unsigned short)val; status->calc_misc(bl, &ed->base_status, ed->db->lv); break; case UDT_ATKRANGE: - ed->base_status.rhw.range = (unsigned short) val; + ed->base_status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - ed->base_status.rhw.atk = (unsigned short) val; + ed->base_status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - ed->base_status.rhw.atk2 = (unsigned short) val; + ed->base_status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - ed->base_status.matk_min = (unsigned short) val; + ed->base_status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - ed->base_status.matk_max = (unsigned short) val; + ed->base_status.matk_max = (unsigned short)val; break; case UDT_DEF: - ed->base_status.def = (defType) val; + ed->base_status.def = (defType)val; break; case UDT_MDEF: - ed->base_status.mdef = (defType) val; + ed->base_status.mdef = (defType)val; break; case UDT_HIT: - ed->base_status.hit = (short) val; + ed->base_status.hit = (short)val; break; case UDT_FLEE: - ed->base_status.flee = (short) val; + ed->base_status.flee = (short)val; break; case UDT_PDODGE: - ed->base_status.flee2 = (short) val; + ed->base_status.flee2 = (short)val; break; case UDT_CRIT: - ed->base_status.cri = (short) val; + ed->base_status.cri = (short)val; break; case UDT_RACE: - ed->base_status.race = (unsigned char) val; + ed->base_status.race = (unsigned char)val; break; case UDT_ELETYPE: - ed->base_status.def_ele = (unsigned char) val; + ed->base_status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - ed->base_status.ele_lv = (unsigned char) val; + ed->base_status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - ed->base_status.amotion = (unsigned short) val; + ed->base_status.amotion = (unsigned short)val; break; case UDT_ADELAY: - ed->base_status.adelay = (unsigned short) val; + ed->base_status.adelay = (unsigned short)val; break; case UDT_DMOTION: - ed->base_status.dmotion = (unsigned short) val; + ed->base_status.dmotion = (unsigned short)val; break; case UDT_LIFETIME: ed->elemental.life_time = val; break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for elemental unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for elemental unit.\n", type); script_pushint(st, 0); return false; } - clif->elemental_info(ed->master); - } + + clif->elemental_info(ed->master); // Send Elemental data. break; - case BL_NPC: - { + } + case BL_NPC: { struct npc_data *nd = BL_UCAST(BL_NPC, bl); - nullpo_retr(false, nd); + if (nd == NULL) { + ShowError("buildin_setunitdata: Can't find NPC for GID %d!\n", script_getnum(st, 2)); + script_pushint(st, 0); + return false; + } - switch (type) - { + switch (type) { case UDT_SIZE: - nd->status.size = (unsigned char) val; + nd->status.size = (unsigned char)val; break; case UDT_LEVEL: - nd->level = (unsigned short) val; + nd->level = (unsigned short)val; break; case UDT_HP: - status->set_hp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_hp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXHP: - nd->status.max_hp = (unsigned int) val; + nd->status.max_hp = (unsigned int)val; break; case UDT_SP: - status->set_sp(bl, (unsigned int) val, STATUS_HEAL_DEFAULT); + status->set_sp(bl, (unsigned int)val, STATUS_HEAL_DEFAULT); break; case UDT_MAXSP: - nd->status.max_sp = (unsigned int) val; + nd->status.max_sp = (unsigned int)val; break; case UDT_MAPIDXY: - unit->warp(bl, (short) val, (short) val2, (short) val3, CLR_TELEPORT); + unit->warp(bl, (short)val, (short)val2, (short)val3, CLR_TELEPORT); break; case UDT_WALKTOXY: - if (!unit->walktoxy(bl, (short) val, (short) val2, 2)) - unit->movepos(bl, (short) val, (short) val2, 0, 0); + if (unit->walk_toxy(bl, (short)val, (short)val2, 2) != 0) + unit->movepos(bl, (short)val, (short)val2, 0, 0); break; case UDT_CLASS: - npc->setclass(nd, (short) val); + npc->setclass(nd, (short)val); break; case UDT_SPEED: - nd->speed = (short) val; + nd->speed = (short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_LOOKDIR: - unit->setdir(bl, (unsigned char) val); + unit->set_dir(bl, (enum unit_dir)val); break; case UDT_STR: - nd->status.str = (unsigned short) val; + nd->status.str = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_AGI: - nd->status.agi = (unsigned short) val; + nd->status.agi = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_VIT: - nd->status.vit = (unsigned short) val; + nd->status.vit = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_INT: - nd->status.int_ = (unsigned short) val; + nd->status.int_ = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_DEX: - nd->status.dex = (unsigned short) val; + nd->status.dex = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_LUK: - nd->status.luk = (unsigned short) val; + nd->status.luk = (unsigned short)val; status->calc_misc(bl, &nd->status, nd->level); break; case UDT_STATPOINT: - nd->stat_point = (unsigned short) val; + nd->stat_point = (unsigned short)val; break; case UDT_ATKRANGE: - nd->status.rhw.range = (unsigned short) val; + nd->status.rhw.range = (unsigned short)val; break; case UDT_ATKMIN: - nd->status.rhw.atk = (unsigned short) val; + nd->status.rhw.atk = (unsigned short)val; break; case UDT_ATKMAX: - nd->status.rhw.atk2 = (unsigned short) val; + nd->status.rhw.atk2 = (unsigned short)val; break; case UDT_MATKMIN: - nd->status.matk_min = (unsigned short) val; + nd->status.matk_min = (unsigned short)val; break; case UDT_MATKMAX: - nd->status.matk_max = (unsigned short) val; + nd->status.matk_max = (unsigned short)val; break; case UDT_DEF: - nd->status.def = (defType) val; + nd->status.def = (defType)val; break; case UDT_MDEF: - nd->status.mdef = (defType) val; + nd->status.mdef = (defType)val; break; case UDT_HIT: - nd->status.hit = (short) val; + nd->status.hit = (short)val; break; case UDT_FLEE: - nd->status.flee = (short) val; + nd->status.flee = (short)val; break; case UDT_PDODGE: - nd->status.flee2 = (short) val; + nd->status.flee2 = (short)val; break; case UDT_CRIT: - nd->status.cri = (short) val; + nd->status.cri = (short)val; break; case UDT_RACE: - nd->status.race = (unsigned char) val; + nd->status.race = (unsigned char)val; break; case UDT_ELETYPE: - nd->status.def_ele = (unsigned char) val; + nd->status.def_ele = (unsigned char)val; break; case UDT_ELELEVEL: - nd->status.ele_lv = (unsigned char) val; + nd->status.ele_lv = (unsigned char)val; break; case UDT_AMOTION: - nd->status.amotion = (unsigned short) val; + nd->status.amotion = (unsigned short)val; break; case UDT_ADELAY: - nd->status.adelay = (unsigned short) val; + nd->status.adelay = (unsigned short)val; break; case UDT_DMOTION: - nd->status.dmotion = (unsigned short) val; + nd->status.dmotion = (unsigned short)val; break; case UDT_SEX: nd->vd.sex = (char)val; @@ -19630,19 +20598,21 @@ static BUILDIN(setunitdata) clif->changelook(bl, LOOK_BODY2, val); break; default: - ShowWarning("buildin_setunitdata: Invalid data type '%s' for NPC unit.\n", udtype); + ShowWarning("buildin_setunitdata: Invalid data type '%d' for NPC unit.\n", type); script_pushint(st, 0); return false; } - } + break; + } default: ShowError("buildin_setunitdata: Unknown object!\n"); script_pushint(st, 0); return false; - } // end of bl->type switch + } // End of bl->type switch. script_pushint(st, 1); + return true; } @@ -19653,6 +20623,10 @@ static BUILDIN(setunitdata) * @param2 DataType Type of Data to be set for the unit. * @param3 Variable array reference to store data into. (used for UDT_MAPIDXY) * @return 0 on failure, <value> on success + + Note: Please make this script command only return ONE INTEGER value. + If the unit data having multiple arguments, or need to return in array, + please introduce a new script command. */ static BUILDIN(getunitdata) { @@ -19667,7 +20641,7 @@ static BUILDIN(getunitdata) if (bl == NULL) { ShowWarning("buildin_getunitdata: Error in finding object with given GID %d!\n", script_getnum(st, 2)); - script_pushint(st, 0); + script_pushint(st, -1); return false; } @@ -19676,15 +20650,15 @@ static BUILDIN(getunitdata) /* Type check */ if (type < UDT_TYPE || type >= UDT_MAX) { ShowError("buildin_getunitdata: Invalid unit data type %d provided.\n", type); - script_pushint(st, 0); + script_pushint(st, -1); return false; } - /* Argument checks */ + /* Argument checks. Subject to deprecate */ if (type == UDT_MAPIDXY) { if (data == NULL || !data_isreference(data)) { ShowWarning("buildin_getunitdata: Error in argument 3. Please provide a reference variable to store values in.\n"); - script_pushint(st, 0); + script_pushint(st, -1); return false; } @@ -19694,10 +20668,19 @@ static BUILDIN(getunitdata) sd = script->rid2sd(st); if (sd == NULL) { ShowWarning("buildin_getunitdata: Player not attached! Cannot use player variable %s.\n",name); - script_pushint(st, 0); + script_pushint(st, -1); 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); @@ -19762,9 +20745,10 @@ static BUILDIN(getunitdata) case UDT_AMOTION: script_pushint(st, md->status.amotion); break; case UDT_ADELAY: script_pushint(st, md->status.adelay); break; case UDT_DMOTION: script_pushint(st, md->status.dmotion); break; + case UDT_DAMAGE_TAKEN_RATE: script_pushint(st, md->dmg_taken_rate); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for Mob unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } @@ -19821,7 +20805,7 @@ static BUILDIN(getunitdata) case UDT_INTIMACY: script_pushint(st, hd->homunculus.intimacy); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for Homunculus unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } @@ -19878,7 +20862,7 @@ static BUILDIN(getunitdata) case UDT_INTIMACY: script_pushint(st, pd->pet.intimate); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for Pet unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } @@ -19934,7 +20918,7 @@ static BUILDIN(getunitdata) case UDT_LIFETIME: script_pushint(st, mc->mercenary.life_time); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for Mercenary unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } @@ -19988,7 +20972,7 @@ static BUILDIN(getunitdata) case UDT_MASTERCID: script_pushint(st, ed->elemental.char_id); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for Elemental unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } @@ -20053,14 +21037,14 @@ static BUILDIN(getunitdata) case UDT_BODY2: script_pushint(st, nd->vd.body_style); break; default: ShowWarning("buildin_getunitdata: Invalid data type '%s' for NPC unit.\n", udtype); - script_pushint(st, 0); + script_pushint(st, -1); return false; } } break; default: ShowError("buildin_getunitdata: Unknown object!\n"); - script_pushint(st, 0); + script_pushint(st, -1); return false; } // end of bl->type switch @@ -20156,7 +21140,47 @@ static BUILDIN(setunitname) } script_pushint(st, 1); - clif->charnameack(0, bl); // Send update to client. + clif->blname_ack(0, bl); // Send update to client. + + 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; } @@ -20168,26 +21192,65 @@ static BUILDIN(setunitname) /// unitwalk(<unit_id>,<target_id>) -> <bool> static BUILDIN(unitwalk) { - struct block_list* bl; + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(script_getnum(st,2)); - if( bl == NULL ) { + if (bl == NULL) { script_pushint(st, 0); return true; } - if( bl->type == BL_NPC ) { - unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure the ((struct npc_data*)bl)->ud is safe to edit + if (ud == NULL) { + ShowWarning("buildin_unitwalk: floating NPC don't have unit data.\n"); + return false; + } } - if( script_hasdata(st,4) ) { - int x = script_getnum(st,3); - int y = script_getnum(st,4); - script_pushint(st, unit->walktoxy(bl,x,y,0));// We'll use harder calculations. - } else { - int target_id = script_getnum(st,3); - script_pushint(st, unit->walktobl(bl,map->id2bl(target_id),1,1)); + if (script_hasdata(st, 4)) { + int x = script_getnum(st, 3); + int y = script_getnum(st, 4); + if (unit->walk_toxy(bl, x, y, 0) == 0) // We'll use harder calculations. + script_pushint(st, 1); + else + script_pushint(st, 0); + } + else { + int target_id = script_getnum(st, 3); + script_pushint(st, unit->walktobl(bl, map->id2bl(target_id), 1, 1)); + } + + return true; +} + +/** + * Checks if a unit is walking. + * + * Returns 1 if unit is walking, 0 if unit is not walking and -1 on error. + * + * @code{.herc} + * unitiswalking({<GID>}); + * @endcode + * + **/ +static BUILDIN(unitiswalking) +{ + int gid = script_hasdata(st, 2) ? script_getnum(st, 2) : st->rid; + struct block_list *bl = map->id2bl(gid); + + if (bl == NULL) { + ShowWarning("buildin_unitiswalking: Error in finding object for GID %d!\n", gid); + script_pushint(st, -1); + return false; } + if (unit->bl2ud(bl) == NULL) { + ShowWarning("buildin_unitiswalking: Error in finding unit_data for GID %d!\n", gid); + script_pushint(st, -1); + return false; + } + + script_pushint(st, unit->is_walking(bl)); + return true; } @@ -20209,32 +21272,34 @@ static BUILDIN(unitkill) /// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool> static BUILDIN(unitwarp) { - int unit_id; + int unit_id = script_getnum(st, 2); + const char *mapname = script_getstr(st, 3); + short x = (short)script_getnum(st, 4); + short y = (short)script_getnum(st, 5); int mapid; - short x; - short y; - struct block_list* bl; - const char *mapname; - - unit_id = script_getnum(st,2); - mapname = script_getstr(st, 3); - x = (short)script_getnum(st,4); - y = (short)script_getnum(st,5); + struct block_list *bl; if (!unit_id) //Warp the script's runner bl = map->id2bl(st->rid); else bl = map->id2bl(unit_id); - if( strcmp(mapname,"this") == 0 ) - mapid = bl?bl->m:-1; + if (strcmp(mapname, "this") == 0) + mapid = bl ? bl->m : -1; else mapid = map->mapname2mapid(mapname); - if( mapid >= 0 && bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit - script_pushint(st, unit->warp(bl,mapid,x,y,CLR_OUTSIGHT)); - } else { + if (mapid >= 0 && bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitwarp: floating NPC don't have unit data.\n"); + return false; + } + } + script_pushint(st, unit->warp(bl, mapid, x, y, CLR_OUTSIGHT)); + } + else { script_pushint(st, 0); } @@ -20305,17 +21370,19 @@ static BUILDIN(unitattack) /// unitstop <unit_id>; static BUILDIN(unitstop) { - int unit_id; - struct block_list* bl; - - unit_id = script_getnum(st,2); + struct block_list *bl = map->id2bl(script_getnum(st, 2)); - bl = map->id2bl(unit_id); - if( bl != NULL ) { - unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl != NULL) { + struct unit_data *ud = unit->bl2ud2(bl); // ensure ((struct npc_data *)bl)->ud is safe to edit + if (bl->type == BL_NPC) { + if (ud == NULL) { + ShowWarning("buildin_unitstop: floating NPC don't have unit data.\n"); + return false; + } + } unit->stop_attack(bl); unit->stop_walking(bl, STOPWALKING_FLAG_NEXTCELL); - if( bl->type == BL_MOB ) + if (bl->type == BL_MOB) BL_UCAST(BL_MOB, bl)->target_id = 0; } @@ -20420,7 +21487,10 @@ static BUILDIN(unitskilluseid) } else { status_calc_npc(nd, SCO_NONE); } + } else if (bl->type == BL_PC) { + pc->autocast_clear(BL_UCAST(BL_PC, bl)); } + unit->skilluse_id(bl, target_id, skill_id, skill_lv); } @@ -20456,7 +21526,10 @@ static BUILDIN(unitskillusepos) } else { status_calc_npc(nd, SCO_NONE); } + } else if (bl->type == BL_PC) { + pc->autocast_clear(BL_UCAST(BL_PC, bl)); } + unit->skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv); } @@ -20981,7 +22054,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); @@ -20993,7 +22065,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; } @@ -21011,15 +22085,13 @@ static BUILDIN(setquestinfo) return false; } - qi = &VECTOR_LAST(map->list[nd->bl.m].qi_data); - if (qi == NULL) { + if (VECTOR_LENGTH(nd->qi_data) == 0) { 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; - } + + qi = &VECTOR_LAST(nd->qi_data); + switch (type) { case QINFO_JOB: { @@ -21383,6 +22455,23 @@ static BUILDIN(achievement_progress) return true; } +static BUILDIN(achievement_iscompleted) +{ + struct map_session_data *sd = script_hasdata(st, 3) ? map->id2sd(script_getnum(st, 3)) : script->rid2sd(st); + if (sd == NULL) + return false; + + int aid = script_getnum(st, 2); + const struct achievement_data *ad = achievement->get(aid); + if (ad == NULL) { + ShowError("buildin_achievement_iscompleted: Invalid Achievement %d provided.\n", aid); + return false; + } + + script_pushint(st, achievement->check_complete(sd, ad)); + return true; +} + /*========================================== * BattleGround System *------------------------------------------*/ @@ -21504,20 +22593,31 @@ static BUILDIN(bg_warp) return true; } +/** + * Spawns a mob with allegiance to the given battle group. + * + * @code{.herc} + * bg_monster(<battle group>, "<map name>", <x>, <y>, "<name to show>", <mob id>{, "<event label>"}); + * @endcode + * + **/ static BUILDIN(bg_monster) { - int class_ = 0, x = 0, y = 0, bg_id = 0; - const char *str, *mapname, *evt=""; + const char *event = ""; + + if (script_hasdata(st, 8)) { + event = script_getstr(st, 8); + script->check_event(st, event); + } + + const char *mapname = script_getstr(st, 3); + const char *name = script_getstr(st, 6); + const int bg_id = script_getnum(st, 2); + const int x = script_getnum(st, 4); + const int y = script_getnum(st, 5); + const int mob_id = script_getnum(st, 7); - bg_id = script_getnum(st,2); - mapname = script_getstr(st,3); - x = script_getnum(st,4); - y = script_getnum(st,5); - str = script_getstr(st,6); - class_ = script_getnum(st,7); - if( script_hasdata(st,8) ) evt = script_getstr(st,8); - script->check_event(st, evt); - script_pushint(st, mob->spawn_bg(mapname,x,y,str,class_,evt,bg_id)); + script_pushint(st, mob->spawn_bg(mapname, x, y, name, mob_id, event, bg_id, st->oid)); return true; } @@ -21535,7 +22635,7 @@ static BUILDIN(bg_monster_set_team) mob_stop_attack(md); mob_stop_walking(md, STOPWALKING_FLAG_NONE); md->target_id = md->attacked_id = 0; - clif->charnameack(0, &md->bl); + clif->blname_ack(0, &md->bl); return true; } @@ -22144,6 +23244,19 @@ static BUILDIN(setfont) return true; } +static BUILDIN(getfont) +{ + struct map_session_data *sd = script->rid2sd(st); + + if (sd == NULL) { + script_pushint(st, 0); + return true; + } + + script_pushint(st, sd->status.font); + return true; +} + static int buildin_mobuseskill_sub(struct block_list *bl, va_list ap) { struct mob_data *md = NULL; @@ -22267,7 +23380,6 @@ static BUILDIN(progressbar_unit) } static BUILDIN(pushpc) { - uint8 dir; int cells, dx, dy; struct map_session_data* sd; @@ -22276,14 +23388,14 @@ static BUILDIN(pushpc) return true; } - dir = script_getnum(st,2); - cells = script_getnum(st,3); + enum unit_dir dir = script_getnum(st, 2); + cells = script_getnum(st,3); - if (dir > 7) { + if (dir >= UNIT_DIR_MAX) { ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir); script->reportsrc(st); - dir%= 8; // trim spin-over + dir %= UNIT_DIR_MAX; // trim spin-over } if(!cells) @@ -22292,10 +23404,11 @@ static BUILDIN(pushpc) } else if(cells<0) {// pushing backwards - dir = (dir+4)%8; // turn around - cells = -cells; + dir = unit_get_opposite_dir(dir); + cells = -cells; } + Assert_retr(false, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); dx = dirx[dir]; dy = diry[dir]; @@ -22470,7 +23583,7 @@ static BUILDIN(getcharip) /* check if a character name is specified */ if (script_hasdata(st, 2)) { if (script_isstringtype(st, 2)) { - sd = map->nick2sd(script_getstr(st, 2)); + sd = map->nick2sd(script_getstr(st, 2), false); } else { int id = script_getnum(st, 2); sd = (map->id2sd(id) ? map->id2sd(id) : map->charid2sd(id)); @@ -23587,7 +24700,7 @@ static BUILDIN(bg_create_team) if( strcmp(map_name,"-") != 0 ) { map_index = script->mapindexname2id(st,map_name); if( map_index == 0 ) { // Invalid Map - script_pushint(st,0); + script_pushint(st, -1); return true; } } @@ -23635,7 +24748,7 @@ static BUILDIN(bg_join_team) *------------------------------------------*/ static BUILDIN(countbound) { - int i, type, j=0, k=0; + int type, j=0, k=0; struct map_session_data *sd = script->rid2sd(st); if (sd == NULL) @@ -23643,7 +24756,7 @@ static BUILDIN(countbound) type = script_hasdata(st,2)?script_getnum(st,2):0; - for(i=0;i<MAX_INVENTORY;i++) { + for (int i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid > 0 && ( (!type && sd->status.inventory[i].bound > 0) || (type && sd->status.inventory[i].bound == type) @@ -23689,20 +24802,21 @@ static BUILDIN(checkbound) ShowError("script_checkbound: Not a valid bind type! Type=%d\n", bound_type); } - ARR_FIND( 0, MAX_INVENTORY, i, (sd->status.inventory[i].nameid == nameid && + ARR_FIND(0, sd->status.inventorySize, i, (sd->status.inventory[i].nameid == nameid && ( sd->status.inventory[i].refine == (script_hasdata(st,4)? script_getnum(st,4) : sd->status.inventory[i].refine) ) && ( sd->status.inventory[i].attribute == (script_hasdata(st,5)? script_getnum(st,5) : sd->status.inventory[i].attribute) ) && ( sd->status.inventory[i].card[0] == (script_hasdata(st,6)? script_getnum(st,6) : sd->status.inventory[i].card[0]) ) && ( sd->status.inventory[i].card[1] == (script_hasdata(st,7)? script_getnum(st,7) : sd->status.inventory[i].card[1]) ) && ( sd->status.inventory[i].card[2] == (script_hasdata(st,8)? script_getnum(st,8) : sd->status.inventory[i].card[2]) ) && ( sd->status.inventory[i].card[3] == (script_hasdata(st,9)? script_getnum(st,9) : sd->status.inventory[i].card[3]) ) && - ((sd->status.inventory[i].bound > 0 && !bound_type) || sd->status.inventory[i].bound == bound_type )) ); + ((sd->status.inventory[i].bound > 0 && !bound_type) || sd->status.inventory[i].bound == bound_type))); - if( i < MAX_INVENTORY ){ + if (i < sd->status.inventorySize) { script_pushint(st, sd->status.inventory[i].bound); return true; - } else + } else { script_pushint(st,0); + } return true; } @@ -23827,6 +24941,133 @@ static BUILDIN(openshop) return true; } +static bool script_sellitemcurrency_add(struct npc_data *nd, struct script_state* st, int argIndex) +{ + nullpo_retr(false, nd); + nullpo_retr(false, st); + + if (!script_hasdata(st, argIndex + 1)) + return false; + + int id = script_getnum(st, argIndex); + struct item_data *it; + if (!(it = itemdb->exists(id))) { + ShowWarning("buildin_sellitemcurrency: unknown item id '%d'!\n", id); + return false; + } + int qty = 0; + if ((qty = script_getnum(st, argIndex + 1)) <= 0) { + ShowError("buildin_sellitemcurrency: invalid 'qty'!\n"); + return false; + } + int refine_level = -1; + if (script_hasdata(st, argIndex + 2)) { + refine_level = script_getnum(st, argIndex + 2); + } + int items = nd->u.scr.shop->items; + if (nd->u.scr.shop == NULL || items == 0) { + ShowWarning("buildin_sellitemcurrency: shop not have items!\n"); + return false; + } + if (nd->u.scr.shop->shop_last_index >= items || nd->u.scr.shop->shop_last_index < 0) { + ShowWarning("buildin_sellitemcurrency: wrong selected shop index!\n"); + return false; + } + + struct npc_item_list *item_list = &nd->u.scr.shop->item[nd->u.scr.shop->shop_last_index]; + int index = item_list->value2; + if (item_list->currency == NULL) { + CREATE(item_list->currency, struct npc_barter_currency, 1); + item_list->value2 ++; + } else { + RECREATE(item_list->currency, struct npc_barter_currency, ++item_list->value2); + } + struct npc_barter_currency *currency = &item_list->currency[index]; + currency->nameid = id; + currency->refine = refine_level; + currency->amount = qty; + return true; +} + +/** + * @call sellitemcurrency <Item_ID>,qty{,refine}}; + * + * adds <Item_ID> to last item in expanded barter shop + **/ +static BUILDIN(sellitemcurrency) +{ + struct npc_data *nd; + if ((nd = map->id2nd(st->oid)) == NULL) { + ShowWarning("buildin_sellitemcurrency: trying to run without a proper NPC!\n"); + return false; + } + if (nd->u.scr.shop == NULL || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_sellitemcurrency: this command can be used only with expanded barter shops!\n"); + return false; + } + + script->sellitemcurrency_add(nd, st, 2); + return true; +} + +/** + * @call endsellitem; + * + * complete sell item in expanded barter shop (NST_EXPANDED_BARTER) + **/ +static BUILDIN(endsellitem) +{ + struct npc_data *nd; + if ((nd = map->id2nd(st->oid)) == NULL) { + ShowWarning("buildin_endsellitem: trying to run without a proper NPC!\n"); + return false; + } + if (nd->u.scr.shop == NULL || nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_endsellitem: this command can be used only with expanded barter shops!\n"); + return false; + } + + int newIndex = nd->u.scr.shop->shop_last_index; + const struct npc_item_list *const newItem = &nd->u.scr.shop->item[newIndex]; + int i = 0; + for (i = 0; i < nd->u.scr.shop->items - 1; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid != newItem->nameid || item->value != newItem->value) + continue; + if (item->value2 != newItem->value2) + continue; + bool found = true; + for (int k = 0; k < item->value2; k ++) { + struct npc_barter_currency *currency = &item->currency[k]; + struct npc_barter_currency *newCurrency = &newItem->currency[k]; + if (currency->nameid != newCurrency->nameid || + currency->amount != newCurrency->amount || + currency->refine != newCurrency->refine) { + found = false; + break; + } + } + if (!found) + continue; + break; + } + + if (i != nd->u.scr.shop->items - 1) { + if (nd->u.scr.shop->item[i].qty != -1) { + nd->u.scr.shop->item[i].qty += nd->u.scr.shop->item[newIndex].qty; + npc->expanded_barter_tosql(nd, i); + } + nd->u.scr.shop->shop_last_index --; + nd->u.scr.shop->items--; + if (nd->u.scr.shop->item[newIndex].currency != NULL) { + aFree(nd->u.scr.shop->item[newIndex].currency); + nd->u.scr.shop->item[newIndex].currency = NULL; + } + } + + return true; +} + /** * @call sellitem <Item_ID>,{,price{,qty}}; * @@ -23839,6 +25080,7 @@ static BUILDIN(sellitem) struct item_data *it; int i = 0, id = script_getnum(st,2); int value = 0; + int value2 = 0; int qty = 0; if( !(nd = map->id2nd(st->oid)) ) { @@ -23849,16 +25091,71 @@ static BUILDIN(sellitem) return false; } - value = script_hasdata(st,3) ? script_getnum(st, 3) : it->value_buy; - if( value == -1 ) - value = it->value_buy; + const bool have_shop = (nd->u.scr.shop != NULL); + if (!have_shop) { + npc->trader_update(nd->src_id ? nd->src_id : nd->bl.id); + } - if( !nd->u.scr.shop ) - npc->trader_update(nd->src_id?nd->src_id:nd->bl.id); - else {/* no need to run this if its empty */ - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == id ) + if (nd->u.scr.shop->type != NST_BARTER) { + value = script_hasdata(st, 3) ? script_getnum(st, 3) : it->value_buy; + if (value == -1) + value = it->value_buy; + } + + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 5)) { + ShowError("buildin_sellitem: invalid number of parameters for barter-type shop!\n"); + return false; + } + value = script_getnum(st, 4); + value2 = script_getnum(st, 5); + } else if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + if (!script_hasdata(st, 4)) { + ShowError("buildin_sellitem: invalid number of parameters for expanded barter type shop!\n"); + return false; + } + if ((qty = script_getnum(st, 4)) <= 0 && qty != -1) { + ShowError("buildin_sellitem: invalid 'qty' for expanded barter type shop!\n"); + return false; + } + } + + if (have_shop) { + if (nd->u.scr.shop->type == NST_BARTER) { + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == id && item->value == value && item->value2 == value2) { + break; + } + } + } else if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid != id || item->value != value) + continue; + if (item->value2 != (script_lastdata(st) - 4) / 3) + continue; + bool found = true; + for (int k = 0; k < item->value2; k ++) { + const int scriptOffset = k * 3 + 5; + struct npc_barter_currency *currency = &item->currency[k]; + if (currency->nameid != script_getnum(st, scriptOffset) || + currency->amount != script_getnum(st, scriptOffset + 1) || + currency->refine != script_getnum(st, scriptOffset + 2)) { + found = false; + break; + } + } + if (!found) + continue; break; + } + } else { + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == id) { + break; + } + } } } @@ -23874,19 +25171,30 @@ static BUILDIN(sellitem) it->name, id, value, (int)(value*0.75), it->value_sell, (int)(it->value_sell*1.24), nd->exname, nd->path); } - if( i != nd->u.scr.shop->items ) { + if (nd->u.scr.shop->type == NST_BARTER) { + qty = script_getnum(st, 3); + if (qty < -1 || value <= 0 || value2 <= 0) { + ShowError("buildin_sellitem: invalid parameters for barter-type shop!\n"); + return false; + } + } + + bool foundInShop = (i != nd->u.scr.shop->items); + if (foundInShop) { nd->u.scr.shop->item[i].value = value; nd->u.scr.shop->item[i].qty = qty; - if( nd->u.scr.shop->type == NST_MARKET ) /* has been manually updated, make it reflect on sql */ - npc->market_tosql(nd,i); + if (nd->u.scr.shop->type == NST_MARKET) /* has been manually updated, make it reflect on sql */ + npc->market_tosql(nd, i); + else if (nd->u.scr.shop->type == NST_BARTER) /* has been manually updated, make it reflect on sql */ + npc->barter_tosql(nd, i); } else { - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == 0 ) + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == 0) break; } - if( i == nd->u.scr.shop->items ) { - if( nd->u.scr.shop->items == USHRT_MAX ) { + if (i == nd->u.scr.shop->items) { + if (nd->u.scr.shop->items == USHRT_MAX) { ShowWarning("buildin_sellitem: Can't add %s (%s/%s), shop list is full!\n", it->name, nd->exname, nd->path); return false; } @@ -23896,9 +25204,86 @@ static BUILDIN(sellitem) nd->u.scr.shop->item[i].nameid = it->nameid; nd->u.scr.shop->item[i].value = value; + nd->u.scr.shop->item[i].value2 = value2; nd->u.scr.shop->item[i].qty = qty; + nd->u.scr.shop->item[i].currency = NULL; + } + nd->u.scr.shop->shop_last_index = i; + + if (!foundInShop) { + for (int k = 5; k <= script_lastdata(st); k += 3) { + script->sellitemcurrency_add(nd, st, k); + } } + if (foundInShop) { + if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { /* has been manually updated, make it reflect on sql */ + npc->expanded_barter_tosql(nd, i); + } + } + return true; +} + +/** + * @call startsellitem <Item_ID>,{,price{,qty}}; + * + * Starts adding item into expanded barter shop (NST_EXPANDED_BARTER) + **/ +static BUILDIN(startsellitem) +{ + struct npc_data *nd; + struct item_data *it; + int i = 0, id = script_getnum(st,2); + int value2 = 0; + int qty = 0; + + if (!(nd = map->id2nd(st->oid))) { + ShowWarning("buildin_startsellitem: trying to run without a proper NPC!\n"); + return false; + } else if (!(it = itemdb->exists(id))) { + ShowWarning("buildin_startsellitem: unknown item id '%d'!\n", id); + return false; + } + + const bool have_shop = (nd->u.scr.shop != NULL); + if (!have_shop) { + npc->trader_update(nd->src_id ? nd->src_id : nd->bl.id); + } + + if (nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("script_startsellitem: can works only for NST_EXPANDED_BARTER shops"); + return false; + } + + int value = script_hasdata(st, 3) ? script_getnum(st, 3) : it->value_buy; + if (value == -1) + value = it->value_buy; + + if ((qty = script_getnum(st, 4)) <= 0 && qty != -1) { + ShowError("buildin_startsellitem: invalid 'qty' for expanded barter type shop!\n"); + return false; + } + + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == 0) + break; + } + + if (i == nd->u.scr.shop->items) { + if (nd->u.scr.shop->items == USHRT_MAX) { + ShowWarning("buildin_startsellitem: Can't add %s (%s/%s), shop list is full!\n", it->name, nd->exname, nd->path); + return false; + } + i = nd->u.scr.shop->items; + RECREATE(nd->u.scr.shop->item, struct npc_item_list, ++nd->u.scr.shop->items); + } + + nd->u.scr.shop->item[i].nameid = it->nameid; + nd->u.scr.shop->item[i].value = value; + nd->u.scr.shop->item[i].value2 = value2; + nd->u.scr.shop->item[i].qty = qty; + nd->u.scr.shop->item[i].currency = NULL; + nd->u.scr.shop->shop_last_index = i; return true; } @@ -23912,44 +25297,86 @@ static BUILDIN(sellitem) static BUILDIN(stopselling) { struct npc_data *nd; - int i, id = script_getnum(st,2); + int i, id = script_getnum(st, 2); - if( !(nd = map->id2nd(st->oid)) || !nd->u.scr.shop ) { + if (!(nd = map->id2nd(st->oid)) || !nd->u.scr.shop) { ShowWarning("buildin_stopselling: trying to run without a proper NPC!\n"); return false; } - for( i = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == id ) - break; + if (nd->u.scr.shop->type == NST_BARTER) { + if (!script_hasdata(st, 4)) { + ShowError("buildin_stopselling: called with wrong number of arguments\n"); + return false; + } + const int id2 = script_getnum(st, 3); + const int amount2 = script_getnum(st, 4); + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == id && item->value == id2 && item->value2 == amount2) { + break; + } + } + } else if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) { + if (!script_hasdata(st, 3)) { + ShowError("buildin_stopselling: called with wrong number of arguments\n"); + return false; + } + const int price = script_getnum(st, 3); + for (i = 0; i < nd->u.scr.shop->items; i++) { + const struct npc_item_list *const item = &nd->u.scr.shop->item[i]; + if (item->nameid == id && item->value == price) { + break; + } + } + } else { + for (i = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == id) { + break; + } + } } - if( i != nd->u.scr.shop->items ) { + if (i != nd->u.scr.shop->items) { int cursor; - if( nd->u.scr.shop->type == NST_MARKET ) - npc->market_delfromsql(nd,i); + if (nd->u.scr.shop->type == NST_MARKET) + npc->market_delfromsql(nd, i); + else if (nd->u.scr.shop->type == NST_BARTER) + npc->barter_delfromsql(nd, i); + else if (nd->u.scr.shop->type == NST_EXPANDED_BARTER) + npc->expanded_barter_delfromsql(nd, i); nd->u.scr.shop->item[i].nameid = 0; nd->u.scr.shop->item[i].value = 0; + nd->u.scr.shop->item[i].value2 = 0; nd->u.scr.shop->item[i].qty = 0; + if (nd->u.scr.shop->item[i].currency != NULL) { + aFree(nd->u.scr.shop->item[i].currency); + nd->u.scr.shop->item[i].currency = NULL; + } - for( i = 0, cursor = 0; i < nd->u.scr.shop->items; i++ ) { - if( nd->u.scr.shop->item[i].nameid == 0 ) + for (i = 0, cursor = 0; i < nd->u.scr.shop->items; i++) { + if (nd->u.scr.shop->item[i].nameid == 0) continue; - if( cursor != i ) { + if (cursor != i) { nd->u.scr.shop->item[cursor].nameid = nd->u.scr.shop->item[i].nameid; nd->u.scr.shop->item[cursor].value = nd->u.scr.shop->item[i].value; + nd->u.scr.shop->item[cursor].value2 = nd->u.scr.shop->item[i].value2; nd->u.scr.shop->item[cursor].qty = nd->u.scr.shop->item[i].qty; + nd->u.scr.shop->item[cursor].currency = nd->u.scr.shop->item[i].currency; } cursor++; } + nd->u.scr.shop->items--; + nd->u.scr.shop->item[nd->u.scr.shop->items].currency = NULL; script_pushint(st, 1); - } else + } else { script_pushint(st, 0); + } return true; } @@ -24006,7 +25433,9 @@ static BUILDIN(tradertype) nd->u.scr.shop->item[i].value = 0; nd->u.scr.shop->item[i].qty = 0; } - npc->market_delfromsql(nd,USHRT_MAX); + npc->market_delfromsql(nd, INT_MAX); + npc->barter_delfromsql(nd, INT_MAX); + npc->expanded_barter_delfromsql(nd, INT_MAX); } #if PACKETVER < 20131223 @@ -24015,6 +25444,18 @@ static BUILDIN(tradertype) script->reportsrc(st); } #endif +#if PACKETVER_MAIN_NUM < 20190116 && PACKETVER_RE_NUM < 20190116 && PACKETVER_ZERO_NUM < 20181226 + if (type == NST_BARTER) { + ShowWarning("buildin_tradertype: NST_BARTER is only available with PACKETVER_ZERO_NUM 20181226 or PACKETVER_MAIN_NUM 20190116 or PACKETVER_RE_NUM 20190116 or newer!\n"); + script->reportsrc(st); + } +#endif +#if PACKETVER_MAIN_NUM < 20191120 && PACKETVER_RE_NUM < 20191106 && PACKETVER_ZERO_NUM < 20191127 + if (type == NST_EXPANDED_BARTER) { + ShowWarning("buildin_tradertype: NST_EXPANDED_BARTER is only available with PACKETVER_ZERO_NUM 20191127 or PACKETVER_MAIN_NUM 20191120 or PACKETVER_RE_NUM 20191106 or newer!\n"); + script->reportsrc(st); + } +#endif if( nd->u.scr.shop ) nd->u.scr.shop->type = type; @@ -24058,8 +25499,8 @@ static BUILDIN(shopcount) } else if ( !nd->u.scr.shop || !nd->u.scr.shop->items ) { ShowWarning("buildin_shopcount(%d): trying to use without any items!\n",id); return false; - } else if ( nd->u.scr.shop->type != NST_MARKET ) { - ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET shop!\n",id); + } else if (nd->u.scr.shop->type != NST_MARKET && nd->u.scr.shop->type != NST_BARTER && nd->u.scr.shop->type != NST_EXPANDED_BARTER) { + ShowWarning("buildin_shopcount(%d): trying to use on a non-NST_MARKET and non-NST_BARTER and non-NST_EXPANDED_BARTER shop!\n",id); return false; } @@ -24160,7 +25601,7 @@ static BUILDIN(showscript) { struct block_list *bl = NULL; const char *msg = script_getstr(st, 2); - int id = 0; + int id = 0, flag = AREA; if (script_hasdata(st, 3)) { id = script_getnum(st, 3); @@ -24172,14 +25613,14 @@ static BUILDIN(showscript) if (!bl) { ShowError("buildin_showscript: Script not attached. (id=%d, rid=%d, oid=%d)\n", id, st->rid, st->oid); - script_pushint(st, 0); return false; } - clif->ShowScript(bl, msg); - - script_pushint(st, 1); + if (script_hasdata(st, 4)) + if (script_getnum(st, 4) == SELF) + flag = SELF; + clif->ShowScript(bl, msg, flag); return true; } @@ -24289,6 +25730,100 @@ 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; +} + +static BUILDIN(setfavoriteitemidx) +{ + struct map_session_data *sd = script_rid2sd(st); + int idx = script_getnum(st, 2); + int value = script_getnum(st, 3); + + if (sd == NULL) { + ShowError("buildin_setfavoriteitemidx: No player attached.\n"); + return false; + } + + if (idx < 0 || idx >= sd->status.inventorySize) { + ShowError("buildin_setfavoriteitemidx: Invalid inventory index %d (min: %d, max: %d).\n", idx, 0, (sd->status.inventorySize - 1)); + return false; + } else if (sd->inventory_data[idx] == NULL || sd->inventory_data[idx]->nameid <= 0) { + ShowWarning("buildin_setfavoriteitemidx: Current inventory index %d has no data.\n", idx); + return false; + } else if (sd->status.inventory[idx].equip > 0) { + ShowWarning("buildin_setfavoriteitemidx: Cant change favorite flag of an equipped item.\n"); + return false; + } else { + sd->status.inventory[idx].favorite = cap_value(value, 0, 1); + clif->favorite_item(sd, idx); + } + + return true; +} + +static BUILDIN(autofavoriteitem) +{ + int nameid = script_getnum(st, 2); + int flag = script_getnum(st, 3); + struct item_data *item_data; + + if ((item_data = itemdb->exists(nameid)) == NULL) { + ShowError("buildin_autofavoriteitem: Invalid item '%d'.\n", nameid); + return false; + } + + item_data->flag.auto_favorite = cap_value(flag, 0, 1); + return true; +} + /** place holder for the translation macro **/ static BUILDIN(_) { @@ -24644,7 +26179,7 @@ static BUILDIN(clan_master) } nd->clan_id = clan_id; - clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, clan_id, 0); + clif->sc_load(&nd->bl, nd->bl.id, AREA, status->get_sc_icon(SC_CLAN_INFO), 0, clan_id, 0); script_pushint(st, true); return true; @@ -24790,6 +26325,249 @@ static BUILDIN(itempreview) return true; } +// insert or remove card into equipped item +static BUILDIN(enchantitem) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + const int pos = script_getnum(st, 2); + if ((pos < EQI_ACC_L || pos > EQI_HAND_R) && pos != EQI_AMMO) { + ShowError("Wrong equip position: %d\n", pos); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int cardId = script_getnum(st, 4); + struct item_data *it = itemdb->exists(cardId); + if (it == NULL || it->type != IT_CARD) { + ShowError("Item id is not card or not exists: %d\n", cardId); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int n = sd->equip_index[pos]; + if (n < 0) { + ShowError("Item in equipment slot %d is not equipped\n", pos); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const int cardSlot = script_getnum(st, 3); + if (cardSlot < 0 || cardSlot >= MAX_SLOTS) { + ShowError("Wrong card slot %d. Must be in range 0-3.\n", cardSlot); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + const bool res = clif->enchant_equipment(sd, pc->equip_pos[pos], cardSlot, cardId); + if (res) { + logs->pick_pc(sd, LOG_TYPE_CARD, -1, &sd->status.inventory[n],sd->inventory_data[n]); + sd->status.inventory[n].card[cardSlot] = cardId; + logs->pick_pc(sd, LOG_TYPE_CARD, 1, &sd->status.inventory[n],sd->inventory_data[n]); + status_calc_pc(sd, SCO_NONE); + } + script_pushint(st, res); + return true; +} + +// send ack to inventory expand request +static BUILDIN(expandinventoryack) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + int itemId = 0; + if (script_hasdata(st, 3)) { + itemId = script_getnum(st, 3); + } + clif->inventoryExpandAck(sd, script_getnum(st, 2), itemId); + return true; +} + +// send final ack to inventory expand request +static BUILDIN(expandinventoryresult) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + clif->inventoryExpandResult(sd, script_getnum(st, 2)); + return true; +} + +// adjust player inventory size to given value positive or negative +static BUILDIN(expandinventory) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + script_pushint(st, pc->expandInventory(sd, script_getnum(st, 2))); + return true; +} + +// return current player inventory size +static BUILDIN(getinventorysize) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + script_pushint(st, sd->status.inventorySize); + 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; +} + +/** + * identify(<item id>) + * Identifies the first unidentified <item id> item on player's inventory. + * Returns -2 on error, -1 if no item to identify was found, identified idx otherwise. + */ +static BUILDIN(identify) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, -2); + return true; + } + + int itemid = script_getnum(st, 2); + if (itemdb->exists(itemid) == NULL) { + ShowError("buildin_identify: Invalid item ID (%d)\n", itemid); + script_pushint(st, -2); + return true; + } + + int idx = -1; + ARR_FIND(0, sd->status.inventorySize, idx, (sd->status.inventory[idx].nameid == itemid && sd->status.inventory[idx].identify == 0)); + + if (idx < 0 || idx >= sd->status.inventorySize) { + script_pushint(st, -1); + return true; + } + + sd->status.inventory[idx].identify = 1; + clif->item_identified(sd, idx, 0); + script_pushint(st, idx); + + return true; +} + +/** + * identifyidx(idx) + * Identifies item at idx. + * Returns true if item is identified, false otherwise. + */ +static BUILDIN(identifyidx) +{ + struct map_session_data *sd = script_rid2sd(st); + + if (sd == NULL) { + script_pushint(st, false); + return true; + } + + int idx = script_getnum(st, 2); + if (idx < 0 || idx >= sd->status.inventorySize) { + ShowError("buildin_identifyidx: Invalid inventory index (%d), expected a value between 0 and %d\n", idx, sd->status.inventorySize); + script_pushint(st, false); + return true; + } + + if (sd->status.inventory[idx].nameid <= 0 || sd->status.inventory[idx].identify != 0) { + script_pushint(st, false); + return true; + } + + sd->status.inventory[idx].identify = 1; + clif->item_identified(sd, idx, 0); + script_pushint(st, true); + + return true; +} + +static BUILDIN(openlapineddukddakboxui) +{ + struct map_session_data *sd = script_rid2sd(st); + if (sd == NULL) + return false; + const int item_id = script_getnum(st, 2); + struct item_data *it = itemdb->exists(item_id); + if (it == NULL) { + ShowError("buildin_openlapineddukddakboxui: Item %d is not valid\n", item_id); + script->reportfunc(st); + script->reportsrc(st); + script_pushint(st, false); + return true; + } + clif->lapineDdukDdak_open(sd, item_id); + script_pushint(st, true); + return true; +} + +// Reset 'Feeling' maps. +BUILDIN(resetfeel) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 2)) + sd = script->id2sd(st, script_getnum(st, 2)); + else + sd = script->rid2sd(st); + + if (sd != NULL) + pc->resetfeel(sd); + + return true; +} + +// Reset hatred target marks. +BUILDIN(resethate) +{ + struct map_session_data *sd; + + if (script_hasdata(st, 2)) + sd = script->id2sd(st, script_getnum(st, 2)); + else + sd = script->rid2sd(st); + + if (sd != NULL) + pc->resethate(sd); + + return true; +} + /** * Adds a built-in script function. * @@ -24939,6 +26717,52 @@ static void script_run_item_unequip_script(struct map_session_data *sd, struct i script->current_item_id = 0; } +static void script_run_item_rental_start_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull(1, 2))); + +/** + * Run item rental start script + * @param sd player session data. Must be correct and checked before. + * @param data rental item data. Must be correct and checked before. + * @param oid npc id. Can be also 0 or fake npc id. + **/ +static void script_run_item_rental_start_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->rental_start_script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + +static void script_run_item_rental_end_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull(1, 2))); + +/** +* Run item rental end script +* @param sd player session data. Must be correct and checked before. +* @param data rental item data. Must be correct and checked before. +* @param oid npc id. Can be also 0 or fake npc id. +**/ +static void script_run_item_rental_end_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->rental_end_script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + +static void script_run_item_lapineddukddak_script(struct map_session_data *sd, struct item_data *data, int oid) __attribute__((nonnull (1, 2))); + +/** + * Run item lapineddukddak script for item. + * + * @param sd player session data. Must be correct and checked before. + * @param data unequipped item data. Must be correct and checked before. + * @param oid npc id. Can be also 0 or fake npc id. + */ +static void script_run_item_lapineddukddak_script(struct map_session_data *sd, struct item_data *data, int oid) +{ + script->current_item_id = data->nameid; + script->run(data->lapineddukddak->script, 0, sd->bl.id, oid); + script->current_item_id = 0; +} + #define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args, false } #define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args, false } #define BUILDIN_DEF_DEPRECATED(x,args) { buildin_ ## x , #x , args, true } @@ -24954,6 +26778,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*"), @@ -24971,8 +26796,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(warp,"sii?"), BUILDIN_DEF(areawarp,"siiiisii??"), BUILDIN_DEF(warpchar,"siii"), // [LuzZza] - BUILDIN_DEF(warpparty,"siii??"), // [Fredzilla] [Paradox924X] [Jedzkie] [Dastgir] - BUILDIN_DEF(warpguild,"siii?"), // [Fredzilla] + BUILDIN_DEF(warpparty,"siii???"), // [Fredzilla] [Paradox924X] [Jedzkie] [Dastgir] + BUILDIN_DEF(warpguild,"siii??"), // [Fredzilla] BUILDIN_DEF(setlook,"ii"), BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it BUILDIN_DEF2(__setr,"set","rv"), @@ -24992,6 +26817,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(makeitem2,"viiiiiiii????"), BUILDIN_DEF(delitem,"vi?"), BUILDIN_DEF(delitem2,"viiiiiiii?"), + BUILDIN_DEF(delitemidx, "i??"), BUILDIN_DEF2(enableitemuse,"enable_items",""), BUILDIN_DEF2(disableitemuse,"disable_items",""), BUILDIN_DEF(cutin,"si"), @@ -25002,6 +26828,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(rand,"i?"), BUILDIN_DEF(countitem,"v"), BUILDIN_DEF(countitem2,"viiiiiii"), + BUILDIN_DEF(countnameditem,"v?"), BUILDIN_DEF(checkweight,"vi*"), BUILDIN_DEF(checkweight2,"rr"), BUILDIN_DEF(readparam,"i?"), @@ -25011,10 +26838,12 @@ static void script_parse_builtin(void) BUILDIN_DEF(getpartyname,"i"), BUILDIN_DEF(getpartymember,"i?"), BUILDIN_DEF(getpartyleader,"i?"), - BUILDIN_DEF(getguildname,"i"), - BUILDIN_DEF(getguildmaster,"i"), - BUILDIN_DEF(getguildmasterid,"i"), + BUILDIN_DEF_DEPRECATED(getguildname,"i"), + BUILDIN_DEF_DEPRECATED(getguildmaster,"i"), + BUILDIN_DEF_DEPRECATED(getguildmasterid,"i"), BUILDIN_DEF(getguildmember,"i?"), + BUILDIN_DEF(getguildinfo,"i?"), + BUILDIN_DEF(getguildonline, "i?"), BUILDIN_DEF(strcharinfo,"i??"), BUILDIN_DEF(strnpcinfo,"i??"), BUILDIN_DEF(charid2rid,"i"), @@ -25027,7 +26856,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(getequipisequiped,"i"), BUILDIN_DEF(getequipisenableref,"i"), BUILDIN_DEF(getequipisidentify,"i"), - BUILDIN_DEF(getequiprefinerycnt,"i"), + BUILDIN_DEF(getequiprefinerycnt,"i*"), BUILDIN_DEF(getequipweaponlv,"i"), BUILDIN_DEF(getequippercentrefinery,"i?"), BUILDIN_DEF(successrefitem,"i?"), @@ -25035,6 +26864,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(downrefitem,"i?"), BUILDIN_DEF(statusup,"i"), BUILDIN_DEF(statusup2,"ii"), + BUILDIN_DEF(needed_status_point, "ii"), BUILDIN_DEF(bonus,"iv"), BUILDIN_DEF2(bonus,"bonus2","ivi"), BUILDIN_DEF2(bonus,"bonus3","ivii"), @@ -25059,13 +26889,13 @@ static void script_parse_builtin(void) BUILDIN_DEF(checkcart,""), BUILDIN_DEF(setfalcon,"?"), BUILDIN_DEF(checkfalcon,""), - BUILDIN_DEF(setmount,"?"), + BUILDIN_DEF(setmount,"??"), BUILDIN_DEF(checkmount,""), BUILDIN_DEF(checkwug,""), BUILDIN_DEF(savepoint,"sii"), BUILDIN_DEF(gettimetick,"i"), BUILDIN_DEF(gettime,"i"), - BUILDIN_DEF(gettimestr,"si"), + BUILDIN_DEF(gettimestr, "si?"), BUILDIN_DEF(openstorage,""), BUILDIN_DEF(guildopenstorage,""), BUILDIN_DEF(itemskill,"vi?"), @@ -25076,6 +26906,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(areamonster,"siiiisii???"), BUILDIN_DEF(killmonster,"ss?"), BUILDIN_DEF(killmonsterall,"s?"), + BUILDIN_DEF(killmonstergid, "i"), BUILDIN_DEF(clone,"siisi????"), BUILDIN_DEF(doevent,"s"), BUILDIN_DEF(donpcevent,"s"), @@ -25092,6 +26923,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest] BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest] BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex] + BUILDIN_DEF(mobattached, ""), BUILDIN_DEF(announce,"si?????"), BUILDIN_DEF(mapannounce,"ssi?????"), BUILDIN_DEF(areaannounce,"siiiisi?????"), @@ -25104,18 +26936,23 @@ static void script_parse_builtin(void) BUILDIN_DEF(disablenpc,"s"), BUILDIN_DEF(hideoffnpc,"s"), BUILDIN_DEF(hideonnpc,"s"), + BUILDIN_DEF(cloakonnpc,"s?"), + BUILDIN_DEF(cloakoffnpc,"s?"), BUILDIN_DEF(sc_start,"iii???"), BUILDIN_DEF2(sc_start,"sc_start2","iiii???"), BUILDIN_DEF2(sc_start,"sc_start4","iiiiii???"), 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"), BUILDIN_DEF(resetstatus,""), BUILDIN_DEF(resetskill,""), + BUILDIN_DEF(resetfeel, "?"), + BUILDIN_DEF(resethate, "?"), BUILDIN_DEF(skillpointcount,""), BUILDIN_DEF(changebase,"i?"), BUILDIN_DEF(changesex,""), @@ -25186,6 +27023,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest] BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris] BUILDIN_DEF(specialeffect,"i???"), // npc skill effect [Valaris] + BUILDIN_DEF(specialeffectnum,"iii???"), // npc skill effect with num [4144] BUILDIN_DEF(removespecialeffect,"i???"), BUILDIN_DEF_DEPRECATED(specialeffect2,"i??"), // skill effect on players[Valaris] BUILDIN_DEF(nude,""), // nude command [Valaris] @@ -25194,6 +27032,7 @@ static void script_parse_builtin(void) BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr] BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr] BUILDIN_DEF(message,"vs"), // [MouseJstr] + BUILDIN_DEF(servicemessage, "si?"), BUILDIN_DEF(npctalk,"s??"), // [Valaris][Murilo BiO] BUILDIN_DEF(mobcount,"ss"), BUILDIN_DEF(getlook,"i"), @@ -25261,7 +27100,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(setnpcdisplay,"sv??"), BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine. BUILDIN_DEF(strcmp,"ss"), - BUILDIN_DEF(getiteminfo,"ii"), //[Lupus] returns Items Buy / sell Price, etc info + BUILDIN_DEF(getiteminfo,"vi"), //[Lupus] returns Items Buy / sell Price, etc info BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item BUILDIN_DEF(getequippedoptioninfo, "i"), @@ -25276,13 +27115,14 @@ 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 ---> BUILDIN_DEF(getd,"s"), BUILDIN_DEF(setd,"sv"), // <--- [zBuffer] List of dynamic var commands - BUILDIN_DEF(petstat,"i"), + BUILDIN_DEF_DEPRECATED(petstat, "i"), // Deprecated 2019-03-11 BUILDIN_DEF(callshop,"s?"), // [Skotlex] BUILDIN_DEF(npcshopitem,"sii*"), // [Lance] BUILDIN_DEF(npcshopadditem,"sii*"), @@ -25310,8 +27150,8 @@ static void script_parse_builtin(void) BUILDIN_DEF(pcfollow,"ii"), BUILDIN_DEF(pcstopfollow,"i"), BUILDIN_DEF_DEPRECATED(pcblockmove,"ii"), // Deprecated 2018-05-04 - BUILDIN_DEF(setpcblock, "ii"), - BUILDIN_DEF(checkpcblock, ""), + BUILDIN_DEF(setpcblock, "ii?"), + BUILDIN_DEF(checkpcblock, "?"), // <--- [zBuffer] List of player cont commands // [zBuffer] List of mob control commands ---> BUILDIN_DEF(getunittype,"i"), @@ -25320,7 +27160,10 @@ 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(unitiswalking, "?"), BUILDIN_DEF(unitkill,"i"), BUILDIN_DEF(unitwarp,"isii"), BUILDIN_DEF(unitattack,"iv?"), @@ -25362,6 +27205,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(mercenary_set_faith,"ii"), BUILDIN_DEF(readbook,"ii"), BUILDIN_DEF(setfont,"i"), + BUILDIN_DEF(getfont, ""), BUILDIN_DEF(areamobuseskill,"siiiiviiiii"), BUILDIN_DEF(progressbar,"si"), BUILDIN_DEF(progressbar_unit,"si?"), @@ -25377,6 +27221,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(agitcheck2,""), // Achievements [Smokexyz/Hercules] BUILDIN_DEF(achievement_progress, "iiii?"), + BUILDIN_DEF(achievement_iscompleted, "i?"), // BattleGround BUILDIN_DEF(waitingroom2bg,"siiss?"), BUILDIN_DEF(waitingroom2bg_single,"isiis"), @@ -25488,8 +27333,11 @@ static void script_parse_builtin(void) /* New Shop Support */ BUILDIN_DEF(openshop,"?"), - BUILDIN_DEF(sellitem,"i??"), - BUILDIN_DEF(stopselling,"i"), + BUILDIN_DEF(sellitem, "i???*"), + BUILDIN_DEF(sellitemcurrency, "ii?"), + BUILDIN_DEF(startsellitem, "iii"), + BUILDIN_DEF(endsellitem, ""), + BUILDIN_DEF(stopselling,"i??"), BUILDIN_DEF(setcurrency,"i?"), BUILDIN_DEF(tradertype,"i"), BUILDIN_DEF(purchaseok,""), @@ -25506,7 +27354,7 @@ static void script_parse_builtin(void) BUILDIN_DEF(channelmes, "ss"), BUILDIN_DEF(addchannelhandler, "ss"), BUILDIN_DEF(removechannelhandler, "ss"), - BUILDIN_DEF(showscript, "s?"), + BUILDIN_DEF(showscript, "s??"), BUILDIN_DEF(mergeitem,""), BUILDIN_DEF(getcalendartime, "ii??"), @@ -25528,6 +27376,20 @@ static void script_parse_builtin(void) BUILDIN_DEF(changecamera, "iii?"), BUILDIN_DEF(itempreview, "i"), + BUILDIN_DEF(enchantitem, "iii"), + BUILDIN_DEF(expandinventoryack, "i?"), + BUILDIN_DEF(expandinventoryresult, "i"), + BUILDIN_DEF(expandinventory, "i"), + BUILDIN_DEF(getinventorysize, ""), + + BUILDIN_DEF(closeroulette, ""), + BUILDIN_DEF(openrefineryui, ""), + BUILDIN_DEF(setfavoriteitemidx, "ii"), + BUILDIN_DEF(autofavoriteitem, "ii"), + + BUILDIN_DEF(identify, "i"), + BUILDIN_DEF(identifyidx, "i"), + BUILDIN_DEF(openlapineddukddakboxui, "i"), }; int i, len = ARRAYLENGTH(BUILDIN); RECREATE(script->buildin, char *, script->buildin_count + len); // Pre-alloc to speed up @@ -25567,15 +27429,20 @@ static void script_hardcoded_constants(void) script->set_constant("MAX_LEVEL",MAX_LEVEL,false, false); script->set_constant("MAX_STORAGE",MAX_STORAGE,false, false); script->set_constant("MAX_GUILD_STORAGE",MAX_GUILD_STORAGE,false, false); - script->set_constant("MAX_CART",MAX_INVENTORY,false, false); + script->set_constant("MAX_CART", MAX_CART, false, false); script->set_constant("MAX_INVENTORY",MAX_INVENTORY,false, false); + script->set_constant("FIXED_INVENTORY_SIZE", FIXED_INVENTORY_SIZE, false, false); script->set_constant("MAX_ZENY",MAX_ZENY,false, false); script->set_constant("MAX_BANK_ZENY", MAX_BANK_ZENY, false, false); script->set_constant("MAX_BG_MEMBERS",MAX_BG_MEMBERS,false, false); script->set_constant("MAX_CHAT_USERS",MAX_CHAT_USERS,false, false); script->set_constant("MAX_REFINE",MAX_REFINE,false, false); + script->set_constant("MAX_ITEM_ID",MAX_ITEM_ID,false, false); script->set_constant("MAX_MENU_OPTIONS", MAX_MENU_OPTIONS, false, false); 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); @@ -25755,6 +27622,7 @@ static void script_hardcoded_constants(void) script->set_constant("PERM_DISABLE_STORE", PC_PERM_DISABLE_STORE, false, false); script->set_constant("PERM_DISABLE_EXP", PC_PERM_DISABLE_EXP, false, false); script->set_constant("PERM_DISABLE_SKILL_USAGE", PC_PERM_DISABLE_SKILL_USAGE, false, false); + script->set_constant("PERM_BYPASS_NOSTORAGE", PC_PERM_BYPASS_NOSTORAGE, false, false); script->constdb_comment("Data types"); script->set_constant("DATATYPE_NIL", DATATYPE_NIL, false, false); @@ -25826,6 +27694,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); @@ -25846,6 +27723,75 @@ static void script_hardcoded_constants(void) script->set_constant("ITEMINFO_MATK", ITEMINFO_MATK, false, false); script->set_constant("ITEMINFO_VIEWSPRITE", ITEMINFO_VIEWSPRITE, false, false); script->set_constant("ITEMINFO_TRADE", ITEMINFO_TRADE, false, false); + script->set_constant("ITEMINFO_ELV_MAX", ITEMINFO_ELV_MAX, false, false); + script->set_constant("ITEMINFO_DROPEFFECT_MODE", ITEMINFO_DROPEFFECT_MODE, false, false); + script->set_constant("ITEMINFO_DELAY", ITEMINFO_DELAY, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_1", ITEMINFO_CLASS_BASE_1, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_2", ITEMINFO_CLASS_BASE_2, false, false); + script->set_constant("ITEMINFO_CLASS_BASE_3", ITEMINFO_CLASS_BASE_3, false, false); + script->set_constant("ITEMINFO_CLASS_UPPER", ITEMINFO_CLASS_UPPER, false, false); + script->set_constant("ITEMINFO_FLAG_NO_REFINE", ITEMINFO_FLAG_NO_REFINE, false, false); + script->set_constant("ITEMINFO_FLAG_DELAY_CONSUME", ITEMINFO_FLAG_DELAY_CONSUME, false, false); + script->set_constant("ITEMINFO_FLAG_AUTOEQUIP", ITEMINFO_FLAG_AUTOEQUIP, false, false); + script->set_constant("ITEMINFO_FLAG_AUTO_FAVORITE", ITEMINFO_FLAG_AUTO_FAVORITE, false, false); + script->set_constant("ITEMINFO_FLAG_BUYINGSTORE", ITEMINFO_FLAG_BUYINGSTORE, false, false); + script->set_constant("ITEMINFO_FLAG_BINDONEQUIP", ITEMINFO_FLAG_BINDONEQUIP, false, false); + script->set_constant("ITEMINFO_FLAG_KEEPAFTERUSE", ITEMINFO_FLAG_KEEPAFTERUSE, false, false); + script->set_constant("ITEMINFO_FLAG_FORCE_SERIAL", ITEMINFO_FLAG_FORCE_SERIAL, false, false); + script->set_constant("ITEMINFO_FLAG_NO_OPTIONS", ITEMINFO_FLAG_NO_OPTIONS, false, false); + script->set_constant("ITEMINFO_FLAG_DROP_ANNOUNCE", ITEMINFO_FLAG_DROP_ANNOUNCE, false, false); + script->set_constant("ITEMINFO_FLAG_SHOWDROPEFFECT", ITEMINFO_FLAG_SHOWDROPEFFECT, false, false); + script->set_constant("ITEMINFO_STACK_AMOUNT", ITEMINFO_STACK_AMOUNT, false, false); + script->set_constant("ITEMINFO_STACK_FLAG", ITEMINFO_STACK_FLAG, false, false); + script->set_constant("ITEMINFO_ITEM_USAGE_FLAG", ITEMINFO_ITEM_USAGE_FLAG, false, false); + script->set_constant("ITEMINFO_ITEM_USAGE_OVERRIDE", ITEMINFO_ITEM_USAGE_OVERRIDE, false, false); + script->set_constant("ITEMINFO_GM_LV_TRADE_OVERRIDE", ITEMINFO_GM_LV_TRADE_OVERRIDE, false, false); + script->set_constant("ITEMINFO_ID", ITEMINFO_ID, false, false); + script->set_constant("ITEMINFO_AEGISNAME", ITEMINFO_AEGISNAME, false, false); + script->set_constant("ITEMINFO_NAME", ITEMINFO_NAME, false, false); + + script->constdb_comment("getmercinfo options"); + script->set_constant("MERCINFO_ID,", MERCINFO_ID, false, false); + script->set_constant("MERCINFO_CLASS", MERCINFO_CLASS, false, false); + script->set_constant("MERCINFO_NAME", MERCINFO_NAME, false, false); + script->set_constant("MERCINFO_FAITH", MERCINFO_FAITH, false, false); + script->set_constant("MERCINFO_CALLS", MERCINFO_CALLS, false, false); + script->set_constant("MERCINFO_KILLCOUNT", MERCINFO_KILLCOUNT, false, false); + script->set_constant("MERCINFO_LIFETIME", MERCINFO_LIFETIME, false, false); + script->set_constant("MERCINFO_LEVEL", MERCINFO_LEVEL, false, false); + script->set_constant("MERCINFO_GID", MERCINFO_GID, false, false); + + script->constdb_comment("getpetinfo options"); + script->set_constant("PETINFO_ID", PETINFO_ID, false, false); + script->set_constant("PETINFO_CLASS", PETINFO_CLASS, false, false); + script->set_constant("PETINFO_NAME", PETINFO_NAME, false, false); + script->set_constant("PETINFO_INTIMACY", PETINFO_INTIMACY, false, false); + script->set_constant("PETINFO_HUNGRY", PETINFO_HUNGRY, false, false); + script->set_constant("PETINFO_RENAME", PETINFO_RENAME, false, false); + script->set_constant("PETINFO_GID", PETINFO_GID, false, false); + script->set_constant("PETINFO_EGGITEM", PETINFO_EGGITEM, false, false); + script->set_constant("PETINFO_FOODITEM", PETINFO_FOODITEM, false, false); + script->set_constant("PETINFO_ACCESSORYITEM", PETINFO_ACCESSORYITEM, false, false); + script->set_constant("PETINFO_ACCESSORYFLAG", PETINFO_ACCESSORYFLAG, false, false); + script->set_constant("PETINFO_EVO_EGGID", PETINFO_EVO_EGGID, false, false); + script->set_constant("PETINFO_AUTOFEED", PETINFO_AUTOFEED, false, false); + + script->constdb_comment("Pet hunger levels"); + script->set_constant("PET_HUNGER_STARVING", PET_HUNGER_STARVING, false, false); + script->set_constant("PET_HUNGER_VERY_HUNGRY", PET_HUNGER_VERY_HUNGRY, false, false); + script->set_constant("PET_HUNGER_HUNGRY", PET_HUNGER_HUNGRY, false, false); + script->set_constant("PET_HUNGER_NEUTRAL", PET_HUNGER_NEUTRAL, false, false); + script->set_constant("PET_HUNGER_SATISFIED", PET_HUNGER_SATISFIED, false, false); + script->set_constant("PET_HUNGER_STUFFED", PET_HUNGER_STUFFED, false, false); + + script->constdb_comment("Pet intimacy levels"); + script->set_constant("PET_INTIMACY_NONE", PET_INTIMACY_NONE, false, false); + script->set_constant("PET_INTIMACY_AWKWARD", PET_INTIMACY_AWKWARD, false, false); + script->set_constant("PET_INTIMACY_SHY", PET_INTIMACY_SHY, false, false); + script->set_constant("PET_INTIMACY_NEUTRAL", PET_INTIMACY_NEUTRAL, false, false); + script->set_constant("PET_INTIMACY_CORDIAL", PET_INTIMACY_CORDIAL, false, false); + script->set_constant("PET_INTIMACY_LOYAL", PET_INTIMACY_LOYAL, false, false); + script->set_constant("PET_INTIMACY_MAX", PET_INTIMACY_MAX, false, false); script->constdb_comment("monster skill states"); script->set_constant("MSS_ANY", MSS_ANY, false, false); @@ -25901,6 +27847,11 @@ static void script_hardcoded_constants(void) script->set_constant("MST_AROUND4", MST_AROUND4, false, false); script->set_constant("MST_AROUND", MST_AROUND , false, false); + script->constdb_comment("Monster group constants"); + script->set_constant("ALL_MOBS_NONBOSS", ALL_MOBS_NONBOSS, false, false); + script->set_constant("ALL_MOBS_BOSS", ALL_MOBS_BOSS, false, false); + script->set_constant("ALL_MOBS", ALL_MOBS, false, false); + script->constdb_comment("pc block constants, use with *setpcblock* and *checkpcblock*"); script->set_constant("PCBLOCK_NONE", PCBLOCK_NONE, false, false); script->set_constant("PCBLOCK_MOVE", PCBLOCK_MOVE, false, false); @@ -25911,6 +27862,7 @@ static void script_hardcoded_constants(void) script->set_constant("PCBLOCK_IMMUNE", PCBLOCK_IMMUNE, false, false); script->set_constant("PCBLOCK_SITSTAND", PCBLOCK_SITSTAND, false, false); script->set_constant("PCBLOCK_COMMANDS", PCBLOCK_COMMANDS, false, false); + script->set_constant("PCBLOCK_NPC", PCBLOCK_NPC, false, false); script->constdb_comment("private airship responds"); script->set_constant("P_AIRSHIP_NONE", P_AIRSHIP_NONE, false, false); @@ -25950,6 +27902,128 @@ static void script_hardcoded_constants(void) script->set_constant("ITR_NOAUCTION", ITR_NOAUCTION, false, false); script->set_constant("ITR_ALL", ITR_ALL, false, false); + script->constdb_comment("inventory expand ack responds"); + script->set_constant("EXPAND_INV_ASK_CONFIRMATION", EXPAND_INVENTORY_ASK_CONFIRMATION, false, false); + script->set_constant("EXPAND_INV_FAILED", EXPAND_INVENTORY_FAILED, false, false); + script->set_constant("EXPAND_INV_OTHER_WORK", EXPAND_INVENTORY_OTHER_WORK, false, false); + script->set_constant("EXPAND_INV_MISSING_ITEM", EXPAND_INVENTORY_MISSING_ITEM, false, false); + script->set_constant("EXPAND_INV_MAX_SIZE", EXPAND_INVENTORY_MAX_SIZE, false, false); + + script->constdb_comment("inventory expand final responds"); + script->set_constant("EXPAND_INV_RESULT_SUCCESS", EXPAND_INVENTORY_RESULT_SUCCESS, false, false); + script->set_constant("EXPAND_INV_RESULT_FAILED", EXPAND_INVENTORY_RESULT_FAILED, false, false); + script->set_constant("EXPAND_INV_RESULT_OTHER_WORK", EXPAND_INVENTORY_RESULT_OTHER_WORK, false, false); + script->set_constant("EXPAND_INV_RESULT_MISSING_ITEM", EXPAND_INVENTORY_RESULT_MISSING_ITEM, false, false); + script->set_constant("EXPAND_INV_RESULT_MAX_SIZE", EXPAND_INVENTORY_RESULT_MAX_SIZE, false, false); + + script->constdb_comment("trader type"); + script->set_constant("NST_ZENY", NST_ZENY, false, false); + script->set_constant("NST_CASH", NST_CASH, false, false); + script->set_constant("NST_MARKET", NST_MARKET, false, false); + script->set_constant("NST_CUSTOM", NST_CUSTOM, false, false); + script->set_constant("NST_BARTER", NST_BARTER, false, false); + script->set_constant("NST_EXPANDED_BARTER", NST_EXPANDED_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->set_constant("UDT_DAMAGE_TAKEN_RATE", UDT_DAMAGE_TAKEN_RATE, false, false); + + script->constdb_comment("getguildonline types"); + script->set_constant("GUILD_ONLINE_ALL", GUILD_ONLINE_ALL, false, false); + script->set_constant("GUILD_ONLINE_VENDOR", GUILD_ONLINE_VENDOR, false, false); + script->set_constant("GUILD_ONLINE_NO_VENDOR", GUILD_ONLINE_NO_VENDOR, false, false); + + script->constdb_comment("Siege Types"); + script->set_constant("SIEGE_TYPE_FE", SIEGE_TYPE_FE, false, false); + script->set_constant("SIEGE_TYPE_SE", SIEGE_TYPE_SE, false, false); + script->set_constant("SIEGE_TYPE_TE", SIEGE_TYPE_TE, false, false); + + script->constdb_comment("guildinfo types"); + script->set_constant("GUILDINFO_NAME", GUILDINFO_NAME, false, false); + script->set_constant("GUILDINFO_ID", GUILDINFO_ID, false, false); + script->set_constant("GUILDINFO_LEVEL", GUILDINFO_LEVEL, false, false); + script->set_constant("GUILDINFO_ONLINE", GUILDINFO_ONLINE, false, false); + script->set_constant("GUILDINFO_AV_LEVEL", GUILDINFO_AV_LEVEL, false, false); + script->set_constant("GUILDINFO_MAX_MEMBERS", GUILDINFO_MAX_MEMBERS, false, false); + script->set_constant("GUILDINFO_EXP", GUILDINFO_EXP, false, false); + script->set_constant("GUILDINFO_NEXT_EXP", GUILDINFO_NEXT_EXP, false, false); + script->set_constant("GUILDINFO_SKILL_POINTS", GUILDINFO_SKILL_POINTS, false, false); + script->set_constant("GUILDINFO_MASTER_NAME", GUILDINFO_MASTER_NAME, false, false); + script->set_constant("GUILDINFO_MASTER_CID", GUILDINFO_MASTER_CID, false, false); + + script->constdb_comment("madogear types"); + script->set_constant("MADO_ROBOT", MADO_ROBOT, false, false); + script->set_constant("MADO_SUITE", MADO_SUITE, false, false); + + script->constdb_comment("itemskill option flags"); + script->set_constant("ISF_NONE", ISF_NONE, false, false); + script->set_constant("ISF_CHECKCONDITIONS", ISF_CHECKCONDITIONS, false, false); + script->set_constant("ISF_INSTANTCAST", ISF_INSTANTCAST, false, false); + script->set_constant("ISF_CASTONSELF", ISF_CASTONSELF, false, false); + + script->constdb_comment("Item Bound Types"); + script->set_constant("IBT_ANY", IBT_NONE, false, false); // for *checkbound() + script->set_constant("IBT_ACCOUNT", IBT_ACCOUNT, false, false); + script->set_constant("IBT_GUILD", IBT_GUILD, false, false); + script->set_constant("IBT_PARTY", IBT_PARTY, false, false); + script->set_constant("IBT_CHARACTER", IBT_CHARACTER, false, false); script->constdb_comment("Renewal"); #ifdef RENEWAL @@ -25988,7 +28062,6 @@ static void script_hardcoded_constants(void) script->set_constant("RENEWAL_ASPD", 0, false, false); #endif script->constdb_comment(NULL); -#include "constants.inc" } /** @@ -26300,15 +28373,21 @@ void script_defaults(void) script->string_dup = script_string_dup; script->load_translations = script_load_translations; script->load_translation_addstring = script_load_translation_addstring; + script->load_translation_file = script_load_translation_file; script->load_translation = script_load_translation; script->translation_db_destroyer = script_translation_db_destroyer; script->clear_translations = script_clear_translations; script->parse_cleanup_timer = script_parse_cleanup_timer; script->add_language = script_add_language; - script->get_translation_file_name = script_get_translation_file_name; + script->get_translation_dir_name = script_get_translation_dir_name; script->parser_clean_leftovers = script_parser_clean_leftovers; script->run_use_script = script_run_use_script; script->run_item_equip_script = script_run_item_equip_script; script->run_item_unequip_script = script_run_item_unequip_script; + script->run_item_rental_start_script = script_run_item_rental_start_script; + script->run_item_rental_end_script = script_run_item_rental_end_script; + script->run_item_lapineddukddak_script = script_run_item_lapineddukddak_script; + + script->sellitemcurrency_add = script_sellitemcurrency_add; } diff --git a/src/map/script.h b/src/map/script.h index 549ad3284..511497a66 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -343,7 +343,9 @@ enum { MF_NOAUTOLOOT, MF_NOVIEWID, MF_PAIRSHIP_STARTABLE, - MF_PAIRSHIP_ENDABLE + MF_PAIRSHIP_ENDABLE, + MF_NOSTORAGE, + MF_NOGSTORAGE }; enum navigation_service { @@ -431,6 +433,8 @@ enum script_unit_data_types { UDT_STATPOINT, UDT_ROBE, UDT_BODY2, + UDT_GROUP, + UDT_DAMAGE_TAKEN_RATE, UDT_MAX }; @@ -456,24 +460,121 @@ enum script_iteminfo_types { ITEMINFO_MATK, ITEMINFO_VIEWSPRITE, ITEMINFO_TRADE, - + ITEMINFO_ELV_MAX, + ITEMINFO_DROPEFFECT_MODE, + ITEMINFO_DELAY, + ITEMINFO_CLASS_BASE_1, + ITEMINFO_CLASS_BASE_2, + ITEMINFO_CLASS_BASE_3, + ITEMINFO_CLASS_UPPER, + // ITEMINFO_FLAG_AVAILABLE, + ITEMINFO_FLAG_NO_REFINE, + ITEMINFO_FLAG_DELAY_CONSUME, + ITEMINFO_FLAG_AUTOEQUIP, + ITEMINFO_FLAG_AUTO_FAVORITE, + ITEMINFO_FLAG_BUYINGSTORE, + ITEMINFO_FLAG_BINDONEQUIP, + ITEMINFO_FLAG_KEEPAFTERUSE, + ITEMINFO_FLAG_FORCE_SERIAL, + ITEMINFO_FLAG_NO_OPTIONS, + ITEMINFO_FLAG_DROP_ANNOUNCE, + ITEMINFO_FLAG_SHOWDROPEFFECT, + ITEMINFO_STACK_AMOUNT, + ITEMINFO_STACK_FLAG, + ITEMINFO_ITEM_USAGE_FLAG, + ITEMINFO_ITEM_USAGE_OVERRIDE, + ITEMINFO_GM_LV_TRADE_OVERRIDE, + ITEMINFO_ID, + ITEMINFO_AEGISNAME, + ITEMINFO_NAME, ITEMINFO_MAX }; /** + * Mercenary Info types. + */ +enum script_mercinfo_types { + MERCINFO_ID = 0, + MERCINFO_CLASS, + MERCINFO_NAME, + MERCINFO_FAITH, + MERCINFO_CALLS, + MERCINFO_KILLCOUNT, + MERCINFO_LIFETIME, + MERCINFO_LEVEL, + MERCINFO_GID, + + MERCINFO_MAX +}; + +/** + * Pet Info types. + */ +enum script_petinfo_types { + PETINFO_ID = 0, + PETINFO_CLASS, + PETINFO_NAME, + PETINFO_INTIMACY, + PETINFO_HUNGRY, + PETINFO_RENAME, + PETINFO_GID, + PETINFO_EGGITEM, + PETINFO_FOODITEM, + PETINFO_ACCESSORYITEM, + PETINFO_ACCESSORYFLAG, + PETINFO_EVO_EGGID, + PETINFO_AUTOFEED, + + PETINFO_MAX +}; + +/** * Player blocking actions related flags. */ enum pcblock_action_flag { - PCBLOCK_NONE = 0x00, - PCBLOCK_MOVE = 0x01, - PCBLOCK_ATTACK = 0x02, - PCBLOCK_SKILL = 0x04, - PCBLOCK_USEITEM = 0x08, - PCBLOCK_CHAT = 0x10, - PCBLOCK_IMMUNE = 0x20, - PCBLOCK_SITSTAND = 0x40, - PCBLOCK_COMMANDS = 0x80, - PCBLOCK_ALL = 0xFF, + PCBLOCK_NONE = 0x000, + PCBLOCK_MOVE = 0x001, + PCBLOCK_ATTACK = 0x002, + PCBLOCK_SKILL = 0x004, + PCBLOCK_USEITEM = 0x008, + PCBLOCK_CHAT = 0x010, + PCBLOCK_IMMUNE = 0x020, + PCBLOCK_SITSTAND = 0x040, + PCBLOCK_COMMANDS = 0x080, + PCBLOCK_NPC = 0x100, + PCBLOCK_ALL = 0x1FF, +}; + +/** + * Types of Siege (WoE) + */ +enum siege_type { + SIEGE_TYPE_FE, + SIEGE_TYPE_SE, + SIEGE_TYPE_TE, + SIEGE_TYPE_MAX +}; + +/** + * Types of MadoGear + */ +enum mado_type { + MADO_ROBOT = 0x00, + // unused = 0x01, + MADO_SUITE = 0x02, +#ifndef MADO_MAX + MADO_MAX +#endif +}; + +/** + * Option flags for itemskill() script command. + **/ +enum itemskill_flag { + ISF_NONE = 0x00, + ISF_CHECKCONDITIONS = 0x01, // Check skill conditions and consume them. + ISF_INSTANTCAST = 0x02, // Cast skill instantaneously. + ISF_CASTONSELF = 0x04, // Forcefully cast skill on invoking character without showing the target selection cursor. }; /** @@ -964,16 +1065,21 @@ struct script_interface { int (*string_dup) (char *str); void (*load_translations) (void); bool (*load_translation_addstring) (const char *file, uint8 lang_id, const char *msgctxt, const struct script_string_buf *msgid, const struct script_string_buf *msgstr); - int (*load_translation) (const char *file, uint8 lang_id); + int (*load_translation_file) (const char *file, uint8 lang_id); + int (*load_translation) (const char *directory, uint8 lang_id); int (*translation_db_destroyer) (union DBKey key, struct DBData *data, va_list ap); void (*clear_translations) (bool reload); int (*parse_cleanup_timer) (int tid, int64 tick, int id, intptr_t data); uint8 (*add_language) (const char *name); - const char *(*get_translation_file_name) (const char *file); + const char *(*get_translation_dir_name) (const char *directory); void (*parser_clean_leftovers) (void); void (*run_use_script) (struct map_session_data *sd, struct item_data *data, int oid); void (*run_item_equip_script) (struct map_session_data *sd, struct item_data *data, int oid); void (*run_item_unequip_script) (struct map_session_data *sd, struct item_data *data, int oid); + void (*run_item_rental_end_script) (struct map_session_data *sd, struct item_data *data, int oid); + void (*run_item_rental_start_script) (struct map_session_data *sd, struct item_data *data, int oid); + void (*run_item_lapineddukddak_script) (struct map_session_data *sd, struct item_data *data, int oid); + bool (*sellitemcurrency_add) (struct npc_data *nd, struct script_state* st, int argIndex); }; #ifdef HERCULES_CORE diff --git a/src/map/searchstore.c b/src/map/searchstore.c index 0c6fa3555..a2f00bb0a 100644 --- a/src/map/searchstore.c +++ b/src/map/searchstore.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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..83705d4ca 100644 --- a/src/map/searchstore.h +++ b/src/map/searchstore.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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 8410a77d0..caa1a0f29 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -66,25 +67,39 @@ #define SKILLUNITTIMER_INTERVAL 100 -// ranges reserved for mapping skill ids to skilldb offsets -#define HM_SKILLRANGEMIN 750 -#define HM_SKILLRANGEMAX (HM_SKILLRANGEMIN + MAX_HOMUNSKILL) -#define MC_SKILLRANGEMIN (HM_SKILLRANGEMAX + 1) -#define MC_SKILLRANGEMAX (MC_SKILLRANGEMIN + MAX_MERCSKILL) -#define EL_SKILLRANGEMIN (MC_SKILLRANGEMAX + 1) -#define EL_SKILLRANGEMAX (EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL) -#define GD_SKILLRANGEMIN (EL_SKILLRANGEMAX + 1) -#define GD_SKILLRANGEMAX (GD_SKILLRANGEMIN + MAX_GUILDSKILL) - -#if GD_SKILLRANGEMAX > 999 - #error GD_SKILLRANGEMAX is greater than 999 -#endif - static struct skill_interface skill_s; -struct s_skill_dbs skilldbs; +static struct s_skill_dbs skilldbs; struct skill_interface *skill; +static const struct { + int start; + int end; +} skill_idx_ranges[] = { + { NV_BASIC, NPC_LEX_AETERNA }, + { KN_CHARGEATK, SA_ELEMENTWIND }, + { RK_ENCHANTBLADE, AB_SILENTIUM }, + { WL_WHITEIMPRISON, SC_FEINTBOMB }, + { LG_CANNONSPEAR, SR_GENTLETOUCH_REVITALIZE }, + { WA_SWING_DANCE, WA_MOONLIT_SERENADE }, + { MI_RUSH_WINDMILL, MI_HARMONIZE }, + { WM_LESSON, WM_UNLIMITED_HUMMING_VOICE }, + { SO_FIREWALK, SO_EARTH_INSIGNIA }, + { GN_TRAINING_SWORD, GN_SLINGITEM_RANGEMELEEATK }, + { AB_SECRAMENT, LG_OVERBRAND_PLUSATK }, + { ALL_ODINS_RECALL, ALL_LIGHTGUARD }, + { RL_GLITTERING_GREED, RL_GLITTERING_GREED_ATK }, + { KO_YAMIKUMO, OB_AKAITSUKI }, + { ECL_SNOWFLIP, ALL_THANATOS_RECALL }, + { GC_DARKCROW, NC_MAGMA_ERUPTION_DOTDAMAGE }, + { SU_BASIC_SKILL, SU_SPIRITOFSEA }, + { HLIF_HEAL, MH_VOLCANIC_ASH }, + { MS_BASH, MER_INVINCIBLEOFF2 }, + { EL_CIRCLE_OF_FIRE, EL_STONE_RAIN }, + { GD_APPROVAL, GD_DEVELOPMENT }, + CUSTOM_SKILL_RANGES +}; + //Since only mob-casted splash skills can hit ice-walls static int skill_splash_target(struct block_list *bl) { @@ -109,41 +124,41 @@ static int skill_name2id(const char *name) /// Returns the skill's array index, or 0 (Unknown Skill). static int skill_get_index(int skill_id) { - // avoid ranges reserved for mapping guild/homun/mercenary skills - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) + int length = ARRAYLENGTH(skill_idx_ranges); + + + if (skill_id < skill_idx_ranges[0].start || skill_id > skill_idx_ranges[length - 1].end) { + ShowWarning("skill_get_index: skill id '%d' is not being handled!\n", skill_id); + Assert_report(0); return 0; + } - // map skill id to skill db index - if( skill_id >= GD_SKILLBASE ) - skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE; - else if( skill_id >= EL_SKILLBASE ) - skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE; - else if( skill_id >= MC_SKILLBASE ) - skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE; - else if( skill_id >= HM_SKILLBASE ) - skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE; - //[Ind/Hercules] GO GO GO LESS! - http://herc.ws/board/topic/512-skill-id-processing-overhaul/ - else if( skill_id > 1019 && skill_id < 8001 ) { - if( skill_id < 2058 ) // 1020 - 2000 are empty - skill_id = 1020 + skill_id - 2001; - else if( skill_id < 2549 ) // 2058 - 2200 are empty - 1020+57 - skill_id = (1077) + skill_id - 2201; - else if ( skill_id < 3036 ) // 2549 - 3000 are empty - 1020+57+348 - skill_id = (1425) + skill_id - 3001; - else if ( skill_id < 5044 ) // 3036 - 5000 are empty - 1020+57+348+35 - skill_id = (1460) + skill_id - 5001; - else - ShowWarning("skill_get_index: skill id '%d' is not being handled!\n",skill_id); + int skill_idx = 0; + bool found = false; + // Map Skill ID to Skill Indexes (in reverse order) + for (int i = 0; i < length; i++) { + // Check if SkillID belongs to this range. + if (skill_id <= skill_idx_ranges[i].end && skill_id >= skill_idx_ranges[i].start) { + skill_idx += (skill_idx_ranges[i].end - skill_id); + found = true; + break; + } + // Add the difference of current range + skill_idx += (skill_idx_ranges[i].end - skill_idx_ranges[i].start + 1); } - // validate result - if (skill_id <= 0|| skill_id >= MAX_SKILL_DB) + if (!found) { + ShowWarning("skill_get_index: skill id '%d' (idx: %d) is not handled as it lies outside the defined ranges!\n", skill_id, skill_idx); + Assert_report(0); + return 0; + } + if (skill_idx >= MAX_SKILL_DB) { + ShowWarning("skill_get_index: skill id '%d'(idx: %d) is not being handled as it exceeds MAX_SKILL_DB!\n", skill_id, skill_idx); + Assert_report(0); return 0; + } - return skill_id; + return skill_idx; } static const char *skill_get_name(int skill_id) @@ -934,6 +949,8 @@ static int skill_calc_heal(struct block_list *src, struct block_list *target, ui hp -= hp * 20/100; if(sc->data[SC_HEALPLUS] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN) hp += hp * sc->data[SC_HEALPLUS]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish] + if (sc->data[SC_VITALIZE_POTION] != NULL && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN) + hp += hp * sc->data[SC_VITALIZE_POTION]->val3 / 100; if(sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) hp += hp / 10; if (sc->data[SC_VITALITYACTIVATION]) @@ -1009,14 +1026,17 @@ static int skillnotok(uint16 skill_id, struct map_session_data *sd) if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL)) return 0; // can do any damn thing they want - if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 ) - return 0; // Teleport lv 3 bypasses this check.[Inkfish] + if (map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y, CELL_CHKNOSKILL)) + return 1; // block usage on 'noskill' cells [Wolfie] + + if (skill_id == AL_TELEPORT && sd->autocast.type == AUTOCAST_ITEM && sd->autocast.skill_lv > 2) + return 0; // Teleport level 3 and higher bypasses this check if cast by itemskill() script commands. // Epoque: // This code will compare the player's attack motion value which is influenced by ASPD before // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion. - if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick && + if (sd->autocast.type == AUTOCAST_NONE && sd->canskill_tick != 0 && DIFF_TICK(timer->gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) ) {// attempted to cast a skill before the attack motion has finished return 1; @@ -1031,7 +1051,7 @@ static int skillnotok(uint16 skill_id, struct map_session_data *sd) * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions below * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map **/ - if( sd->skillitem == skill_id ) + if (sd->autocast.type == AUTOCAST_ITEM) return 0; if( sd->sc.data[SC_ALL_RIDING] ) @@ -1176,10 +1196,34 @@ static int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md) return skill->not_ok(skill_id, md->master); } +/** + * Validates the plausibility of auto-cast related data and calls pc_autocast_clear() if necessary. + * + * @param sd The character who cast the skill. + * @param skill_id The cast skill's ID. + * @param skill_lv The cast skill's level. (clif_parse_UseSkillMap() passes 0.) + * + **/ +static void skill_validate_autocast_data(struct map_session_data *sd, int skill_id, int skill_lv) +{ + nullpo_retv(sd); + + // Determine if called by clif_parse_UseSkillMap(). + bool use_skill_map = (skill_lv == 0 && (skill_id == AL_WARP || skill_id == AL_TELEPORT)); + + if (sd->autocast.type == AUTOCAST_NONE) + pc->autocast_clear(sd); // No auto-cast type set. Preventively unset all auto-cast related data. + else if (sd->autocast.type == AUTOCAST_TEMP) + pc->autocast_clear(sd); // AUTOCAST_TEMP should have been unset straight after usage. + else if (sd->autocast.skill_id == 0 || skill_id == 0 || sd->autocast.skill_id != skill_id) + pc->autocast_clear(sd); // Implausible skill ID. + else if (sd->autocast.skill_lv == 0 || (!use_skill_map && (skill_lv == 0 || sd->autocast.skill_lv != skill_lv))) + pc->autocast_clear(sd); // Implausible skill level. +} + static struct s_skill_unit_layout *skill_get_unit_layout(uint16 skill_id, uint16 skill_lv, struct block_list *src, int x, int y) { int pos = skill->get_unit_layout_type(skill_id,skill_lv); - uint8 dir; nullpo_retr(&skill->dbs->unit_layout[0], src); if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) { @@ -1190,7 +1234,9 @@ static struct s_skill_unit_layout *skill_get_unit_layout(uint16 skill_id, uint16 if (pos != -1) // simple single-definition layout return &skill->dbs->unit_layout[pos]; - dir = (src->x == x && src->y == y) ? 6 : map->calc_dir(src,x,y); // 6 - default aegis direction + enum unit_dir dir = UNIT_DIR_EAST; // default aegis direction + if (src->x != x || src->y != y) + dir = map->calc_dir(src, x, y); if (skill_id == MG_FIREWALL) return &skill->dbs->unit_layout [skill->firewall_unit_pos + dir]; @@ -2042,7 +2088,7 @@ static int skill_additional_effect(struct block_list *src, struct block_list *bl if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if ( battle_config.display_status_timers ) - clif->status_change(src, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } @@ -2063,9 +2109,9 @@ static int skill_additional_effect(struct block_list *src, struct block_list *bl temp = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(temp, sd); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -2116,11 +2162,12 @@ static int skill_additional_effect(struct block_list *src, struct block_list *bl else if (temp == PF_SPIDERWEB) //Special case, due to its nature of coding. type = CAST_GROUND; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(sd,temp,auto_skill_lv,1); skill->toggle_magicpower(src, temp); skill->castend_type(type, src, tbl, temp, auto_skill_lv, tick, 0); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; + //Set canact delay. [Skotlex] ud = unit->bl2ud(src); if (ud) { @@ -2128,7 +2175,7 @@ static int skill_additional_effect(struct block_list *src, struct block_list *bl if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if (battle_config.display_status_timers) - clif->status_change(src, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } @@ -2189,6 +2236,9 @@ static int skill_onskillusage(struct map_session_data *sd, struct block_list *bl if( sd == NULL || !skill_id ) return 0; + // Preserve auto-cast type if bAutoSpellOnSkill was triggered by a skill which was cast by Abracadabra, Improvised Song or an item. + enum autocast_type ac_type = sd->autocast.type; + for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) { if( sd->autospell3[i].flag != skill_id ) continue; @@ -2198,9 +2248,9 @@ static int skill_onskillusage(struct map_session_data *sd, struct block_list *bl temp = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id; - sd->state.autocast = 1; + sd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(temp, sd); - sd->state.autocast = 0; + sd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -2246,14 +2296,16 @@ static int skill_onskillusage(struct map_session_data *sd, struct block_list *bl !battle->check_range(&sd->bl, tbl, skill->get_range2(&sd->bl, temp,skill_lv) + (temp == RG_CLOSECONFINE?0:1)) ) continue; - sd->state.autocast = 1; sd->autospell3[i].lock = true; + sd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(sd,temp,skill_lv,1); skill->castend_type(type, &sd->bl, tbl, temp, skill_lv, tick, 0); + sd->autocast.type = AUTOCAST_NONE; sd->autospell3[i].lock = false; - sd->state.autocast = 0; } + sd->autocast.type = ac_type; + if (sd->autobonus3[0].rate) { for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ ) { if( rnd()%1000 >= sd->autobonus3[i].rate ) @@ -2400,6 +2452,9 @@ static int skill_counter_additional_effect(struct block_list *src, struct block_ struct unit_data *ud; int i, auto_skill_id, auto_skill_lv, type, notok; + // Preserve auto-cast type if bAutoSpellWhenHit was triggered during cast of a skill which was cast by Abracadabra, Improvised Song or an item. + enum autocast_type ac_type = dstsd->autocast.type; + for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) { if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK && @@ -2415,9 +2470,9 @@ static int skill_counter_additional_effect(struct block_list *src, struct block_ if (attack_type&BF_LONG) rate>>=1; - dstsd->state.autocast = 1; + dstsd->autocast.type = AUTOCAST_TEMP; notok = skill->not_ok(auto_skill_id, dstsd); - dstsd->state.autocast = 0; + dstsd->autocast.type = AUTOCAST_NONE; if ( notok ) continue; @@ -2458,10 +2513,11 @@ static int skill_counter_additional_effect(struct block_list *src, struct block_ if( !battle->check_range(src, tbl, skill->get_range2(src, auto_skill_id,auto_skill_lv) + (auto_skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range ) continue; - dstsd->state.autocast = 1; + dstsd->autocast.type = AUTOCAST_TEMP; skill->consume_requirement(dstsd,auto_skill_id,auto_skill_lv,1); skill->castend_type(type, bl, tbl, auto_skill_id, auto_skill_lv, tick, 0); - dstsd->state.autocast = 0; + dstsd->autocast.type = AUTOCAST_NONE; + // Set canact delay. [Skotlex] ud = unit->bl2ud(bl); if (ud) { @@ -2469,10 +2525,12 @@ static int skill_counter_additional_effect(struct block_list *src, struct block_ if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){ ud->canact_tick = tick+rate; if (battle_config.display_status_timers) - clif->status_change(bl, SI_POSTDELAY, 1, rate, 0, 0, 0); + clif->status_change(bl, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, rate, 0, 0, 0); } } } + + dstsd->autocast.type = ac_type; } //Autobonus when attacked @@ -2625,11 +2683,11 @@ static int skill_strip_equip(struct block_list *bl, unsigned short where, int ra /*========================================================================= * Used to knock back players, monsters, traps, etc * 'count' is the number of squares to knock back - * 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior) + * 'direction' indicates the way OPPOSITE to the knockback direction (or UNIT_DIR_UNDEFINED for default behavior) * if 'flag&0x1', position update packets must not be sent. * if 'flag&0x2', skill blown ignores players' special_state.no_knockback */ -static int skill_blown(struct block_list *src, struct block_list *target, int count, int8 dir, int flag) +static int skill_blown(struct block_list *src, struct block_list *target, int count, enum unit_dir dir, int flag) { int dx = 0, dy = 0; struct status_change *tsc = status->get_sc(target); @@ -2671,10 +2729,10 @@ static int skill_blown(struct block_list *src, struct block_list *target, int co break; } - if (dir == -1) // <optimized>: do the computation here instead of outside + if (dir == UNIT_DIR_UNDEFINED) // <optimized>: do the computation here instead of outside dir = map->calc_dir(target, src->x, src->y); // direction from src to target, reversed - if (dir >= 0 && dir < 8) { + if (dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX) { // take the reversed 'direction' and reverse it dx = -dirx[dir]; dy = -diry[dir]; @@ -2892,14 +2950,28 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li } #endif /* MAGIC_REFLECTION_TYPE */ } - if(sc && sc->data[SC_MAGICROD] && src == dsrc) { - int sp = skill->get_sp(skill_id,skill_lv); + if (sc && sc->data[SC_MAGICROD] && src == dsrc) { + int sp = skill->get_sp(skill_id, skill_lv); dmg.damage = dmg.damage2 = 0; dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex] sp = sp * sc->data[SC_MAGICROD]->val2 / 100; - if(skill_id == WZ_WATERBALL && skill_lv > 1) - sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball + if (skill_id == WZ_WATERBALL && skill_lv > 1) + sp = sp / ((skill_lv | 1) * (skill_lv | 1)); //Estimate SP cost of a single water-ball status->heal(bl, 0, sp, STATUS_HEAL_SHOWEFFECT); + if (battle->bc->magicrod_type == 1) + clif->skill_nodamage(bl, bl, SA_MAGICROD, sc->data[SC_MAGICROD]->val1, 1); // Animation used here in eAthena [Wolfie] + } + } + + if (bl->type == BL_MOB) { + struct mob_data *md = BL_CAST(BL_MOB, bl); + if (md != NULL) { + if (md->db->dmg_taken_rate != 100) { + if (dmg.damage > 0) + dmg.damage = apply_percentrate64(dmg.damage, md->db->dmg_taken_rate, 100); + if (dmg.damage2 > 0) + dmg.damage2 = apply_percentrate64(dmg.damage2, md->db->dmg_taken_rate, 100); + } } } @@ -3282,7 +3354,7 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills) if (dmg.blewcount > 0 && bl!=dsrc && !status->isdead(bl)) { - int8 dir = -1; // default + enum unit_dir dir = UNIT_DIR_UNDEFINED; // default switch(skill_id) {//direction case MG_FIREWALL: case PR_SANCTUARY: @@ -3295,13 +3367,13 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics. case WZ_STORMGUST: if(!battle_config.stormgust_knockback) - dir = rnd()%8; + dir = rnd() % UNIT_DIR_MAX; break; case WL_CRIMSONROCK: dir = map->calc_dir(bl,skill->area_temp[4],skill->area_temp[5]); break; case MC_CARTREVOLUTION: - dir = 6; // Official servers push target to the West + dir = UNIT_DIR_EAST; // Official servers push target to the West break; default: dir = skill->attack_dir_unknown(&attack_type, src, dsrc, bl, &skill_id, &skill_lv, &tick, &flag); @@ -3322,8 +3394,12 @@ static int skill_attack(int attack_type, struct block_list *src, struct block_li case SR_KNUCKLEARROW: if( skill->blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) { short dir_x, dir_y; - dir_x = dirx[(dir+4)%8]; - dir_y = diry[(dir+4)%8]; + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + dir_x = dirx[unit_get_opposite_dir(dir)]; + dir_y = diry[unit_get_opposite_dir(dir)]; if (map->getcell(bl->m, bl, bl->x + dir_x, bl->y + dir_y, CELL_CHKNOPASS) != 0) skill->addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4); } @@ -3483,10 +3559,12 @@ static int skill_attack_copy_unknown(int *attack_type, struct block_list *src, s static int skill_attack_dir_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag) { - return -1; + return UNIT_DIR_UNDEFINED; } -static void skill_attack_blow_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage, int8 *dir) +static void skill_attack_blow_unknown(int *attack_type, struct block_list *src, struct block_list *dsrc, struct block_list *bl, + uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, + struct Damage *dmg, int64 *damage, enum unit_dir *dir) { nullpo_retv(bl); nullpo_retv(dmg); @@ -3497,7 +3575,7 @@ static void skill_attack_blow_unknown(int *attack_type, struct block_list *src, if (!dmg->blewcount && bl->type == BL_SKILL && *damage > 0){ struct skill_unit *su = BL_UCAST(BL_SKILL, bl); if (su->group && su->group->skill_id == HT_BLASTMINE) - skill->blown(src, bl, 3, -1, 0); + skill->blown(src, bl, 3, UNIT_DIR_UNDEFINED, 0); } } @@ -3774,7 +3852,7 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill_id, if (itemid[i] < 1) continue; // No item index[i] = pc->search_inventory(sd, itemid[i]); if (index[i] == INDEX_NOT_FOUND || sd->status.inventory[index[i]].amount < amount[i]) { - clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, amount[i], itemid[i] << 16); + clif->skill_fail(sd, skill_id, USESKILL_FAIL_NEED_ITEM, amount[i], itemid[i]); return 0; } } @@ -4153,10 +4231,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; @@ -4387,7 +4464,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case KN_CHARGEATK: { bool path_exists = path->search_long(NULL, src, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL); unsigned int dist = distance_bl(src, bl); - uint8 dir = map->calc_dir(bl, src->x, src->y); + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); // teleport to target (if not on WoE grounds) if( !map_flag_gvg2(src->m) && !map->list[src->m].flag.battleground && unit->movepos(src, bl->x, bl->y, 0, 1) ) @@ -4399,7 +4476,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl skill->blown(src, bl, dist, dir, 0); //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore, // make the caster look in the direction of the target - unit->setdir(src, (dir+4)%8); + unit->set_dir(src, unit_get_opposite_dir(dir)); } } @@ -4438,12 +4515,13 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case RG_BACKSTAP: { - uint8 dir = map->calc_dir(src, bl->x, bl->y), t_dir = unit->getdir(bl); - if ((!check_distance_bl(src, bl, 0) && !map->check_dir(dir, t_dir)) || bl->type == BL_SKILL) { + enum unit_dir dir = map->calc_dir(src, bl->x, bl->y); + enum unit_dir t_dir = unit->getdir(bl); + if ((!check_distance_bl(src, bl, 0) && map->check_dir(dir, t_dir) == 0) || bl->type == BL_SKILL) { status_change_end(src, SC_HIDING, INVALID_TIMER); skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); - dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest] - unit->setdir(bl,dir); + dir = unit_get_opposite_dir(dir); // change direction [Celest] + unit->set_dir(bl, dir); } else if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); @@ -4470,7 +4548,6 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl { short x, y, i = 2; // Move 2 cells for Issen(from target) struct block_list *mbl = bl; - short dir = 0; skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); @@ -4492,13 +4569,13 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl status->set_hp(src, 1, STATUS_HEAL_DEFAULT); #endif // RENEWAL } - dir = map->calc_dir(src,bl->x,bl->y); - if( dir > 0 && dir < 4) x = -i; - else if( dir > 4 ) x = i; - else x = 0; - if( dir > 2 && dir < 6 ) y = -i; - else if( dir == 7 || dir < 2 ) y = i; - else y = 0; + enum unit_dir dir = map->calc_dir(src, bl->x, bl->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + x = i * dirx[dir]; + y = i * diry[dir]; if ((mbl == src || (!map_flag_gvg2(src->m) && !map->list[src->m].flag.battleground))) { // only NJ_ISSEN don't have slide effect in GVG if (!(unit->movepos(src, mbl->x+x, mbl->y+y, 1, 1))) { // The cell is not reachable (wall, object, ...), move next to the target @@ -4725,12 +4802,12 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if(idb_exists(skill->bowling_db, bl->id)) break; // Random direction - dir = rnd()%8; + dir = rnd() % UNIT_DIR_MAX; } else { // Create an empty list of already hit targets db_clear(skill->bowling_db); // Direction is walkpath - dir = (unit->getdir(src)+4)%8; + dir = unit_get_opposite_dir(unit->getdir(src)); } // Add current target to the list of already hit targets idb_put(skill->bowling_db, bl->id, bl); @@ -4739,6 +4816,10 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl ty = bl->y; for(i=0;i<c;i++) { // Target coordinates (get changed even if knockback fails) + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } tx -= dirx[dir]; ty -= diry[dir]; // If target cell is a wall then break @@ -4767,18 +4848,24 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if (bl->id==skill->area_temp[1]) break; if (skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION)) - skill->blown(src,bl,skill->area_temp[2],-1,0); + skill->blown(src, bl, skill->area_temp[2], UNIT_DIR_UNDEFINED, 0); } else { - int x=bl->x,y=bl->y,i,dir; - dir = map->calc_dir(bl,src->x,src->y); + int x = bl->x; + int y = bl->y; + int i; + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); skill->area_temp[1] = bl->id; skill->area_temp[2] = skill->get_blewcount(skill_id,skill_lv); // all the enemies between the caster and the target are hit, as well as the target if (skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0)) - skill->blown(src,bl,skill->area_temp[2],-1,0); + skill->blown(src, bl, skill->area_temp[2], UNIT_DIR_UNDEFINED, 0); for (i=0;i<4;i++) { map->foreachincell(skill->area_sub,bl->m,x,y,BL_CHAR,src,skill_id,skill_lv, tick,flag|BCT_ENEMY|1,skill->castend_damage_id); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } x += dirx[dir]; y += diry[dir]; } @@ -4935,7 +5022,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if( (tsc = status->get_sc(bl)) && (tsc->data[SC_HIDING] )) { clif->skill_nodamage(src,src,skill_id,skill_lv,1); } else - skill->attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag); + skill->attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag); } break; case NPC_SELFDESTRUCTION: { @@ -5000,7 +5087,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl if(rnd()%100 < (10 + 3*skill_lv)) { if( !sd || pc->checkskill(sd,KN_SPEARBOOMERANG) == 0 ) break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang. - skill->blown(src,bl,6,-1,0); + skill->blown(src, bl, 6, UNIT_DIR_UNDEFINED, 0); skill->addtimerskill(src,tick+800,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,flag); skill->castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0); } @@ -5008,7 +5095,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case RK_PHANTOMTHRUST: { struct map_session_data *tsd = BL_CAST(BL_PC, bl); - unit->setdir(src,map->calc_dir(src, bl->x, bl->y)); + unit->set_dir(src, map->calc_dir(src, bl->x, bl->y)); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); skill->blown(src,bl,distance_bl(src,bl)-1,unit->getdir(src),0); @@ -5022,16 +5109,13 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl case KO_JYUMONJIKIRI: case GC_DARKILLUSION: { - short x, y; - short dir = map->calc_dir(bl, src->x, src->y); - - if ( dir < 4 ) { - x = bl->x + 2 * (dir > 0) - 3 * (dir > 0); - y = bl->y + 1 - (dir / 2) - (dir > 2); - } else { - x = bl->x + 2 * (dir > 4) - 1 * (dir > 4); - y = bl->y + (dir / 6) - 1 + (dir > 6); + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; } + short x = bl->x + dirx[dir]; + short y = bl->y + diry[dir]; if ( unit->movepos(src, x, y, 1, 1) ) { clif->slide(src, x, y); @@ -5154,7 +5238,7 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl skill->castend_type(skill->get_casttype(spell_skill_id), src, bl, spell_skill_id, spell_skill_lv, tick, 0); sd->ud.canact_tick = tick + skill->delay_fix(src, spell_skill_id, spell_skill_lv); - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, spell_skill_id, spell_skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, spell_skill_id, spell_skill_lv), 0, 0, 0); cooldown = skill->get_cooldown(spell_skill_id, spell_skill_lv); for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { @@ -5188,14 +5272,16 @@ static int skill_castend_damage_id(struct block_list *src, struct block_list *bl skill->attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK); break; case RA_WUGSTRIKE: - if( sd && pc_isridingwug(sd) ){ - short x[8]={0,-1,-1,-1,0,1,1,1}; - short y[8]={1,1,0,-1,-1,-1,0,1}; - uint8 dir = map->calc_dir(bl, src->x, src->y); - - if( unit->movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) ) - { - clif->slide(src, bl->x+x[dir], bl->y+y[dir]); + if (sd != NULL && pc_isridingwug(sd)) { + enum unit_dir dir = map->calc_dir(bl, src->x, src->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } + short x = bl->x + dirx[dir]; + short y = bl->y + diry[dir]; + if (unit->movepos(src, x, y, 1, 1) != 0) { + clif->slide(src, x, y); clif->fixpos(src); skill->attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag); } @@ -5599,6 +5685,8 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) // Use a do so that you can break out of it when the skill fails. do { + bool is_asura = (ud->skill_id == MO_EXTREMITYFIST); + if(!target || target->prev==NULL) break; if(src->m != target->m || status->isdead(src)) break; @@ -5636,8 +5724,9 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) } if(ud->skill_id == RG_BACKSTAP) { - uint8 dir = map->calc_dir(src,target->x,target->y),t_dir = unit->getdir(target); - if(check_distance_bl(src, target, 0) || map->check_dir(dir,t_dir)) { + enum unit_dir dir = map->calc_dir(src, target->x, target->y); + enum unit_dir t_dir = unit->getdir(target); + if (check_distance_bl(src, target, 0) || map->check_dir(dir, t_dir) != 0) { break; } } @@ -5754,7 +5843,7 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH) unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick + skill->delay_fix(src, ud->skill_id, ud->skill_lv); // Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish] if (sd) { // Cooldown application int i, cooldown = skill->get_cooldown(ud->skill_id, ud->skill_lv); @@ -5768,7 +5857,7 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) skill->blockpc_start(sd, ud->skill_id, cooldown); } if( battle_config.display_status_timers && sd ) - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); if( sd ) { switch( ud->skill_id ) @@ -5823,7 +5912,7 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) } if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish] - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); if (ud->skilltimer == INVALID_TIMER) { if(md) md->skill_idx = -1; @@ -5831,8 +5920,9 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) ud->skill_lv = ud->skilltarget = 0; } - if (src->id != target->id) - unit->setdir(src, map->calc_dir(src, target->x, target->y)); + // Asura Strike caster doesn't look to their target in the end + if (src->id != target->id && !is_asura) + unit->set_dir(src, map->calc_dir(src, target->x, target->y)); map->freeblock_unlock(); return 1; @@ -5855,31 +5945,30 @@ static int skill_castend_id(int tid, int64 tick, int id, intptr_t data) } if (target && target->m == src->m) { //Move character to target anyway. - int dir, x, y; - dir = map->calc_dir(src,target->x,target->y); - if( dir > 0 && dir < 4) x = -2; - else if( dir > 4 ) x = 2; - else x = 0; - if( dir > 2 && dir < 6 ) y = -2; - else if( dir == 7 || dir < 2 ) y = 2; - else y = 0; - if (unit->movepos(src, src->x+x, src->y+y, 1, 1)) { + enum unit_dir dir = map->calc_dir(src, target->x, target->y); + Assert_ret(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); + int dist = 3; // number of cells that asura caster will walk + int x = dist * dirx[dir]; + int y = dist * diry[dir]; + + if (unit->movepos(src, src->x + x, src->y + y, 1, 1) != 0) { //Display movement + animation. - clif->slide(src,src->x,src->y); + clif->slide(src, src->x, src->y); clif->spiritball(src); } - clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); + // "Skill Failed" message was already shown when checking that target is invalid + //clif->skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0, 0); } } - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick; ud->skill_id = ud->skill_lv = ud->skilltarget = 0; //You can't place a skill failed packet here because it would be //sent in ALL cases, even cases where skill_check_condition fails //which would lead to double 'skill failed' messages u.u [Skotlex] if(sd) - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); else if(md) md->skill_idx = -1; return 0; @@ -6261,9 +6350,9 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * if (sd) { // player-casted - sd->state.abra_flag = 1; - sd->skillitem = abra_skill_id; - sd->skillitemlv = abra_skill_lv; + sd->autocast.type = AUTOCAST_ABRA; + sd->autocast.skill_id = abra_skill_id; + sd->autocast.skill_lv = abra_skill_lv; clif->item_skill(sd, abra_skill_id, abra_skill_lv); } else { // mob-casted @@ -6516,10 +6605,6 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * clif->skill_nodamage (src,src,skill_id,skill_lv,1); // Initiate 10% of your damage becomes fire element. sc_start4(src,src,SC_SUB_WEAPONPROPERTY,100,3,20,0,0,skill->get_time2(skill_id, skill_lv)); - if( sd ) - skill->blockpc_start(sd, skill_id, skill->get_time(skill_id, skill_lv)); - else if( bl->type == BL_MER ) - skill->blockmerc_start(BL_UCAST(BL_MER, bl), skill_id, skill->get_time(skill_id, skill_lv)); break; case TK_JUMPKICK: @@ -7191,7 +7276,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches) char temp[70]; - snprintf(temp, sizeof(temp), "%s : %s !!", md->name, skill->get_desc(skill_id)); + snprintf(temp, sizeof(temp), msg_txt(882), md->name, skill->get_desc(skill_id)); // %s : %s !! clif->disp_overhead(&md->bl, temp, AREA_CHAT_WOC, NULL); } break; @@ -7296,27 +7381,34 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * break; case PR_STRECOVERY: - if(status->isimmune(bl)) { - clif->skill_nodamage(src,bl,skill_id,skill_lv,0); + if (status->isimmune(bl) != 0) { + clif->skill_nodamage(src, bl, skill_id, skill_lv, 0); break; } - if (tsc && tsc->opt1) { - status_change_end(bl, SC_FREEZE, INVALID_TIMER); - status_change_end(bl, SC_STONE, INVALID_TIMER); - status_change_end(bl, SC_SLEEP, INVALID_TIMER); - status_change_end(bl, SC_STUN, INVALID_TIMER); - status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER); - } - status_change_end(bl, SC_NETHERWORLD, INVALID_TIMER); - //Is this equation really right? It looks so... special. - if( battle->check_undead(tstatus->race,tstatus->def_ele) ) { - status->change_start(src, bl, SC_BLIND, - 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)), 1,0,0,0, - skill->get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,SCFLAG_NONE); + + if (!battle->check_undead(tstatus->race, tstatus->def_ele)) { + if (tsc != NULL && tsc->opt1 != 0) { + status_change_end(bl, SC_FREEZE, INVALID_TIMER); + status_change_end(bl, SC_STONE, INVALID_TIMER); + status_change_end(bl, SC_SLEEP, INVALID_TIMER); + status_change_end(bl, SC_STUN, INVALID_TIMER); + status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER); + } + + status_change_end(bl, SC_NETHERWORLD, INVALID_TIMER); + } else { + int rate = 100 * (100 - (tstatus->int_ / 2 + tstatus->vit / 3 + tstatus->luk / 10)); + int duration = skill->get_time2(skill_id, skill_lv); + + duration = duration * (100 - (tstatus->int_ + tstatus->vit) / 2) / 100; + status->change_start(src, bl, SC_BLIND, rate, 1, 0, 0, 0, duration, SCFLAG_NONE); } - clif->skill_nodamage(src,bl,skill_id,skill_lv,1); - if(dstmd) - mob->unlocktarget(dstmd,tick); + + clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); + + if (dstmd != NULL) + mob->unlocktarget(dstmd, tick); + break; // Mercenary Supportive Skills @@ -7389,7 +7481,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * map->freeblock_unlock(); return 1; } - if( sd->skillitem != skill_id ) + if (sd->autocast.type == AUTOCAST_NONE) status_zap(src, 0, skill->get_sp(skill_id, skill_lv)); // consume sp only if succeeded } break; @@ -7426,7 +7518,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * break; } - if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 ) + if (sd->autocast.type == AUTOCAST_TEMP || ((sd->autocast.skill_id == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1) || skill_lv == 3) { if( skill_lv == 1 ) pc->randomwarp(sd,CLR_TELEPORT); @@ -7799,7 +7891,9 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case TK_HIGHJUMP: { - int x,y, dir = unit->getdir(src); + int x; + int y; + enum unit_dir dir = unit->getdir(src); //Fails on noteleport maps, except for GvG and BG maps [Skotlex] if( map->list[src->m].flag.noteleport @@ -7881,8 +7975,9 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * } break; case SA_MAGICROD: - clif->skill_nodamage(src,src,SA_MAGICROD,skill_lv,1); - sc_start(src,bl,type,100,skill_lv,skill->get_time(skill_id,skill_lv)); + if (battle->bc->magicrod_type == 0) + clif->skill_nodamage(src, src, SA_MAGICROD, skill_lv, 1); // Animation used here in official [Wolfie] + sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv)); break; case SA_AUTOSPELL: clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -8047,11 +8142,19 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case NPC_RUN: { - const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}}; - uint8 dir = (bl == src)?unit->getdir(src):map->calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away. + enum unit_dir dir; + if (bl == src) //If cast on self, run forward, else run away. + dir = unit->getdir(src); + else + dir = map->calc_dir(src, bl->x, bl->y); + if (Assert_chk(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX)) { + map->freeblock_unlock(); // unblock before assert-returning + return 0; + } unit->stop_attack(src); //Run skillv tiles overriding the can-move check. - if (unit->walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md) + if (unit->walk_toxy(src, src->x + skill_lv * -dirx[dir], src->y + skill_lv * -diry[dir], 2) == 0 + && md != NULL) md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the AI. } break; @@ -8452,7 +8555,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * status_fix_damage(src, bl, 1000, 0); clif->damage(src,bl,0,0,1000,0,BDT_NORMAL,0); if( !status->isdead(bl) ) { - int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT }; + int where[] = {EQP_ARMOR, EQP_SHIELD, EQP_HELM}; skill->break_equip(bl, where[rnd() % ARRAYLENGTH(where)], 10000, BCT_ENEMY); } } @@ -8695,7 +8798,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case AM_REST: if (sd) { - if (homun->vaporize(sd,HOM_ST_REST)) + if (homun->vaporize(sd, HOM_ST_REST, false)) clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); else clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); @@ -8743,12 +8846,20 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * int r = rnd()%100; int target = (skill_lv-1)%5; int hp; - if(r<per[target][0]) //Self + if (r < per[target][0]) { //Self bl = src; - else if(r<per[target][1]) //Master + } else if (r < per[target][1]) { //Master bl = battle->get_master(src); - else //Enemy - bl = map->id2bl(battle->get_target(src)); + } else if ((per[target][1] - per[target][0]) < per[target][0] + && bl == battle->get_master(src)) { + /** + * Skill rolled for enemy, but there's nothing the Homunculus is attacking. + * So bl has been set to its master in unit->skilluse_id2. + * If it's more likely that it will heal itself, + * we let it heal itself. + */ + bl = src; + } if (!bl) bl = src; hp = skill->calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true); @@ -9404,7 +9515,9 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case NC_F_SIDESLIDE: case NC_B_SIDESLIDE: { - uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit->getdir(src)+4)%8 : unit->getdir(src); + enum unit_dir dir = unit->getdir(src); + if (skill_id == NC_F_SIDESLIDE) + dir = unit_get_opposite_dir(dir); skill->blown(src,bl,skill->get_blewcount(skill_id,skill_lv),dir,0); clif->slide(src,src->x,src->y); clif->skill_nodamage(src,bl,skill_id,skill_lv,1); @@ -9414,7 +9527,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case NC_SELFDESTRUCTION: if (sd) { if (pc_ismadogear(sd)) - pc->setmadogear(sd, false); + pc->setmadogear(sd, false, MADO_ROBOT); clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); skill->castend_damage_id(src, src, skill_id, skill_lv, tick, flag); status->set_sp(src, 0, STATUS_HEAL_DEFAULT); @@ -9979,9 +10092,9 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * clif->skill_nodamage (src, bl, skill_id, skill_lv, 1); if (sd != NULL) { - sd->state.abra_flag = 2; - sd->skillitem = improv_skill_id; - sd->skillitemlv = improv_skill_lv; + sd->autocast.type = AUTOCAST_IMPROVISE; + sd->autocast.skill_id = improv_skill_id; + sd->autocast.skill_lv = improv_skill_lv; clif->item_skill(sd, improv_skill_id, improv_skill_lv); } else { struct unit_data *ud = unit->bl2ud(src); @@ -10374,7 +10487,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * case KO_KAZEHU_SEIRAN: case KO_DOHU_KOUKAI: if(sd) { - int ttype = skill->get_ele(skill_id, skill_lv); + enum spirit_charm_types ttype = skill->get_ele(skill_id, skill_lv); clif->skill_nodamage(src, bl, skill_id, skill_lv, 1); pc->add_charm(sd, skill->get_time(skill_id, skill_lv), MAX_SPIRITCHARM, ttype); // replace existing charms of other type } @@ -10384,7 +10497,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * if(sd) { struct mob_data *summon_md; - summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE); + summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), MOBID_KO_KAGE, "", SZ_SMALL, AI_NONE, 0); if( summon_md ) { summon_md->master_id = src->id; summon_md->special_state.ai = AI_ZANZOU; @@ -10567,7 +10680,7 @@ static int skill_castend_nodamage_id(struct block_list *src, struct block_list * for (i = 0; i < summons[skill_lv-1].quantity; i++) { struct mob_data *summon_md = mob->once_spawn_sub(src, src->m, src->x, src->y, clif->get_bl_name(src), - summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK); + summons[skill_lv-1].mob_id, "", SZ_SMALL, AI_ATTACK, 0); if (summon_md != NULL) { summon_md->master_id = src->id; if (summon_md->deletetimer != INVALID_TIMER) @@ -10750,7 +10863,7 @@ static int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) if (ud->walktimer != INVALID_TIMER) unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick + skill->delay_fix(src, ud->skill_id, ud->skill_lv); if (sd) { //Cooldown application int i, cooldown = skill->get_cooldown(ud->skill_id, ud->skill_lv); @@ -10764,7 +10877,7 @@ static int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) skill->blockpc_start(sd, ud->skill_id, cooldown); } if( battle_config.display_status_timers && sd ) - clif->status_change(src, SI_POSTDELAY, 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); + clif->status_change(src, status->get_sc_icon(SC_POSTDELAY), status->get_sc_relevant_bl_types(SC_POSTDELAY), 1, skill->delay_fix(src, ud->skill_id, ud->skill_lv), 0, 0, 0); #if 0 if (sd) { switch (ud->skill_id) { @@ -10779,10 +10892,10 @@ static int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) map->freeblock_lock(); skill->castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0); - if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish] - sd->skillitem = sd->skillitemlv = 0; + if (sd != NULL && sd->autocast.skill_id != AL_WARP) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish] + pc->autocast_clear(sd); - unit->setdir(src, map->calc_dir(src, ud->skillx, ud->skilly)); + unit->set_dir(src, map->calc_dir(src, ud->skillx, ud->skilly)); if (ud->skilltimer == INVALID_TIMER) { if (md) md->skill_idx = -1; @@ -10794,11 +10907,11 @@ static int skill_castend_pos(int tid, int64 tick, int id, intptr_t data) return 1; } while(0); - if( !sd || sd->skillitem != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) ) + if (sd == NULL || sd->autocast.skill_id != ud->skill_id || skill->get_delay(ud->skill_id,ud->skill_lv) != 0) ud->canact_tick = tick; ud->skill_id = ud->skill_lv = 0; if(sd) - sd->skillitem = sd->skillitemlv = 0; + pc->autocast_clear(sd); else if(md) md->skill_idx = -1; return 0; @@ -10831,6 +10944,37 @@ static int skill_count_wos(struct block_list *bl, va_list ap) return 0; } +/** + * Returns the linked song/dance skill ID, if any (for the Bard/Dancer Soul Link). + * + * @param skill_id The skill ID to look up + * + * @return The linked song or dance's skill ID if any + * @retval 0 if the given skill_id doesn't have a linked skill ID + */ +static int skill_get_linked_song_dance_id(int skill_id) +{ + switch (skill_id) { + case BA_WHISTLE: + return DC_HUMMING; + case BA_ASSASSINCROSS: + return DC_DONTFORGETME; + case BA_POEMBRAGI: + return DC_FORTUNEKISS; + case BA_APPLEIDUN: + return DC_SERVICEFORYOU; + case DC_HUMMING: + return BA_WHISTLE; + case DC_DONTFORGETME: + return BA_ASSASSINCROSS; + case DC_FORTUNEKISS: + return BA_POEMBRAGI; + case DC_SERVICEFORYOU: + return BA_APPLEIDUN; + } + return 0; +} + /*========================================== * *------------------------------------------*/ @@ -10928,7 +11072,7 @@ static int skill_castend_map(struct map_session_data *sd, uint16 skill_id, const } } - lv = sd->skillitem==skill_id?sd->skillitemlv:pc->checkskill(sd,skill_id); + lv = (sd->autocast.type > AUTOCAST_TEMP) ? sd->autocast.skill_lv : pc->checkskill(sd, skill_id); wx = sd->menuskill_val>>16; wy = sd->menuskill_val&0xffff; @@ -10951,7 +11095,7 @@ static int skill_castend_map(struct map_session_data *sd, uint16 skill_id, const } skill->consume_requirement(sd,sd->menuskill_id,lv,2); - sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish] + pc->autocast_clear(sd); // Clear data which was skipped in skill_castend_pos(). if((group=skill->unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) { skill_failed(sd); @@ -11041,9 +11185,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: @@ -11199,7 +11344,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill FALLTHROUGH case GS_GROUNDDRIFT: //Ammo should be deleted right away. if ( skill_id == WM_SEVERE_RAINSTORM ) - sc_start(src,src,SC_NO_SWITCH_EQUIP,100,0,skill->get_time(skill_id,skill_lv)); + sc_start(src, src, type, 100, 0, skill->get_time(skill_id, skill_lv)); skill->unitsetting(src,skill_id,skill_lv,x,y,0); break; case WZ_ICEWALL: @@ -11349,7 +11494,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill } // Correct info, don't change any of this! [Celest] - md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), class_, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA; @@ -11451,7 +11596,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0, 0); } else { int mob_id = skill_lv < 2 ? MOBID_BLACK_MUSHROOM + rnd()%2 : MOBID_RED_PLANT + rnd()%6; - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, DEFAULT_MOB_JNAME, mob_id, "", SZ_SMALL, AI_NONE, 0); int i; if (md == NULL) break; @@ -11560,17 +11705,16 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill case WL_EARTHSTRAIN: { - int i, wave = skill_lv + 4, dir = map->calc_dir(src,x,y); + int i; + int wave = skill_lv + 4; + enum unit_dir dir = map->calc_dir(src, x, y); + Assert_ret(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting - for( i = 1; i <= wave; i++ ) - { - switch( dir ){ - case 0: case 1: case 7: sy = y + i; break; - case 3: case 4: case 5: sy = y - i; break; - case 2: sx = x - i; break; - case 6: sx = x + i; break; - } + for (i = 1; i <= wave; i++) { + sy = y + i * diry[dir]; + if (dir == UNIT_DIR_WEST || dir == UNIT_DIR_EAST) + sx = x + i * dirx[dir]; skill->addtimerskill(src,timer->gettick() + (140 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2); } } @@ -11597,7 +11741,7 @@ static int skill_castend_pos2(struct block_list *src, int x, int y, uint16 skill case NC_SILVERSNIPER: { - struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE); + struct mob_data *md = mob->once_spawn_sub(src, src->m, x, y, clif->get_bl_name(src), MOBID_SILVERSNIPER, "", SZ_SMALL, AI_NONE, 0); if (md) { md->master_id = src->id; md->special_state.ai = AI_FLORA; @@ -12760,6 +12904,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: { @@ -12912,10 +13063,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") @@ -13781,12 +13933,14 @@ static int skill_check_condition_char_sub(struct block_list *bl, va_list ap) } else { switch(skill_id) { - case PR_BENEDICTIO: { - uint8 dir = map->calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y); - dir = (unit->getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing. - if ((tsd->job & MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest. - && sd->status.sp >= 10) + case PR_BENEDICTIO: + { + enum unit_dir dir = map->calc_dir(&sd->bl, tsd->bl.x, tsd->bl.y); + dir = (unit->getdir(&sd->bl) + dir) % UNIT_DIR_MAX; //This adjusts dir to account for the direction the sd is facing. + if ((tsd->job & MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == UNIT_DIR_WEST || dir == UNIT_DIR_EAST) //Must be standing to the left/right of Priest. + && sd->status.sp >= 10) { p_sd[(*c)++]=tsd->bl.id; + } return 1; } case AB_ADORAMUS: @@ -13934,27 +14088,9 @@ static int skill_isammotype(struct map_session_data *sd, int skill_id) **/ static bool skill_is_combo(int skill_id) { - switch( skill_id ) - { - case MO_CHAINCOMBO: - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - case MO_EXTREMITYFIST: - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - case TK_JUMPKICK: - case HT_POWER: - case GC_COUNTERSLASH: - case GC_WEAPONCRUSH: - case SR_FALLENEMPIRE: - case SR_DRAGONCOMBO: - case SR_TIGERCANNON: - case SR_GATEOFHELL: - return true; - } + if (skill->get_inf2(skill_id) & INF2_IS_COMBO_SKILL) + return true; + return false; } @@ -13966,11 +14102,19 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s nullpo_ret(sd); + if (skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL) + return 0; + if (sd->chat_id != 0) return 0; - if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id) { - //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] + if (((sd->autocast.itemskill_conditions_checked || !sd->autocast.itemskill_check_conditions) + && sd->autocast.type == AUTOCAST_ITEM) || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + + if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->autocast.type != AUTOCAST_ITEM) { + // GMs don't override the AUTOCAST_ITEM check, otherwise they can use items without them being consumed! sd->state.arrow_atk = skill->get_ammotype(skill_id)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. return 1; @@ -14001,33 +14145,7 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s if( !sc->count ) sc = NULL; - if( sd->skillitem == skill_id ) { - if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish] - sd->state.abra_flag = 0; - else { - int i; - // When a target was selected, consume items that were skipped in pc_use_item [Skotlex] - if( (i = sd->itemindex) == -1 || - sd->status.inventory[i].nameid != sd->itemid || - sd->inventory_data[i] == NULL || - !sd->inventory_data[i]->flag.delay_consume || - sd->status.inventory[i].amount < 1 - ) { - //Something went wrong, item exploit? - sd->itemid = sd->itemindex = -1; - return 0; - } - //Consume - sd->itemid = sd->itemindex = -1; - if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007] - ; //Do not consume item. - else if( sd->status.inventory[i].expire_time == 0 ) // Rental usable items are not consumed until expiration - pc->delitem(sd, i, 1, 0, DELITEM_NORMAL, LOG_TYPE_CONSUME); - } - return 1; - } - - if( pc_is90overweight(sd) ) { + if (pc_is90overweight(sd) && sd->autocast.type != AUTOCAST_ITEM) { // Skill casting items ignore the overweight restriction. clif->skill_fail(sd, skill_id, USESKILL_FAIL_WEIGHTOVER, 0, 0); return 0; } @@ -14151,9 +14269,6 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s } } - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return 0; - require = skill->get_requirement(sd,skill_id,skill_lv); //Can only update state when weapon/arrow info is checked. @@ -14490,8 +14605,8 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s **/ case AB_ANCILLA: { - int count = 0, i; - for( i = 0; i < MAX_INVENTORY; i ++ ) + int count = 0; + for (int i = 0; i < sd->status.inventorySize; i ++) if (sd->status.inventory[i].nameid == ITEMID_ANSILA) count += sd->status.inventory[i].amount; if( count >= 3 ) { @@ -14645,7 +14760,7 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s if (map->foreachinrange(mob->count_sub, &sd->bl, skill->get_splash(skill_id, skill_lv), BL_MOB, MOBID_EMPELIUM, MOBID_S_EMPEL_1, MOBID_S_EMPEL_2)) { char output[128]; - sprintf(output, "You're too close to a stone or emperium to do this skill"); /* TODO official response? or message.conf it */ + sprintf(output, "%s", msg_txt(883)); /* TODO official response */ // You are too close to a stone or emperium to do this skill clif->messagecolor_self(sd->fd, COLOR_RED, output); return 0; } @@ -14901,7 +15016,7 @@ static int skill_check_condition_castbegin(struct map_session_data *sd, uint16 s return 0; } - if( require.sp > 0 && st->sp < (unsigned int)require.sp) { + if (require.sp > 0 && st->sp < (unsigned int)require.sp && sd->autocast.type == AUTOCAST_NONE) { // Auto-cast skills don't consume SP. clif->skill_fail(sd, skill_id, USESKILL_FAIL_SP_INSUFFICIENT, 0, 0); return 0; } @@ -14959,8 +15074,13 @@ static int skill_check_condition_castend(struct map_session_data *sd, uint16 ski if (sd->chat_id != 0) return 0; - if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) { - //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] + if (((sd->autocast.itemskill_conditions_checked || !sd->autocast.itemskill_check_conditions) + && sd->autocast.type == AUTOCAST_ITEM) || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + + if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->autocast.type != AUTOCAST_ITEM) { + // GMs don't override the AUTOCAST_ITEM check, otherwise they can use items without them being consumed! sd->state.arrow_atk = skill->get_ammotype(skill_id)?1:0; //Need to do arrow state check. sd->spiritball_old = sd->spiritball; //Need to do Spiritball check. return 1; @@ -14986,14 +15106,8 @@ static int skill_check_condition_castend(struct map_session_data *sd, uint16 ski return 0; break; } - /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ -#if 0 - if( sd->state.abra_flag ) // Casting finished (Hocus-Pocus) - return 1; -#endif - if( sd->skillitem == skill_id ) - return 1; - if( pc_is90overweight(sd) ) { + + if (pc_is90overweight(sd) && sd->autocast.type != AUTOCAST_ITEM) { // Skill casting items ignore the overweight restriction. clif->skill_fail(sd, skill_id, USESKILL_FAIL_WEIGHTOVER, 0, 0); return 0; } @@ -15092,17 +15206,15 @@ static int skill_check_condition_castend(struct map_session_data *sd, uint16 ski return 0; } else if( sd->status.inventory[i].amount < require.ammo_qty ) { char e_msg[100]; - sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.", + sprintf(e_msg, msg_txt(884), // Skill Failed. [%s] requires %dx %s. skill->get_desc(skill_id), require.ammo_qty, itemdb_jname(sd->status.inventory[i].nameid)); clif->messagecolor_self(sd->fd, COLOR_RED, e_msg); return 0; } - if (!(require.ammo&1<<sd->inventory_data[i]->subtype)) { //Ammo type check. Send the "wrong weapon type" message - //which is the closest we have to wrong ammo type. [Skotlex] - clif->arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex] - //clif->skill_fail(sd, skill_id, USESKILL_FAIL_THIS_WEAPON, 0, 0); + if ((require.ammo & (1 << sd->inventory_data[i]->subtype)) == 0 || !battle->check_arrows(sd)) { // Ammo type check. + clif->arrow_fail(sd, 0); // "Please equip the proper ammunition first." return 0; } } @@ -15166,6 +15278,11 @@ static int skill_consume_requirement(struct map_session_data *sd, uint16 skill_i nullpo_ret(sd); + if ((!sd->autocast.itemskill_check_conditions && sd->autocast.type == AUTOCAST_ITEM) + || sd->autocast.type == AUTOCAST_IMPROVISE) { + return 1; + } + req = skill->get_requirement(sd,skill_id,skill_lv); if (type&1) { @@ -15174,9 +15291,15 @@ static int skill_consume_requirement(struct map_session_data *sd, uint16 skill_i case MC_IDENTIFY: req.sp = 0; break; + case WZ_EARTHSPIKE: + if (sd->sc.count > 0 && sd->sc.data[SC_EARTHSCROLL] != NULL) // If Earth Spike Scroll is used while SC_EARTHSCROLL is active, 10 SP are consumed. [Kenpachi] + req.sp = 10; + + break; default: - if( sd->state.autocast ) + if (sd->autocast.type != AUTOCAST_NONE) // Auto-cast skills don't consume SP. req.sp = 0; + break; } @@ -15254,12 +15377,6 @@ static struct skill_condition skill_get_requirement(struct map_session_data *sd, if( !sd ) return req; -#if 0 /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ - if( sd->state.abra_flag ) -#else // not 0 - if( sd->skillitem == skill_id ) -#endif // 0 - return req; // Hocus-Pocus don't have requirements. sc = &sd->sc; if( !sc->count ) @@ -15668,6 +15785,8 @@ static int skill_castfix_sc(struct block_list *bl, int time) } if (sc->data[SC_POEMBRAGI]) time -= time * sc->data[SC_POEMBRAGI]->val2 / 100; + if (sc->data[SC_SKF_CAST] != NULL) + time -= time * sc->data[SC_SKF_CAST]->val1 / 100; if (sc->data[SC_IZAYOI]) time -= time * 50 / 100; } @@ -15769,6 +15888,8 @@ static int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, } if (sc->data[SC_MYSTICSCROLL]) VARCAST_REDUCTION(sc->data[SC_MYSTICSCROLL]->val1); + if (sc->data[SC_SKF_CAST] != NULL) + VARCAST_REDUCTION(sc->data[SC_SKF_CAST]->val1); // Fixed cast reduction bonuses if( sc->data[SC__LAZINESS] ) @@ -15920,11 +16041,11 @@ struct square { int val2[5]; }; -static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int16 y) +static void skill_brandishspear_first(struct square *tc, enum unit_dir dir, int16 x, int16 y) { nullpo_retv(tc); - if(dir == 0){ + if (dir == UNIT_DIR_NORTH) { tc->val1[0]=x-2; tc->val1[1]=x-1; tc->val1[2]=x; @@ -15935,7 +16056,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]= tc->val2[3]= tc->val2[4]=y-1; - } else if(dir==2){ + } else if (dir == UNIT_DIR_WEST) { tc->val1[0]= tc->val1[1]= tc->val1[2]= @@ -15946,7 +16067,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]=y; tc->val2[3]=y-1; tc->val2[4]=y-2; - } else if(dir==4){ + } else if (dir == UNIT_DIR_SOUTH) { tc->val1[0]=x-2; tc->val1[1]=x-1; tc->val1[2]=x; @@ -15957,7 +16078,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]= tc->val2[3]= tc->val2[4]=y+1; - } else if(dir==6){ + } else if (dir == UNIT_DIR_EAST) { tc->val1[0]= tc->val1[1]= tc->val1[2]= @@ -15968,7 +16089,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]=y; tc->val2[3]=y-1; tc->val2[4]=y-2; - } else if(dir==1){ + } else if (dir == UNIT_DIR_NORTHWEST) { tc->val1[0]=x-1; tc->val1[1]=x; tc->val1[2]=x+1; @@ -15979,7 +16100,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]=y-1; tc->val2[3]=y; tc->val2[4]=y+1; - } else if(dir==3){ + } else if (dir == UNIT_DIR_SOUTHWEST) { tc->val1[0]=x+3; tc->val1[1]=x+2; tc->val1[2]=x+1; @@ -15990,7 +16111,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]=y+1; tc->val2[3]=y+2; tc->val2[4]=y+3; - } else if(dir==5){ + } else if (dir == UNIT_DIR_SOUTHEAST) { tc->val1[0]=x+1; tc->val1[1]=x; tc->val1[2]=x-1; @@ -16001,7 +16122,7 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int tc->val2[2]=y+1; tc->val2[3]=y; tc->val2[4]=y-1; - } else if(dir==7){ + } else if (dir == UNIT_DIR_NORTHEAST) { tc->val1[0]=x-3; tc->val1[1]=x-2; tc->val1[2]=x-1; @@ -16016,36 +16137,27 @@ static void skill_brandishspear_first(struct square *tc, uint8 dir, int16 x, int } -static void skill_brandishspear_dir(struct square *tc, uint8 dir, int are) +static void skill_brandishspear_dir(struct square *tc, enum unit_dir dir, int are) { - int c; nullpo_retv(tc); + Assert_retv(dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); - for( c = 0; c < 5; c++ ) { - switch( dir ) { - case 0: tc->val2[c]+=are; break; - case 1: tc->val1[c]-=are; tc->val2[c]+=are; break; - case 2: tc->val1[c]-=are; break; - case 3: tc->val1[c]-=are; tc->val2[c]-=are; break; - case 4: tc->val2[c]-=are; break; - case 5: tc->val1[c]+=are; tc->val2[c]-=are; break; - case 6: tc->val1[c]+=are; break; - case 7: tc->val1[c]+=are; tc->val2[c]+=are; break; - } + for (int c = 0; c < 5; c++) { + tc->val1[c] += dirx[dir] * are; + tc->val2[c] += diry[dir] * are; } } static void skill_brandishspear(struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) { int c,n=4; - uint8 dir; struct square tc; int x, y; nullpo_retv(bl); x = bl->x; y = bl->y; - dir = map->calc_dir(src, x, y); + enum unit_dir dir = map->calc_dir(src, x, y); skill->brandishspear_first(&tc,dir,x,y); skill->brandishspear_dir(&tc,dir,4); skill->area_temp[1] = bl->id; @@ -16109,7 +16221,7 @@ static void skill_repairweapon(struct map_session_data *sd, int idx) if (idx == 0xFFFF || idx == -1) // No item selected ('Cancel' clicked) return; - if( idx < 0 || idx >= MAX_INVENTORY ) + if (idx < 0 || idx >= sd->status.inventorySize) return; //Invalid index?? item = &target_sd->status.inventory[idx]; @@ -16157,7 +16269,7 @@ static void skill_identify(struct map_session_data *sd, int idx) nullpo_retv(sd); sd->state.workinprogress = 0; - if(idx >= 0 && idx < MAX_INVENTORY) { + if (idx >= 0 && idx < sd->status.inventorySize) { if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ flag=0; sd->status.inventory[idx].identify=1; @@ -16173,7 +16285,7 @@ static void skill_weaponrefine(struct map_session_data *sd, int idx) { nullpo_retv(sd); - if (idx >= 0 && idx < MAX_INVENTORY) { + if (idx >= 0 && idx < sd->status.inventorySize) { struct item *item; struct item_data *ditem = sd->inventory_data[idx]; item = &sd->status.inventory[idx]; @@ -16201,7 +16313,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) @@ -16385,9 +16497,9 @@ static int skill_sit(struct map_session_data *sd, int type) } if( type ) { - clif->sc_load(&sd->bl,sd->bl.id,SELF,SI_SIT,0,0,0); + clif->sc_load(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT), 0, 0, 0); } else { - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_SIT); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_SIT)); } if (!flag) return 0; @@ -17072,6 +17184,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); @@ -17992,8 +18112,8 @@ static int skill_can_produce_mix(struct map_session_data *sd, int nameid, int tr if (pc->search_inventory(sd,id) == INDEX_NOT_FOUND) return 0; } else { - int x, y; - for(y=0,x=0;y<MAX_INVENTORY;y++) + int x = 0; + for (int y = 0; y < sd->status.inventorySize; y++) if( sd->status.inventory[y].nameid == id ) x+=sd->status.inventory[y].amount; if(x<qty*skill->dbs->produce_db[i].mat_amount[j]) @@ -18064,7 +18184,7 @@ static int skill_produce_mix(struct map_session_data *sd, uint16 skill_id, int n else temp_qty = 1; if (data->stack.inventory) { - for( i = 0; i < MAX_INVENTORY; i++ ) { + for (i = 0; i < sd->status.inventorySize; i++ ) { if( sd->status.inventory[i].nameid == nameid ) { if( sd->status.inventory[i].amount >= data->stack.amount ) { #if PACKETVER >= 20090729 @@ -18761,7 +18881,7 @@ static int skill_magicdecoy(struct map_session_data *sd, int nameid) break; } - md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE); + md = mob->once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE, 0); if( md ) { md->master_id = sd->bl.id; md->special_state.ai = AI_FLORA; @@ -18872,7 +18992,7 @@ static int skill_elementalanalysis(struct map_session_data *sd, uint16 skill_lv, del_amount -= (del_amount % 10); add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ; - if (idx < 0 || idx >= MAX_INVENTORY + if (idx < 0 || idx >= sd->status.inventorySize || (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount < 0 || del_amount > sd->status.inventory[idx].amount) { clif->skill_fail(sd, SO_EL_ANALYSIS, USESKILL_FAIL_LEVEL, 0, 0); @@ -18943,7 +19063,7 @@ static int skill_changematerial(struct map_session_data *sd, const struct itemli for (k = 0; k < VECTOR_LENGTH(*item_list); k++) { const struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, k); int idx = entry->id; - Assert_ret(idx >= 0 && idx < MAX_INVENTORY); + Assert_ret(idx >= 0 && idx < sd->status.inventorySize); amount = entry->amount; nameid = sd->status.inventory[idx].nameid; if (nameid > 0 && sd->status.inventory[idx].identify == 0) { @@ -19740,23 +19860,23 @@ static int skill_get_elemental_type(uint16 skill_id, uint16 skill_lv) static void skill_cooldown_save(struct map_session_data *sd) { int i; - struct skill_cd* cd = NULL; + struct skill_cd *cd = NULL; int64 now = 0; - // always check to make sure the session properly exists nullpo_retv(sd); - if( !(cd = idb_get(skill->cd_db, sd->status.char_id)) ) {// no skill cooldown is associated with this character + if ((cd = idb_get(skill->cd_db, sd->status.char_id)) == NULL) return; - } now = timer->gettick(); - // process each individual cooldown associated with the character - for( i = 0; i < cd->cursor; i++ ) { - cd->entry[i]->duration = DIFF_TICK32(cd->entry[i]->started+cd->entry[i]->duration,now); - if( cd->entry[i]->timer != INVALID_TIMER ) { - timer->delete(cd->entry[i]->timer,skill->blockpc_end); + for (i = 0; i < cd->cursor; i++) { + if (battle_config.guild_skill_relog_delay == 1 && cd->entry[i]->skill_id > GD_SKILLBASE && cd->entry[i]->skill_id < GD_MAX) + continue; + + cd->entry[i]->duration = DIFF_TICK32(cd->entry[i]->started + cd->entry[i]->duration, now); + if (cd->entry[i]->timer != INVALID_TIMER) { + timer->delete(cd->entry[i]->timer, skill->blockpc_end); cd->entry[i]->timer = INVALID_TIMER; } } @@ -20237,6 +20357,18 @@ 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, "IsCombo") == 0) { + if (on) { + sk->inf2 |= INF2_IS_COMBO_SKILL; + } else { + sk->inf2 &= ~INF2_IS_COMBO_SKILL; + } } else if (strcmpi(type, "None") != 0) { skilldb_invalid_error(type, config_setting_name(t), sk->nameid); } @@ -21067,7 +21199,7 @@ static bool skill_read_skilldb(const char *filename) nullpo_retr(false, filename); - sprintf(filepath,"db/%s",filename); + libconfig->format_db_path(filename, filepath, sizeof(filepath)); if (!libconfig->load_file(&skilldb, filepath)) { return false; // Libconfig error report. @@ -21576,6 +21708,7 @@ void skill_defaults(void) skill->not_ok_hom = skillnotok_hom; skill->not_ok_hom_unknown = skillnotok_hom_unknown; skill->not_ok_mercenary = skillnotok_mercenary; + skill->validate_autocast_data = skill_validate_autocast_data; skill->chastle_mob_changetarget = skill_chastle_mob_changetarget; skill->can_produce_mix = skill_can_produce_mix; skill->produce_mix = skill_produce_mix; @@ -21716,4 +21849,5 @@ void skill_defaults(void) skill->splash_target = skill_splash_target; skill->check_npc_chaospanic = skill_check_npc_chaospanic; skill->count_wos = skill_count_wos; + skill->get_linked_song_dance_id = skill_get_linked_song_dance_id; } diff --git a/src/map/skill.h b/src/map/skill.h index 0ace19927..4dbbaf147 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include "map/map.h" // struct block_list #include "map/status.h" // enum sc_type +#include "map/unitdefines.h" // enum unit_dir #include "common/hercules.h" #include "common/db.h" #include "common/mmo.h" // MAX_SKILL_DB, struct square @@ -59,6 +60,12 @@ struct status_change_entry; #define MAX_SKILLUNITGROUPTICKSET 25 #define MAX_SKILL_NAME_LENGTH 32 +// Custom Skill Ranges is used in skill_get_index, to allocate indexes based on ID and gaps between 2 SkillID +#ifndef CUSTOM_SKILL_RANGES + #define CUSTOM_SKILL_RANGES +#endif // CUSTOM_SKILL_RANGES + + // (Epoque:) To-do: replace this macro with some sort of skill tree check (rather than hard-coded skill names) #define skill_ischangesex(id) ( \ ((id) >= BD_ADAPTATION && (id) <= DC_SERVICEFORYOU) || ((id) >= CG_ARROWVULCAN && (id) <= CG_MARIONETTE) || \ @@ -124,6 +131,8 @@ 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) + INF2_IS_COMBO_SKILL = 0x100000, // Sets whether a skill can be used in combos or not }; @@ -1715,6 +1724,15 @@ enum { UNT_MAX = 0x190 }; +/** Constants to identify the auto-cast type. **/ +enum autocast_type { + AUTOCAST_NONE = 0, + AUTOCAST_TEMP, // Used when type is only required during the execution of the calling instance. (For example bAutoSpell* skills.) + AUTOCAST_ABRA, // Used for Abracadabra (Hocus pocus). + AUTOCAST_IMPROVISE, // Used for Improvised Song. + AUTOCAST_ITEM, // Used for itemskill() script command. +}; + /** * Structures **/ @@ -1806,6 +1824,7 @@ struct skill_unit { int limit; int val1,val2; + bool visible; short alive,range; int prev; }; @@ -1993,7 +2012,7 @@ struct skill_interface { int (*addtimerskill) (struct block_list *src, int64 tick, int target, int x, int y, uint16 skill_id, uint16 skill_lv, int type, int flag); int (*additional_effect) (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, int64 tick); int (*counter_additional_effect) (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int64 tick); - int (*blown) (struct block_list* src, struct block_list* target, int count, int8 dir, int flag); + int (*blown) (struct block_list* src, struct block_list* target, int count, enum unit_dir dir, int flag); int (*break_equip) (struct block_list *bl, unsigned short where, int rate, int flag); int (*strip_equip) (struct block_list *bl, unsigned short where, int rate, int lv, int time); struct skill_unit_group* (*id2group) (int group_id); @@ -2035,6 +2054,7 @@ struct skill_interface { int (*not_ok_hom) (uint16 skill_id, struct homun_data *hd); int (*not_ok_hom_unknown) (uint16 skill_id, struct homun_data *hd); int (*not_ok_mercenary) (uint16 skill_id, struct mercenary_data *md); + void (*validate_autocast_data) (struct map_session_data *sd, int skill_id, int skill_lv); int (*chastle_mob_changetarget) (struct block_list *bl,va_list ap); int (*can_produce_mix) ( struct map_session_data *sd, int nameid, int trigger, int qty); int (*produce_mix) ( struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty ); @@ -2075,8 +2095,8 @@ struct skill_interface { bool (*dance_switch) (struct skill_unit* su, int flag); int (*check_condition_char_sub) (struct block_list *bl, va_list ap); int (*check_condition_mob_master_sub) (struct block_list *bl, va_list ap); - void (*brandishspear_first) (struct square *tc, uint8 dir, int16 x, int16 y); - void (*brandishspear_dir) (struct square* tc, uint8 dir, int are); + void (*brandishspear_first) (struct square *tc, enum unit_dir dir, int16 x, int16 y); + void (*brandishspear_dir) (struct square* tc, enum unit_dir dir, int are); int (*get_fixed_cast) (int skill_id, int skill_lv); int (*sit_count) (struct block_list *bl, va_list ap); int (*sit_in) (struct block_list *bl, va_list ap); @@ -2153,7 +2173,7 @@ struct skill_interface { void (*attack_display_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage); int (*attack_copy_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag); int (*attack_dir_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag); - void (*attack_blow_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage, int8 *dir); + void (*attack_blow_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag, int *type, struct Damage *dmg, int64 *damage, enum unit_dir *dir); void (*attack_post_unknown) (int *attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 *skill_id, uint16 *skill_lv, int64 *tick, int *flag); bool (*timerskill_dead_unknown) (struct block_list *src, struct unit_data *ud, struct skill_timerskill *skl); void (*timerskill_target_unknown) (int tid, int64 tick, struct block_list *src, struct block_list *target, struct unit_data *ud, struct skill_timerskill *skl); @@ -2180,6 +2200,7 @@ struct skill_interface { int (*splash_target) (struct block_list* bl); int (*check_npc_chaospanic) (struct block_list *bl, va_list args); int (*count_wos) (struct block_list *bl, va_list ap); + int (*get_linked_song_dance_id) (int skill_id); }; #ifdef HERCULES_CORE diff --git a/src/map/status.c b/src/map/status.c index 75744c59b..9a274c080 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -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" @@ -114,20 +115,27 @@ static unsigned int status_sc2scb_flag(sc_type sc) /** * Returns the bl types which require a status change packet to be sent for a given client status identifier. - * @param type The client-side status identifier to look up (see enum si_type) + * @param type status identifier to look up (see enum sc_type) * @return The bl types relevant to the type (see enum bl_type) */ -static int status_type2relevant_bl_types(int type) +static int status_get_sc_relevant_bl_types(enum sc_type type) { - if( type < 0 || type >= SI_MAX ) { - ShowError("status_type2relevant_bl_types: Unsupported type %d\n", type); + if (type < 0 || type >= SC_MAX) { + ShowError("status_get_sc_relevant_bl_types: Unsupported type %d\n", type); return BL_NUL; } - return status->dbs->RelevantBLTypes[type]; + return status->dbs->IconChangeTable[type].relevant_bl_types; } -static void status_set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag) +static int status_get_sc_icon(enum sc_type type) +{ + Assert_retr(SI_BLANK, type >= SC_NONE && type < SC_MAX); + + return status->dbs->IconChangeTable[type].id; +} + +static void status_set_sc(uint16 skill_id, sc_type sc, unsigned int flag) { uint16 idx; if( (idx = skill->get_index(skill_id)) == 0 ) { @@ -141,8 +149,6 @@ static void status_set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int fl if( status->dbs->SkillChangeTable[sc] == 0 ) status->dbs->SkillChangeTable[sc] = skill_id; - if( status->dbs->IconChangeTable[sc] == SI_BLANK ) - status->dbs->IconChangeTable[sc] = icon; status->dbs->ChangeFlagTable[sc] |= flag; if( status->dbs->Skill2SCTable[idx] == SC_NONE ) @@ -151,103 +157,102 @@ static void status_set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int fl static void initChangeTables(void) { -#define add_sc(skill,sc) status->set_sc((skill),(sc),SI_BLANK,SCB_NONE) +#define add_sc(skill,sc) status->set_sc((skill),(sc),SCB_NONE) // indicates that the status displays a visual effect for the affected unit, and should be sent to the client for all supported units -#define set_sc_with_vfx(skill, sc, icon, flag) do { status->set_sc((skill), (sc), (icon), (flag)); if((icon) < SI_MAX) status->dbs->RelevantBLTypes[(icon)] |= BL_SCEFFECT; } while(0) +#define set_sc_with_vfx(skill, sc, flag) do { status->set_sc((skill), (sc), (flag)); status->dbs->IconChangeTable[sc].relevant_bl_types |= BL_SCEFFECT; } while(0) int i; - for (i = 0; i < SC_MAX; i++) - status->dbs->IconChangeTable[i] = SI_BLANK; - for (i = 0; i < MAX_SKILL_DB; i++) status->dbs->Skill2SCTable[i] = SC_NONE; - for (i = 0; i < SI_MAX; i++) - status->dbs->RelevantBLTypes[i] = BL_PC; + for (i = 0; i < SC_MAX; i++) { + status->dbs->IconChangeTable[i].id = SI_BLANK; + status->dbs->IconChangeTable[i].relevant_bl_types = BL_PC; + } memset(status->dbs->SkillChangeTable, 0, sizeof(status->dbs->SkillChangeTable)); memset(status->dbs->ChangeFlagTable, 0, sizeof(status->dbs->ChangeFlagTable)); memset(status->dbs->DisplayType, 0, sizeof(status->dbs->DisplayType)); //First we define the skill for common ailments. These are used in skill_additional_effect through sc cards. [Skotlex] - status->set_sc( NPC_PETRIFYATTACK , SC_STONE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); - status->set_sc( NPC_WIDEFREEZE , SC_FREEZE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); - status->set_sc( NPC_STUNATTACK , SC_STUN , SI_BLANK , SCB_NONE ); - status->set_sc( NPC_SLEEPATTACK , SC_SLEEP , SI_BLANK , SCB_NONE ); - status->set_sc( NPC_POISON , SC_POISON , SI_BLANK , SCB_DEF2|SCB_REGEN ); - status->set_sc( NPC_CURSEATTACK , SC_CURSE , SI_BLANK , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED ); - status->set_sc( NPC_SILENCEATTACK , SC_SILENCE , SI_BLANK , SCB_NONE ); - status->set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SI_BLANK , SCB_NONE ); - status->set_sc( NPC_BLINDATTACK , SC_BLIND , SI_BLANK , SCB_HIT|SCB_FLEE ); - status->set_sc( NPC_BLEEDING , SC_BLOODING , SI_BLOODING , SCB_REGEN ); - status->set_sc( NPC_POISON , SC_DPOISON , SI_BLANK , SCB_DEF2|SCB_REGEN ); + status->set_sc( NPC_PETRIFYATTACK , SC_STONE , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); + status->set_sc( NPC_WIDEFREEZE , SC_FREEZE , SCB_DEF_ELE|SCB_DEF|SCB_MDEF ); + status->set_sc( NPC_STUNATTACK , SC_STUN , SCB_NONE ); + status->set_sc( NPC_SLEEPATTACK , SC_SLEEP , SCB_NONE ); + status->set_sc( NPC_POISON , SC_POISON , SCB_DEF2|SCB_REGEN ); + status->set_sc( NPC_CURSEATTACK , SC_CURSE , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED ); + status->set_sc( NPC_SILENCEATTACK , SC_SILENCE , SCB_NONE ); + status->set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SCB_NONE ); + status->set_sc( NPC_BLINDATTACK , SC_BLIND , SCB_HIT|SCB_FLEE ); + status->set_sc( NPC_BLEEDING , SC_BLOODING , SCB_REGEN ); + status->set_sc( NPC_POISON , SC_DPOISON , SCB_DEF2|SCB_REGEN ); //The main status definitions add_sc( SM_BASH , SC_STUN ); - status->set_sc( SM_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); + status->set_sc( SM_PROVOKE , SC_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); add_sc( SM_MAGNUM , SC_SUB_WEAPONPROPERTY ); - status->set_sc( SM_ENDURE , SC_ENDURE , SI_ENDURE , SCB_MDEF|SCB_DSPD ); + status->set_sc( SM_ENDURE , SC_ENDURE , SCB_MDEF|SCB_DSPD ); add_sc( MG_SIGHT , SC_SIGHT ); add_sc( MG_SAFETYWALL , SC_SAFETYWALL ); add_sc( MG_FROSTDIVER , SC_FREEZE ); add_sc( MG_STONECURSE , SC_STONE ); add_sc( AL_RUWACH , SC_RUWACH ); add_sc( AL_PNEUMA , SC_PNEUMA ); - status->set_sc( AL_INCAGI , SC_INC_AGI , SI_INC_AGI , SCB_AGI|SCB_SPEED ); - status->set_sc( AL_DECAGI , SC_DEC_AGI , SI_DEC_AGI , SCB_AGI|SCB_SPEED ); - status->set_sc( AL_CRUCIS , SC_CRUCIS , SI_CRUCIS , SCB_DEF ); - status->set_sc( AL_ANGELUS , SC_ANGELUS , SI_ANGELUS , SCB_DEF2 ); - status->set_sc( AL_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - status->set_sc( AC_CONCENTRATION , SC_CONCENTRATION , SI_CONCENTRATION , SCB_AGI|SCB_DEX ); - status->set_sc( TF_HIDING , SC_HIDING , SI_HIDING , SCB_SPEED ); + status->set_sc( AL_INCAGI , SC_INC_AGI , SCB_AGI|SCB_SPEED ); + status->set_sc( AL_DECAGI , SC_DEC_AGI , SCB_AGI|SCB_SPEED ); + status->set_sc( AL_CRUCIS , SC_CRUCIS , SCB_DEF ); + status->set_sc( AL_ANGELUS , SC_ANGELUS , SCB_DEF2 ); + status->set_sc( AL_BLESSING , SC_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); + status->set_sc( AC_CONCENTRATION , SC_CONCENTRATION , SCB_AGI|SCB_DEX ); + status->set_sc( TF_HIDING , SC_HIDING , SCB_SPEED ); add_sc( TF_POISON , SC_POISON ); - status->set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SI_TWOHANDQUICKEN , SCB_ASPD ); + status->set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SCB_ASPD ); add_sc( KN_AUTOCOUNTER , SC_AUTOCOUNTER ); - status->set_sc( PR_IMPOSITIO , SC_IMPOSITIO , SI_IMPOSITIO , + status->set_sc( PR_IMPOSITIO , SC_IMPOSITIO , #ifdef RENEWAL SCB_NONE ); #else SCB_WATK ); #endif - status->set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SI_SUFFRAGIUM , SCB_NONE ); - status->set_sc( PR_ASPERSIO , SC_ASPERSIO , SI_ASPERSIO , SCB_ATK_ELE ); - status->set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE ); - status->set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SI_SLOWPOISON , SCB_REGEN ); - status->set_sc( PR_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE ); - status->set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN ); - status->set_sc( PR_GLORIA , SC_GLORIA , SI_GLORIA , SCB_LUK ); + status->set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SCB_NONE ); + status->set_sc( PR_ASPERSIO , SC_ASPERSIO , SCB_ATK_ELE ); + status->set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SCB_DEF_ELE ); + status->set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SCB_REGEN ); + status->set_sc( PR_KYRIE , SC_KYRIE , SCB_NONE ); + status->set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SCB_REGEN ); + status->set_sc( PR_GLORIA , SC_GLORIA , SCB_LUK ); add_sc( PR_LEXDIVINA , SC_SILENCE ); - status->set_sc( PR_LEXAETERNA , SC_LEXAETERNA , SI_LEXAETERNA , SCB_NONE ); + status->set_sc( PR_LEXAETERNA , SC_LEXAETERNA , SCB_NONE ); add_sc( WZ_METEOR , SC_STUN ); add_sc( WZ_VERMILION , SC_BLIND ); add_sc( WZ_FROSTNOVA , SC_FREEZE ); add_sc( WZ_STORMGUST , SC_FREEZE ); - status->set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SI_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED ); - status->set_sc( BS_ADRENALINE , SC_ADRENALINE , SI_ADRENALINE , SCB_ASPD ); - status->set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECT , SI_WEAPONPERFECT, SCB_NONE ); - status->set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SI_OVERTHRUST , SCB_NONE ); - status->set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SI_MAXIMIZE , SCB_REGEN ); + status->set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED ); + status->set_sc( BS_ADRENALINE , SC_ADRENALINE , SCB_ASPD ); + status->set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECT , SCB_NONE ); + status->set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SCB_NONE ); + status->set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SCB_REGEN ); add_sc( HT_LANDMINE , SC_STUN ); - status->set_sc( HT_ANKLESNARE , SC_ANKLESNARE , SI_ANKLESNARE , SCB_NONE ); + status->set_sc( HT_ANKLESNARE , SC_ANKLESNARE , SCB_NONE ); add_sc( HT_SANDMAN , SC_SLEEP ); add_sc( HT_FLASHER , SC_BLIND ); add_sc( HT_FREEZINGTRAP , SC_FREEZE ); - status->set_sc( AS_CLOAKING , SC_CLOAKING , SI_CLOAKING , SCB_CRI|SCB_SPEED ); + status->set_sc( AS_CLOAKING , SC_CLOAKING , SCB_CRI|SCB_SPEED ); add_sc( AS_SONICBLOW , SC_STUN ); - status->set_sc( AS_ENCHANTPOISON , SC_ENCHANTPOISON , SI_ENCHANTPOISON, SCB_ATK_ELE ); - status->set_sc( AS_POISONREACT , SC_POISONREACT , SI_POISONREACT , SCB_NONE ); + status->set_sc( AS_ENCHANTPOISON , SC_ENCHANTPOISON , SCB_ATK_ELE ); + status->set_sc( AS_POISONREACT , SC_POISONREACT , SCB_NONE ); add_sc( AS_VENOMDUST , SC_POISON ); add_sc( AS_SPLASHER , SC_SPLASHER ); - status->set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SI_TRICKDEAD , SCB_REGEN ); - status->set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE ); + status->set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SCB_REGEN ); + status->set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SCB_NONE ); add_sc( TF_SPRINKLESAND , SC_BLIND ); add_sc( TF_THROWSTONE , SC_STUN ); - status->set_sc( MC_LOUD , SC_SHOUT , SI_SHOUT , SCB_STR ); - status->set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SI_ENERGYCOAT , SCB_NONE ); - status->set_sc( NPC_EMOTION , SC_MODECHANGE , SI_BLANK , SCB_MODE ); + status->set_sc( MC_LOUD , SC_SHOUT , SCB_STR ); + status->set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SCB_NONE ); + status->set_sc( NPC_EMOTION , SC_MODECHANGE , SCB_MODE ); add_sc( NPC_EMOTION_ON , SC_MODECHANGE ); - status->set_sc( NPC_ATTRICHANGE , SC_ARMOR_PROPERTY , SI_ARMOR_PROPERTY , SCB_DEF_ELE ); + status->set_sc( NPC_ATTRICHANGE , SC_ARMOR_PROPERTY , SCB_DEF_ELE ); add_sc( NPC_CHANGEWATER , SC_ARMOR_PROPERTY ); add_sc( NPC_CHANGEGROUND , SC_ARMOR_PROPERTY ); add_sc( NPC_CHANGEFIRE , SC_ARMOR_PROPERTY ); @@ -264,184 +269,184 @@ static void initChangeTables(void) add_sc( NPC_CURSEATTACK , SC_CURSE ); add_sc( NPC_SLEEPATTACK , SC_SLEEP ); add_sc( NPC_MAGICALATTACK , SC_MAGICALATTACK ); - status->set_sc( NPC_KEEPING , SC_KEEPING , SI_BLANK , SCB_DEF ); + status->set_sc( NPC_KEEPING , SC_KEEPING , SCB_DEF ); add_sc( NPC_DARKBLESSING , SC_COMA ); - status->set_sc( NPC_BARRIER , SC_BARRIER , SI_BLANK , SCB_MDEF|SCB_DEF ); + status->set_sc( NPC_BARRIER , SC_BARRIER , SCB_MDEF|SCB_DEF ); add_sc( NPC_DEFENDER , SC_ARMOR ); add_sc( NPC_LICK , SC_STUN ); - status->set_sc( NPC_HALLUCINATION , SC_ILLUSION , SI_ILLUSION , SCB_NONE ); + status->set_sc( NPC_HALLUCINATION , SC_ILLUSION , SCB_NONE ); add_sc( NPC_REBIRTH , SC_REBIRTH ); add_sc( RG_RAID , SC_STUN ); #ifdef RENEWAL add_sc( RG_RAID , SC_RAID ); add_sc( RG_BACKSTAP , SC_STUN ); #endif - status->set_sc( RG_STRIPWEAPON , SC_NOEQUIPWEAPON , SI_NOEQUIPWEAPON , SCB_WATK ); - status->set_sc( RG_STRIPSHIELD , SC_NOEQUIPSHIELD , SI_NOEQUIPSHIELD , SCB_DEF ); - status->set_sc( RG_STRIPARMOR , SC_NOEQUIPARMOR , SI_NOEQUIPARMOR , SCB_VIT ); - status->set_sc( RG_STRIPHELM , SC_NOEQUIPHELM , SI_NOEQUIPHELM , SCB_INT ); + status->set_sc( RG_STRIPWEAPON , SC_NOEQUIPWEAPON , SCB_WATK ); + status->set_sc( RG_STRIPSHIELD , SC_NOEQUIPSHIELD , SCB_DEF ); + status->set_sc( RG_STRIPARMOR , SC_NOEQUIPARMOR , SCB_VIT ); + status->set_sc( RG_STRIPHELM , SC_NOEQUIPHELM , SCB_INT ); add_sc( AM_ACIDTERROR , SC_BLOODING ); - status->set_sc( AM_CP_WEAPON , SC_PROTECTWEAPON , SI_PROTECTWEAPON , SCB_NONE ); - status->set_sc( AM_CP_SHIELD , SC_PROTECTSHIELD , SI_PROTECTSHIELD , SCB_NONE ); - status->set_sc( AM_CP_ARMOR , SC_PROTECTARMOR , SI_PROTECTARMOR , SCB_NONE ); - status->set_sc( AM_CP_HELM , SC_PROTECTHELM , SI_PROTECTHELM , SCB_NONE ); - status->set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE ); + status->set_sc( AM_CP_WEAPON , SC_PROTECTWEAPON , SCB_NONE ); + status->set_sc( AM_CP_SHIELD , SC_PROTECTSHIELD , SCB_NONE ); + status->set_sc( AM_CP_ARMOR , SC_PROTECTARMOR , SCB_NONE ); + status->set_sc( AM_CP_HELM , SC_PROTECTHELM , SCB_NONE ); + status->set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SCB_NONE ); add_sc( CR_SHIELDCHARGE , SC_STUN ); - status->set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE ); + status->set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SCB_NONE ); add_sc( CR_HOLYCROSS , SC_BLIND ); add_sc( CR_GRANDCROSS , SC_BLIND ); add_sc( CR_DEVOTION , SC_DEVOTION ); - status->set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SI_PROVIDENCE , SCB_ALL ); - status->set_sc( CR_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD ); - status->set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SI_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE ); - status->set_sc( MO_STEELBODY , SC_STEELBODY , SI_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED ); + status->set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SCB_ALL ); + status->set_sc( CR_DEFENDER , SC_DEFENDER , SCB_SPEED|SCB_ASPD ); + status->set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE ); + status->set_sc( MO_STEELBODY , SC_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED ); add_sc( MO_BLADESTOP , SC_BLADESTOP_WAIT ); add_sc( MO_BLADESTOP , SC_BLADESTOP ); - status->set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN ); - status->set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SI_BLANK , SCB_REGEN ); + status->set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN ); + status->set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SCB_REGEN ); #ifdef RENEWAL - status->set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SI_EXTREMITYFIST , SCB_NONE ); + status->set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SCB_NONE ); #endif add_sc( SA_MAGICROD , SC_MAGICROD ); - status->set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SI_AUTOSPELL , SCB_NONE ); - status->set_sc( SA_FLAMELAUNCHER , SC_PROPERTYFIRE , SI_PROPERTYFIRE , SCB_ATK_ELE ); - status->set_sc( SA_FROSTWEAPON , SC_PROPERTYWATER , SI_PROPERTYWATER , SCB_ATK_ELE ); - status->set_sc( SA_LIGHTNINGLOADER , SC_PROPERTYWIND , SI_PROPERTYWIND , SCB_ATK_ELE ); - status->set_sc( SA_SEISMICWEAPON , SC_PROPERTYGROUND , SI_PROPERTYGROUND , SCB_ATK_ELE ); - status->set_sc( SA_VOLCANO , SC_VOLCANO , SI_GROUNDMAGIC , SCB_WATK ); - status->set_sc( SA_DELUGE , SC_DELUGE , SI_GROUNDMAGIC , SCB_MAXHP ); - status->set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SI_GROUNDMAGIC , SCB_FLEE ); + status->set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SCB_NONE ); + status->set_sc( SA_FLAMELAUNCHER , SC_PROPERTYFIRE , SCB_ATK_ELE ); + status->set_sc( SA_FROSTWEAPON , SC_PROPERTYWATER , SCB_ATK_ELE ); + status->set_sc( SA_LIGHTNINGLOADER , SC_PROPERTYWIND , SCB_ATK_ELE ); + status->set_sc( SA_SEISMICWEAPON , SC_PROPERTYGROUND , SCB_ATK_ELE ); + status->set_sc( SA_VOLCANO , SC_VOLCANO , SCB_WATK ); + status->set_sc( SA_DELUGE , SC_DELUGE , SCB_MAXHP ); + status->set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SCB_FLEE ); add_sc( SA_REVERSEORCISH , SC_ORCISH ); add_sc( SA_COMA , SC_COMA ); - status->set_sc( BD_ENCORE , SC_DANCING , SI_BLANK , SCB_SPEED|SCB_REGEN ); + status->set_sc( BD_ENCORE , SC_DANCING , SCB_SPEED|SCB_REGEN ); add_sc( BD_RICHMANKIM , SC_RICHMANKIM ); - status->set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SI_BLANK , SCB_DEF2 ); - status->set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SI_BLANK , SCB_WATK|SCB_DEF ); - status->set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SI_BLANK , SCB_WATK ); + status->set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SCB_DEF2 ); + status->set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SCB_WATK|SCB_DEF ); + status->set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SCB_WATK ); add_sc( BD_ROKISWEIL , SC_ROKISWEIL ); add_sc( BD_INTOABYSS , SC_INTOABYSS ); - status->set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SI_BLANK , SCB_ALL ); + status->set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SCB_ALL ); add_sc( BA_FROSTJOKER , SC_FREEZE ); - status->set_sc( BA_WHISTLE , SC_WHISTLE , SI_BLANK , SCB_FLEE|SCB_FLEE2 ); - status->set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SI_BLANK , SCB_ASPD ); + status->set_sc( BA_WHISTLE , SC_WHISTLE , SCB_FLEE|SCB_FLEE2 ); + status->set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SCB_ASPD ); add_sc( BA_POEMBRAGI , SC_POEMBRAGI ); - status->set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SI_BLANK , SCB_MAXHP ); + status->set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SCB_MAXHP ); add_sc( DC_SCREAM , SC_STUN ); - status->set_sc( DC_HUMMING , SC_HUMMING , SI_BLANK , SCB_HIT ); - status->set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SI_BLANK , SCB_SPEED|SCB_ASPD ); - status->set_sc( DC_FORTUNEKISS , SC_FORTUNE , SI_BLANK , SCB_CRI ); - status->set_sc( DC_SERVICEFORYOU , SC_SERVICEFORYOU , SI_BLANK , SCB_ALL ); + status->set_sc( DC_HUMMING , SC_HUMMING , SCB_HIT ); + status->set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SCB_SPEED|SCB_ASPD ); + status->set_sc( DC_FORTUNEKISS , SC_FORTUNE , SCB_CRI ); + status->set_sc( DC_SERVICEFORYOU , SC_SERVICEFORYOU , SCB_ALL ); add_sc( NPC_DARKCROSS , SC_BLIND ); add_sc( NPC_GRANDDARKNESS , SC_BLIND ); - status->set_sc( NPC_STOP , SC_STOP , SI_STOP , SCB_NONE ); - status->set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SI_BROKENWEAPON , SCB_NONE ); - status->set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SI_BROKENARMOR , SCB_NONE ); - status->set_sc( NPC_CHANGEUNDEAD , SC_PROPERTYUNDEAD , SI_PROPERTYUNDEAD , SCB_DEF_ELE ); - status->set_sc( NPC_POWERUP , SC_INCHITRATE , SI_BLANK , SCB_HIT ); - status->set_sc( NPC_AGIUP , SC_INCFLEERATE , SI_BLANK , SCB_FLEE ); + status->set_sc( NPC_STOP , SC_STOP , SCB_NONE ); + status->set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SCB_NONE ); + status->set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SCB_NONE ); + status->set_sc( NPC_CHANGEUNDEAD , SC_PROPERTYUNDEAD , SCB_DEF_ELE ); + status->set_sc( NPC_POWERUP , SC_INCHITRATE , SCB_HIT ); + status->set_sc( NPC_AGIUP , SC_INCFLEERATE , SCB_FLEE ); add_sc( NPC_INVISIBLE , SC_CLOAKING ); - status->set_sc( LK_AURABLADE , SC_AURABLADE , SI_AURABLADE , SCB_NONE ); - status->set_sc( LK_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE ); + status->set_sc( LK_AURABLADE , SC_AURABLADE , SCB_NONE ); + status->set_sc( LK_PARRYING , SC_PARRYING , SCB_NONE ); #ifndef RENEWAL - status->set_sc( LK_CONCENTRATION , SC_LKCONCENTRATION , SI_LKCONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2); + status->set_sc( LK_CONCENTRATION , SC_LKCONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2); #else - status->set_sc( LK_CONCENTRATION , SC_LKCONCENTRATION , SI_LKCONCENTRATION , SCB_HIT|SCB_DEF); + status->set_sc( LK_CONCENTRATION , SC_LKCONCENTRATION , SCB_HIT|SCB_DEF); #endif - status->set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SI_TENSIONRELAX , SCB_REGEN ); - status->set_sc( LK_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); - status->set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE ); + status->set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SCB_REGEN ); + status->set_sc( LK_BERSERK , SC_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); + status->set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SCB_NONE ); add_sc( HP_BASILICA , SC_BASILICA ); - status->set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SI_MAGICPOWER , SCB_MATK ); + status->set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SCB_MATK ); add_sc( PA_SACRIFICE , SC_SACRIFICE ); - status->set_sc( PA_GOSPEL , SC_GOSPEL , SI_BLANK , SCB_SPEED|SCB_ASPD ); + status->set_sc( PA_GOSPEL , SC_GOSPEL , SCB_SPEED|SCB_ASPD ); add_sc( PA_GOSPEL , SC_SCRESIST ); add_sc( CH_TIGERFIST , SC_STOP ); - status->set_sc( ASC_EDP , SC_EDP , SI_EDP , SCB_NONE ); - status->set_sc( SN_SIGHT , SC_TRUESIGHT , SI_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT ); - status->set_sc( SN_WINDWALK , SC_WINDWALK , SI_WINDWALK , SCB_FLEE|SCB_SPEED ); - status->set_sc( WS_MELTDOWN , SC_MELTDOWN , SI_MELTDOWN , SCB_NONE ); - status->set_sc( WS_CARTBOOST , SC_CARTBOOST , SI_CARTBOOST , SCB_SPEED ); - status->set_sc( ST_CHASEWALK , SC_CHASEWALK , SI_BLANK , SCB_SPEED ); - status->set_sc( ST_REJECTSWORD , SC_SWORDREJECT , SI_SWORDREJECT , SCB_NONE ); + status->set_sc( ASC_EDP , SC_EDP , SCB_NONE ); + status->set_sc( SN_SIGHT , SC_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT ); + status->set_sc( SN_WINDWALK , SC_WINDWALK , SCB_FLEE|SCB_SPEED ); + status->set_sc( WS_MELTDOWN , SC_MELTDOWN , SCB_NONE ); + status->set_sc( WS_CARTBOOST , SC_CARTBOOST , SCB_SPEED ); + status->set_sc( ST_CHASEWALK , SC_CHASEWALK , SCB_SPEED ); + status->set_sc( ST_REJECTSWORD , SC_SWORDREJECT , SCB_NONE ); add_sc( ST_REJECTSWORD , SC_AUTOCOUNTER ); - status->set_sc( CG_MARIONETTE , SC_MARIONETTE_MASTER , SI_MARIONETTE_MASTER , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - status->set_sc( CG_MARIONETTE , SC_MARIONETTE , SI_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + status->set_sc( CG_MARIONETTE , SC_MARIONETTE_MASTER , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + status->set_sc( CG_MARIONETTE , SC_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); add_sc( LK_SPIRALPIERCE , SC_STOP ); add_sc( LK_HEADCRUSH , SC_BLOODING ); - status->set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SI_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD ); + status->set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD ); add_sc( HW_NAPALMVULCAN , SC_CURSE ); - status->set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SI_BLANK , SCB_MATK|SCB_MDEF2 ); + status->set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SCB_MATK|SCB_MDEF2 ); add_sc( PF_MEMORIZE , SC_MEMORIZE ); add_sc( PF_FOGWALL , SC_FOGWALL ); - status->set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SI_BLANK , SCB_FLEE ); - status->set_sc( WE_BABY , SC_BABY , SI_PROTECTEXP , SCB_NONE ); - status->set_sc( TK_RUN , SC_RUN , SI_RUN , SCB_SPEED|SCB_DSPD ); - status->set_sc( TK_RUN , SC_STRUP , SI_STRUP , SCB_STR ); - status->set_sc( TK_READYSTORM , SC_STORMKICK_READY , SI_STORMKICK_ON , SCB_NONE ); - status->set_sc( TK_READYDOWN , SC_DOWNKICK_READY , SI_DOWNKICK_ON , SCB_NONE ); + status->set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SCB_FLEE ); + status->set_sc( WE_BABY , SC_BABY , SCB_NONE ); + status->set_sc( TK_RUN , SC_RUN , SCB_SPEED|SCB_DSPD ); + status->set_sc( TK_RUN , SC_STRUP , SCB_STR ); + status->set_sc( TK_READYSTORM , SC_STORMKICK_READY , SCB_NONE ); + status->set_sc( TK_READYDOWN , SC_DOWNKICK_READY , SCB_NONE ); add_sc( TK_DOWNKICK , SC_STUN ); - status->set_sc( TK_READYTURN , SC_TURNKICK_READY , SI_TURNKICK_ON , SCB_NONE ); - status->set_sc( TK_READYCOUNTER , SC_COUNTERKICK_READY , SI_COUNTER_ON , SCB_NONE ); - status->set_sc( TK_DODGE , SC_DODGE_READY , SI_DODGE_ON , SCB_NONE ); - status->set_sc( TK_SPTIME , SC_EARTHSCROLL , SI_EARTHSCROLL , SCB_NONE ); + status->set_sc( TK_READYTURN , SC_TURNKICK_READY , SCB_NONE ); + status->set_sc( TK_READYCOUNTER , SC_COUNTERKICK_READY , SCB_NONE ); + status->set_sc( TK_DODGE , SC_DODGE_READY , SCB_NONE ); + status->set_sc( TK_SPTIME , SC_EARTHSCROLL , SCB_NONE ); add_sc( TK_SEVENWIND , SC_TK_SEVENWIND ); - status->set_sc( TK_SEVENWIND , SC_PROPERTYTELEKINESIS , SI_PROPERTYTELEKINESIS , SCB_ATK_ELE ); - status->set_sc( TK_SEVENWIND , SC_PROPERTYDARK , SI_PROPERTYDARK , SCB_ATK_ELE ); - status->set_sc( SG_SUN_WARM , SC_WARM , SI_SG_SUN_WARM , SCB_NONE ); + status->set_sc( TK_SEVENWIND , SC_PROPERTYTELEKINESIS , SCB_ATK_ELE ); + status->set_sc( TK_SEVENWIND , SC_PROPERTYDARK , SCB_ATK_ELE ); + status->set_sc( SG_SUN_WARM , SC_WARM , SCB_NONE ); add_sc( SG_MOON_WARM , SC_WARM ); add_sc( SG_STAR_WARM , SC_WARM ); - status->set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SI_SUN_COMFORT , SCB_DEF2 ); - status->set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SI_MOON_COMFORT , SCB_FLEE ); - status->set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SI_STAR_COMFORT , SCB_ASPD ); + status->set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SCB_DEF2 ); + status->set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SCB_FLEE ); + status->set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SCB_ASPD ); add_sc( SG_FRIEND , SC_SKILLRATE_UP ); - status->set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SI_BLANK , SCB_ALL ); - status->set_sc( SG_FUSION , SC_FUSION , SI_BLANK , SCB_SPEED ); - status->set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SI_ADRENALINE2 , SCB_ASPD ); - status->set_sc( SL_KAIZEL , SC_KAIZEL , SI_KAIZEL , SCB_NONE ); - status->set_sc( SL_KAAHI , SC_KAAHI , SI_KAAHI , SCB_NONE ); - status->set_sc( SL_KAUPE , SC_KAUPE , SI_KAUPE , SCB_NONE ); - status->set_sc( SL_KAITE , SC_KAITE , SI_KAITE , SCB_NONE ); + status->set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SCB_ALL ); + status->set_sc( SG_FUSION , SC_FUSION , SCB_SPEED ); + status->set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SCB_ASPD ); + status->set_sc( SL_KAIZEL , SC_KAIZEL , SCB_NONE ); + status->set_sc( SL_KAAHI , SC_KAAHI , SCB_NONE ); + status->set_sc( SL_KAUPE , SC_KAUPE , SCB_NONE ); + status->set_sc( SL_KAITE , SC_KAITE , SCB_NONE ); add_sc( SL_STUN , SC_STUN ); - status->set_sc( SL_SWOO , SC_SWOO , SI_BLANK , SCB_SPEED ); - status->set_sc( SL_SKE , SC_SKE , SI_BLANK , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 ); - status->set_sc( SL_SKA , SC_SKA , SI_BLANK , SCB_DEF|SCB_MDEF|SCB_ASPD ); - status->set_sc( SL_SMA , SC_SMA_READY , SI_SMA_READY , SCB_NONE ); - status->set_sc( SM_SELFPROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); - status->set_sc( ST_PRESERVE , SC_PRESERVE , SI_PRESERVE , SCB_NONE ); - status->set_sc( PF_DOUBLECASTING , SC_DOUBLECASTING , SI_DOUBLECASTING , SCB_NONE ); - status->set_sc( HW_GRAVITATION , SC_GRAVITATION , SI_BLANK , SCB_ASPD ); + status->set_sc( SL_SWOO , SC_SWOO , SCB_SPEED ); + status->set_sc( SL_SKE , SC_SKE , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 ); + status->set_sc( SL_SKA , SC_SKA , SCB_DEF|SCB_MDEF|SCB_ASPD ); + status->set_sc( SL_SMA , SC_SMA_READY , SCB_NONE ); + status->set_sc( SM_SELFPROVOKE , SC_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); + status->set_sc( ST_PRESERVE , SC_PRESERVE , SCB_NONE ); + status->set_sc( PF_DOUBLECASTING , SC_DOUBLECASTING , SCB_NONE ); + status->set_sc( HW_GRAVITATION , SC_GRAVITATION , SCB_ASPD ); add_sc( WS_CARTTERMINATION , SC_STUN ); - status->set_sc( WS_OVERTHRUSTMAX , SC_OVERTHRUSTMAX , SI_OVERTHRUSTMAX , SCB_NONE ); - status->set_sc( CG_LONGINGFREEDOM , SC_LONGING , SI_BLANK , SCB_SPEED|SCB_ASPD ); + status->set_sc( WS_OVERTHRUSTMAX , SC_OVERTHRUSTMAX , SCB_NONE ); + status->set_sc( CG_LONGINGFREEDOM , SC_LONGING , SCB_SPEED|SCB_ASPD ); add_sc( CG_HERMODE , SC_HERMODE ); - status->set_sc( CG_TAROTCARD , SC_TAROTCARD , SI_TAROTCARD , SCB_NONE ); - status->set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SI_BLANK , SCB_ATK_ELE ); - status->set_sc( SL_HIGH , SC_SOULLINK , SI_SOULLINK , SCB_ALL ); - status->set_sc( KN_ONEHAND , SC_ONEHANDQUICKEN , SI_ONEHANDQUICKEN , SCB_ASPD ); - status->set_sc( GS_FLING , SC_FLING , SI_BLANK , SCB_DEF|SCB_DEF2 ); + status->set_sc( CG_TAROTCARD , SC_TAROTCARD , SCB_NONE ); + status->set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SCB_ATK_ELE ); + status->set_sc( SL_HIGH , SC_SOULLINK , SCB_ALL ); + status->set_sc( KN_ONEHAND , SC_ONEHANDQUICKEN , SCB_ASPD ); + status->set_sc( GS_FLING , SC_FLING , SCB_DEF|SCB_DEF2 ); add_sc( GS_CRACKER , SC_STUN ); add_sc( GS_DISARM , SC_NOEQUIPWEAPON ); add_sc( GS_PIERCINGSHOT , SC_BLOODING ); - status->set_sc( GS_MADNESSCANCEL , SC_GS_MADNESSCANCEL , SI_GS_MADNESSCANCEL , SCB_ASPD + status->set_sc( GS_MADNESSCANCEL , SC_GS_MADNESSCANCEL , SCB_ASPD #ifndef RENEWAL |SCB_BATK ); #else ); #endif - status->set_sc( GS_ADJUSTMENT , SC_GS_ADJUSTMENT , SI_GS_ADJUSTMENT , SCB_HIT|SCB_FLEE ); - status->set_sc( GS_INCREASING , SC_GS_ACCURACY , SI_GS_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT ); - status->set_sc( GS_GATLINGFEVER , SC_GS_GATLINGFEVER , SI_GS_GATLINGFEVER , SCB_FLEE|SCB_SPEED|SCB_ASPD + status->set_sc( GS_ADJUSTMENT , SC_GS_ADJUSTMENT , SCB_HIT|SCB_FLEE ); + status->set_sc( GS_INCREASING , SC_GS_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT ); + status->set_sc( GS_GATLINGFEVER , SC_GS_GATLINGFEVER , SCB_FLEE|SCB_SPEED|SCB_ASPD #ifndef RENEWAL |SCB_BATK ); #else ); #endif - status->set_sc( NJ_TATAMIGAESHI , SC_NJ_TATAMIGAESHI , SI_BLANK , SCB_NONE ); - status->set_sc( NJ_SUITON , SC_NJ_SUITON , SI_NJ_SUITON , SCB_AGI|SCB_SPEED ); + status->set_sc( NJ_TATAMIGAESHI , SC_NJ_TATAMIGAESHI , SCB_NONE ); + status->set_sc( NJ_SUITON , SC_NJ_SUITON , SCB_AGI|SCB_SPEED ); add_sc( NJ_HYOUSYOURAKU , SC_FREEZE ); - status->set_sc( NJ_NEN , SC_NJ_NEN , SI_NJ_NEN , SCB_STR|SCB_INT ); - status->set_sc( NJ_UTSUSEMI , SC_NJ_UTSUSEMI , SI_NJ_UTSUSEMI , SCB_NONE ); - status->set_sc( NJ_BUNSINJYUTSU , SC_NJ_BUNSINJYUTSU , SI_NJ_BUNSINJYUTSU , SCB_DYE ); + status->set_sc( NJ_NEN , SC_NJ_NEN , SCB_STR|SCB_INT ); + status->set_sc( NJ_UTSUSEMI , SC_NJ_UTSUSEMI , SCB_NONE ); + status->set_sc( NJ_BUNSINJYUTSU , SC_NJ_BUNSINJYUTSU , SCB_DYE ); add_sc( NPC_ICEBREATH , SC_FREEZE ); add_sc( NPC_ACIDBREATH , SC_POISON ); @@ -455,202 +460,202 @@ static void initChangeTables(void) add_sc( NPC_WIDESIGHT , SC_SIGHT ); add_sc( NPC_EVILLAND , SC_BLIND ); add_sc( NPC_MAGICMIRROR , SC_MAGICMIRROR ); - status->set_sc( NPC_SLOWCAST , SC_SLOWCAST , SI_SLOWCAST , SCB_NONE ); - status->set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SI_CRITICALWOUND , SCB_NONE ); - status->set_sc( NPC_STONESKIN , SC_STONESKIN , SI_BLANK , SCB_DEF|SCB_MDEF ); + status->set_sc( NPC_SLOWCAST , SC_SLOWCAST , SCB_NONE ); + status->set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SCB_NONE ); + status->set_sc( NPC_STONESKIN , SC_STONESKIN , SCB_DEF|SCB_MDEF ); add_sc( NPC_ANTIMAGIC , SC_STONESKIN ); add_sc( NPC_WIDECURSE , SC_CURSE ); add_sc( NPC_WIDESTUN , SC_STUN ); - status->set_sc( NPC_HELLPOWER , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE ); - status->set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE ); - status->set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SI_INVINCIBLE , SCB_SPEED ); - status->set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SI_BLANK , SCB_SPEED ); + status->set_sc( NPC_HELLPOWER , SC_HELLPOWER , SCB_NONE ); + status->set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SCB_NONE ); + status->set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SCB_SPEED ); + status->set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SCB_SPEED ); - status->set_sc( CASH_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - status->set_sc( CASH_INCAGI , SC_INC_AGI , SI_INC_AGI , SCB_AGI|SCB_SPEED ); - status->set_sc( CASH_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE ); + status->set_sc( CASH_BLESSING , SC_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); + status->set_sc( CASH_INCAGI , SC_INC_AGI , SCB_AGI|SCB_SPEED ); + status->set_sc( CASH_ASSUMPTIO , SC_ASSUMPTIO , SCB_NONE ); - status->set_sc( ALL_PARTYFLEE , SC_PARTYFLEE , SI_PARTYFLEE , SCB_NONE ); - status->set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SI_ODINS_POWER , SCB_WATK | SCB_MATK | SCB_MDEF | SCB_DEF); + status->set_sc( ALL_PARTYFLEE , SC_PARTYFLEE , SCB_NONE ); + status->set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SCB_WATK | SCB_MATK | SCB_MDEF | SCB_DEF); - status->set_sc( CR_SHRINK , SC_CR_SHRINK , SI_CR_SHRINK , SCB_NONE ); - status->set_sc( RG_CLOSECONFINE , SC_RG_CCONFINE_S , SI_RG_CCONFINE_S , SCB_NONE ); - status->set_sc( RG_CLOSECONFINE , SC_RG_CCONFINE_M , SI_RG_CCONFINE_M , SCB_FLEE ); - status->set_sc( WZ_SIGHTBLASTER , SC_WZ_SIGHTBLASTER , SI_WZ_SIGHTBLASTER , SCB_NONE ); - status->set_sc( DC_WINKCHARM , SC_DC_WINKCHARM , SI_DC_WINKCHARM , SCB_NONE ); + status->set_sc( CR_SHRINK , SC_CR_SHRINK , SCB_NONE ); + status->set_sc( RG_CLOSECONFINE , SC_RG_CCONFINE_S , SCB_NONE ); + status->set_sc( RG_CLOSECONFINE , SC_RG_CCONFINE_M , SCB_FLEE ); + status->set_sc( WZ_SIGHTBLASTER , SC_WZ_SIGHTBLASTER , SCB_NONE ); + status->set_sc( DC_WINKCHARM , SC_DC_WINKCHARM , SCB_NONE ); add_sc( MO_BALKYOUNG , SC_STUN ); add_sc( SA_ELEMENTWATER , SC_ARMOR_PROPERTY ); add_sc( SA_ELEMENTFIRE , SC_ARMOR_PROPERTY ); add_sc( SA_ELEMENTGROUND , SC_ARMOR_PROPERTY ); add_sc( SA_ELEMENTWIND , SC_ARMOR_PROPERTY ); - status->set_sc( HLIF_AVOID , SC_HLIF_AVOID , SI_BLANK , SCB_SPEED ); - status->set_sc( HLIF_CHANGE , SC_HLIF_CHANGE , SI_BLANK , SCB_VIT|SCB_INT ); - status->set_sc( HFLI_FLEET , SC_HLIF_FLEET , SI_BLANK , SCB_ASPD|SCB_BATK|SCB_WATK ); - status->set_sc( HFLI_SPEED , SC_HLIF_SPEED , SI_BLANK , SCB_FLEE ); - status->set_sc( HAMI_DEFENCE , SC_HAMI_DEFENCE , SI_BLANK , SCB_DEF ); - status->set_sc( HAMI_BLOODLUST , SC_HAMI_BLOODLUST , SI_BLANK , SCB_BATK|SCB_WATK ); + status->set_sc( HLIF_AVOID , SC_HLIF_AVOID , SCB_SPEED ); + status->set_sc( HLIF_CHANGE , SC_HLIF_CHANGE , SCB_VIT|SCB_INT ); + status->set_sc( HFLI_FLEET , SC_HLIF_FLEET , SCB_ASPD|SCB_BATK|SCB_WATK ); + status->set_sc( HFLI_SPEED , SC_HLIF_SPEED , SCB_FLEE ); + status->set_sc( HAMI_DEFENCE , SC_HAMI_DEFENCE , SCB_DEF ); + status->set_sc( HAMI_BLOODLUST , SC_HAMI_BLOODLUST , SCB_BATK|SCB_WATK ); // Homunculus S - status->set_sc( MH_LIGHT_OF_REGENE , SC_LIGHT_OF_REGENE , SI_LIGHT_OF_REGENE , SCB_NONE ); - status->set_sc( MH_OVERED_BOOST , SC_OVERED_BOOST , SI_OVERED_BOOST , SCB_FLEE|SCB_ASPD|SCB_DEF ); + status->set_sc( MH_LIGHT_OF_REGENE , SC_LIGHT_OF_REGENE , SCB_NONE ); + status->set_sc( MH_OVERED_BOOST , SC_OVERED_BOOST , SCB_FLEE|SCB_ASPD|SCB_DEF ); add_sc(MH_STAHL_HORN, SC_STUN); - status->set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SI_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP); - status->set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SI_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP); + status->set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP); + status->set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP); add_sc( MH_STEINWAND, SC_SAFETYWALL ); - status->set_sc(MH_VOLCANIC_ASH, SC_VOLCANIC_ASH, SI_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE); - status->set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SI_GRANITIC_ARMOR, SCB_NONE); - status->set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SI_MAGMA_FLOW, SCB_NONE); - status->set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SI_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE); + status->set_sc(MH_VOLCANIC_ASH, SC_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE); + status->set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SCB_NONE); + status->set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SCB_NONE); + status->set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE); add_sc(MH_LAVA_SLIDE, SC_BURNING); - status->set_sc(MH_NEEDLE_OF_PARALYZE, SC_NEEDLE_OF_PARALYZE, SI_NEEDLE_OF_PARALYZE, SCB_DEF2); + status->set_sc(MH_NEEDLE_OF_PARALYZE, SC_NEEDLE_OF_PARALYZE, SCB_DEF2); add_sc(MH_POISON_MIST, SC_BLIND); - status->set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD); + status->set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SCB_ASPD); - status->set_sc( MH_SILENT_BREEZE , SC_SILENCE , SI_SILENT_BREEZE , SCB_NONE ); + status->set_sc( MH_SILENT_BREEZE , SC_SILENCE , SCB_NONE ); add_sc( MH_STYLE_CHANGE , SC_STYLE_CHANGE); - status->set_sc( MH_TINDER_BREAKER , SC_RG_CCONFINE_S , SI_RG_CCONFINE_S , SCB_NONE ); - status->set_sc( MH_TINDER_BREAKER , SC_RG_CCONFINE_M , SI_RG_CCONFINE_M , SCB_FLEE ); + status->set_sc( MH_TINDER_BREAKER , SC_RG_CCONFINE_S , SCB_NONE ); + status->set_sc( MH_TINDER_BREAKER , SC_RG_CCONFINE_M , SCB_FLEE ); add_sc( MER_CRASH , SC_STUN ); - status->set_sc( MER_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); + status->set_sc( MER_PROVOKE , SC_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK ); add_sc( MS_MAGNUM , SC_SUB_WEAPONPROPERTY ); add_sc( MER_SIGHT , SC_SIGHT ); - status->set_sc( MER_DECAGI , SC_DEC_AGI , SI_DEC_AGI , SCB_AGI|SCB_SPEED ); - status->set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN ); + status->set_sc( MER_DECAGI , SC_DEC_AGI , SCB_AGI|SCB_SPEED ); + status->set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SCB_REGEN ); add_sc( MER_LEXDIVINA , SC_SILENCE ); add_sc( MA_LANDMINE , SC_STUN ); add_sc( MA_SANDMAN , SC_SLEEP ); add_sc( MA_FREEZINGTRAP , SC_FREEZE ); - status->set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE ); - status->set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE ); - status->set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE ); - status->set_sc( ML_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD ); - status->set_sc( MS_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE ); - status->set_sc( MS_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); + status->set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SCB_NONE ); + status->set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SCB_NONE ); + status->set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SCB_NONE ); + status->set_sc( ML_DEFENDER , SC_DEFENDER , SCB_SPEED|SCB_ASPD ); + status->set_sc( MS_PARRYING , SC_PARRYING , SCB_NONE ); + status->set_sc( MS_BERSERK , SC_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN ); add_sc( ML_SPIRALPIERCE , SC_STOP ); - status->set_sc( MER_QUICKEN , SC_MER_QUICKEN , SI_BLANK , SCB_ASPD ); + status->set_sc( MER_QUICKEN , SC_MER_QUICKEN , SCB_ASPD ); add_sc( ML_DEVOTION , SC_DEVOTION ); - status->set_sc( MER_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE ); - status->set_sc( MER_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); - status->set_sc( MER_INCAGI , SC_INC_AGI , SI_INC_AGI , SCB_AGI|SCB_SPEED ); + status->set_sc( MER_KYRIE , SC_KYRIE , SCB_NONE ); + status->set_sc( MER_BLESSING , SC_BLESSING , SCB_STR|SCB_INT|SCB_DEX ); + status->set_sc( MER_INCAGI , SC_INC_AGI , SCB_AGI|SCB_SPEED ); - status->set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SI_BLANK , SCB_STR ); - status->set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SI_BLANK , SCB_VIT ); - status->set_sc( GD_SOULCOLD , SC_SOULCOLD , SI_BLANK , SCB_AGI ); - status->set_sc( GD_HAWKEYES , SC_HAWKEYES , SI_BLANK , SCB_DEX ); + status->set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SCB_STR ); + status->set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SCB_VIT ); + status->set_sc( GD_SOULCOLD , SC_SOULCOLD , SCB_AGI ); + status->set_sc( GD_HAWKEYES , SC_HAWKEYES , SCB_DEX ); - status->set_sc( GD_BATTLEORDER , SC_GDSKILL_BATTLEORDER , SI_BLANK , SCB_STR|SCB_INT|SCB_DEX ); - status->set_sc( GD_REGENERATION , SC_GDSKILL_REGENERATION , SI_BLANK , SCB_REGEN ); + status->set_sc( GD_BATTLEORDER , SC_GDSKILL_BATTLEORDER , SCB_STR|SCB_INT|SCB_DEX ); + status->set_sc( GD_REGENERATION , SC_GDSKILL_REGENERATION , SCB_REGEN ); /** * Rune Knight **/ - status->set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE ); - status->set_sc( RK_DRAGONHOWLING , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); - status->set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE ); - status->set_sc( RK_WINDCUTTER , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT ); + status->set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SCB_NONE ); + status->set_sc( RK_DRAGONHOWLING , SC_FEAR , SCB_FLEE|SCB_HIT ); + status->set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SCB_NONE ); + status->set_sc( RK_WINDCUTTER , SC_FEAR , SCB_FLEE|SCB_HIT ); add_sc( RK_DRAGONBREATH , SC_BURNING ); - status->set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_BLANK , SCB_NONE ); - status->set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE ); - status->set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR ); - status->set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_NONE ); - status->set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SI_VITALITYACTIVATION, SCB_REGEN ); - status->set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD ); - status->set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE ); - status->set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE ); + status->set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SCB_NONE ); + status->set_sc( RK_REFRESH , SC_REFRESH , SCB_NONE ); + status->set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SCB_STR ); + status->set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SCB_NONE ); + status->set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SCB_REGEN ); + status->set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD ); + status->set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SCB_NONE ); + status->set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SCB_NONE ); add_sc( RK_DRAGONBREATH_WATER, SC_FROSTMISTY ); /** * GC Guillotine Cross **/ - set_sc_with_vfx( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SI_VENOMIMPRESS , SCB_NONE ); - status->set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SI_POISONINGWEAPON , SCB_NONE ); - status->set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SI_WEAPONBLOCKING , SCB_NONE ); - status->set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SI_CLOAKINGEXCEED , SCB_SPEED ); - status->set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SI_HALLUCINATIONWALK, SCB_FLEE ); - status->set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SI_ROLLINGCUTTER , SCB_NONE ); - set_sc_with_vfx( GC_DARKCROW , SC_DARKCROW , SI_DARKCROW , SCB_NONE ); + set_sc_with_vfx( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SCB_NONE ); + status->set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SCB_NONE ); + status->set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SCB_NONE ); + status->set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SCB_SPEED ); + status->set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SCB_FLEE ); + status->set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SCB_NONE ); + set_sc_with_vfx( GC_DARKCROW , SC_DARKCROW , SCB_NONE ); /** * Arch Bishop **/ - status->set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED ); + status->set_sc( AB_ADORAMUS , SC_ADORAMUS , SCB_AGI|SCB_SPEED ); add_sc( AB_CLEMENTIA , SC_BLESSING ); add_sc( AB_CANTO , SC_INC_AGI ); - status->set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP ); + status->set_sc( AB_EPICLESIS , SC_EPICLESIS , SCB_MAXHP ); add_sc( AB_PRAEFATIO , SC_KYRIE ); - set_sc_with_vfx( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE ); - status->set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT ); - status->set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK ); - status->set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN ); - status->set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_ATK_ELE ); - status->set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE ); - status->set_sc( AB_SECRAMENT , SC_SECRAMENT , SI_SECRAMENT , SCB_NONE ); - status->set_sc( AB_OFFERTORIUM , SC_OFFERTORIUM , SI_OFFERTORIUM , SCB_NONE ); + set_sc_with_vfx( AB_ORATIO , SC_ORATIO , SCB_NONE ); + status->set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SCB_VIT ); + status->set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SCB_LUK ); + status->set_sc( AB_RENOVATIO , SC_RENOVATIO , SCB_REGEN ); + status->set_sc( AB_EXPIATIO , SC_EXPIATIO , SCB_ATK_ELE ); + status->set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SCB_NONE ); + status->set_sc( AB_SECRAMENT , SC_SECRAMENT , SCB_NONE ); + status->set_sc( AB_OFFERTORIUM , SC_OFFERTORIUM , SCB_NONE ); /** * Warlock **/ add_sc( WL_WHITEIMPRISON , SC_WHITEIMPRISON ); - set_sc_with_vfx( WL_FROSTMISTY , SC_FROSTMISTY , SI_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF ); - status->set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SI_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_AGI|SCB_DEX ); - status->set_sc(WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SI_RECOGNIZEDSPELL , SCB_MATK); - status->set_sc( WL_STASIS , SC_STASIS , SI_STASIS , SCB_NONE ); - status->set_sc( WL_TELEKINESIS_INTENSE, SC_TELEKINESIS_INTENSE , SI_TELEKINESIS_INTENSE , SCB_MATK ); + set_sc_with_vfx( WL_FROSTMISTY , SC_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF ); + status->set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_AGI|SCB_DEX ); + status->set_sc(WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SCB_MATK); + status->set_sc( WL_STASIS , SC_STASIS , SCB_NONE ); + status->set_sc( WL_TELEKINESIS_INTENSE, SC_TELEKINESIS_INTENSE , SCB_MATK ); /** * Ranger **/ - status->set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SI_FEARBREEZE , SCB_NONE ); - status->set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SI_ELECTRICSHOCKER , SCB_NONE ); - status->set_sc( RA_WUGDASH , SC_WUGDASH , SI_WUGDASH , SCB_SPEED ); - status->set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SI_CAMOUFLAGE , SCB_SPEED ); + status->set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SCB_NONE ); + status->set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SCB_NONE ); + status->set_sc( RA_WUGDASH , SC_WUGDASH , SCB_SPEED ); + status->set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SCB_SPEED ); add_sc( RA_MAGENTATRAP , SC_ARMOR_PROPERTY ); add_sc( RA_COBALTTRAP , SC_ARMOR_PROPERTY ); add_sc( RA_MAIZETRAP , SC_ARMOR_PROPERTY ); add_sc( RA_VERDURETRAP , SC_ARMOR_PROPERTY ); add_sc( RA_FIRINGTRAP , SC_BURNING ); add_sc( RA_ICEBOUNDTRAP , SC_FROSTMISTY ); - status->set_sc( RA_UNLIMIT , SC_UNLIMIT , SI_UNLIMIT , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); + status->set_sc( RA_UNLIMIT , SC_UNLIMIT , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); /** * Mechanic **/ - status->set_sc( NC_ACCELERATION , SC_ACCELERATION , SI_ACCELERATION , SCB_SPEED ); - status->set_sc( NC_HOVERING , SC_HOVERING , SI_HOVERING , SCB_SPEED ); - status->set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SI_SHAPESHIFT , SCB_DEF_ELE ); - status->set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SI_INFRAREDSCAN , SCB_FLEE ); - status->set_sc( NC_ANALYZE , SC_ANALYZE , SI_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); - status->set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SI_MAGNETICFIELD , SCB_NONE ); - status->set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SI_NEUTRALBARRIER , SCB_DEF|SCB_MDEF ); - status->set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SI_STEALTHFIELD , SCB_NONE ); + status->set_sc( NC_ACCELERATION , SC_ACCELERATION , SCB_SPEED ); + status->set_sc( NC_HOVERING , SC_HOVERING , SCB_SPEED ); + status->set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SCB_DEF_ELE ); + status->set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SCB_FLEE ); + status->set_sc( NC_ANALYZE , SC_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 ); + status->set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SCB_NONE ); + status->set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SCB_DEF|SCB_MDEF ); + status->set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SCB_NONE ); /** * Royal Guard **/ - status->set_sc( LG_REFLECTDAMAGE , SC_LG_REFLECTDAMAGE , SI_LG_REFLECTDAMAGE, SCB_NONE ); - status->set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SI_FORCEOFVANGUARD , SCB_MAXHP ); - status->set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SI_EXEEDBREAK , SCB_NONE ); - status->set_sc( LG_PRESTIGE , SC_PRESTIGE , SI_PRESTIGE , SCB_DEF ); - status->set_sc( LG_BANDING , SC_BANDING , SI_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2 - status->set_sc( LG_PIETY , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE ); - status->set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SI_EARTHDRIVE , SCB_DEF|SCB_ASPD ); - status->set_sc( LG_INSPIRATION , SC_INSPIRATION , SI_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK); - status->set_sc( LG_KINGS_GRACE , SC_KINGS_GRACE , SI_KINGS_GRACE , SCB_NONE ); + status->set_sc( LG_REFLECTDAMAGE , SC_LG_REFLECTDAMAGE , SCB_NONE ); + status->set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SCB_MAXHP ); + status->set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SCB_NONE ); + status->set_sc( LG_PRESTIGE , SC_PRESTIGE , SCB_DEF ); + status->set_sc( LG_BANDING , SC_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2 + status->set_sc( LG_PIETY , SC_BENEDICTIO , SCB_DEF_ELE ); + status->set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SCB_DEF|SCB_ASPD ); + status->set_sc( LG_INSPIRATION , SC_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK); + status->set_sc( LG_KINGS_GRACE , SC_KINGS_GRACE , SCB_NONE ); /** * Shadow Chaser **/ - status->set_sc( SC_REPRODUCE , SC__REPRODUCE , SI_REPRODUCE , SCB_NONE ); - status->set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SI_AUTOSHADOWSPELL , SCB_NONE ); - status->set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SI_SHADOWFORM , SCB_NONE ); - status->set_sc( SC_BODYPAINT , SC__BODYPAINT , SI_BODYPAINT , SCB_ASPD ); - status->set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SI_INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE ); - status->set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SI_DEADLYINFECT , SCB_NONE ); - status->set_sc( SC_ENERVATION , SC__ENERVATION , SI_ENERVATION , SCB_BATK ); - status->set_sc( SC_GROOMY , SC__GROOMY , SI_GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED ); - status->set_sc( SC_IGNORANCE , SC__IGNORANCE , SI_IGNORANCE , SCB_NONE ); - status->set_sc( SC_LAZINESS , SC__LAZINESS , SI_LAZINESS , SCB_FLEE ); - status->set_sc( SC_UNLUCKY , SC__UNLUCKY , SI_UNLUCKY , SCB_CRI|SCB_FLEE2 ); - status->set_sc( SC_WEAKNESS , SC__WEAKNESS , SI_WEAKNESS , SCB_FLEE2|SCB_MAXHP ); - status->set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSARY , SI_STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK ); - set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE ); + status->set_sc( SC_REPRODUCE , SC__REPRODUCE , SCB_NONE ); + status->set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SCB_NONE ); + status->set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SCB_NONE ); + status->set_sc( SC_BODYPAINT , SC__BODYPAINT , SCB_ASPD ); + status->set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE ); + status->set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SCB_NONE ); + status->set_sc( SC_ENERVATION , SC__ENERVATION , SCB_BATK ); + status->set_sc( SC_GROOMY , SC__GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED ); + status->set_sc( SC_IGNORANCE , SC__IGNORANCE , SCB_NONE ); + status->set_sc( SC_LAZINESS , SC__LAZINESS , SCB_FLEE ); + status->set_sc( SC_UNLUCKY , SC__UNLUCKY , SCB_CRI|SCB_FLEE2 ); + status->set_sc( SC_WEAKNESS , SC__WEAKNESS , SCB_FLEE2|SCB_MAXHP ); + status->set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK ); + set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SCB_NONE ); add_sc( SC_CHAOSPANIC , SC__CHAOS ); add_sc( SC_MAELSTROM , SC__MAELSTROM ); add_sc( SC_BLOODYLUST , SC_BERSERK ); @@ -660,131 +665,132 @@ static void initChangeTables(void) **/ add_sc( SR_DRAGONCOMBO , SC_STUN ); add_sc( SR_EARTHSHAKER , SC_STUN ); - status->set_sc( SR_FALLENEMPIRE , SC_FALLENEMPIRE , SI_FALLENEMPIRE , SCB_NONE ); - status->set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SI_CRESCENTELBOW , SCB_NONE ); - set_sc_with_vfx( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SI_CURSEDCIRCLE_TARGET , SCB_NONE ); - status->set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SI_LIGHTNINGWALK , SCB_NONE ); - status->set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP ); - status->set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GENTLETOUCH_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE ); - status->set_sc( SR_GENTLETOUCH_CHANGE , SC_GENTLETOUCH_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_ASPD|SCB_MDEF|SCB_MAXHP ); - status->set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GENTLETOUCH_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_DEF2|SCB_REGEN ); - status->set_sc( SR_FLASHCOMBO , SC_FLASHCOMBO , SI_FLASHCOMBO , SCB_WATK ); + status->set_sc( SR_FALLENEMPIRE , SC_FALLENEMPIRE , SCB_NONE ); + status->set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SCB_NONE ); + set_sc_with_vfx( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SCB_NONE ); + status->set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SCB_NONE ); + status->set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP ); + status->set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GENTLETOUCH_ENERGYGAIN , SCB_NONE ); + status->set_sc( SR_GENTLETOUCH_CHANGE , SC_GENTLETOUCH_CHANGE , SCB_ASPD|SCB_MDEF|SCB_MAXHP ); + status->set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GENTLETOUCH_REVITALIZE , SCB_MAXHP|SCB_DEF2|SCB_REGEN ); + status->set_sc( SR_FLASHCOMBO , SC_FLASHCOMBO , SCB_WATK ); /** * Wanderer / Minstrel **/ - status->set_sc( WA_SWING_DANCE , SC_SWING , SI_SWINGDANCE , SCB_SPEED|SCB_ASPD ); - status->set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONY_LOVE , SI_SYMPHONYOFLOVERS , SCB_MDEF ); - status->set_sc( WA_MOONLIT_SERENADE , SC_MOONLIT_SERENADE , SI_MOONLITSERENADE , SCB_MATK ); - status->set_sc( MI_RUSH_WINDMILL , SC_RUSH_WINDMILL , SI_RUSHWINDMILL , SCB_WATK ); - status->set_sc( MI_ECHOSONG , SC_ECHOSONG , SI_ECHOSONG , SCB_DEF2 ); - status->set_sc( MI_HARMONIZE , SC_HARMONIZE , SI_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); - set_sc_with_vfx(WM_POEMOFNETHERWORLD, SC_NETHERWORLD , SI_NETHERWORLD , SCB_NONE); - set_sc_with_vfx( WM_VOICEOFSIREN , SC_SIREN , SI_SIREN , SCB_NONE ); - set_sc_with_vfx( WM_LULLABY_DEEPSLEEP , SC_DEEP_SLEEP , SI_DEEPSLEEP , SCB_NONE ); - status->set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SI_SIRCLEOFNATURE , SCB_NONE ); - status->set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SI_GLOOMYDAY , SCB_FLEE|SCB_ASPD ); - status->set_sc( WM_SONG_OF_MANA , SC_SONG_OF_MANA , SI_SONG_OF_MANA , SCB_NONE ); - status->set_sc( WM_DANCE_WITH_WUG , SC_DANCE_WITH_WUG , SI_DANCEWITHWUG , SCB_ASPD ); - status->set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAY_NIGHT_FEVER , SI_SATURDAYNIGHTFEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN ); - status->set_sc( WM_LERADS_DEW , SC_LERADS_DEW , SI_LERADSDEW , SCB_MAXHP ); - status->set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_INT ); - status->set_sc( WM_BEYOND_OF_WARCRY , SC_BEYOND_OF_WARCRY , SI_WARCRYOFBEYOND , SCB_STR|SCB_CRI|SCB_MAXHP ); - status->set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITED_HUMMING_VOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE ); - status->set_sc( WM_FRIGG_SONG , SC_FRIGG_SONG , SI_FRIGG_SONG , SCB_MAXHP ); + status->set_sc( WA_SWING_DANCE , SC_SWING , SCB_SPEED|SCB_ASPD ); + status->set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONY_LOVE , SCB_MDEF ); + status->set_sc( WA_MOONLIT_SERENADE , SC_MOONLIT_SERENADE , SCB_MATK ); + status->set_sc( MI_RUSH_WINDMILL , SC_RUSH_WINDMILL , SCB_WATK ); + status->set_sc( MI_ECHOSONG , SC_ECHOSONG , SCB_DEF2 ); + status->set_sc( MI_HARMONIZE , SC_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + set_sc_with_vfx(WM_POEMOFNETHERWORLD, SC_NETHERWORLD , SCB_NONE); + set_sc_with_vfx( WM_VOICEOFSIREN , SC_SIREN , SCB_NONE ); + set_sc_with_vfx( WM_LULLABY_DEEPSLEEP , SC_DEEP_SLEEP , SCB_NONE ); + status->set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SCB_NONE ); + status->set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SCB_FLEE|SCB_ASPD ); + status->set_sc( WM_SONG_OF_MANA , SC_SONG_OF_MANA , SCB_NONE ); + status->set_sc( WM_DANCE_WITH_WUG , SC_DANCE_WITH_WUG , SCB_ASPD ); + status->set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAY_NIGHT_FEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN ); + status->set_sc( WM_LERADS_DEW , SC_LERADS_DEW , SCB_MAXHP ); + status->set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SCB_INT ); + status->set_sc( WM_BEYOND_OF_WARCRY , SC_BEYOND_OF_WARCRY , SCB_STR|SCB_CRI|SCB_MAXHP ); + status->set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITED_HUMMING_VOICE, SCB_NONE ); + status->set_sc( WM_FRIGG_SONG , SC_FRIGG_SONG , SCB_MAXHP ); + status->set_sc( WM_SEVERE_RAINSTORM , SC_NO_SWITCH_EQUIP , SCB_NONE ); /** * Sorcerer **/ - status->set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); - status->set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE ); - status->set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE ); - set_sc_with_vfx( SO_DIAMONDDUST , SC_COLD , SI_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it. - status->set_sc( SO_CLOUD_KILL , SC_POISON , SI_CLOUDKILL , SCB_NONE ); - status->set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI ); + status->set_sc( SO_FIREWALK , SC_PROPERTYWALK , SCB_NONE ); + status->set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SCB_NONE ); + status->set_sc( SO_SPELLFIST , SC_SPELLFIST , SCB_NONE ); + set_sc_with_vfx( SO_DIAMONDDUST , SC_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it. + status->set_sc( SO_CLOUD_KILL , SC_POISON , SCB_NONE ); + status->set_sc( SO_STRIKING , SC_STRIKING , SCB_WATK|SCB_CRI ); add_sc( SO_WARMER , SC_WARMER ); // At the moment, no icon on officials - status->set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE ); - status->set_sc( SO_ARRULLO , SC_DEEP_SLEEP , SI_DEEPSLEEP , SCB_NONE ); - status->set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SI_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - status->set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SI_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - status->set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SI_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); - status->set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SI_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); + status->set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SCB_NONE ); + status->set_sc( SO_ARRULLO , SC_DEEP_SLEEP , SCB_NONE ); + status->set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); + status->set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); + status->set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); + status->set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN ); add_sc( SO_ELEMENTAL_SHIELD , SC_SAFETYWALL ); /** * Genetic **/ - status->set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED ); - status->set_sc( GN_THORNS_TRAP , SC_THORNS_TRAP , SI_THORNTRAP , SCB_NONE ); - set_sc_with_vfx( GN_BLOOD_SUCKER , SC_BLOOD_SUCKER , SI_BLOODSUCKER , SCB_NONE ); - status->set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE ); - status->set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_FIRE_EXPANSION_SMOKE_POWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE ); - status->set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_FIRE_EXPANSION_TEAR_GAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE ); - status->set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT ); + status->set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SCB_SPEED ); + status->set_sc( GN_THORNS_TRAP , SC_THORNS_TRAP , SCB_NONE ); + set_sc_with_vfx( GN_BLOOD_SUCKER , SC_BLOOD_SUCKER , SCB_NONE ); + status->set_sc( GN_WALLOFTHORN , SC_STOP , SCB_NONE ); + status->set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_FIRE_EXPANSION_SMOKE_POWDER , SCB_NONE ); + status->set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_FIRE_EXPANSION_TEAR_GAS , SCB_NONE ); + status->set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SCB_INT ); /** * Summoner */ - status->set_sc(SU_HIDE, SC_SUHIDE, SI_SUHIDE, SCB_SPEED); + status->set_sc(SU_HIDE, SC_SUHIDE, SCB_SPEED); add_sc(SU_SCRATCH, SC_BLOODING); - status->set_sc(SU_STOOP, SC_SU_STOOP, SI_SU_STOOP, SCB_NONE); - status->set_sc(SU_FRESHSHRIMP, SC_FRESHSHRIMP, SI_FRESHSHRIMP, SCB_NONE); + status->set_sc(SU_STOOP, SC_SU_STOOP, SCB_NONE); + status->set_sc(SU_FRESHSHRIMP, SC_FRESHSHRIMP, SCB_NONE); add_sc(SU_SV_STEMSPEAR, SC_BLOODING); - status->set_sc(SU_CN_POWDERING, SC_CATNIPPOWDER, SI_CATNIPPOWDER, SCB_WATK | SCB_SPEED | SCB_REGEN); + status->set_sc(SU_CN_POWDERING, SC_CATNIPPOWDER, SCB_WATK | SCB_SPEED | SCB_REGEN); add_sc(SU_CN_METEOR, SC_CURSE); - set_sc_with_vfx(SU_SV_ROOTTWIST, SC_SV_ROOTTWIST, SI_SV_ROOTTWIST, SCB_NONE); + set_sc_with_vfx(SU_SV_ROOTTWIST, SC_SV_ROOTTWIST, SCB_NONE); add_sc(SU_SCAROFTAROU, SC_STUN ); - status->set_sc(SU_SCAROFTAROU, SC_BITESCAR, SI_BITESCAR, SCB_NONE); - status->set_sc(SU_ARCLOUSEDASH, SC_ARCLOUSEDASH, SI_ARCLOUSEDASH, SCB_AGI | SCB_SPEED); + status->set_sc(SU_SCAROFTAROU, SC_BITESCAR, SCB_NONE); + status->set_sc(SU_ARCLOUSEDASH, SC_ARCLOUSEDASH, SCB_AGI | SCB_SPEED); add_sc(SU_LUNATICCARROTBEAT, SC_STUN); - status->set_sc(SU_TUNAPARTY, SC_TUNAPARTY, SI_TUNAPARTY, SCB_NONE); - status->set_sc(SU_BUNCHOFSHRIMP, SC_SHRIMP, SI_SHRIMP, SCB_BATK | SCB_MATK); + status->set_sc(SU_TUNAPARTY, SC_TUNAPARTY, SCB_NONE); + status->set_sc(SU_BUNCHOFSHRIMP, SC_SHRIMP, SCB_BATK | SCB_MATK); // Elemental Spirit summoner's 'side' status changes. - status->set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE ); - status->set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL ); - status->set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE ); - status->set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL ); - status->set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_WATK|SCB_FLEE ); - status->set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE ); - status->set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL ); - status->set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE ); - status->set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP ); - status->set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL ); - status->set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED ); - status->set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK ); - status->set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK ); - status->set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK ); - status->set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK ); - status->set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK ); - status->set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK ); - status->set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_ASPD ); - status->set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_ASPD ); - status->set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_ASPD ); - status->set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_MAXHP ); - status->set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_MAXHP ); - status->set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_MAXHP ); - status->set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL ); - status->set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF ); - status->set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED ); + status->set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SCB_NONE ); + status->set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SCB_ALL ); + status->set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SCB_NONE ); + status->set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SCB_ALL ); + status->set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SCB_WATK|SCB_FLEE ); + status->set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE ); + status->set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SCB_ALL ); + status->set_sc( EL_ZEPHYR , SC_ZEPHYR , SCB_FLEE ); + status->set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP ); + status->set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SCB_ALL ); + status->set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED ); + status->set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SCB_WATK ); + status->set_sc( EL_HEATER , SC_HEATER_OPTION , SCB_WATK ); + status->set_sc( EL_TROPIC , SC_TROPIC_OPTION , SCB_WATK ); + status->set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SCB_MATK ); + status->set_sc( EL_COOLER , SC_COOLER_OPTION , SCB_MATK ); + status->set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SCB_MATK ); + status->set_sc( EL_GUST , SC_GUST_OPTION , SCB_ASPD ); + status->set_sc( EL_BLAST , SC_BLAST_OPTION , SCB_ASPD ); + status->set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SCB_ASPD ); + status->set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SCB_MAXHP ); + status->set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SCB_MAXHP ); + status->set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SCB_MAXHP ); + status->set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SCB_ALL ); + status->set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SCB_DEF ); + status->set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SCB_SPEED ); add_sc( KO_YAMIKUMO , SC_HIDING ); - set_sc_with_vfx( KO_JYUMONJIKIRI , SC_KO_JYUMONJIKIRI , SI_KO_JYUMONJIKIRI , SCB_NONE ); + set_sc_with_vfx( KO_JYUMONJIKIRI , SC_KO_JYUMONJIKIRI , SCB_NONE ); add_sc( KO_MAKIBISHI , SC_STUN ); - status->set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SI_MEIKYOUSISUI , SCB_NONE ); - status->set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SI_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + status->set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SCB_NONE ); + status->set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); add_sc( KO_JYUSATSU , SC_CURSE ); - status->set_sc( KO_ZENKAI , SC_ZENKAI , SI_ZENKAI , SCB_NONE ); - status->set_sc( KO_IZAYOI , SC_IZAYOI , SI_IZAYOI , SCB_MATK ); - status->set_sc( KG_KYOMU , SC_KYOMU , SI_KYOMU , SCB_NONE ); - status->set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SI_KAGEMUSYA , SCB_NONE ); - status->set_sc( KG_KAGEHUMI , SC_KG_KAGEHUMI , SI_KG_KAGEHUMI , SCB_NONE ); - status->set_sc( OB_ZANGETSU , SC_ZANGETSU , SI_ZANGETSU , SCB_MATK|SCB_BATK ); - set_sc_with_vfx( OB_AKAITSUKI, SC_AKAITSUKI , SI_AKAITSUKI , SCB_NONE ); - status->set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE ); + status->set_sc( KO_ZENKAI , SC_ZENKAI , SCB_NONE ); + status->set_sc( KO_IZAYOI , SC_IZAYOI , SCB_MATK ); + status->set_sc( KG_KYOMU , SC_KYOMU , SCB_NONE ); + status->set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SCB_NONE ); + status->set_sc( KG_KAGEHUMI , SC_KG_KAGEHUMI , SCB_NONE ); + status->set_sc( OB_ZANGETSU , SC_ZANGETSU , SCB_MATK|SCB_BATK ); + set_sc_with_vfx( OB_AKAITSUKI, SC_AKAITSUKI , SCB_NONE ); + status->set_sc( OB_OBOROGENSOU , SC_GENSOU , SCB_NONE ); - status->set_sc( ALL_FULL_THROTTLE , SC_FULL_THROTTLE , SI_FULL_THROTTLE , SCB_SPEED|SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); + status->set_sc( ALL_FULL_THROTTLE , SC_FULL_THROTTLE , SCB_SPEED|SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK ); add_sc( ALL_REVERSEORCISH , SC_ORCISH ); - status->set_sc( ALL_ANGEL_PROTECT , SC_ANGEL_PROTECT , SI_ANGEL_PROTECT , SCB_REGEN ); + status->set_sc( ALL_ANGEL_PROTECT , SC_ANGEL_PROTECT , SCB_REGEN ); add_sc( NPC_WIDEHEALTHFEAR , SC_FEAR ); add_sc( NPC_WIDEBODYBURNNING , SC_BURNING ); @@ -793,238 +799,24 @@ static void initChangeTables(void) add_sc( NPC_WIDE_DEEP_SLEEP , SC_DEEP_SLEEP ); add_sc( NPC_WIDESIREN , SC_SIREN ); - set_sc_with_vfx( GN_ILLUSIONDOPING , SC_ILLUSIONDOPING , SI_ILLUSIONDOPING , SCB_HIT ); + set_sc_with_vfx( GN_ILLUSIONDOPING , SC_ILLUSIONDOPING , SCB_HIT ); // Storing the target job rather than simply SC_SOULLINK simplifies code later on. - status->dbs->Skill2SCTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST, - status->dbs->Skill2SCTable[SL_MONK] = (sc_type)MAPID_MONK, - status->dbs->Skill2SCTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR, - status->dbs->Skill2SCTable[SL_SAGE] = (sc_type)MAPID_SAGE, - status->dbs->Skill2SCTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER, - status->dbs->Skill2SCTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE, - status->dbs->Skill2SCTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT, - status->dbs->Skill2SCTable[SL_WIZARD] = (sc_type)MAPID_WIZARD, - status->dbs->Skill2SCTable[SL_PRIEST] = (sc_type)MAPID_PRIEST, - status->dbs->Skill2SCTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER, - status->dbs->Skill2SCTable[SL_ROGUE] = (sc_type)MAPID_ROGUE, - status->dbs->Skill2SCTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN, - status->dbs->Skill2SCTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH, - status->dbs->Skill2SCTable[SL_HUNTER] = (sc_type)MAPID_HUNTER, - status->dbs->Skill2SCTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER, - - // Status that don't have a skill associated. - status->dbs->IconChangeTable[SC_WEIGHTOVER50] = SI_WEIGHTOVER50; - status->dbs->IconChangeTable[SC_WEIGHTOVER90] = SI_WEIGHTOVER90; - status->dbs->IconChangeTable[SC_ATTHASTE_POTION1] = SI_ATTHASTE_POTION1; - status->dbs->IconChangeTable[SC_ATTHASTE_POTION2] = SI_ATTHASTE_POTION2; - status->dbs->IconChangeTable[SC_ATTHASTE_POTION3] = SI_ATTHASTE_POTION3; - status->dbs->IconChangeTable[SC_MOVHASTE_POTION] = SI_MOVHASTE_POTION; - status->dbs->IconChangeTable[SC_ATTHASTE_INFINITY] = SI_ATTHASTE_INFINITY; - status->dbs->IconChangeTable[SC_MOVHASTE_HORSE] = SI_MOVHASTE_HORSE; - status->dbs->IconChangeTable[SC_MOVHASTE_INFINITY] = SI_MOVHASTE_INFINITY; - status->dbs->IconChangeTable[SC_MOVESLOW_POTION] = SI_MOVESLOW_POTION; - status->dbs->IconChangeTable[SC_CHASEWALK2] = SI_INCSTR; - status->dbs->IconChangeTable[SC_MIRACLE] = SI_SOULLINK; - status->dbs->IconChangeTable[SC_CLAIRVOYANCE] = SI_CLAIRVOYANCE; - status->dbs->IconChangeTable[SC_FOOD_STR] = SI_FOOD_STR; - status->dbs->IconChangeTable[SC_FOOD_AGI] = SI_FOOD_AGI; - status->dbs->IconChangeTable[SC_FOOD_VIT] = SI_FOOD_VIT; - status->dbs->IconChangeTable[SC_FOOD_INT] = SI_FOOD_INT; - status->dbs->IconChangeTable[SC_FOOD_DEX] = SI_FOOD_DEX; - status->dbs->IconChangeTable[SC_FOOD_LUK] = SI_FOOD_LUK; - status->dbs->IconChangeTable[SC_FOOD_BASICAVOIDANCE]= SI_FOOD_BASICAVOIDANCE; - status->dbs->IconChangeTable[SC_FOOD_BASICHIT] = SI_FOOD_BASICHIT; - status->dbs->IconChangeTable[SC_MANU_ATK] = SI_MANU_ATK; - status->dbs->IconChangeTable[SC_MANU_DEF] = SI_MANU_DEF; - status->dbs->IconChangeTable[SC_SPL_ATK] = SI_SPL_ATK; - status->dbs->IconChangeTable[SC_SPL_DEF] = SI_SPL_DEF; - status->dbs->IconChangeTable[SC_MANU_MATK] = SI_MANU_MATK; - status->dbs->IconChangeTable[SC_SPL_MATK] = SI_SPL_MATK; - status->dbs->IconChangeTable[SC_PLUSATTACKPOWER] = SI_PLUSATTACKPOWER; - status->dbs->IconChangeTable[SC_PLUSMAGICPOWER] = SI_PLUSMAGICPOWER; - status->dbs->IconChangeTable[SC_FOOD_CRITICALSUCCESSVALUE] = SI_FOOD_CRITICALSUCCESSVALUE; - status->dbs->IconChangeTable[SC_MORA_BUFF] = SI_MORA_BUFF; - status->dbs->IconChangeTable[SC_BUCHEDENOEL] = SI_BUCHEDENOEL; - status->dbs->IconChangeTable[SC_PHI_DEMON] = SI_PHI_DEMON; - - // Cash Items - status->dbs->IconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH; - status->dbs->IconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH; - status->dbs->IconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH; - status->dbs->IconChangeTable[SC_FOOD_DEX_CASH] = SI_FOOD_DEX_CASH; - status->dbs->IconChangeTable[SC_FOOD_INT_CASH] = SI_FOOD_INT_CASH; - status->dbs->IconChangeTable[SC_FOOD_LUK_CASH] = SI_FOOD_LUK_CASH; - status->dbs->IconChangeTable[SC_CASH_PLUSEXP] = SI_CASH_PLUSEXP; - status->dbs->IconChangeTable[SC_CASH_RECEIVEITEM] = SI_CASH_RECEIVEITEM; - status->dbs->IconChangeTable[SC_CASH_PLUSONLYJOBEXP] = SI_CASH_PLUSONLYJOBEXP; - status->dbs->IconChangeTable[SC_CASH_DEATHPENALTY] = SI_CASH_DEATHPENALTY; - status->dbs->IconChangeTable[SC_CASH_BOSS_ALARM] = SI_CASH_BOSS_ALARM; - status->dbs->IconChangeTable[SC_PROTECT_DEF] = SI_PROTECT_DEF; - status->dbs->IconChangeTable[SC_PROTECT_MDEF] = SI_PROTECT_MDEF; - status->dbs->IconChangeTable[SC_CRITICALPERCENT] = SI_CRITICALPERCENT; - status->dbs->IconChangeTable[SC_PLUSAVOIDVALUE] = SI_PLUSAVOIDVALUE; - status->dbs->IconChangeTable[SC_HEALPLUS] = SI_HEALPLUS; - status->dbs->IconChangeTable[SC_S_LIFEPOTION] = SI_S_LIFEPOTION; - status->dbs->IconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION; - status->dbs->IconChangeTable[SC_ATKER_BLOOD] = SI_ATKER_BLOOD; - status->dbs->IconChangeTable[SC_TARGET_BLOOD] = SI_TARGET_BLOOD; - status->dbs->IconChangeTable[SC_ACARAJE] = SI_ACARAJE; - status->dbs->IconChangeTable[SC_TARGET_ASPD] = SI_TARGET_ASPD; - status->dbs->IconChangeTable[SC_ATKER_ASPD] = SI_ATKER_ASPD; - status->dbs->IconChangeTable[SC_ATKER_MOVESPEED] = SI_ATKER_MOVESPEED; - status->dbs->IconChangeTable[SC_CUP_OF_BOZA] = SI_CUP_OF_BOZA; - status->dbs->IconChangeTable[SC_OVERLAPEXPUP] = SI_OVERLAPEXPUP; - status->dbs->IconChangeTable[SC_GM_BATTLE] = SI_GM_BATTLE; - status->dbs->IconChangeTable[SC_GM_BATTLE2] = SI_GM_BATTLE2; - status->dbs->IconChangeTable[SC_2011RWC] = SI_2011RWC; - status->dbs->IconChangeTable[SC_STR_SCROLL] = SI_STR_SCROLL; - status->dbs->IconChangeTable[SC_INT_SCROLL] = SI_INT_SCROLL; - status->dbs->IconChangeTable[SC_STEAMPACK] = SI_STEAMPACK; - status->dbs->IconChangeTable[SC_MAGIC_CANDY] = SI_MAGIC_CANDY; - status->dbs->IconChangeTable[SC_M_LIFEPOTION] = SI_M_LIFEPOTION; - status->dbs->IconChangeTable[SC_G_LIFEPOTION] = SI_G_LIFEPOTION; - status->dbs->IconChangeTable[SC_MYSTICPOWDER] = SI_MYSTICPOWDER; - - // Eden Crystal Synthesis - status->dbs->IconChangeTable[SC_QUEST_BUFF1] = SI_QUEST_BUFF1; - status->dbs->IconChangeTable[SC_QUEST_BUFF2] = SI_QUEST_BUFF2; - status->dbs->IconChangeTable[SC_QUEST_BUFF3] = SI_QUEST_BUFF3; - - // Geffen Magic Tournament - status->dbs->IconChangeTable[SC_GEFFEN_MAGIC1] = SI_GEFFEN_MAGIC1; - status->dbs->IconChangeTable[SC_GEFFEN_MAGIC2] = SI_GEFFEN_MAGIC2; - status->dbs->IconChangeTable[SC_GEFFEN_MAGIC3] = SI_GEFFEN_MAGIC3; - status->dbs->IconChangeTable[SC_FENRIR_CARD] = SI_FENRIR_CARD; - - // MVP Scrolls - status->dbs->IconChangeTable[SC_MVPCARD_TAOGUNKA] = SI_MVPCARD_TAOGUNKA; - status->dbs->IconChangeTable[SC_MVPCARD_MISTRESS] = SI_MVPCARD_MISTRESS; - status->dbs->IconChangeTable[SC_MVPCARD_ORCHERO] = SI_MVPCARD_ORCHERO; - status->dbs->IconChangeTable[SC_MVPCARD_ORCLORD] = SI_MVPCARD_ORCLORD; - - // Mercenary Bonus Effects - status->dbs->IconChangeTable[SC_MER_FLEE] = SI_MER_FLEE; - status->dbs->IconChangeTable[SC_MER_ATK] = SI_MER_ATK; - status->dbs->IconChangeTable[SC_MER_HP] = SI_MER_HP; - status->dbs->IconChangeTable[SC_MER_SP] = SI_MER_SP; - status->dbs->IconChangeTable[SC_MER_HIT] = SI_MER_HIT; - - // Warlock Spheres - status->dbs->IconChangeTable[SC_SUMMON1] = SI_SPHERE_1; - status->dbs->IconChangeTable[SC_SUMMON2] = SI_SPHERE_2; - status->dbs->IconChangeTable[SC_SUMMON3] = SI_SPHERE_3; - status->dbs->IconChangeTable[SC_SUMMON4] = SI_SPHERE_4; - status->dbs->IconChangeTable[SC_SUMMON5] = SI_SPHERE_5; - - // Warlock Preserved spells - status->dbs->IconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1; - status->dbs->IconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2; - status->dbs->IconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3; - status->dbs->IconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4; - status->dbs->IconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5; - status->dbs->IconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6; - status->dbs->IconChangeTable[SC_SPELLBOOK7] = SI_SPELLBOOK7; - - // Mechanic status icon - status->dbs->IconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER; - status->dbs->IconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER; - status->dbs->IconChangeTable[SC_OVERHEAT] = SI_OVERHEAT; - status->dbs->IconChangeTable[SC_OVERHEAT_LIMITPOINT] = SI_OVERHEAT_LIMITPOINT; - - // Guillotine Cross status icons - status->dbs->IconChangeTable[SC_HALLUCINATIONWALK_POSTDELAY] = SI_HALLUCINATIONWALK_POSTDELAY; - status->dbs->IconChangeTable[SC_TOXIN] = SI_TOXIN; - status->dbs->IconChangeTable[SC_PARALYSE] = SI_PARALYSE; - status->dbs->IconChangeTable[SC_VENOMBLEED] = SI_VENOMBLEED; - status->dbs->IconChangeTable[SC_MAGICMUSHROOM] = SI_MAGICMUSHROOM; - status->dbs->IconChangeTable[SC_DEATHHURT] = SI_DEATHHURT; - status->dbs->IconChangeTable[SC_PYREXIA] = SI_PYREXIA; - status->dbs->IconChangeTable[SC_OBLIVIONCURSE] = SI_OBLIVIONCURSE; - status->dbs->IconChangeTable[SC_LEECHESEND] = SI_LEECHESEND; - - // Royal Guard status icons - status->dbs->IconChangeTable[SC_SHIELDSPELL_DEF] = SI_SHIELDSPELL_DEF; - status->dbs->IconChangeTable[SC_SHIELDSPELL_MDEF] = SI_SHIELDSPELL_MDEF; - status->dbs->IconChangeTable[SC_SHIELDSPELL_REF] = SI_SHIELDSPELL_REF; - status->dbs->IconChangeTable[SC_BANDING_DEFENCE] = SI_BANDING_DEFENCE; - - // Sura status icon - status->dbs->IconChangeTable[SC_CURSEDCIRCLE_ATKER] = SI_CURSEDCIRCLE_ATKER; - - // Genetics Food items / Throwable items status icons - status->dbs->IconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK; - status->dbs->IconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD; - status->dbs->IconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ; - status->dbs->IconChangeTable[SC_SIROMA_ICE_TEA] = SI_SIROMA_ICE_TEA; - status->dbs->IconChangeTable[SC_DROCERA_HERB_STEAMED] = SI_DROCERA_HERB_STEAMED; - status->dbs->IconChangeTable[SC_PUTTI_TAILS_NOODLES] = SI_PUTTI_TAILS_NOODLES; - status->dbs->IconChangeTable[SC_BOOST500] |= SI_BOOST500; - status->dbs->IconChangeTable[SC_FULL_SWING_K] |= SI_FULL_SWING_K; - status->dbs->IconChangeTable[SC_MANA_PLUS] |= SI_MANA_PLUS; - status->dbs->IconChangeTable[SC_MUSTLE_M] |= SI_MUSTLE_M; - status->dbs->IconChangeTable[SC_LIFE_FORCE_F] |= SI_LIFE_FORCE_F; - status->dbs->IconChangeTable[SC_EXTRACT_WHITE_POTION_Z] |= SI_EXTRACT_WHITE_POTION_Z; - status->dbs->IconChangeTable[SC_VITATA_500] |= SI_VITATA_500; - status->dbs->IconChangeTable[SC_EXTRACT_SALAMINE_JUICE] |= SI_EXTRACT_SALAMINE_JUICE; - status->dbs->IconChangeTable[SC_STOMACHACHE] = SI_STOMACHACHE; - status->dbs->IconChangeTable[SC_MYSTERIOUS_POWDER] = SI_MYSTERIOUS_POWDER; - status->dbs->IconChangeTable[SC_MELON_BOMB] = SI_MELON_BOMB; - status->dbs->IconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB; - status->dbs->IconChangeTable[SC_BANANA_BOMB_SITDOWN_POSTDELAY] = SI_BANANA_BOMB_SITDOWN_POSTDELAY; - status->dbs->IconChangeTable[SC_PROMOTE_HEALTH_RESERCH] = SI_PROMOTE_HEALTH_RESERCH; - status->dbs->IconChangeTable[SC_ENERGY_DRINK_RESERCH] = SI_ENERGY_DRINK_RESERCH; - - // Elemental Spirit's 'side' status change icons. - status->dbs->IconChangeTable[SC_CIRCLE_OF_FIRE] = SI_CIRCLE_OF_FIRE; - status->dbs->IconChangeTable[SC_FIRE_CLOAK] = SI_FIRE_CLOAK; - status->dbs->IconChangeTable[SC_WATER_SCREEN] = SI_WATER_SCREEN; - status->dbs->IconChangeTable[SC_WATER_DROP] = SI_WATER_DROP; - status->dbs->IconChangeTable[SC_WIND_STEP] = SI_WIND_STEP; - status->dbs->IconChangeTable[SC_WIND_CURTAIN] = SI_WIND_CURTAIN; - status->dbs->IconChangeTable[SC_SOLID_SKIN] = SI_SOLID_SKIN; - status->dbs->IconChangeTable[SC_STONE_SHIELD] = SI_STONE_SHIELD; - status->dbs->IconChangeTable[SC_PYROTECHNIC] = SI_PYROTECHNIC; - status->dbs->IconChangeTable[SC_HEATER] = SI_HEATER; - status->dbs->IconChangeTable[SC_TROPIC] = SI_TROPIC; - status->dbs->IconChangeTable[SC_AQUAPLAY] = SI_AQUAPLAY; - status->dbs->IconChangeTable[SC_COOLER] = SI_COOLER; - status->dbs->IconChangeTable[SC_CHILLY_AIR] = SI_CHILLY_AIR; - status->dbs->IconChangeTable[SC_GUST] = SI_GUST; - status->dbs->IconChangeTable[SC_BLAST] = SI_BLAST; - status->dbs->IconChangeTable[SC_WILD_STORM] = SI_WILD_STORM; - status->dbs->IconChangeTable[SC_PETROLOGY] = SI_PETROLOGY; - status->dbs->IconChangeTable[SC_CURSED_SOIL] = SI_CURSED_SOIL; - status->dbs->IconChangeTable[SC_UPHEAVAL] = SI_UPHEAVAL; - status->dbs->IconChangeTable[SC_PUSH_CART] = SI_ON_PUSH_CART; - status->dbs->IconChangeTable[SC_REBOUND] = SI_REBOUND; - status->dbs->IconChangeTable[SC_ALL_RIDING] = SI_ALL_RIDING; - status->dbs->IconChangeTable[SC_MONSTER_TRANSFORM] = SI_MONSTER_TRANSFORM; - - // Costumes - status->dbs->IconChangeTable[SC_DRESS_UP] = SI_DRESS_UP; - status->dbs->IconChangeTable[SC_MOONSTAR] = SI_MOONSTAR; - status->dbs->IconChangeTable[SC_SUPER_STAR] = SI_SUPER_STAR; - status->dbs->IconChangeTable[SC_STRANGELIGHTS] = SI_STRANGELIGHTS; - status->dbs->IconChangeTable[SC_DECORATION_OF_MUSIC] = SI_DECORATION_OF_MUSIC; - status->dbs->IconChangeTable[SC_LJOSALFAR] = SI_LJOSALFAR; - status->dbs->IconChangeTable[SC_MERMAID_LONGING] = SI_MERMAID_LONGING; - status->dbs->IconChangeTable[SC_HAT_EFFECT] = SI_HAT_EFFECT; - status->dbs->IconChangeTable[SC_FLOWERSMOKE] = SI_FLOWERSMOKE; - status->dbs->IconChangeTable[SC_FSTONE] = SI_FSTONE; - status->dbs->IconChangeTable[SC_HAPPINESS_STAR] = SI_HAPPINESS_STAR; - status->dbs->IconChangeTable[SC_MAPLE_FALLS] = SI_MAPLE_FALLS; - status->dbs->IconChangeTable[SC_TIME_ACCESSORY] = SI_TIME_ACCESSORY; - status->dbs->IconChangeTable[SC_MAGICAL_FEATHER] = SI_MAGICAL_FEATHER; - status->dbs->IconChangeTable[SC_BLOSSOM_FLUTTERING] = SI_BLOSSOM_FLUTTERING; - - // Summoner - status->dbs->IconChangeTable[SC_SPRITEMABLE] = SI_SPRITEMABLE; - - // Clan System - status->dbs->IconChangeTable[SC_CLAN_INFO] = SI_CLAN_INFO; - - // RoDEX - status->dbs->IconChangeTable[SC_DAILYSENDMAILCNT] = SI_DAILYSENDMAILCNT; + status->dbs->Skill2SCTable[skill->get_index(SL_ALCHEMIST)] = (sc_type)MAPID_ALCHEMIST, + status->dbs->Skill2SCTable[skill->get_index(SL_MONK)] = (sc_type)MAPID_MONK, + status->dbs->Skill2SCTable[skill->get_index(SL_STAR)] = (sc_type)MAPID_STAR_GLADIATOR, + status->dbs->Skill2SCTable[skill->get_index(SL_SAGE)] = (sc_type)MAPID_SAGE, + status->dbs->Skill2SCTable[skill->get_index(SL_CRUSADER)] = (sc_type)MAPID_CRUSADER, + status->dbs->Skill2SCTable[skill->get_index(SL_SUPERNOVICE)] = (sc_type)MAPID_SUPER_NOVICE, + status->dbs->Skill2SCTable[skill->get_index(SL_KNIGHT)] = (sc_type)MAPID_KNIGHT, + status->dbs->Skill2SCTable[skill->get_index(SL_WIZARD)] = (sc_type)MAPID_WIZARD, + status->dbs->Skill2SCTable[skill->get_index(SL_PRIEST)] = (sc_type)MAPID_PRIEST, + status->dbs->Skill2SCTable[skill->get_index(SL_BARDDANCER)] = (sc_type)MAPID_BARDDANCER, + status->dbs->Skill2SCTable[skill->get_index(SL_ROGUE)] = (sc_type)MAPID_ROGUE, + status->dbs->Skill2SCTable[skill->get_index(SL_ASSASIN)] = (sc_type)MAPID_ASSASSIN, + status->dbs->Skill2SCTable[skill->get_index(SL_BLACKSMITH)] = (sc_type)MAPID_BLACKSMITH, + status->dbs->Skill2SCTable[skill->get_index(SL_HUNTER)] = (sc_type)MAPID_HUNTER, + status->dbs->Skill2SCTable[skill->get_index(SL_SOULLINKER)] = (sc_type)MAPID_SOUL_LINKER, // Other SC which are not necessarily associated to skills. status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION1] |= SCB_ASPD; @@ -1093,6 +885,13 @@ static void initChangeTables(void) status->dbs->ChangeFlagTable[SC_PHI_DEMON] |= SCB_ALL; status->dbs->ChangeFlagTable[SC_MAGIC_CANDY] |= SCB_MATK | SCB_ALL; status->dbs->ChangeFlagTable[SC_MYSTICPOWDER] |= SCB_FLEE | SCB_LUK; + status->dbs->ChangeFlagTable[SC_POPECOOKIE] |= SCB_BASE | SCB_BATK | SCB_MATK; + status->dbs->ChangeFlagTable[SC_VITALIZE_POTION] |= SCB_BATK | SCB_MATK; + status->dbs->ChangeFlagTable[SC_SKF_MATK] |= SCB_MATK; + status->dbs->ChangeFlagTable[SC_SKF_ATK] |= SCB_BATK; + status->dbs->ChangeFlagTable[SC_SKF_ASPD] |= SCB_ASPD; + status->dbs->ChangeFlagTable[SC_SKF_CAST] |= SCB_NONE; + status->dbs->ChangeFlagTable[SC_ALMIGHTY] |= SCB_BATK | SCB_MATK; // Cash Items status->dbs->ChangeFlagTable[SC_FOOD_STR_CASH] |= SCB_STR; @@ -1205,8 +1004,6 @@ static void initChangeTables(void) status->dbs->ChangeFlagTable[SC_MAGICAL_FEATHER] |= SCB_NONE; status->dbs->ChangeFlagTable[SC_BLOSSOM_FLUTTERING] |= SCB_NONE; - if( !battle_config.display_hallucination ) //Disable Hallucination. - status->dbs->IconChangeTable[SC_ILLUSION] = SI_BLANK; #undef add_sc #undef set_sc_with_vfx } @@ -1795,7 +1592,7 @@ static int status_check_skilluse(struct block_list *src, struct block_list *targ } if( skill_id ) { - if (src != NULL && (sd == NULL || sd->skillitem == 0)) { + if (src != NULL && (sd == NULL || sd->autocast.type != AUTOCAST_ITEM)) { // Items that cast skills using 'itemskill' will not be handled by map_zone_db. int i; @@ -1839,7 +1636,7 @@ static int status_check_skilluse(struct block_list *src, struct block_list *targ if (src != NULL && map->getcell(src->m, src, src->x, src->y, CELL_CHKLANDPROTECTOR) && !(st->mode&MD_BOSS) - && (src->type != BL_PC || sd->skillitem != skill_id)) + && (src->type != BL_PC || sd->autocast.type != AUTOCAST_ITEM)) return 0; break; default: @@ -1918,7 +1715,7 @@ static int status_check_skilluse(struct block_list *src, struct block_list *targ return 0; //Can't amp out of Wand of Hermode :/ [Skotlex] } - if (skill_id != 0 /* Do not block item-casted skills.*/ && (src->type != BL_PC || sd->skillitem != skill_id)) { + if (skill_id != 0 /* Do not block item-casted skills.*/ && (src->type != BL_PC || sd->autocast.type != AUTOCAST_ITEM)) { //Skills blocked through status changes... if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through sc->data[SC_SILENCE] || @@ -2446,7 +2243,7 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o sd->regen.sregen = &sd->sregen; sd->regen.ssregen = &sd->ssregen; sd->weight=0; - for(i=0;i<MAX_INVENTORY;i++){ + for (i = 0; i < sd->status.inventorySize; i++) { if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL) continue; sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount; @@ -2482,7 +2279,7 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o memset(ZEROED_BLOCK_POS(&(sd->left_weapon)), 0, ZEROED_BLOCK_SIZE(&(sd->left_weapon))); if (sd->special_state.intravision && !sd->sc.data[SC_CLAIRVOYANCE]) //Clear intravision as long as nothing else is using it - clif->sc_end(&sd->bl,sd->bl.id,SELF,SI_CLAIRVOYANCE); + clif->sc_end(&sd->bl, sd->bl.id, SELF, status->get_sc_icon(SC_CLAIRVOYANCE)); memset(&sd->special_state,0,sizeof(sd->special_state)); @@ -2577,18 +2374,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 +2420,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 @@ -2783,12 +2580,16 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o status->calc_pc_additional(sd, opt); - if( sd->pd ) { // Pet Bonus + if (sd->pd != NULL) { // Pet bonus. struct pet_data *pd = sd->pd; - if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly ) - script->run(pd->petDB->equip_script,0,sd->bl.id,0); - if( pd && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus ) - pc->bonus(sd,pd->bonus->type, pd->bonus->val); + + if (pd->petDB != NULL && pd->petDB->equip_script != NULL) + script->run(pd->petDB->equip_script, 0, sd->bl.id, 0); + + if (pd->pet.intimate > PET_INTIMACY_NONE && pd->state.skillbonus == 1 && pd->bonus != NULL + && (battle_config.pet_equip_required == 0 || pd->pet.equip > 0)) { + pc->bonus(sd, pd->bonus->type, pd->bonus->val); + } } //param_bonus now holds card bonuses. @@ -3249,6 +3050,18 @@ static int status_calc_pc_(struct map_session_data *sd, enum e_status_calc_opt o sd->subele[ELE_EARTH] += i; sd->subele[ELE_FIRE] -= i; } + if (sc->data[SC_POPECOOKIE] != NULL) { + i = sc->data[SC_POPECOOKIE]->val3; + sd->subele[ELE_WATER] += i; + sd->subele[ELE_EARTH] += i; + sd->subele[ELE_FIRE] += i; + sd->subele[ELE_WIND] += i; + sd->subele[ELE_POISON] += i; + sd->subele[ELE_HOLY] += i; + sd->subele[ELE_DARK] += i; + sd->subele[ELE_GHOST] += i; + sd->subele[ELE_UNDEAD] += i; + } if (sc->data[SC_MTF_MLEATKED]) sd->subele[ELE_NEUTRAL] += sc->data[SC_MTF_MLEATKED]->val1; if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3) @@ -3853,7 +3666,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 +4261,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 +4318,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 +4418,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 +4815,21 @@ 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); + if (sc->data[SC_POPECOOKIE] != NULL) + batk += batk * sc->data[SC_POPECOOKIE]->val1 / 100; + if (sc->data[SC_VITALIZE_POTION] != NULL) + batk += batk * sc->data[SC_VITALIZE_POTION]->val1 / 100; + return cap_value(batk, battle_config.batk_min, battle_config.batk_max); } #ifndef RENEWAL if(sc->data[SC_PLUSATTACKPOWER]) @@ -5083,18 +4908,22 @@ static unsigned short status_calc_batk(struct block_list *bl, struct status_chan batk += batk * sc->data[SC_2011RWC]->val2 / 100; if (sc->data[SC_STEAMPACK]) batk += sc->data[SC_STEAMPACK]->val1; + if (sc->data[SC_SKF_ATK] != NULL) + batk += sc->data[SC_SKF_ATK]->val1; + if (sc->data[SC_ALMIGHTY] != NULL) + batk += sc->data[SC_ALMIGHTY]->val1; 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 +4931,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 +5009,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 +5037,26 @@ 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); + if (sc->data[SC_POPECOOKIE] != NULL) + matk += matk * sc->data[SC_POPECOOKIE]->val2 / 100; + if (sc->data[SC_VITALIZE_POTION] != NULL) + matk += matk * sc->data[SC_VITALIZE_POTION]->val2 / 100; + return cap_value(matk, battle_config.matk_min, battle_config.matk_max); } #ifndef RENEWAL @@ -5280,18 +5113,22 @@ static unsigned short status_calc_matk(struct block_list *bl, struct status_chan matk += matk * sc->data[SC_2011RWC]->val2 / 100; if (sc->data[SC_MAGIC_CANDY]) matk += sc->data[SC_MAGIC_CANDY]->val1; + if (sc->data[SC_SKF_MATK] != NULL) + matk += sc->data[SC_SKF_MATK]->val1; + if (sc->data[SC_ALMIGHTY] != NULL) + matk += sc->data[SC_ALMIGHTY]->val2; - 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 +5159,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 +5214,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 +5311,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 +5333,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) @@ -6104,6 +5941,8 @@ static short status_calc_aspd(struct block_list *bl, struct status_change *sc, s bonus += sc->data[SC_BATTLESCROLL]->val1; if (sc->data[SC_STEAMPACK]) bonus += sc->data[SC_STEAMPACK]->val2; + if (sc->data[SC_SKF_ASPD] != NULL) + bonus += sc->data[SC_SKF_ASPD]->val1; } return (bonus + pots); @@ -6271,6 +6110,8 @@ static short status_calc_aspd_rate(struct block_list *bl, struct status_change * aspd_rate += sc->data[SC_BATTLESCROLL]->val1 * 10; if (sc->data[SC_STEAMPACK]) aspd_rate += sc->data[SC_STEAMPACK]->val2 * 10; + if (sc->data[SC_SKF_ASPD] != NULL) + aspd_rate -= sc->data[SC_SKF_ASPD]->val1 * 10; return (short)cap_value(aspd_rate,0,SHRT_MAX); } @@ -7426,6 +7267,9 @@ static int status_get_sc_def(struct block_list *src, struct block_list *bl, enum case SC_NETHERWORLD: tick = max(tick, 4000); break; + case SC_SIREN: + tick = max(tick, 10000); // Minimum duration 10s + break; default: //Skills need to trigger even if the duration is reduced below 1ms tick = max(tick, 1); @@ -7503,7 +7347,7 @@ static void status_display_remove(struct map_session_data *sd, enum sc_type type } /** - * Starts a status change. + * Starts a status change with a set remaining time. * * @param src Status change source bl. * @param bl Status change target bl. @@ -7513,13 +7357,14 @@ static void status_display_remove(struct map_session_data *sd, enum sc_type type * @param val2 Additional value (meaning depends on type). * @param val3 Additional value (meaning depends on type). * @param val4 Additional value (meaning depends on type). - * @param tick Base duration (milliseconds). + * @param tick Remaining duration (miliseconds). (if flag doesn't contain SCFLAG_LOADED, it will become the final total_tick) + * @param total_tick Base duration (milliseconds). * @param flag Special flags (@see enum scstart_flag). * * @retval 0 if no status change happened. * @retval 1 if the status change was successfully applied. */ -static int status_change_start(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int flag) +static int status_change_start_sub(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int total_tick, int flag) { struct map_session_data *sd = NULL; struct status_change* sc; @@ -7533,7 +7378,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en st = status->get_status_data(bl); if (type <= SC_NONE || type >= SC_MAX) { - ShowError("status_change_start: invalid status change (%d)!\n", type); + ShowError("status_change_start_sub: invalid status change (%d)!\n", type); return 0; } @@ -7558,10 +7403,10 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en sd = BL_CAST(BL_PC, bl); - //Adjust tick according to status resistances + //Adjust total_tick according to status resistances if( !(flag&(SCFLAG_NOAVOID|SCFLAG_LOADED)) ) { - tick = status->get_sc_def(src, bl, type, rate, tick, flag); - if( !tick ) return 0; + total_tick = status->get_sc_def(src, bl, type, rate, total_tick, flag); + if( !total_tick ) return 0; } undead_flag = battle->check_undead(st->race, st->def_ele); @@ -7594,6 +7439,9 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en //Undead are immune to Freeze/Stone if (undead_flag && !(flag&SCFLAG_NOAVOID)) return 0; + // SC_LEXAETERNA should be removed when applying SC_STONE or SC_FREEZE + if (sc->data[SC_LEXAETERNA] != NULL) + status_change_end(bl, SC_LEXAETERNA, INVALID_TIMER); FALLTHROUGH case SC_SLEEP: case SC_STUN: @@ -7712,7 +7560,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en } if (!opt_flag) return 0; } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC + if (total_tick == 1) return 1; //Minimal duration: Only strip without causing the SC break; case SC_NOEQUIPSHIELD: if (val2 == 1) { @@ -7728,7 +7576,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); } } - if (tick == 1) + if (total_tick == 1) return 1; //Minimal duration: Only strip without causing the SC break; case SC_NOEQUIPARMOR: @@ -7741,7 +7589,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en return 0; pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC + if (total_tick == 1) return 1; //Minimal duration: Only strip without causing the SC break; case SC_NOEQUIPHELM: if (sd && !(flag&SCFLAG_LOADED)) { @@ -7753,7 +7601,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en return 0; pc->unequipitem(sd, i, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE); } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC + if (total_tick == 1) return 1; //Minimal duration: Only strip without causing the SC break; case SC_MER_FLEE: case SC_MER_ATK: @@ -7807,7 +7655,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en if( i < 0 ) return 0; } - if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC + if (total_tick == 1) return 1; //Minimal duration: Only strip without causing the SC break; case SC_TOXIN: case SC_PARALYSE: @@ -7961,6 +7809,9 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en calc_flag = status->dbs->ChangeFlagTable[type]; if(!(flag&SCFLAG_LOADED)) { // Do not parse val settings when loading SCs switch(type) { + case SC_AUTOTRADE: + case SC_KSPROTECTED: + break; // Prevent calling status_change_start_unknown_sc(). case SC_ADORAMUS: sc_start(src,bl,SC_BLIND,100,val1,skill->get_time(status->sc2skill(type),val1)); // Fall through to SC_INC_AGI @@ -7977,33 +7828,33 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en int i; for( i = 0; i < MAX_PC_DEVOTION; i++ ) { if (sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) != NULL) - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, val3, val4, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, val3, val4, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } else if (bl->type == BL_MER) { struct mercenary_data *mc = BL_UCAST(BL_MER, bl); if (mc->devotion_flag && (tsd = mc->master) != NULL) { - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, val3, val4, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, val3, val4, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } } //val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk) if (val4) - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_AUTOBERSERK: if (st->hp < st->max_hp>>2 && (!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val2==0)) sc_start4(src,bl,SC_PROVOKE,100,10,1,0,0,60000); - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_CRUCIS: val2 = 10 + 4*val1; //Def reduction - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; clif->emotion(bl,E_SWT); break; case SC_MAXIMIZEPOWER: - tick_time = val2 = tick>0?tick:60000; - tick = INFINITE_DURATION; // duration sent to the client should be infinite + tick_time = val2 = total_tick>0?total_tick:60000; + total_tick = INFINITE_DURATION; // duration sent to the client should be infinite break; case SC_EDP: // [Celest] //Chance to Poison enemies. @@ -8014,7 +7865,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en #endif val3 = 50 * (val1 + 1); //Damage increase (+50 +50*lv%) if( sd )//[Ind] - iROwiki says each level increases its duration by 3 seconds - tick += pc->checkskill(sd,GC_RESEARCHNEWPOISON)*3000; + total_tick += pc->checkskill(sd,GC_RESEARCHNEWPOISON)*3000; break; case SC_POISONREACT: val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex] @@ -8044,7 +7895,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_SACRIFICE: val2 = 5; //Lasts 5 hits - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_ENCHANTPOISON: val2= 250+50*val1; //Poisoning Chance (2.5+0.5%) in 1/10000 rate @@ -8081,12 +7932,12 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en int i; for( i = 0; i < MAX_PC_DEVOTION; i++ ) { if (sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) != NULL) - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } else if (bl->type == BL_MER) { struct mercenary_data *mc = BL_UCAST(BL_MER, bl); if (mc->devotion_flag && (tsd = mc->master) != NULL) { - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } } @@ -8165,9 +8016,9 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en //val3 : Brings the skill_lv (merged into val1 here) //val4 : Partner if (val1 == CG_MOONLIT) - clif->status_change(bl,SI_MOON,1,tick,0, 0, 0); + clif->status_change(bl, status->get_sc_icon(SC_MOON), status->get_sc_relevant_bl_types(SC_MOON), 1, total_tick, 0, 0, 0); val1|= (val3<<16); - val3 = tick/1000; //Tick duration + val3 = total_tick/1000; //Tick duration tick_time = 1000; // [GodLesZ] tick time break; case SC_LONGING: @@ -8204,8 +8055,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en // mmocharstatus.manner, each negative point results in 1 minute with this status activated // This is done this way because the message that the client displays is hardcoded, and only // shows how many minutes are remaining. [Panikon] - tick = 60000; - val1 = battle_config.manner_system; //Mute filters. + total_tick = 60000; if (sd) { clif->changestatus(sd,SP_MANNER,sd->status.manner); @@ -8214,11 +8064,11 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_STONE: - val3 = tick/1000; //Petrified HP-damage iterations. + val3 = total_tick/1000; //Petrified HP-damage iterations. if(val3 < 1) val3 = 1; - tick = val4; //Petrifying time. + total_tick = val4; //Petrifying time. if(val4 > 500) // not with WL_SIENNAEXECRATE - tick = max(tick, 1000); //Min time + total_tick = max(total_tick, 1000); //Min time calc_flag = 0; //Actual status changes take effect on petrified state. break; @@ -8237,7 +8087,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en } // fall through case SC_POISON: - val3 = tick/1000; //Damage iterations + val3 = total_tick/1000; //Damage iterations if(val3 < 1) val3 = 1; tick_time = 1000; // [GodLesZ] tick time //val4: HP damage @@ -8251,7 +8101,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en clif->emotion(bl,E_WHAT); break; case SC_BLOODING: - val4 = tick/10000; + val4 = total_tick/10000; if (!val4) val4 = 1; tick_time = 10000; // [GodLesZ] tick time break; @@ -8264,7 +8114,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en // val2 = seconds between heals // val4 = total of heals if (val2 < 1) val2 = 1; - if ((val4 = tick / (val2 * 1000)) < 1) + if ((val4 = total_tick / (val2 * 1000)) < 1) val4 = 1; tick_time = val2 * 1000; // [GodLesZ] tick time break; @@ -8277,19 +8127,19 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en return 0; // No need to start SC } val1 = boss_md->bl.id; - if( (val4 = tick/1000) < 1 ) + if( (val4 = total_tick/1000) < 1 ) val4 = 1; tick_time = 1000; // [GodLesZ] tick time } break; case SC_HIDING: - val2 = tick/1000; + val2 = total_tick/1000; tick_time = 1000; // [GodLesZ] tick time val3 = 0; // unused, previously speed adjustment val4 = val1+3; //Seconds before SP substraction happen. break; case SC_CHASEWALK: - val2 = tick>0?tick:10000; //Interval at which SP is drained. + val2 = total_tick>0?total_tick:10000; //Interval at which SP is drained. val3 = 35 - 5 * val1; //Speed adjustment. if (sc->data[SC_SOULLINK] && sc->data[SC_SOULLINK]->val2 == SL_ROGUE) val3 -= 40; @@ -8299,8 +8149,8 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_CLOAKING: if (!sd) //Monsters should be able to walk with no penalties. [Skotlex] val1 = 10; - tick_time = val2 = tick>0?tick:60000; //SP consumption rate. - tick = INFINITE_DURATION; // duration sent to the client should be infinite + tick_time = val2 = total_tick>0?total_tick:60000; //SP consumption rate. + total_tick = INFINITE_DURATION; // duration sent to the client should be infinite val3 = 0; // unused, previously walk speed adjustment //val4&1 signals the presence of a wall. //val4&2 makes cloak not end on normal attacks [Skotlex] @@ -8314,7 +8164,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_RUWACH: case SC_WZ_SIGHTBLASTER: val3 = skill->get_splash(val2, val1); //Val2 should bring the skill-id. - val2 = tick/20; + val2 = total_tick/20; tick_time = 20; // [GodLesZ] tick time break; @@ -8332,7 +8182,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_DODGE_READY: case SC_PUSH_CART: case SC_DAILYSENDMAILCNT: - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_AUTOGUARD: @@ -8348,12 +8198,12 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en if( sd ) { for( i = 0; i < MAX_PC_DEVOTION; i++ ) { if (sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) != NULL) - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } else if (bl->type == BL_MER) { struct mercenary_data *mc = BL_UCAST(BL_MER, bl); if (mc->devotion_flag && (tsd = mc->master) != NULL) { - status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCFLAG_NOAVOID|SCFLAG_NOICON); + status->change_start(bl, &tsd->bl, type, 10000, val1, val2, 0, 0, total_tick, SCFLAG_NOAVOID|SCFLAG_NOICON); } } } @@ -8372,7 +8222,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en for (i = 0; i < MAX_PC_DEVOTION; i++) { //See if there are devoted characters, and pass the status to them. [Skotlex] if (sd->devotion[i] && (tsd = map->id2sd(sd->devotion[i])) != NULL) - status->change_start(bl, &tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,SCFLAG_NOAVOID); + status->change_start(bl, &tsd->bl,type,10000,val1,5+val1*5,val3,val4,total_tick,SCFLAG_NOAVOID); } } } @@ -8385,8 +8235,8 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en } val2 = 12; //SP cost val4 = 10000; //Decrease at 10secs intervals. - val3 = tick/val4; - tick = INFINITE_DURATION; // duration sent to the client should be infinite + val3 = total_tick/val4; + total_tick = INFINITE_DURATION; // duration sent to the client should be infinite tick_time = val4; // [GodLesZ] tick time break; case SC_PARRYING: @@ -8404,21 +8254,21 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_BERSERK: if( val3 == SC__BLOODYLUST ) - sc_start(src,bl,(sc_type)val3,100,val1,tick); + sc_start(src,bl,(sc_type)val3,100,val1,total_tick); if (!val3 && (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4)) - sc_start4(src, bl, SC_ENDURE, 100,10,0,0,2, tick); + sc_start4(src, bl, SC_ENDURE, 100,10,0,0,2, total_tick); //HP healing is performing after the calc_status call. //Val2 holds HP penalty if (!val4) val4 = skill->get_time2(status->sc2skill(type),val1); if (!val4) val4 = 10000; //Val4 holds damage interval - val3 = tick/val4; //val3 holds skill duration + val3 = total_tick/val4; //val3 holds skill duration tick_time = val4; // [GodLesZ] tick time break; case SC_GOSPEL: if(val4 == BCT_SELF) { // self effect - val2 = tick/10000; + val2 = total_tick/10000; tick_time = 10000; // [GodLesZ] tick time status->change_clear_buffs(bl,3); //Remove buffs/debuffs } @@ -8471,23 +8321,23 @@ 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: val2 = 15*val1; //Reflect chance val3 = 3; //Reflections - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_MEMORIZE: val2 = 5; //Memorized casts. - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_GRAVITATION: @@ -8541,11 +8391,11 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en struct status_change_entry *sce2 = sc2 ? sc2->data[SC_RG_CCONFINE_M] : NULL; if (src2 && sc2) { if (!sce2) //Start lock on caster. - sc_start4(src,src2,SC_RG_CCONFINE_M,100,val1,1,0,0,tick+1000); + sc_start4(src,src2,SC_RG_CCONFINE_M,100,val1,1,0,0,total_tick+1000); else { //Increase count of locked enemies and refresh time. (sce2->val2)++; timer->delete(sce2->timer, status->change_timer); - sce2->timer = timer->add(timer->gettick()+tick+1000, status->change_timer, src2->id, SC_RG_CCONFINE_M); + sce2->timer = timer->add(timer->gettick()+total_tick+1000, status->change_timer, src2->id, SC_RG_CCONFINE_M); } } else //Status failed. return 0; @@ -8581,13 +8431,13 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en //val4: TK: Combo time struct unit_data *ud = unit->bl2ud(bl); if( ud && (!val3 || val3 == 2) ) { - tick += 300 * battle_config.combo_delay_rate/100; - ud->attackabletime = timer->gettick()+tick; + total_tick += 300 * battle_config.combo_delay_rate/100; + ud->attackabletime = timer->gettick()+total_tick; if( !val3 ) - unit->set_walkdelay(bl, timer->gettick(), tick, 1); + unit->set_walkdelay(bl, timer->gettick(), total_tick, 1); } val3 = 0; - val4 = tick; + val4 = total_tick; break; } case SC_EARTHSCROLL: @@ -8601,7 +8451,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = (int)(currenttick&0x00000000ffffffffLL); val4 = (int)((currenttick&0xffffffff00000000LL)>>32); } - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_KAAHI: val2 = 200*val1; //HP heal @@ -8616,7 +8466,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_TRICKDEAD: if (vd) vd->dead_sit = 1; - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_CONCENTRATION: val2 = 2 + val1; @@ -8634,7 +8484,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en //val2 holds if it was casted on self, or is bonus received from others val3 = 5*val1; //Power increase if(sd && pc->checkskill(sd,BS_HILTBINDING)>0) - tick += tick / 10; + total_tick += total_tick / 10; break; case SC_ADRENALINE2: case SC_ADRENALINE: @@ -8642,13 +8492,13 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en FALLTHROUGH case SC_WEAPONPERFECT: if(sd && pc->checkskill(sd,BS_HILTBINDING)>0) - tick += tick / 10; + total_tick += total_tick / 10; break; case SC_LKCONCENTRATION: val2 = 5*val1; //Batk/Watk Increase val3 = 10*val1; //Hit Increase val4 = 5*val1; //Def reduction - sc_start(src, bl, SC_ENDURE, 100, 1, tick); //Endure effect + sc_start(src, bl, SC_ENDURE, 100, 1, total_tick); //Endure effect break; case SC_ANGELUS: val2 = 5*val1; //def increase @@ -8718,7 +8568,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = 12*val1; //mdef2 reduction. break; case SC_SKA: - val2 = tick/1000; + val2 = total_tick/1000; val3 = rnd()%100; //Def changes randomly every second... tick_time = 1000; // [GodLesZ] tick time break; @@ -8731,7 +8581,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en // When renewing status' information // val3 Return map_index // val4 return coordinates - tick = val1>0?1000:250; + total_tick = val1>0?1000:250; if (sd) { if (sd->mapindex != val2) @@ -8766,11 +8616,11 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_SWOO: if(st->mode&MD_BOSS) - tick /= 5; //TODO: Reduce skill's duration. But for how long? + total_tick /= 5; //TODO: Reduce skill's duration. But for how long? break; case SC_SPIDERWEB: if( bl->type == BL_PC ) - tick /= 2; + total_tick /= 2; break; case SC_ARMOR: //NPC_DEFENDER: @@ -8835,7 +8685,14 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_KAIZEL: val2 = 10*val1; //% of life to be revived with break; - // case SC_ARMORPROPERTY: + case SC_ARMORPROPERTY: + { + int ele = (val1 > 0 ? SC_RESIST_PROPERTY_WATER : + (val2 > 0 ? SC_RESIST_PROPERTY_GROUND : + (val3 > 0 ? SC_RESIST_PROPERTY_FIRE : + (val4 > 0 ? SC_RESIST_PROPERTY_WIND : SI_BLANK)))); + clif->status_change(bl, status->get_sc_icon(ele), status->get_sc_relevant_bl_types(ele), 1, total_tick, 0, 0, 0); + break; // case SC_ARMOR_RESIST: // Mod your resistance against elements: // val1 = water | val2 = earth | val3 = fire | val4 = wind @@ -8844,7 +8701,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en //Place here SCs that have no SCB_* data, no skill associated, no ICON //associated, and yet are not wrong/unknown. [Skotlex] //break; - + } case SC_MER_FLEE: case SC_MER_ATK: case SC_MER_HIT: @@ -8876,11 +8733,11 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en **/ case SC_FEAR: val2 = 2; - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_BURNING: - val4 = tick / 3000; // Total Ticks to Burn!! + val4 = total_tick / 3000; // Total Ticks to Burn!! tick_time = 3000; // [GodLesZ] tick time break; /** @@ -8894,14 +8751,14 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val1 = sd->status.job_level * pc->checkskill(sd, RK_RUNEMASTERY) / 4; //DEF/MDEF Increase break; case SC_ABUNDANCE: - val4 = tick / 10000; + val4 = total_tick / 10000; tick_time = 10000; // [GodLesZ] tick time break; /** * Arch Bishop **/ case SC_RENOVATIO: - val4 = tick / 5000; + val4 = total_tick / 5000; tick_time = 5000; break; case SC_SECRAMENT: @@ -8912,28 +8769,28 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_WEAPONBLOCKING: val2 = 10 + 2 * val1; // Chance - val4 = tick / 5000; + val4 = total_tick / 5000; tick_time = 5000; // [GodLesZ] tick time break; case SC_TOXIN: - val4 = tick / 10000; + val4 = total_tick / 10000; tick_time = 10000; // [GodLesZ] tick time break; case SC_MAGICMUSHROOM: - val4 = tick / 4000; + val4 = total_tick / 4000; tick_time = 4000; // [GodLesZ] tick time break; case SC_PYREXIA: status->change_start(src, bl,SC_BLIND,10000,val1,0,0,0,30000,SCFLAG_NOAVOID|SCFLAG_FIXEDTICK|SCFLAG_FIXEDRATE); // Blind status that last for 30 seconds - val4 = tick / 3000; + val4 = total_tick / 3000; tick_time = 3000; // [GodLesZ] tick time break; case SC_LEECHESEND: - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_OBLIVIONCURSE: - val4 = tick / 3000; + val4 = total_tick / 3000; tick_time = 3000; // [GodLesZ] tick time break; case SC_CLOAKINGEXCEED: @@ -8972,7 +8829,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_SUMMON3: case SC_SUMMON4: case SC_SUMMON5: - val4 = tick / 1000; + val4 = total_tick / 1000; if( val4 < 1 ) val4 = 1; tick_time = 1000; // [GodLesZ] tick time @@ -8987,19 +8844,19 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en } break; case SC_STEALTHFIELD_MASTER: - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 2000 + (1000 * val1); break; case SC_ELECTRICSHOCKER: case SC_COLD: case SC_MEIKYOUSISUI: - val4 = tick / 1000; + val4 = total_tick / 1000; if( val4 < 1 ) val4 = 1; tick_time = 1000; // [GodLesZ] tick time break; case SC_CAMOUFLAGE: - val4 = tick/1000; + val4 = total_tick/1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_WUGDASH: @@ -9010,17 +8867,17 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = (int)(currenttick&0x00000000ffffffffLL); val4 = (int)((currenttick&0xffffffff00000000LL)>>32); } - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC__REPRODUCE: - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; break; case SC__SHADOWFORM: { struct map_session_data * s_sd = map->id2sd(val2); if( s_sd ) s_sd->shadowform_id = bl->id; - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time } break; @@ -9031,7 +8888,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC__INVISIBILITY: val2 = 50 - 10 * val1; // ASPD val3 = 200 * val1; // CRITICAL - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC__ENERVATION: @@ -9058,7 +8915,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en if (sd->status.pet_id > 0) pet->menu(sd, 3); if (homun_alive(sd->hd)) - homun->vaporize(sd,HOM_ST_REST); + homun->vaporize(sd, HOM_ST_REST, true); if (sd->md) mercenary->delete(sd->md,3); } @@ -9073,8 +8930,8 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC__WEAKNESS: val2 = 10 * val1; // bypasses coating protection and MADO - sc_start(src, bl,SC_NOEQUIPWEAPON,100,val1,tick); - sc_start(src, bl,SC_NOEQUIPSHIELD,100,val1,tick); + sc_start(src, bl,SC_NOEQUIPWEAPON,100,val1,total_tick); + sc_start(src, bl,SC_NOEQUIPSHIELD,100,val1,total_tick); break; case SC_GN_CARTBOOST: if( val1 < 3 ) @@ -9094,7 +8951,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_STRIKING: val1 = 6 - val1;//spcost = 6 - level (lvl1:5 ... lvl 5: 1) - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_BLOOD_SUCKER: @@ -9103,7 +8960,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = 1; if(src2) val3 = 200 + 100 * val1 + status_get_int(src2); - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time } break; @@ -9124,22 +8981,22 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val2 = 5 + 5 * val1; break; case SC_SIREN: - val4 = tick / 2000; + val4 = total_tick / 2000; tick_time = 2000; // [GodLesZ] tick time break; case SC_DEEP_SLEEP: - val4 = tick / 2000; + val4 = total_tick / 2000; tick_time = 2000; // [GodLesZ] tick time break; case SC_SIRCLEOFNATURE: val2 = 40 * val1;//HP recovery val3 = 4 * val1;//SP drain - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_SONG_OF_MANA: val3 = 10 + 5 * val2; - val4 = tick/5000; + val4 = total_tick/5000; tick_time = 5000; // [GodLesZ] tick time break; case SC_SATURDAY_NIGHT_FEVER: @@ -9147,7 +9004,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en if ( val2 < 1000 ) val2 = 1000;//Added to prevent val3 from dividing by 0 when using level 6 or higher through commands. [Rytech] val3 = tick/val2;*/ - val3 = tick / 3000; + val3 = total_tick / 3000; tick_time = 3000;// [GodLesZ] tick time break; case SC_GLOOMYDAY: @@ -9183,7 +9040,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_MELODYOFSINK: val3 = val1 * (2 + val2);//INT Reduction. Formula Includes Caster And 2nd Performer. - val4 = tick/1000; + val4 = total_tick/1000; tick_time = 1000; break; case SC_BEYOND_OF_WARCRY: @@ -9201,13 +9058,13 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_LG_REFLECTDAMAGE: val2 = 15 + 5 * val1; val3 = 25 + 5 * val1; //Number of Reflects - val4 = tick/1000; + val4 = total_tick/1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_FORCEOFVANGUARD: val2 = 8 + 12 * val1; // Chance val3 = 5 + 2 * val1; // Max rage counters - tick = INFINITE_DURATION; //endless duration in the client + total_tick = INFINITE_DURATION; //endless duration in the client break; case SC_EXEEDBREAK: if( sd ){ @@ -9230,7 +9087,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en tick_time = 5000; // [GodLesZ] tick time break; case SC_MAGNETICFIELD: - val3 = tick / 1000; + val3 = total_tick / 1000; tick_time = 1000; // [GodLesZ] tick time break; case SC_INSPIRATION: @@ -9238,7 +9095,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val2 = 40 * val1 + 3 * sd->status.job_level;// ATK bonus val3 = sd->status.base_level / 10 + sd->status.job_level / 5;// All stat bonus } - val4 = tick / 5000; + val4 = total_tick / 5000; tick_time = 5000; // [GodLesZ] tick time status->change_clear_buffs(bl,3); //Remove buffs/debuffs break; @@ -9249,7 +9106,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1; break; case SC_RAISINGDRAGON: - val3 = tick / 5000; + val3 = total_tick / 5000; tick_time = 5000; // [GodLesZ] tick time break; case SC_GENTLETOUCH_CHANGE: @@ -9359,7 +9216,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_STONE_SHIELD: val2 += 5; val3 += 1000; - val4 = tick; + val4 = total_tick; tick_time = val3; break; case SC_WATER_BARRIER: @@ -9382,18 +9239,18 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_STOMACHACHE: val3 = 8; // SP consume. - val4 = tick / 10000; + val4 = total_tick / 10000; tick_time = 10000; // [GodLesZ] tick time break; case SC_STEAMPACK: // [Frost] val3 = 100; // HP Consume. - val4 = tick / 10000; + val4 = total_tick / 10000; tick_time = 10000; - sc_start(src, bl, SC_ENDURE, 100, 10, tick); // Endure effect + sc_start(src, bl, SC_ENDURE, 100, 10, total_tick); // Endure effect break; case SC_MAGIC_CANDY: // [Frost] val3 = 90; // SP Consume. - val4 = tick / 10000; + val4 = total_tick / 10000; tick_time = 10000; break; case SC_PROMOTE_HEALTH_RESERCH: @@ -9432,7 +9289,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = val1 * 2; FALLTHROUGH case SC_IZAYOI: - val2 = tick/1000; + val2 = total_tick/1000; tick_time = 1000; break; case SC_ZANGETSU: @@ -9472,12 +9329,12 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en case SC_ANGRIFFS_MODUS: val2 = 50 + 20 * val1; //atk bonus val3 = 40 + 20 * val1; // Flee reduction. - val4 = tick/1000; // hp/sp reduction timer + val4 = total_tick/1000; // hp/sp reduction timer tick_time = 1000; break; case SC_NEUTRALBARRIER: - tick_time = tick; - tick = INFINITE_DURATION; + tick_time = total_tick; + total_tick = INFINITE_DURATION; break; case SC_GOLDENE_FERSE: val2 = 10 + 10*val1; //max hp bonus @@ -9506,10 +9363,10 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val2 = 2*val1; //aspd reduction % val3 = 2*val1; //dmg reduction % if(sc->data[SC_NEEDLE_OF_PARALYZE]) - sc_start(src, bl, SC_ENDURE, 100, val1, tick); //start endure for same duration + sc_start(src, bl, SC_ENDURE, 100, val1, total_tick); //start endure for same duration break; case SC_STYLE_CHANGE: //[Lighta] need real info - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; if(val2 == MH_MD_FIGHTING) val2 = MH_MD_GRAPPLING; else val2 = MH_MD_FIGHTING; break; @@ -9517,12 +9374,12 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en status_percent_heal(bl,100,0); val2 = 7 - val1; tick_time = 1000; - val4 = tick / tick_time; + val4 = total_tick / tick_time; break; case SC_KINGS_GRACE: val2 = 3 + val1; tick_time = 1000; - val4 = tick / tick_time; + val4 = total_tick / tick_time; break; case SC_TELEKINESIS_INTENSE: val2 = 10 * val1; @@ -9535,7 +9392,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val2 = 5 * val1; val3 = (20 * val1) + 80; tick_time = 1000; - val4 = tick / tick_time; + val4 = total_tick / tick_time; break; case SC_DARKCROW: val2 = 30 * val1; @@ -9546,7 +9403,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_SPRITEMABLE: case SC_ALL_RIDING: - tick = INFINITE_DURATION; + total_tick = INFINITE_DURATION; break; case SC_FLASHCOMBO: /** @@ -9559,7 +9416,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en * Summoner */ case SC_FRESHSHRIMP: - val4 = tick / (10000 - ((val1 - 1) * 1000)); + val4 = total_tick / (10000 - ((val1 - 1) * 1000)); tick_time = 10000 - ((val1 - 1) * 1000); if (val4 <= 0) // Prevents a negeative value from happening val4 = 0; @@ -9575,7 +9432,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en break; case SC_BITESCAR: val2 = 2 * val1; // MHP% damage - val4 = tick / 1000; + val4 = total_tick / 1000; tick_time = 1000; break; case SC_SHRIMP: @@ -9586,7 +9443,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en val3 = 25 * val1; // Move speed reduction break; default: - if (status->change_start_unknown_sc(src, bl, type, calc_flag, rate, val1, val2, val3, val4, tick, flag)) { + if (status->change_start_unknown_sc(src, bl, type, calc_flag, rate, val1, val2, val3, val4, total_tick, flag)) { return 0; } } @@ -9656,13 +9513,15 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en } #endif - if(!(flag&SCFLAG_NOICON) && !(flag&SCFLAG_LOADED && status->dbs->DisplayType[type])) - clif->status_change(bl,status->dbs->IconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0); + if (!(flag & SCFLAG_LOADED)) + tick = total_tick; // When starting a new SC (not loading), its remaining duration is the same as the total + if(!(flag & SCFLAG_NOICON) && !(flag & SCFLAG_LOADED && status->dbs->DisplayType[type])) + clif->status_change_sub(bl, status->get_sc_icon(type), status->get_sc_relevant_bl_types(type), 1, tick, total_tick, (val_flag & 1) ? val1 : 1, (val_flag & 2) ? val2 : 0, (val_flag & 4) ? val3 : 0); /** * used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first. **/ - if( tick_time ) + if(tick_time) tick = tick_time; //Don't trust the previous sce assignment, in case the SC ended somewhere between there and here. @@ -9678,6 +9537,7 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en sce->val2 = val2; sce->val3 = val3; sce->val4 = val4; + sce->total_tick = total_tick; if (tick >= 0) { sce->timer = timer->add(timer->gettick() + tick, status->change_timer, bl->id, type); @@ -9786,10 +9646,10 @@ static int status_change_start(struct block_list *src, struct block_list *bl, en return 1; } -static bool status_change_start_unknown_sc(struct block_list *src, struct block_list *bl, enum sc_type type, int calc_flag, int rate, int val1, int val2, int val3, int val4, int tick, int flag) +static bool status_change_start_unknown_sc(struct block_list *src, struct block_list *bl, enum sc_type type, int calc_flag, int rate, int val1, int val2, int val3, int val4, int total_tick, int flag) { Assert_retr(false, type >= SC_NONE && type < SC_MAX); - if (calc_flag == SCB_NONE && status->dbs->SkillChangeTable[type] == 0 && status->dbs->IconChangeTable[type] == 0) { + if (calc_flag == SCB_NONE && status->dbs->SkillChangeTable[type] == 0 && status->get_sc_icon(type) == SI_BLANK) { //Status change with no calc, no icon, and no skill associated...? ShowError("UnknownStatusChange [%d]\n", type); return true; @@ -9797,6 +9657,28 @@ static bool status_change_start_unknown_sc(struct block_list *src, struct block_ return false; } +/** + * Starts a status change in its full duration. + * + * @param src Status change source bl. + * @param bl Status change target bl. + * @param type Status change type. + * @param rate Base success rate. 1 means 0.01%, 10000 means 100%. + * @param val1 Additional value (meaning depends on type). + * @param val2 Additional value (meaning depends on type). + * @param val3 Additional value (meaning depends on type). + * @param val4 Additional value (meaning depends on type). + * @param tick Base duration (milliseconds). + * @param flag Special flags (@see enum scstart_flag). + * + * @retval 0 if no status change happened. + * @retval 1 if the status change was successfully applied. + */ +static int status_change_start(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int flag) +{ + return status->change_start_sub(src, bl, type, rate, val1, val2, val3, val4, 0, tick, flag); +} + static void status_change_start_display(struct map_session_data *sd, enum sc_type type, int val1, int val2, int val3, int val4) { Assert_retv(type >= SC_NONE && type < SC_MAX); @@ -9962,6 +9844,9 @@ static int status_get_val_flag(enum sc_type type) case SC_DAILYSENDMAILCNT: val_flag |= 1 | 2; break; + case SC_MADOGEAR: + val_flag |= 1; + break; } return val_flag; } @@ -10175,7 +10060,7 @@ static void status_change_start_stop_action(struct block_list *bl, enum sc_type switch (type) { case SC_VACUUM_EXTREME: if (!map_flag_gvg(bl->m)) - unit->stop_walking(bl,1); + unit->stop_walking(bl, STOPWALKING_FLAG_FIXPOS); break; case SC_FREEZE: case SC_STUN: @@ -10541,55 +10426,10 @@ static bool status_is_boss_resist_sc(enum sc_type type) { if (type >= SC_COMMON_MIN && type <= SC_COMMON_MAX) return true; - switch (type) { - case SC_BLESSING: - case SC_DEC_AGI: - case SC_PROVOKE: - case SC_COMA: - case SC_GRAVITATION: - case SC_NJ_SUITON: - case SC_RICHMANKIM: - case SC_ROKISWEIL: - case SC_FOGWALL: - case SC_FROSTMISTY: - case SC_BURNING: - case SC_MARSHOFABYSS: - case SC_ADORAMUS: - case SC_NEEDLE_OF_PARALYZE: - case SC_DEEP_SLEEP: - case SC_COLD: - // Exploit prevention - kRO Fix - case SC_PYREXIA: - case SC_DEATHHURT: - case SC_TOXIN: - case SC_PARALYSE: - case SC_VENOMBLEED: - case SC_MAGICMUSHROOM: - case SC_OBLIVIONCURSE: - case SC_LEECHESEND: - - // Ranger Effects - case SC_WUGBITE: - case SC_ELECTRICSHOCKER: - case SC_MAGNETICFIELD: - - // Masquerades - case SC__ENERVATION: - case SC__GROOMY: - case SC__LAZINESS: - case SC__UNLUCKY: - case SC__WEAKNESS: - case SC__IGNORANCE: + if (status->get_sc_type(type) & SC_NO_BOSS) + return true; - // Other Effects - case SC_VACUUM_EXTREME: - case SC_NETHERWORLD: - case SC_FRESHSHRIMP: - case SC_SV_ROOTTWIST: - case SC_BITESCAR: - return true; - } return false; } @@ -10728,9 +10568,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, struct status_data *st; struct view_data *vd; int opt_flag=0, calc_flag; -#ifdef ANTI_MAYAP_CHEAT bool invisible = false; -#endif nullpo_ret(bl); @@ -10785,10 +10623,8 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, status->display_remove(sd,type); } -#ifdef ANTI_MAYAP_CHEAT - if (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_INVISIBLE)) + if ((sc->option & (OPTION_HIDE | OPTION_CLOAK | OPTION_INVISIBLE)) != 0) invisible = true; -#endif vd = status->get_viewdata(bl); calc_flag = status->dbs->ChangeFlagTable[type]; @@ -10939,7 +10775,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, } if ((sce->val1&0xFFFF) == CG_MOONLIT) - clif->sc_end(bl,bl->id,AREA,SI_MOON); + clif->sc_end(bl, bl->id, AREA, status->get_sc_icon(SC_MOON)); status_change_end(bl, SC_LONGING, INVALID_TIMER); } @@ -11415,6 +11251,12 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, } #endif + // update HP bar when becoming visible again + if (invisible && (sc->option & (OPTION_HIDE | OPTION_CLOAK | OPTION_INVISIBLE)) == 0) { + if (sd != NULL && battle_config.party_hp_mode == 0 && sd->status.party_id != 0) + clif->party_hp(sd); + } + if (calc_flag&SCB_DYE) { //Restore DYE color if (vd && !vd->cloth_color && sce->val4) clif->changelook(bl,LOOK_CLOTHES_COLOR,sce->val4); @@ -11430,7 +11272,7 @@ static int status_change_end_(struct block_list *bl, enum sc_type type, int tid, #endif //On Aegis, when turning off a status change, first goes the sc packet, then the option packet. - clif->sc_end(bl,bl->id,AREA,status->dbs->IconChangeTable[type]); + clif->sc_end(bl, bl->id, AREA, status->get_sc_icon(type)); if( opt_flag&8 ) //bugreport:681 clif->changeoption2(bl); @@ -11794,10 +11636,10 @@ static int status_change_timer(int tid, int64 tick, int id, intptr_t data) case SC_SPLASHER: #if 0 // custom Venom Splasher countdown timer - if (sce->val4 % 1000 == 0) { + if (sce->val4 % 1000 == 0 && bl && bl->type == BL_PC) { char counter[10]; snprintf (counter, 10, "%d", sce->val4/1000); - clif->message(bl, counter); + clif->message(BL_UCCAST(BL_PC, bl)->fd, counter); } #endif // 0 if((sce->val4 -= 500) > 0) { @@ -12505,10 +12347,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; } @@ -12620,10 +12462,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; } @@ -13073,25 +12915,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) { @@ -13344,10 +13167,12 @@ static void status_read_job_db(void) int i = 0; struct config_t job_db_conf; struct config_setting_t *jdb = NULL; + char config_filename[256]; + #ifdef RENEWAL_ASPD - const char *config_filename = "db/re/job_db.conf"; + libconfig->format_db_path(DBPATH_RE"job_db.conf", config_filename, sizeof(config_filename)); #else - const char *config_filename = "db/pre-re/job_db.conf"; + libconfig->format_db_path(DBPATH_PRE"job_db.conf", config_filename, sizeof(config_filename)); #endif if (!libconfig->load_file(&job_db_conf, config_filename)) @@ -13402,190 +13227,114 @@ 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)); +static bool status_read_scdb_libconfig(void) +{ + struct config_t status_conf; + char filepath[256]; + safesnprintf(filepath, sizeof(filepath), "%s/%s", map->db_path, "sc_config.conf"); - 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 (libconfig->load_file(&status_conf, filepath) == CONFIG_FALSE) { + ShowError("status_read_scdb_libconfig: can't read %s\n", filepath); + return false; + } - 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; - } + int i = 0; + int count = 0; + struct config_setting_t *it = NULL; - level--; + while ((it = libconfig->setting_get_elem(status_conf.root, i++)) != NULL) { + if (status->read_scdb_libconfig_sub(it, i - 1, filepath)) + ++count; + } - 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; - } + // @TODO: find a better way of handling this + if (!battle_config.display_hallucination) //Disable Hallucination. + status->dbs->IconChangeTable[SC_ILLUSION].id = SI_BLANK; - if (libconfig->setting_lookup_int(t, "NormalChance", &i32) != 0) - chance[REFINE_CHANCE_TYPE_NORMAL][level] = i32; - else - chance[REFINE_CHANCE_TYPE_NORMAL][level] = 100; + libconfig->destroy(&status_conf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, filepath); + return true; +} - 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. +static bool status_read_scdb_libconfig_sub(struct config_setting_t *it, int idx, const char *source) +{ + nullpo_retr(false, it); + nullpo_retr(false, source); - 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; + int i32; + int status_id; + const char *name = config_setting_name(it); - 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 (!script->get_constant(name, &status_id) || status_id <= SC_NONE || status_id >= SC_MAX) { + ShowWarning("status_read_scdb_libconfig_sub: Invalid status type (%s) in \"%s\" entry #%d, skipping.\n", name, source, idx); + return false; + } - if (libconfig->setting_lookup_int(t, "Bonus", &i32) != 0) - bonus[level] += i32; + libconfig->setting_lookup_bool_real(it, "Visible", &status->dbs->DisplayType[status_id]); - 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; - } + struct config_setting_t *fg = libconfig->setting_get_member(it, "Flags"); + if (fg != NULL) + status->read_scdb_libconfig_sub_flag(fg, status_id, source); - return type + 1; + if (itemdb->lookup_const(it, "Icon", &i32) && i32 >= 0) + status->dbs->IconChangeTable[status_id].id = i32; + else + status->dbs->IconChangeTable[status_id].id = SI_BLANK; + return true; } -/** - * 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) +static bool status_read_scdb_libconfig_sub_flag(struct config_setting_t *it, int type, const char *source) { - bool duplicate[REFINE_TYPE_MAX]; - struct config_t refine_db_conf; - struct config_setting_t *r; - char filepath[256]; - int i = 0, count = 0; + nullpo_retr(false, it); + nullpo_retr(false, source); + Assert_retr(false, type > SC_NONE && type < SC_MAX); - 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); + int i = 0; + struct config_setting_t *t = NULL; + while ((t = libconfig->setting_get_elem(it, i++)) != NULL) { + const char *flag = config_setting_name(t); + bool on = libconfig->setting_get_bool_real(t); + int j; + + struct { + const char *name; + enum sc_conf_type value; + } flags[] = { + { "NoDeathReset", SC_NO_REM_DEATH }, + { "NoSave", SC_NO_SAVE }, + { "NoDispelReset", SC_NO_DISPELL }, + { "NoClearanceReset", SC_NO_CLEARANCE }, + { "Buff", SC_BUFF }, + { "Debuff", SC_DEBUFF }, + { "NoMadoReset", SC_MADO_NO_RESET }, + { "NoAllReset", SC_NO_CLEAR }, + { "NoBoss", SC_NO_BOSS }, + }; + + ARR_FIND(0, ARRAYLENGTH(flags), j, strcmpi(flag, flags[j].name) == 0); + if (j != ARRAYLENGTH(flags)) { + if (strcmp(flag, flags[j].name) != 0) { + ShowWarning("status_read_scdb_libconfig_sub_flag: flag (%s) for status effect (%d) is casesensitive, correct it to (%s).", flag, type, flags[i].name); + } + if (on) { + status->dbs->sc_conf[type] |= flags[j].value; } else { - duplicate[type-1] = true; + status->dbs->sc_conf[type] &= ~flags[j].value; } - count++; + } else { + if (!status->read_scdb_libconfig_sub_flag_additional(it, type, source)) + ShowWarning("status_read_scdb_libconfig_sub_flag: invalid flag (%s) for status effect (%d).", flag, type); } } - 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; + return true; } -static bool status_readdb_scconfig(char *fields[], int columns, int current) +static bool status_read_scdb_libconfig_sub_flag_additional(struct config_setting_t *it, int type, const char *source) { - int val = 0; - char* type = fields[0]; - - nullpo_retr(false, fields); - if( !script->get_constant(type, &val) ){ - ShowWarning("status_readdb_sc_conf: Invalid status type %s specified.\n", type); - return false; - } - - status->dbs->sc_conf[val] = (int)strtol(fields[1], NULL, 0); - if (status->dbs->sc_conf[val] & SC_VISIBLE) - { - status->dbs->DisplayType[val] = true; - } - - return true; + // to be used by plugins + return false; } + /** * Read status db * job1.txt @@ -13624,8 +13373,7 @@ 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_scdb_libconfig(); status->read_job_db(); pc->validate_levels(); @@ -13682,13 +13430,13 @@ 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; status->sc2scb_flag = status_sc2scb_flag; - status->type2relevant_bl_types = status_type2relevant_bl_types; + status->get_sc_relevant_bl_types = status_get_sc_relevant_bl_types; status->get_sc_type = status_get_sc_type; + status->get_sc_icon = status_get_sc_icon; status->damage = status_damage; //Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills) @@ -13726,6 +13474,7 @@ void status_defaults(void) status->get_sc_def = status_get_sc_def; status->change_start = status_change_start; + status->change_start_sub = status_change_start_sub; status->change_end_ = status_change_end_; status->kaahi_heal_timer = kaahi_heal_timer; status->change_timer = status_change_timer; @@ -13816,9 +13565,10 @@ 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_scdb_libconfig = status_read_scdb_libconfig; + status->read_scdb_libconfig_sub = status_read_scdb_libconfig_sub; + status->read_scdb_libconfig_sub_flag = status_read_scdb_libconfig_sub_flag; + status->read_scdb_libconfig_sub_flag_additional = status_read_scdb_libconfig_sub_flag_additional; status->read_job_db = status_read_job_db; status->read_job_db_sub = status_read_job_db_sub; status->set_sc = status_set_sc; diff --git a/src/map/status.h b/src/map/status.h index 17af22703..d5cb3da75 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,29 +49,8 @@ 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 + * @see db/sc_config.conf for more information **/ typedef enum sc_conf_type { SC_NO_REM_DEATH = 0x001, @@ -82,7 +61,8 @@ typedef enum sc_conf_type { SC_DEBUFF = 0x020, SC_MADO_NO_RESET = 0x040, SC_NO_CLEAR = 0x080, - SC_VISIBLE = 0x100 + SC_VISIBLE = 0x100, + SC_NO_BOSS = 0x200 } sc_conf_type; /** @@ -858,6 +838,29 @@ typedef enum sc_type { // Clan System SC_CLAN_INFO, + + SC_SIT, + SC_MOON, + SC_TING, + SC_DEVIL1, + SC_RIDING, + SC_FALCON, + SC_WUGRIDER, + SC_POSTDELAY, + SC_ON_PUSH_CART, + SC_RESIST_PROPERTY_WATER, + SC_RESIST_PROPERTY_GROUND, + SC_RESIST_PROPERTY_FIRE, + SC_RESIST_PROPERTY_WIND, + SC_CLIENT_ONLY_EQUIP_ARROW, + SC_MADOGEAR, + SC_POPECOOKIE, + SC_VITALIZE_POTION, + SC_SKF_MATK, + SC_SKF_ATK, + SC_SKF_ASPD, + SC_SKF_CAST, + SC_ALMIGHTY, #ifndef SC_MAX SC_MAX, //Automatically updated max, used in for's to check we are within bounds. #endif @@ -865,1034 +868,7 @@ typedef enum sc_type { /// Official status change ids, used to display status icons in the client. enum si_type { - SI_BLANK = -1, - - SI_PROVOKE = 0, - SI_ENDURE = 1, - SI_TWOHANDQUICKEN = 2, - SI_CONCENTRATION = 3, - SI_HIDING = 4, - SI_CLOAKING = 5, - SI_ENCHANTPOISON = 6, - SI_POISONREACT = 7, - SI_QUAGMIRE = 8, - SI_ANGELUS = 9, - SI_BLESSING = 10, - SI_CRUCIS = 11, - SI_INC_AGI = 12, - SI_DEC_AGI = 13, - SI_SLOWPOISON = 14, - SI_IMPOSITIO = 15, - SI_SUFFRAGIUM = 16, - SI_ASPERSIO = 17, - SI_BENEDICTIO = 18, - SI_KYRIE = 19, - SI_MAGNIFICAT = 20, - SI_GLORIA = 21, - SI_LEXAETERNA = 22, - SI_ADRENALINE = 23, - SI_WEAPONPERFECT = 24, - SI_OVERTHRUST = 25, - SI_MAXIMIZE = 26, - SI_RIDING = 27, - SI_FALCON = 28, - SI_TRICKDEAD = 29, - SI_SHOUT = 30, - SI_ENERGYCOAT = 31, - SI_BROKENARMOR = 32, - SI_BROKENWEAPON = 33, - SI_ILLUSION = 34, - SI_WEIGHTOVER50 = 35, - SI_WEIGHTOVER90 = 36, - SI_ATTHASTE_POTION1 = 37, - SI_ATTHASTE_POTION2 = 38, - SI_ATTHASTE_POTION3 = 39, - SI_ATTHASTE_INFINITY = 40, - SI_MOVHASTE_POTION = 41, - SI_MOVHASTE_INFINITY = 42, - //SI_AUTOCOUNTER = 43, - //SI_SPLASHER = 44, - SI_ANKLESNARE = 45, - SI_POSTDELAY = 46, - //SI_NOACTION = 47, - //SI_IMPOSSIBLEPICKUP = 48, - //SI_BARRIER = 49, - - SI_NOEQUIPWEAPON = 50, - SI_NOEQUIPSHIELD = 51, - SI_NOEQUIPARMOR = 52, - SI_NOEQUIPHELM = 53, - SI_PROTECTWEAPON = 54, - SI_PROTECTSHIELD = 55, - SI_PROTECTARMOR = 56, - SI_PROTECTHELM = 57, - SI_AUTOGUARD = 58, - SI_REFLECTSHIELD = 59, - //SI_DEVOTION = 60, - SI_PROVIDENCE = 61, - SI_DEFENDER = 62, - //SI_MAGICROD = 63, - //SI_WEAPONPROPERTY = 64, - SI_AUTOSPELL = 65, - //SI_SPECIALZONE = 66, - //SI_MASK = 67, - SI_SPEARQUICKEN = 68, - //SI_BDPLAYING = 69, - //SI_WHISTLE = 70, - //SI_ASSASSINCROSS = 71, - //SI_POEMBRAGI = 72, - //SI_APPLEIDUN = 73, - //SI_HUMMING = 74, - //SI_DONTFORGETME = 75, - //SI_FORTUNEKISS = 76, - //SI_SERVICEFORYOU = 77, - //SI_RICHMANKIM = 78, - //SI_ETERNALCHAOS = 79, - //SI_DRUMBATTLEFIELD = 80, - //SI_RINGNIBELUNGEN = 81, - //SI_ROKISWEIL = 82, - //SI_INTOABYSS = 83, - //SI_SIEGFRIED = 84, - //SI_BLADESTOP = 85, - SI_EXPLOSIONSPIRITS = 86, - SI_STEELBODY = 87, - SI_EXTREMITYFIST = 88, - //SI_COMBOATTACK = 89, - SI_PROPERTYFIRE = 90, - SI_PROPERTYWATER = 91, - SI_PROPERTYWIND = 92, - SI_PROPERTYGROUND = 93, - //SI_MAGICATTACK = 94, - SI_STOP = 95, - //SI_WEAPONBRAKER = 96, - SI_PROPERTYUNDEAD = 97, - //SI_POWERUP = 98, - //SI_AGIUP = 99, - - //SI_SIEGEMODE = 100, - //SI_INVISIBLE = 101, - //SI_STATUSONE = 102, - SI_AURABLADE = 103, - SI_PARRYING = 104, - SI_LKCONCENTRATION = 105, - SI_TENSIONRELAX = 106, - SI_BERSERK = 107, - //SI_SACRIFICE = 108, - //SI_GOSPEL = 109, - SI_ASSUMPTIO = 110, - //SI_BASILICA = 111, - SI_GROUNDMAGIC = 112, - SI_MAGICPOWER = 113, - SI_EDP = 114, - SI_TRUESIGHT = 115, - SI_WINDWALK = 116, - SI_MELTDOWN = 117, - SI_CARTBOOST = 118, - //SI_CHASEWALK = 119, - SI_SWORDREJECT = 120, - SI_MARIONETTE_MASTER = 121, - SI_MARIONETTE = 122, - SI_MOON = 123, - SI_BLOODING = 124, - SI_JOINTBEAT = 125, - //SI_MINDBREAKER = 126, - //SI_MEMORIZE = 127, - //SI_FOGWALL = 128, - //SI_SPIDERWEB = 129, - SI_PROTECTEXP = 130, - //SI_SUB_WEAPONPROPERTY = 131, - SI_AUTOBERSERK = 132, - SI_RUN = 133, - SI_TING = 134, - SI_STORMKICK_ON = 135, - SI_STORMKICK_READY = 136, - SI_DOWNKICK_ON = 137, - SI_DOWNKICK_READY = 138, - SI_TURNKICK_ON = 139, - SI_TURNKICK_READY = 140, - SI_COUNTER_ON = 141, - SI_COUNTER_READY = 142, - SI_DODGE_ON = 143, - SI_DODGE_READY = 144, - SI_STRUP = 145, - SI_PROPERTYDARK = 146, - SI_ADRENALINE2 = 147, - SI_PROPERTYTELEKINESIS = 148, - SI_SOULLINK = 149, - - SI_PLUSATTACKPOWER = 150, - SI_PLUSMAGICPOWER = 151, - SI_DEVIL1 = 152, - SI_KAITE = 153, - //SI_SWOO = 154, - //SI_STAR2 = 155, - SI_KAIZEL = 156, - SI_KAAHI = 157, - SI_KAUPE = 158, - SI_SMA_READY = 159, - SI_SKE = 160, - SI_ONEHANDQUICKEN = 161, - //SI_FRIEND = 162, - //SI_FRIENDUP = 163, - //SI_SG_WARM = 164, - SI_SG_SUN_WARM = 165, - //SI_SG_MOON_WARM = 166, // The three show the exact same display: ultra red character (165, 166, 167) - //SI_SG_STAR_WARM = 167, - //SI_EMOTION = 168, - SI_SUN_COMFORT = 169, - SI_MOON_COMFORT = 170, - SI_STAR_COMFORT = 171, - //SI_EXPUP = 172, - //SI_GDSKILL_BATTLEORDER = 173, - //SI_GDSKILL_REGENERATION = 174, - //SI_GDSKILL_POSTDELAY = 175, - //SI_RESISTHANDICAP = 176, - //SI_MAXHPPERCENT = 177, - //SI_MAXSPPERCENT = 178, - //SI_DEFENCE = 179, - //SI_SLOWDOWN = 180, - SI_PRESERVE = 181, - SI_INCSTR = 182, - //SI_NOT_EXTREMITYFIST = 183, - SI_CLAIRVOYANCE = 184, - SI_MOVESLOW_POTION = 185, - SI_DOUBLECASTING = 186, - //SI_GRAVITATION = 187, - SI_OVERTHRUSTMAX = 188, - //SI_LONGING = 189, - //SI_HERMODE = 190, - SI_TAROTCARD = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG] - //SI_HLIF_AVOID = 192, - //SI_HFLI_FLEET = 193, - //SI_HFLI_SPEED = 194, - //SI_HLIF_CHANGE = 195, - //SI_HAMI_BLOODLUST = 196, - SI_CR_SHRINK = 197, - SI_WZ_SIGHTBLASTER = 198, - SI_DC_WINKCHARM = 199, - - SI_RG_CCONFINE_M = 200, - SI_RG_CCONFINE_S = 201, - //SI_DISABLEMOVE = 202, - SI_GS_MADNESSCANCEL = 203, //[blackhole89] - SI_GS_GATLINGFEVER = 204, - SI_EARTHSCROLL = 205, - SI_NJ_UTSUSEMI = 206, - SI_NJ_BUNSINJYUTSU = 207, - SI_NJ_NEN = 208, - SI_GS_ADJUSTMENT = 209, - SI_GS_ACCURACY = 210, - SI_NJ_SUITON = 211, - //SI_PET = 212, - //SI_MENTAL = 213, - //SI_EXPMEMORY = 214, - //SI_PERFORMANCE = 215, - //SI_GAIN = 216, - //SI_GRIFFON = 217, - //SI_DRIFT = 218, - //SI_WALLSHIFT = 219, - //SI_REINCARNATION = 220, - //SI_PATTACK = 221, - //SI_PSPEED = 222, - //SI_PDEFENSE = 223, - //SI_PCRITICAL = 224, - //SI_RANKING = 225, - //SI_PTRIPLE = 226, - //SI_DENERGY = 227, - //SI_WAVE1 = 228, - //SI_WAVE2 = 229, - //SI_WAVE3 = 230, - //SI_WAVE4 = 231, - //SI_DAURA = 232, - //SI_DFREEZER = 233, - //SI_DPUNISH = 234, - //SI_DBARRIER = 235, - //SI_DWARNING = 236, - //SI_MOUSEWHEEL = 237, - //SI_DGAUGE = 238, - //SI_DACCEL = 239, - //SI_DBLOCK = 240, - SI_FOOD_STR = 241, - SI_FOOD_AGI = 242, - SI_FOOD_VIT = 243, - SI_FOOD_DEX = 244, - SI_FOOD_INT = 245, - SI_FOOD_LUK = 246, - SI_FOOD_BASICAVOIDANCE = 247, - SI_FOOD_BASICHIT = 248, - SI_FOOD_CRITICALSUCCESSVALUE = 249, - - SI_CASH_PLUSEXP = 250, - SI_CASH_DEATHPENALTY = 251, - SI_CASH_RECEIVEITEM = 252, - SI_CASH_BOSS_ALARM = 253, - //SI_DA_ENERGY = 254, - //SI_DA_FIRSTSLOT = 255, - //SI_DA_HEADDEF = 256, - //SI_DA_SPACE = 257, - //SI_DA_TRANSFORM = 258, - //SI_DA_ITEMREBUILD = 259, - //SI_DA_ILLUSION = 260, // All mobs display as Turtle General - //SI_DA_DARKPOWER = 261, - //SI_DA_EARPLUG = 262, - //SI_DA_CONTRACT = 263, // Bio Mob effect on you and SI_TRICKDEAD icon - //SI_DA_BLACK = 264, // For short time blurry screen - //SI_DA_MAGICCART = 265, - //SI_CRYSTAL = 266, - //SI_DA_REBUILD = 267, - //SI_DA_EDARKNESS = 268, - //SI_DA_EGUARDIAN = 269, - //SI_DA_TIMEOUT = 270, - SI_FOOD_STR_CASH = 271, - SI_FOOD_AGI_CASH = 272, - SI_FOOD_VIT_CASH = 273, - SI_FOOD_DEX_CASH = 274, - SI_FOOD_INT_CASH = 275, - SI_FOOD_LUK_CASH = 276, - SI_MER_FLEE = 277, - SI_MER_ATK = 278, - SI_MER_HP = 279, - SI_MER_SP = 280, - SI_MER_HIT = 281, - SI_SLOWCAST = 282, - //SI_MAGICMIRROR = 283, - //SI_STONESKIN = 284, - //SI_ANTIMAGIC = 285, - SI_CRITICALWOUND = 286, - //SI_NPC_DEFENDER = 287, - //SI_NOACTION_WAIT = 288, - SI_MOVHASTE_HORSE = 289, - SI_PROTECT_DEF = 290, - SI_PROTECT_MDEF = 291, - SI_HEALPLUS = 292, - SI_S_LIFEPOTION = 293, - SI_L_LIFEPOTION = 294, - SI_CRITICALPERCENT = 295, - SI_PLUSAVOIDVALUE = 296, - SI_ATKER_ASPD = 297, - SI_TARGET_ASPD = 298, - SI_ATKER_MOVESPEED = 299, - - SI_ATKER_BLOOD = 300, - SI_TARGET_BLOOD = 301, - SI_ARMOR_PROPERTY = 302, - //SI_REUSE_LIMIT_A = 303, - SI_HELLPOWER = 304, - SI_STEAMPACK = 305, - //SI_REUSE_LIMIT_B = 306, - //SI_REUSE_LIMIT_C = 307, - //SI_REUSE_LIMIT_D = 308, - //SI_REUSE_LIMIT_E = 309, - //SI_REUSE_LIMIT_F = 310, - SI_INVINCIBLE = 311, - SI_CASH_PLUSONLYJOBEXP = 312, - SI_PARTYFLEE = 313, - SI_ANGEL_PROTECT = 314, - //SI_ENDURE_MDEF = 315, - SI_ENCHANTBLADE = 316, - SI_DEATHBOUND = 317, - SI_REFRESH = 318, - SI_GIANTGROWTH = 319, - SI_STONEHARDSKIN = 320, - SI_VITALITYACTIVATION = 321, - SI_FIGHTINGSPIRIT = 322, - SI_ABUNDANCE = 323, - SI_REUSE_MILLENNIUMSHIELD = 324, - SI_REUSE_CRUSHSTRIKE = 325, - SI_REUSE_REFRESH = 326, - SI_REUSE_STORMBLAST = 327, - SI_VENOMIMPRESS = 328, - SI_EPICLESIS = 329, - SI_ORATIO = 330, - SI_LAUDAAGNUS = 331, - SI_LAUDARAMUS = 332, - SI_CLOAKINGEXCEED = 333, - SI_HALLUCINATIONWALK = 334, - SI_HALLUCINATIONWALK_POSTDELAY = 335, - SI_RENOVATIO = 336, - SI_WEAPONBLOCKING = 337, - SI_WEAPONBLOCKING_POSTDELAY = 338, - SI_ROLLINGCUTTER = 339, - SI_EXPIATIO = 340, - SI_POISONINGWEAPON = 341, - SI_TOXIN = 342, - SI_PARALYSE = 343, - SI_VENOMBLEED = 344, - SI_MAGICMUSHROOM = 345, - SI_DEATHHURT = 346, - SI_PYREXIA = 347, - SI_OBLIVIONCURSE = 348, - SI_LEECHESEND = 349, - - SI_DUPLELIGHT = 350, - SI_FROSTMISTY = 351, - SI_FEARBREEZE = 352, - SI_ELECTRICSHOCKER = 353, - SI_MARSHOFABYSS = 354, - SI_RECOGNIZEDSPELL = 355, - SI_STASIS = 356, - SI_WUGRIDER = 357, - SI_WUGDASH = 358, - SI_WUGBITE = 359, - SI_CAMOUFLAGE = 360, - SI_ACCELERATION = 361, - SI_HOVERING = 362, - SI_SPHERE_1 = 363, - SI_SPHERE_2 = 364, - SI_SPHERE_3 = 365, - SI_SPHERE_4 = 366, - SI_SPHERE_5 = 367, - SI_MVPCARD_TAOGUNKA = 368, - SI_MVPCARD_MISTRESS = 369, - SI_MVPCARD_ORCHERO = 370, - SI_MVPCARD_ORCLORD = 371, - SI_OVERHEAT_LIMITPOINT = 372, - SI_OVERHEAT = 373, - SI_SHAPESHIFT = 374, - SI_INFRAREDSCAN = 375, - SI_MAGNETICFIELD = 376, - SI_NEUTRALBARRIER = 377, - SI_NEUTRALBARRIER_MASTER = 378, - SI_STEALTHFIELD = 379, - SI_STEALTHFIELD_MASTER = 380, - SI_MANU_ATK = 381, - SI_MANU_DEF = 382, - SI_SPL_ATK = 383, - SI_SPL_DEF = 384, - SI_REPRODUCE = 385, - SI_MANU_MATK = 386, - SI_SPL_MATK = 387, - SI_STR_SCROLL = 388, - SI_INT_SCROLL = 389, - SI_LG_REFLECTDAMAGE = 390, - SI_FORCEOFVANGUARD = 391, - SI_BUCHEDENOEL = 392, - SI_AUTOSHADOWSPELL = 393, - SI_SHADOWFORM = 394, - SI_RAID = 395, - SI_SHIELDSPELL_DEF = 396, - SI_SHIELDSPELL_MDEF = 397, - SI_SHIELDSPELL_REF = 398, - SI_BODYPAINT = 399, - - SI_EXEEDBREAK = 400, - SI_ADORAMUS = 401, - SI_PRESTIGE = 402, - SI_INVISIBILITY = 403, - SI_DEADLYINFECT = 404, - SI_BANDING = 405, - SI_EARTHDRIVE = 406, - SI_INSPIRATION = 407, - SI_ENERVATION = 408, - SI_GROOMY = 409, - SI_RAISINGDRAGON = 410, - SI_IGNORANCE = 411, - SI_LAZINESS = 412, - SI_LIGHTNINGWALK = 413, - SI_ACARAJE = 414, - SI_UNLUCKY = 415, - SI_CURSEDCIRCLE_ATKER = 416, - SI_CURSEDCIRCLE_TARGET = 417, - SI_WEAKNESS = 418, - SI_CRESCENTELBOW = 419, - SI_NOEQUIPACCESSARY = 420, - SI_STRIPACCESSARY = 421, - SI_MANHOLE = 422, - SI_POPECOOKIE = 423, - SI_FALLENEMPIRE = 424, - SI_GENTLETOUCH_ENERGYGAIN = 425, - SI_GENTLETOUCH_CHANGE = 426, - SI_GENTLETOUCH_REVITALIZE = 427, - SI_BLOODYLUST = 428, - SI_SWINGDANCE = 429, - SI_SYMPHONYOFLOVERS = 430, - SI_PROPERTYWALK = 431, - SI_SPELLFIST = 432, - SI_NETHERWORLD = 433, - SI_SIREN = 434, - SI_DEEPSLEEP = 435, - SI_SIRCLEOFNATURE = 436, - SI_COLD = 437, - SI_GLOOMYDAY = 438, - SI_SONG_OF_MANA = 439, - SI_CLOUDKILL = 440, - SI_DANCEWITHWUG = 441, - SI_RUSHWINDMILL = 442, - SI_ECHOSONG = 443, - SI_HARMONIZE = 444, - SI_STRIKING = 445, - //SI_WARMER = 446, - SI_MOONLITSERENADE = 447, - SI_SATURDAYNIGHTFEVER = 448, - SI_SITDOWN_FORCE = 449, - - SI_ANALYZE = 450, - SI_LERADSDEW = 451, - SI_MELODYOFSINK = 452, - SI_WARCRYOFBEYOND = 453, - SI_UNLIMITEDHUMMINGVOICE = 454, - SI_SPELLBOOK1 = 455, - SI_SPELLBOOK2 = 456, - SI_SPELLBOOK3 = 457, - SI_FREEZE_SP = 458, - SI_GN_TRAINING_SWORD = 459, - SI_GN_REMODELING_CART = 460, - SI_CARTSBOOST = 461, - SI_FIXEDCASTINGTM_REDUCE = 462, - SI_THORNTRAP = 463, - SI_BLOODSUCKER = 464, - SI_SPORE_EXPLOSION = 465, - SI_DEMONIC_FIRE = 466, - SI_FIRE_EXPANSION_SMOKE_POWDER = 467, - SI_FIRE_EXPANSION_TEAR_GAS = 468, - SI_BLOCKING_PLAY = 469, - SI_MANDRAGORA = 470, - SI_ACTIVATE = 471, - SI_SECRAMENT = 472, - SI_ASSUMPTIO2 = 473, - SI_TK_SEVENWIND = 474, - SI_LIMIT_ODINS_RECALL = 475, - SI_STOMACHACHE = 476, - SI_MYSTERIOUS_POWDER = 477, - SI_MELON_BOMB = 478, - SI_BANANA_BOMB_SITDOWN_POSTDELAY = 479, - SI_PROMOTE_HEALTH_RESERCH = 480, - SI_ENERGY_DRINK_RESERCH = 481, - SI_EXTRACT_WHITE_POTION_Z = 482, - SI_VITATA_500 = 483, - SI_EXTRACT_SALAMINE_JUICE = 484, - SI_BOOST500 = 485, - SI_FULL_SWING_K = 486, - SI_MANA_PLUS = 487, - SI_MUSTLE_M = 488, - SI_LIFE_FORCE_F = 489, - SI_VACUUM_EXTREME = 490, - SI_SAVAGE_STEAK = 491, - SI_COCKTAIL_WARG_BLOOD = 492, - SI_MINOR_BBQ = 493, - SI_SIROMA_ICE_TEA = 494, - SI_DROCERA_HERB_STEAMED = 495, - SI_PUTTI_TAILS_NOODLES = 496, - SI_BANANA_BOMB = 497, - SI_SUMMON_AGNI = 498, - SI_SPELLBOOK4 = 499, - - SI_SPELLBOOK5 = 500, - SI_SPELLBOOK6 = 501, - SI_SPELLBOOK7 = 502, - SI_ELEMENTAL_AGGRESSIVE = 503, - SI_RETURN_TO_ELDICASTES = 504, - SI_BANDING_DEFENCE = 505, - SI_SKELSCROLL = 506, - SI_DISTRUCTIONSCROLL = 507, - SI_ROYALSCROLL = 508, - SI_IMMUNITYSCROLL = 509, - SI_MYSTICSCROLL = 510, - SI_BATTLESCROLL = 511, - SI_ARMORSCROLL = 512, - SI_FREYJASCROLL = 513, - SI_SOULSCROLL = 514, - SI_CIRCLE_OF_FIRE = 515, - SI_CIRCLE_OF_FIRE_OPTION = 516, - SI_FIRE_CLOAK = 517, - SI_FIRE_CLOAK_OPTION = 518, - SI_WATER_SCREEN = 519, - SI_WATER_SCREEN_OPTION = 520, - SI_WATER_DROP = 521, - SI_WATER_DROP_OPTION = 522, - SI_WIND_STEP = 523, - SI_WIND_STEP_OPTION = 524, - SI_WIND_CURTAIN = 525, - SI_WIND_CURTAIN_OPTION = 526, - SI_WATER_BARRIER = 527, - SI_ZEPHYR = 528, - SI_SOLID_SKIN = 529, - SI_SOLID_SKIN_OPTION = 530, - SI_STONE_SHIELD = 531, - SI_STONE_SHIELD_OPTION = 532, - SI_POWER_OF_GAIA = 533, - //SI_EL_WAIT = 534, - //SI_EL_PASSIVE = 535, - //SI_EL_DEFENSIVE = 536, - //SI_EL_OFFENSIVE = 537, - //SI_EL_COST = 538, - SI_PYROTECHNIC = 539, - SI_PYROTECHNIC_OPTION = 540, - SI_HEATER = 541, - SI_HEATER_OPTION = 542, - SI_TROPIC = 543, - SI_TROPIC_OPTION = 544, - SI_AQUAPLAY = 545, - SI_AQUAPLAY_OPTION = 546, - SI_COOLER = 547, - SI_COOLER_OPTION = 548, - SI_CHILLY_AIR = 549, - - SI_CHILLY_AIR_OPTION = 550, - SI_GUST = 551, - SI_GUST_OPTION = 552, - SI_BLAST = 553, - SI_BLAST_OPTION = 554, - SI_WILD_STORM = 555, - SI_WILD_STORM_OPTION = 556, - SI_PETROLOGY = 557, - SI_PETROLOGY_OPTION = 558, - SI_CURSED_SOIL = 559, - SI_CURSED_SOIL_OPTION = 560, - SI_UPHEAVAL = 561, - SI_UPHEAVAL_OPTION = 562, - SI_TIDAL_WEAPON = 563, - SI_TIDAL_WEAPON_OPTION = 564, - SI_ROCK_CRUSHER = 565, - SI_ROCK_CRUSHER_ATK = 566, - SI_FIRE_INSIGNIA = 567, - SI_WATER_INSIGNIA = 568, - SI_WIND_INSIGNIA = 569, - SI_EARTH_INSIGNIA = 570, - SI_EQUIPED_FLOOR = 571, - SI_GUARDIAN_RECALL = 572, - SI_MORA_BUFF = 573, - SI_REUSE_LIMIT_G = 574, - SI_REUSE_LIMIT_H = 575, - SI_NEEDLE_OF_PARALYZE = 576, - SI_PAIN_KILLER = 577, - SI_G_LIFEPOTION = 578, - SI_VITALIZE_POTION = 579, - SI_LIGHT_OF_REGENE = 580, - SI_OVERED_BOOST = 581, - SI_SILENT_BREEZE = 582, - SI_ODINS_POWER = 583, - SI_STYLE_CHANGE = 584, - SI_SONIC_CLAW_POSTDELAY = 585, - //SI_ = 586, - //SI_ = 587, - //SI_ = 588, - //SI_ = 589, - //SI_ = 590, - //SI_ = 591, - //SI_ = 592, - //SI_ = 593, - //SI_ = 594, - //SI_ = 595, - SI_SILVERVEIN_RUSH_POSTDELAY = 596, - SI_MIDNIGHT_FRENZY_POSTDELAY = 597, - SI_GOLDENE_FERSE = 598, - SI_ANGRIFFS_MODUS = 599, - - SI_TINDER_BREAKER = 600, - SI_TINDER_BREAKER_POSTDELAY = 601, - SI_CBC = 602, - SI_CBC_POSTDELAY = 603, - SI_EQC = 604, - SI_MAGMA_FLOW = 605, - SI_GRANITIC_ARMOR = 606, - SI_PYROCLASTIC = 607, - SI_VOLCANIC_ASH = 608, - SI_SPIRITS_SAVEINFO1 = 609, - SI_SPIRITS_SAVEINFO2 = 610, - SI_MAGIC_CANDY = 611, - SI_SEARCH_STORE_INFO = 612, - SI_ALL_RIDING = 613, - SI_ALL_RIDING_REUSE_LIMIT = 614, - SI_MACRO = 615, - SI_MACRO_POSTDELAY = 616, - SI_BEER_BOTTLE_CAP = 617, - SI_OVERLAPEXPUP = 618, - SI_PC_IZ_DUN05 = 619, - SI_CRUSHSTRIKE = 620, - SI_MONSTER_TRANSFORM = 621, - SI_SIT = 622, - SI_ONAIR = 623, - SI_MTF_ASPD = 624, - SI_MTF_RANGEATK = 625, - SI_MTF_MATK = 626, - SI_MTF_MLEATKED = 627, - SI_MTF_CRIDAMAGE = 628, - SI_REUSE_LIMIT_MTF = 629, - SI_MACRO_PERMIT = 630, - SI_MACRO_PLAY = 631, - SI_SKF_CAST = 632, - SI_SKF_ASPD = 633, - SI_SKF_ATK = 634, - SI_SKF_MATK = 635, - SI_REWARD_PLUSONLYJOBEXP = 636, - SI_HANDICAPSTATE_NORECOVER = 637, - SI_SET_NUM_DEF = 638, - SI_SET_NUM_MDEF = 639, - SI_SET_PER_DEF = 640, - SI_SET_PER_MDEF = 641, - SI_PARTYBOOKING_SEARCH_DEALY = 642, - SI_PARTYBOOKING_REGISTER_DEALY = 643, - SI_PERIOD_TIME_CHECK_DETECT_SKILL = 644, - SI_KO_JYUMONJIKIRI = 645, - SI_MEIKYOUSISUI = 646, - SI_ATTHASTE_CASH = 647, - SI_EQUIPPED_DIVINE_ARMOR = 648, - SI_EQUIPPED_HOLY_ARMOR = 649, - - SI_2011RWC = 650, - SI_KYOUGAKU = 651, - SI_IZAYOI = 652, - SI_ZENKAI = 653, - SI_KG_KAGEHUMI = 654, - SI_KYOMU = 655, - SI_KAGEMUSYA = 656, - SI_ZANGETSU = 657, - SI_PHI_DEMON = 658, - SI_GENSOU = 659, - SI_AKAITSUKI = 660, - SI_TETANY = 661, - SI_GM_BATTLE = 662, - SI_GM_BATTLE2 = 663, - SI_2011RWC_SCROLL = 664, - SI_ACTIVE_MONSTER_TRANSFORM = 665, - SI_MYSTICPOWDER = 666, - SI_ECLAGE_RECALL = 667, - SI_ENTRY_QUEUE_APPLY_DELAY = 668, - SI_REUSE_LIMIT_ECL = 669, - SI_M_LIFEPOTION = 670, - SI_ENTRY_QUEUE_NOTIFY_ADMISSION_TIME_OUT = 671, - SI_UNKNOWN_NAME = 672, - SI_ON_PUSH_CART = 673, - SI_HAT_EFFECT = 674, - SI_FLOWER_LEAF = 675, - SI_RAY_OF_PROTECTION = 676, - SI_GLASTHEIM_ATK = 677, - SI_GLASTHEIM_DEF = 678, - SI_GLASTHEIM_HEAL = 679, - SI_GLASTHEIM_HIDDEN = 680, - SI_GLASTHEIM_STATE = 681, - SI_GLASTHEIM_ITEMDEF = 682, - SI_GLASTHEIM_HPSP = 683, - SI_HOMUN_SKILL_POSTDELAY = 684, - SI_ALMIGHTY = 685, - SI_GVG_GIANT = 686, - SI_GVG_GOLEM = 687, - SI_GVG_STUN = 688, - SI_GVG_STONE = 689, - SI_GVG_FREEZ = 690, - SI_GVG_SLEEP = 691, - SI_GVG_CURSE = 692, - SI_GVG_SILENCE = 693, - SI_GVG_BLIND = 694, - SI_CLIENT_ONLY_EQUIP_ARROW = 695, - SI_CLAN_INFO = 696, - SI_JP_EVENT01 = 697, - SI_JP_EVENT02 = 698, - SI_JP_EVENT03 = 699, - - SI_JP_EVENT04 = 700, - SI_TELEPORT_FIXEDCASTINGDELAY = 701, - SI_GEFFEN_MAGIC1 = 702, - SI_GEFFEN_MAGIC2 = 703, - SI_GEFFEN_MAGIC3 = 704, - SI_QUEST_BUFF1 = 705, - SI_QUEST_BUFF2 = 706, - SI_QUEST_BUFF3 = 707, - SI_REUSE_LIMIT_RECALL = 708, - SI_SAVEPOSITION = 709, - SI_HANDICAPSTATE_ICEEXPLO = 710, - SI_FENRIR_CARD = 711, - SI_REUSE_LIMIT_ASPD_POTION = 712, - SI_MAXPAIN = 713, - SI_PC_STOP = 714, - SI_FRIGG_SONG = 715, - SI_OFFERTORIUM = 716, - SI_TELEKINESIS_INTENSE = 717, - SI_MOONSTAR = 718, - SI_STRANGELIGHTS = 719, - SI_FULL_THROTTLE = 720, - SI_REBOUND = 721, - SI_UNLIMIT = 722, - SI_KINGS_GRACE = 723, - SI_ITEM_ATKMAX = 724, - SI_ITEM_ATKMIN = 725, - SI_ITEM_MATKMAX = 726, - SI_ITEM_MATKMIN = 727, - SI_SUPER_STAR = 728, - SI_HIGH_RANKER = 729, - SI_DARKCROW = 730, - SI_2013_VALENTINE1 = 731, - SI_2013_VALENTINE2 = 732, - SI_2013_VALENTINE3 = 733, - SI_ILLUSIONDOPING = 734, - //SI_WIDEWEB = 735, - SI_CHILL = 736, - SI_BURNT = 737, - //SI_PCCAFE_PLAY_TIME = 738, - //SI_TWISTED_TIME = 739, - SI_FLASHCOMBO = 740, - //SI_JITTER_BUFF1 = 741, - //SI_JITTER_BUFF2 = 742, - //SI_JITTER_BUFF3 = 743, - //SI_JITTER_BUFF4 = 744, - //SI_JITTER_BUFF5 = 745, - //SI_JITTER_BUFF6 = 746, - //SI_JITTER_BUFF7 = 747, - //SI_JITTER_BUFF8 = 748, - //SI_JITTER_BUFF9 = 749, - - //SI_JITTER_BUFF10 = 750, - SI_CUP_OF_BOZA = 751, - SI_B_TRAP = 752, - SI_E_CHAIN = 753, - SI_E_QD_SHOT_READY = 754, - SI_C_MARKER = 755, - SI_H_MINE = 756, - SI_H_MINE_SPLASH = 757, - SI_P_ALTER = 758, - SI_HEAT_BARREL = 759, - SI_ANTI_M_BLAST = 760, - SI_SLUGSHOT = 761, - SI_SWORDCLAN = 762, - SI_ARCWANDCLAN = 763, - SI_GOLDENMACECLAN = 764, - SI_CROSSBOWCLAN = 765, - SI_PACKING_ENVELOPE1 = 766, - SI_PACKING_ENVELOPE2 = 767, - SI_PACKING_ENVELOPE3 = 768, - SI_PACKING_ENVELOPE4 = 769, - SI_PACKING_ENVELOPE5 = 770, - SI_PACKING_ENVELOPE6 = 771, - SI_PACKING_ENVELOPE7 = 772, - SI_PACKING_ENVELOPE8 = 773, - SI_PACKING_ENVELOPE9 = 774, - SI_PACKING_ENVELOPE10 = 775, - SI_GLASTHEIM_TRANS = 776, - //SI_ZONGZI_POUCH_TRANS = 777, - SI_HEAT_BARREL_AFTER = 778, - SI_DECORATION_OF_MUSIC = 779, - //SI_OVERSEAEXPUP = 780, - //SI_CLOWN_N_GYPSY_CARD = 781, - //SI_OPEN_NPC_MARKET = 782, - //SI_BEEF_RIB_STEW = 783, - //SI_PORK_RIB_STEW = 784, - //SI_CHUSEOK_MONDAY = 785, - //SI_CHUSEOK_TUESDAY = 786, - //SI_CHUSEOK_WEDNESDAY = 787, - //SI_CHUSEOK_THURSDAY = 788, - //SI_CHUSEOK_FRIDAY = 789, - //SI_CHUSEOK_WEEKEND = 790, - //SI_ALL_LIGHTGUARD = 791, - //SI_ALL_LIGHTGUARD_COOL_TIME = 792, - SI_MTF_MHP = 793, - SI_MTF_MSP = 794, - SI_MTF_PUMPKIN = 795, - SI_MTF_HITFLEE = 796, - //SI_MTF_CRIDAMAGE2 = 797, - //SI_MTF_SPDRAIN = 798, - //SI_ACUO_MINT_GUM = 799, - - //SI_S_HEALPOTION = 800, - //SI_REUSE_LIMIT_S_HEAL_POTION = 801, - //SI_PLAYTIME_STATISTICS = 802, - //SI_GN_CHANGEMATERIAL_OPERATOR = 803, - //SI_GN_MIX_COOKING_OPERATOR = 804, - //SI_GN_MAKEBOMB_OPERATOR = 805, - //SI_GN_S_PHARMACY_OPERATOR = 806, - //SI_SO_EL_ANALYSIS_DISASSEMBLY_OPERATOR = 807, - //SI_SO_EL_ANALYSIS_COMBINATION_OPERATOR = 808, - //SI_NC_MAGICDECOY_OPERATOR = 809, - //SI_GUILD_STORAGE = 810, - //SI_GC_POISONINGWEAPON_OPERATOR = 811, - //SI_WS_WEAPONREFINE_OPERATOR = 812, - //SI_BS_REPAIRWEAPON_OPERATOR = 813, - //SI_GET_MAILBOX = 814, - //SI_JUMPINGCLAN = 815, - //SI_JP_OTP = 816, - //SI_HANDICAPTOLERANCE_LEVELGAP = 817, - //SI_MTF_RANGEATK2 = 818, - //SI_MTF_ASPD2 = 819, - //SI_MTF_MATK2 = 820, - //SI_SHOW_NPCHPBAR = 821, - SI_FLOWERSMOKE = 822, - SI_FSTONE = 823, - SI_DAILYSENDMAILCNT = 824, - //SI_QSCARABA = 825, - SI_LJOSALFAR = 826, - //SI_PAD_READER_KNIGHT = 827, - //SI_PAD_READER_CRUSADER = 828, - //SI_PAD_READER_BLACKSMITH = 829, - //SI_PAD_READER_ALCHEMIST = 830, - //SI_PAD_READER_ASSASSIN = 831, - //SI_PAD_READER_ROGUE = 832, - //SI_PAD_READER_WIZARD = 833, - //SI_PAD_READER_SAGE = 834, - //SI_PAD_READER_PRIEST = 835, - //SI_PAD_READER_MONK = 836, - //SI_PAD_READER_HUNTER = 837, - //SI_PAD_READER_BARD = 838, - //SI_PAD_READER_DANCER = 839, - //SI_PAD_READER_TAEKWON = 840, - //SI_PAD_READER_NINJA = 841, - //SI_PAD_READER_GUNSLINGER = 842, - //SI_PAD_READER_SUPERNOVICE = 843, - //SI_ESSENCE_OF_TIME = 844, - //SI_MINIGAME_ROULETTE = 845, - //SI_MINIGAME_GOLD_POINT = 846, - //SI_MINIGAME_SILVER_POINT = 847, - //SI_MINIGAME_BRONZE_POINT = 848, - SI_HAPPINESS_STAR = 849, - - //SI_SUMMEREVENT01 = 850, - //SI_SUMMEREVENT02 = 851, - //SI_SUMMEREVENT03 = 852, - //SI_SUMMEREVENT04 = 853, - //SI_SUMMEREVENT05 = 854, - //SI_MINIGAME_ROULETTE_BONUS_ITEM = 855, - SI_DRESS_UP = 856, - SI_MAPLE_FALLS = 857, - //SI_ALL_NIFLHEIM_RECALL = 858, - //SI_ = 859, - //SI_MTF_MARIONETTE = 860, - //SI_MTF_LUDE = 861, - //SI_MTF_CRUISER = 862, - SI_MERMAID_LONGING = 863, - SI_MAGICAL_FEATHER = 864, - //SI_DRACULA_CARD = 865, - //SI_ = 866, - //SI_LIMIT_POWER_BOOSTER = 867, - //SI_ = 868, - //SI_ = 869, - //SI_ = 870, - //SI_ = 871, - SI_TIME_ACCESSORY = 872, - //SI_EP16_DEF = 873, - //SI_NORMAL_ATKED_SP = 874, - //SI_BODYSTATE_STONECURSE = 875, - //SI_BODYSTATE_FREEZING = 876, - //SI_BODYSTATE_STUN = 877, - //SI_BODYSTATE_SLEEP = 878, - //SI_BODYSTATE_UNDEAD = 879, - //SI_BODYSTATE_STONECURSE_ING = 880, - //SI_BODYSTATE_BURNNING = 881, - //SI_BODYSTATE_IMPRISON = 882, - //SI_HEALTHSTATE_POISON = 883, - //SI_HEALTHSTATE_CURSE = 884, - //SI_HEALTHSTATE_SILENCE = 885, - //SI_HEALTHSTATE_CONFUSION = 886, - //SI_HEALTHSTATE_BLIND = 887, - //SI_HEALTHSTATE_ANGELUS = 888, - //SI_HEALTHSTATE_BLOODING = 889, - //SI_HEALTHSTATE_HEAVYPOISON = 890, - //SI_HEALTHSTATE_FEAR = 891, - //SI_CHERRY_BLOSSOM_CAKE = 892, - SI_SU_STOOP = 893, - SI_CATNIPPOWDER = 894, - SI_BLOSSOM_FLUTTERING = 895, - SI_SV_ROOTTWIST = 896, - //SI_ATTACK_PROPERTY_NOTHING = 897, - //SI_ATTACK_PROPERTY_WATER = 898, - //SI_ATTACK_PROPERTY_GROUND = 899, - - //SI_ATTACK_PROPERTY_FIRE = 900, - //SI_ATTACK_PROPERTY_WIND = 901, - //SI_ATTACK_PROPERTY_POISON = 902, - //SI_ATTACK_PROPERTY_SAINT = 903, - //SI_ATTACK_PROPERTY_DARKNESS = 904, - //SI_ATTACK_PROPERTY_TELEKINESIS = 905, - //SI_ATTACK_PROPERTY_UNDEAD = 906, - //SI_RESIST_PROPERTY_NOTHING = 907, - //SI_RESIST_PROPERTY_WATER = 908, - //SI_RESIST_PROPERTY_GROUND = 909, - //SI_RESIST_PROPERTY_FIRE = 910, - //SI_RESIST_PROPERTY_WIND = 911, - //SI_RESIST_PROPERTY_POISON = 912, - //SI_RESIST_PROPERTY_SAINT = 913, - //SI_RESIST_PROPERTY_DARKNESS = 914, - //SI_RESIST_PROPERTY_TELEKINESIS = 915, - //SI_RESIST_PROPERTY_UNDEAD = 916, - SI_BITESCAR = 917, - SI_ARCLOUSEDASH = 918, - SI_TUNAPARTY = 919, - SI_SHRIMP = 920, - SI_FRESHSHRIMP = 921, - //SI_PERIOD_RECEIVEITEM = 922, - //SI_PERIOD_PLUSEXP = 923, - //SI_PERIOD_PLUSJOBEXP = 924, - //SI_RUNEHELM = 925, - //SI_HELM_VERKANA = 926, - //SI_HELM_RHYDO = 927, - //SI_HELM_TURISUS = 928, - //SI_HELM_HAGALAS = 929, - //SI_HELM_ISIA = 930, - //SI_HELM_ASIR = 931, - //SI_HELM_URJ = 932, - SI_SUHIDE = 933, - //SI_ = 934, - //SI_DORAM_BUF_01 = 935, - //SI_DORAM_BUF_02 = 936, - SI_SPRITEMABLE = 937, - //SI_AID_PERIOD_RECEIVEITEM = 938, - //SI_AID_PERIOD_PLUSEXP = 939, - //SI_AID_PERIOD_PLUSJOBEXP = 940, - //SI_AID_PERIOD_DEADPENALTY = 941, - //SI_AID_PERIOD_ADDSTOREITEMCOUNT = 942, - //SI_ = 943, - //SI_ = 944, - //SI_ = 945, - //SI_ = 946, - //SI_ = 947, - //SI_ = 948, - //SI_ = 949, - //SI_HISS = 950, - //SI_ = 951, - //SI_NYANGGRASS = 952, - //SI_CHATTERING = 953, - //SI_ = 954, - //SI_ = 955, - //SI_ = 956, - //SI_ = 957, - //SI_ = 958, - //SI_ = 959, - //SI_ = 960, - //SI_GROOMING = 961, - //SI_PROTECTIONOFSHRIMP = 962, - //SI_EP16_2_BUFF_SS = 963, - //SI_EP16_2_BUFF_SC = 964, - //SI_EP16_2_BUFF_AC = 965, - //SI_GS_MAGICAL_BULLET = 966, - //SI_ = 967, - //SI_ = 968, - //SI_ = 969, - //SI_ = 970, - //SI_ = 971, - //SI_ = 972, - //SI_ = 973, - //SI_ = 974, - //SI_ = 975, - //SI_FALLEN_ANGEL = 976, - //SI_ = 977, - //SI_ = 978, - //SI_BLAZE_BEAD = 979, - //SI_FROZEN_BEAD = 980, - //SI_BREEZE_BEAD = 981, - //SI_ = 982, - //SI_AID_PERIOD_RECEIVEITEM_2ND = 983, - //SI_AID_PERIOD_PLUSEXP_2ND = 984, - //SI_AID_PERIOD_PLUSJOBEXP_2ND = 985, - //SI_PRONTERA_JP = 986, - //SI_ = 987, - //SI_GLOOM_CARD = 988, - //SI_PHARAOH_CARD = 989, - //SI_KIEL_CARD = 990, - //SI_ = 991, - //SI_CHEERUP = 992, - //SI_ = 993, - //SI_ = 994, - //SI_S_MANAPOTION = 995, - //SI_M_DEFSCROLL = 996, - //SI_ = 997, - //SI_ = 998, - //SI_ = 999, - //SI_AS_RAGGED_GOLEM_CARD = 1000, - //SI_LHZ_DUN_N1 = 1001, - //SI_LHZ_DUN_N2 = 1002, - //SI_LHZ_DUN_N3 = 1003, - //SI_LHZ_DUN_N4 = 1004, -#ifndef SI_MAX - SI_MAX, -#endif + SI_BLANK = -1, }; // JOINTBEAT stackable ailments @@ -2066,29 +1042,28 @@ 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 - str, agi, vit, int_, dex, luk, + uint16 str, agi, vit, int_, dex, luk; + uint32 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, @@ -2160,6 +1135,7 @@ struct sc_display_entry { struct status_change_entry { int timer; + int total_tick; int val1,val2,val3,val4; bool infinite_duration; }; @@ -2249,21 +1225,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]; @@ -2271,14 +1232,14 @@ BEGIN_ZEROED_BLOCK; /* Everything within this block will be memset to 0 when sta int SP_table[CLASS_COUNT][MAX_LEVEL + 1]; int aspd_base[CLASS_COUNT][MAX_SINGLE_WEAPON_TYPE+1]; // +1 for RENEWAL_ASPD sc_type Skill2SCTable[MAX_SKILL_DB]; // skill -> status - int IconChangeTable[SC_MAX]; // status -> "icon" (icon is a bit of a misnomer, since there exist values with no icon associated) + struct { + int id; + int relevant_bl_types; + } IconChangeTable[SC_MAX]; unsigned int ChangeFlagTable[SC_MAX]; // status -> flags int SkillChangeTable[SC_MAX]; // status -> skill - 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,13 +1268,13 @@ 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); unsigned int (*sc2scb_flag) (sc_type sc); - int (*type2relevant_bl_types) (int type); + int (*get_sc_relevant_bl_types) (sc_type type); int (*get_sc_type) (sc_type idx); + int (*get_sc_icon) (sc_type type); int (*damage) (struct block_list *src,struct block_list *target,int64 hp,int64 sp, int walkdelay, int flag); //Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills) int (*charge) (struct block_list* bl, int64 hp, int64 sp); @@ -2346,6 +1307,7 @@ struct status_interface { int (*isimmune) (struct block_list *bl); int (*get_sc_def) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int tick, int flag); int (*change_start) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int flag); + int (*change_start_sub) (struct block_list *src, struct block_list *bl, enum sc_type type, int rate, int val1, int val2, int val3, int val4, int tick, int total_tick, int flag); int (*change_end_) (struct block_list* bl, enum sc_type type, int tid, const char* file, int line); bool (*is_immune_to_status) (struct status_change* sc, enum sc_type type); bool (*is_boss_resist_sc) (enum sc_type type); @@ -2354,7 +1316,7 @@ struct status_interface { int (*change_start_set_option) (struct block_list *bl, struct status_change* sc, enum sc_type type, int val1, int val2, int val3, int val4); int (*get_val_flag) (enum sc_type type); void (*change_start_display) (struct map_session_data *sd, enum sc_type type, int val1, int val2, int val3, int val4); - bool (*change_start_unknown_sc) (struct block_list *src, struct block_list *bl, enum sc_type type, int calc_flag, int rate, int val1, int val2, int val3, int val4, int tick, int flag); + bool (*change_start_unknown_sc) (struct block_list *src, struct block_list *bl, enum sc_type type, int calc_flag, int rate, int val1, int val2, int val3, int val4, int total_tick, int flag); int (*kaahi_heal_timer) (int tid, int64 tick, int id, intptr_t data); int (*change_timer) (int tid, int64 tick, int id, intptr_t data); int (*change_timer_sub) (struct block_list* bl, va_list ap); @@ -2379,8 +1341,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 +1353,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 +1365,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 +1381,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 +1389,16 @@ 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); + bool (*read_scdb_libconfig) (void); + bool (*read_scdb_libconfig_sub) (struct config_setting_t *it, int idx, const char *source); + bool (*read_scdb_libconfig_sub_flag) (struct config_setting_t *it, int type, const char *source); + bool (*read_scdb_libconfig_sub_flag_additional) (struct config_setting_t *it, int type, const char *source); 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 (*set_sc) (uint16 skill_id, sc_type sc, 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/storage.c b/src/map/storage.c index a6f0715e7..b83bd8489 100644 --- a/src/map/storage.c +++ b/src/map/storage.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -285,7 +285,7 @@ static int storage_add_from_inventory(struct map_session_data *sd, int index, in if (sd->storage.aggregate > MAX_STORAGE) return 0; // storage full - if (index < 0 || index >= MAX_INVENTORY) + if (index < 0 || index >= sd->status.inventorySize) return 0; if (sd->status.inventory[index].nameid <= 0) @@ -623,7 +623,7 @@ static int storage_guild_storageadd(struct map_session_data *sd, int index, int if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE ) return 0; - if( index<0 || index>=MAX_INVENTORY ) + if (index < 0 || index >= sd->status.inventorySize) return 0; if( sd->status.inventory[index].nameid <= 0 ) diff --git a/src/map/storage.h b/src/map/storage.h index 5c6152894..7a342efb2 100644 --- a/src/map/storage.h +++ b/src/map/storage.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/stylist.c b/src/map/stylist.c new file mode 100644 index 000000000..8b4180971 --- /dev/null +++ b/src/map/stylist.c @@ -0,0 +1,229 @@ +/** +* This file is part of Hercules. +* http://herc.ws - http://github.com/HerculesWS/Hercules +* +* Copyright (C) 2018-2020 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 "map/stylist.h" + +#include "common/conf.h" +#include "common/db.h" +#include "common/memmgr.h" +#include "common/nullpo.h" +#include "common/showmsg.h" + +#include "map/clif.h" +#include "map/intif.h" +#include "map/itemdb.h" +#include "map/pc.h" +#include "map/script.h" + +static struct stylist_interface stylist_s; +struct stylist_interface *stylist; + +static bool stylist_read_db_libconfig(void) +{ + struct config_t stylist_conf; + struct config_setting_t *stylist_db = NULL, *it = NULL; + char config_filename[256]; + libconfig->format_db_path("stylist_db.conf", config_filename, sizeof(config_filename)); + int i = 0; + + if (!libconfig->load_file(&stylist_conf, config_filename)) + return false; + + if ((stylist_db = libconfig->setting_get_member(stylist_conf.root, "stylist_db")) == NULL) { + ShowError("can't read %s\n", config_filename); + return false; + } + + stylist->vector_clear(); + + while ((it = libconfig->setting_get_elem(stylist_db, i++))) { + stylist->read_db_libconfig_sub(it, i - 1, config_filename); + } + + libconfig->destroy(&stylist_conf); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, config_filename); + return true; +} + +static bool stylist_read_db_libconfig_sub(struct config_setting_t *it, int idx, const char *source) +{ + struct stylist_data_entry entry = { 0 }; + int i32 = 0, type = 0; + int64 i64 = 0; + + nullpo_ret(it); + nullpo_ret(source); + + if (!itemdb->lookup_const(it, "Type", &type) || type >= MAX_STYLIST_TYPE || type < 0) { + ShowWarning("stylist_read_db_libconfig_sub: Invalid or missing Type (%d) in \"%s\", entry #%d, skipping.\n", type, source, idx); + return false; + } + if (!itemdb->lookup_const(it, "Id", &i32) || i32 < 0) { + ShowWarning("stylist_read_db_libconfig_sub: Invalid or missing Id (%d) in \"%s\", entry #%d, skipping.\n", i32, source, idx); + return false; + } + entry.id = i32; + + if (libconfig->setting_lookup_int64(it, "Zeny", &i64)) { + if (i64 > MAX_ZENY) { + ShowWarning("stylist_read_db_libconfig_sub: zeny is too big in \"%s\", entry #%d, capping to MAX_ZENY.\n", source, idx); + entry.zeny = MAX_ZENY; + } else { + entry.zeny = (int)i64; + } + } + + if (itemdb->lookup_const(it, "ItemID", &i32)) + entry.itemid = i32; + + if (itemdb->lookup_const(it, "BoxItemID", &i32)) + entry.boxid = i32; + + if (libconfig->setting_lookup_bool(it, "AllowDoram", &i32)) + entry.allow_doram = (i32 == 0) ? false : true; + + VECTOR_ENSURE(stylist->data[type], 1, 1); + VECTOR_PUSH(stylist->data[type], entry); + return true; +} + +static bool stylist_validate_requirements(struct map_session_data *sd, int type, int16 idx) +{ + struct item it; + struct stylist_data_entry *entry; + + nullpo_retr(false, sd); + Assert_retr(false, type >= 0 && type < MAX_STYLIST_TYPE); + Assert_retr(false, idx >= 0 && idx < VECTOR_LENGTH(stylist->data[type])); + + entry = &VECTOR_INDEX(stylist->data[type], idx); + + if (sd->status.class == JOB_SUMMONER && (entry->allow_doram == false)) + return false; + + if (entry->id >= 0) { + if (entry->zeny != 0) { + if (sd->status.zeny < entry->zeny) + return false; + + sd->status.zeny -= entry->zeny; + clif->updatestatus(sd, SP_ZENY); + } else if (entry->itemid != 0) { + it.nameid = entry->itemid; + it.amount = 1; + return script->buildin_delitem_search(sd, &it, false); + } else if (entry->boxid != 0) { + it.nameid = entry->boxid; + it.amount = 1; + return script->buildin_delitem_search(sd, &it, false); + } + return true; + } + return false; +} + +static void stylist_send_rodexitem(struct map_session_data *sd, int itemid) +{ + struct rodex_message msg = { 0 }; + + nullpo_retv(sd); + + msg.receiver_id = sd->status.char_id; + msg.items[0].item.nameid = itemid; + msg.items[0].item.amount = 1; + msg.items[0].item.identify = 1; + msg.type = MAIL_TYPE_NPC | MAIL_TYPE_ITEM; + + safestrncpy(msg.sender_name, msg_txt(366), NAME_LENGTH); + safestrncpy(msg.title, msg_txt(367), RODEX_TITLE_LENGTH); + safestrncpy(msg.body, msg_txt(368), MAIL_BODY_LENGTH); + msg.send_date = (int)time(NULL); + msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; + + intif->rodex_sendmail(&msg); +} + +static void stylist_request_style_change(struct map_session_data *sd, int type, int16 idx, bool isitem) +{ + struct stylist_data_entry *entry; + + nullpo_retv(sd); + Assert_retv(idx > 0); + Assert_retv(type >= 0 && type < MAX_STYLIST_TYPE); + + if ((idx - 1) < VECTOR_LENGTH(stylist->data[type])) { + entry = &VECTOR_INDEX(stylist->data[type], idx - 1); + if (stylist->validate_requirements(sd, type, idx - 1)) { + if (isitem == false) + pc->changelook(sd, type, entry->id); + else + stylist->send_rodexitem(sd, entry->id); + } + } +} + +static void stylist_vector_init(void) +{ + for (int i = 0; i < MAX_STYLIST_TYPE; i++) + VECTOR_INIT(stylist->data[i]); +} +static void stylist_vector_clear(void) +{ + for (int i = 0; i < MAX_STYLIST_TYPE; i++) + VECTOR_CLEAR(stylist->data[i]); +} + +static void do_init_stylist(bool minimal) +{ + if (minimal) + return; + + // Initialize the db + stylist->vector_init(); + + // Load the db + stylist->read_db_libconfig(); +} + +static void do_final_stylist(void) +{ + // Clear the db + stylist->vector_clear(); +} + +void stylist_defaults(void) +{ + stylist = &stylist_s; + + /* core */ + stylist->init = do_init_stylist; + stylist->final = do_final_stylist; + /* */ + stylist->vector_init = stylist_vector_init; + stylist->vector_clear = stylist_vector_clear; + /* database */ + stylist->read_db_libconfig = stylist_read_db_libconfig; + stylist->read_db_libconfig_sub = stylist_read_db_libconfig_sub; + /* */ + stylist->request_style_change = stylist_request_style_change; + stylist->validate_requirements = stylist_validate_requirements; + stylist->send_rodexitem = stylist_send_rodexitem; +} diff --git a/src/map/stylist.h b/src/map/stylist.h new file mode 100644 index 000000000..1b8e20616 --- /dev/null +++ b/src/map/stylist.h @@ -0,0 +1,69 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2018-2020 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_STYLIST_H +#define MAP_STYLIST_H + +#include "common/hercules.h" +#include "map/map.h" // LOOK_MAX + +struct map_session_data; + +/* Maximum available types for stylist */ +#ifndef MAX_STYLIST_TYPE +#define MAX_STYLIST_TYPE LOOK_MAX +#endif + +/* Stylist data [Asheraf/Hercules]*/ +struct stylist_data_entry { + int16 id; + int32 zeny; + int itemid; + int boxid; + bool allow_doram; +}; + +/** + * stylist.c Interface + **/ +struct stylist_interface { + VECTOR_DECL(struct stylist_data_entry) data[MAX_STYLIST_TYPE]; + + void (*init) (bool minimal); + void (*final) (void); + + void (*vector_init) (void); + void (*vector_clear) (void); + + bool (*read_db_libconfig) (void); + bool (*read_db_libconfig_sub) (struct config_setting_t *it, int idx, const char *source); + + void (*request_style_change) (struct map_session_data *sd, int type, int16 idx, bool isitem); + bool (*validate_requirements) (struct map_session_data *sd, int type, int16 idx); + void (*send_rodexitem) (struct map_session_data *sd, int itemid); + +}; + +#ifdef HERCULES_CORE +void stylist_defaults(void); +#endif // HERCULES_CORE + +HPShared struct stylist_interface *stylist; ///< Pointer to the stylist interface. + +#endif /* MAP_STYLIST_H */ diff --git a/src/map/trade.c b/src/map/trade.c index d4f846d19..e727c3c70 100644 --- a/src/map/trade.c +++ b/src/map/trade.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,6 +50,9 @@ static void trade_traderequest(struct map_session_data *sd, struct map_session_d { nullpo_retv(sd); + if (sd == target_sd) + return; + if (map->list[sd->bl.m].flag.notrade) { clif->message (sd->fd, msg_sd(sd,272)); // You can't trade in this map return; @@ -163,8 +166,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); @@ -209,7 +212,7 @@ static int impossible_trade_check(struct map_session_data *sd) // remove this part: arrows can be trade and equipped // re-added! [celest] // remove equipped items (they can not be trade) - for (i = 0; i < MAX_INVENTORY; i++) + for (i = 0; i < sd->status.inventorySize; i++) if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & EQP_AMMO)) memset(&inventory[i], 0, sizeof(struct item)); @@ -218,14 +221,14 @@ static int impossible_trade_check(struct map_session_data *sd) if (!sd->deal.item[i].amount) continue; index = sd->deal.item[i].index; - if (index < 0 || index >= MAX_INVENTORY) + if (index < 0 || index >= sd->status.inventorySize) return 1; if (inventory[index].amount < sd->deal.item[i].amount) { // if more than the player have -> hack snprintf(message_to_gm, sizeof(message_to_gm), msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has. - intif->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); + pc->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); snprintf(message_to_gm, sizeof(message_to_gm), msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them. - intif->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); + pc->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); // if we block people if (battle_config.ban_hack_trade < 0) { chrif->char_ask_name(-1, sd->status.name, CHAR_ASK_NAME_BLOCK, 0, 0, 0, 0, 0, 0); @@ -242,7 +245,7 @@ static int impossible_trade_check(struct map_session_data *sd) // message about the ban safestrncpy(message_to_gm, msg_txt(508), sizeof(message_to_gm)); // This player hasn't been banned (Ban option is disabled). - intif->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); + pc->wis_message_to_gm(map->wisp_server_name, PC_PERM_RECEIVE_HACK_INFO, message_to_gm); return 1; } inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory @@ -281,9 +284,9 @@ static int trade_check(struct map_session_data *sd, struct map_session_data *tsd return 0; //qty Exploit? data = itemdb->search(inventory[n].nameid); - i = MAX_INVENTORY; + i = tsd->status.inventorySize; if (itemdb->isstackable2(data)) { //Stackable item. - for(i = 0; i < MAX_INVENTORY; i++) + for(i = 0; i < tsd->status.inventorySize; i++) if (inventory2[i].nameid == inventory[n].nameid && inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] && inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) { @@ -295,9 +298,9 @@ static int trade_check(struct map_session_data *sd, struct map_session_data *tsd } } - if (i == MAX_INVENTORY) {// look for an empty slot. - for(i = 0; i < MAX_INVENTORY && inventory2[i].nameid; i++); - if (i == MAX_INVENTORY) + if (i == tsd->status.inventorySize) {// look for an empty slot. + for (i = 0; i < tsd->status.inventorySize && inventory2[i].nameid; i++); + if (i == tsd->status.inventorySize) return 0; memcpy(&inventory2[i], &inventory[n], sizeof(struct item)); inventory2[i].amount = amount; @@ -308,15 +311,15 @@ static int trade_check(struct map_session_data *sd, struct map_session_data *tsd if (!amount) continue; n = tsd->deal.item[trade_i].index; - if (n < 0 || n >= MAX_INVENTORY) + if (n < 0 || n >= tsd->status.inventorySize) return 0; if (amount > inventory2[n].amount) return 0; // search if it's possible to add item (for full inventory) data = itemdb->search(inventory2[n].nameid); - i = MAX_INVENTORY; + i = sd->status.inventorySize; if (itemdb->isstackable2(data)) { - for(i = 0; i < MAX_INVENTORY; i++) + for(i = 0; i < sd->status.inventorySize; i++) if (inventory[i].nameid == inventory2[n].nameid && inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] && inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) { @@ -327,9 +330,9 @@ static int trade_check(struct map_session_data *sd, struct map_session_data *tsd break; } } - if (i == MAX_INVENTORY) { - for(i = 0; i < MAX_INVENTORY && inventory[i].nameid; i++); - if (i == MAX_INVENTORY) + if (i == sd->status.inventorySize) { + for(i = 0; i < sd->status.inventorySize && inventory[i].nameid; i++); + if (i == sd->status.inventorySize) return 0; memcpy(&inventory[i], &inventory2[n], sizeof(struct item)); inventory[i].amount = amount; @@ -369,7 +372,7 @@ static void trade_tradeadditem(struct map_session_data *sd, short index, short a index -= 2; // 0 is for zeny, 1 is unknown. Gravity, go figure... //Item checks... - if( index < 0 || index >= MAX_INVENTORY ) + if (index < 0 || index >= sd->status.inventorySize) return; if( amount < 0 || amount > sd->status.inventory[index].amount ) return; diff --git a/src/map/trade.h b/src/map/trade.h index ac732122f..da55f5057 100644 --- a/src/map/trade.h +++ b/src/map/trade.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/unit.c b/src/map/unit.c index ee856cb76..19f09f83c 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -57,6 +57,7 @@ #include "common/showmsg.h" #include "common/socket.h" #include "common/timer.h" +#include "common/utils.h" #include <stdio.h> #include <stdlib.h> @@ -71,20 +72,69 @@ struct unit_interface *unit; /** * Returns the unit_data for the given block_list. If the object is using * shared unit_data (i.e. in case of BL_NPC), it returns the shared data. - * @param bl block_list to process + * + * __Warning:__ if bl->type is not known or NULL, + * an assertion will be triggered and NULL returned. + * @param bl block_list to process, it is expected to be not NULL. * @return a pointer to the given object's unit_data **/ static struct unit_data *unit_bl2ud(struct block_list *bl) { - if (bl == NULL) return NULL; - if (bl->type == BL_PC) return &BL_UCAST(BL_PC, bl)->ud; - if (bl->type == BL_MOB) return &BL_UCAST(BL_MOB, bl)->ud; - if (bl->type == BL_PET) return &BL_UCAST(BL_PET, bl)->ud; - if (bl->type == BL_NPC) return BL_UCAST(BL_NPC, bl)->ud; - if (bl->type == BL_HOM) return &BL_UCAST(BL_HOM, bl)->ud; - if (bl->type == BL_MER) return &BL_UCAST(BL_MER, bl)->ud; - if (bl->type == BL_ELEM) return &BL_UCAST(BL_ELEM, bl)->ud; - return NULL; + Assert_retr(NULL, bl != NULL); + switch (bl->type) { + case BL_PC: + return &BL_UCAST(BL_PC, bl)->ud; + case BL_MOB: + return &BL_UCAST(BL_MOB, bl)->ud; + case BL_PET: + return &BL_UCAST(BL_PET, bl)->ud; + case BL_NPC: + return BL_UCAST(BL_NPC, bl)->ud; + case BL_HOM: + return &BL_UCAST(BL_HOM, bl)->ud; + case BL_MER: + return &BL_UCAST(BL_MER, bl)->ud; + case BL_ELEM: + return &BL_UCAST(BL_ELEM, bl)->ud; + case BL_SKILL: // No assertion to not spam the server console when attacking a skill type unit such as Ice Wall. + return NULL; + default: + Assert_retr(NULL, false); + } +} + +/** + * Returns the const unit_data for the given const block_list. If the object is using + * shared unit_data (i.e. in case of BL_NPC), it returns the shared data. + * + * __Warning:__ if bl->type is not known or NULL, + * an assertion will be triggered and NULL returned. + * @param bl block_list to process, it is expected to be not NULL. + * @return a pointer to the given object's unit_data + **/ +static const struct unit_data *unit_cbl2ud(const struct block_list *bl) +{ + Assert_retr(NULL, bl != NULL); + switch (bl->type) { + case BL_PC: + return &BL_UCCAST(BL_PC, bl)->ud; + case BL_MOB: + return &BL_UCCAST(BL_MOB, bl)->ud; + case BL_PET: + return &BL_UCCAST(BL_PET, bl)->ud; + case BL_NPC: + return BL_UCCAST(BL_NPC, bl)->ud; + case BL_HOM: + return &BL_UCCAST(BL_HOM, bl)->ud; + case BL_MER: + return &BL_UCCAST(BL_MER, bl)->ud; + case BL_ELEM: + return &BL_UCCAST(BL_ELEM, bl)->ud; + case BL_SKILL: // No assertion to not spam the server console when attacking a skill type unit such as Ice Wall. + return NULL; + default: + Assert_retr(NULL, false); + } } /** @@ -105,42 +155,46 @@ static struct unit_data *unit_bl2ud2(struct block_list *bl) return unit->bl2ud(bl); } -static int unit_walktoxy_sub(struct block_list *bl) +/** + * TODO: understand purpose of this function + * @param bl block_list to process + * @return 0: success, 1: fail, 2: nullpointer + */ +static int unit_walk_toxy_sub(struct block_list *bl) { - int i; - struct walkpath_data wpd; - struct unit_data *ud = NULL; - - nullpo_retr(1, bl); - ud = unit->bl2ud(bl); - if(ud == NULL) return 0; + nullpo_retr(2, bl); + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) + return 2; - memset(&wpd, 0, sizeof(wpd)); + struct walkpath_data wpd = {0}; - if( !path->search(&wpd,bl,bl->m,bl->x,bl->y,ud->to_x,ud->to_y,ud->state.walk_easy,CELL_CHKNOPASS) ) - return 0; + if (!path->search(&wpd, bl, bl->m, bl->x, bl->y, ud->to_x, ud->to_y, ud->state.walk_easy, CELL_CHKNOPASS)) + return 1; #ifdef OFFICIAL_WALKPATH - if( !path->search_long(NULL, bl, bl->m, bl->x, bl->y, ud->to_x, ud->to_y, CELL_CHKNOPASS) // Check if there is an obstacle between - && wpd.path_len > 14 // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] - && (bl->type != BL_NPC) ) // If type is a NPC, please disregard. - return 0; + if (bl->type != BL_NPC // If type is an NPC, disregard. + && !path->search_long(NULL, bl, bl->m, bl->x, bl->y, ud->to_x, ud->to_y, CELL_CHKNOPASS) // Check if there is an obstacle between + && wpd.path_len > 14) { // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] + return 1; + } #endif - memcpy(&ud->walkpath,&wpd,sizeof(wpd)); + ud->walkpath = wpd; - if (ud->target_to && ud->chaserange>1) { - //Generally speaking, the walk path is already to an adjacent tile - //so we only need to shorten the path if the range is greater than 1. + if (ud->target_to != 0 && ud->chaserange > 1) { + // Generally speaking, the walk path is already to an adjacent tile + // so we only need to shorten the path if the range is greater than 1. - //Trim the last part of the path to account for range, - //but always move at least one cell when requested to move. - for (i = (ud->chaserange*10)-10; i > 0 && ud->walkpath.path_len>1;) { - uint8 dir; + // Trim the last part of the path to account for range, + // but always move at least one cell when requested to move. + for (int i = ud->chaserange * 10 - 10; i > 0 && ud->walkpath.path_len > 1;) { + enum unit_dir dir; ud->walkpath.path_len--; dir = ud->walkpath.path[ud->walkpath.path_len]; - if (dir&1) - i -= MOVE_COST*20; //When chasing, units will target a diamond-shaped area in range [Playtester] + Assert_retr(1, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); + if (unit_is_diagonal_dir(dir)) + i -= MOVE_COST * 20; // When chasing, units will target a diamond-shaped area in range [Playtester] else i -= MOVE_COST; ud->to_x -= dirx[dir]; @@ -148,7 +202,7 @@ static int unit_walktoxy_sub(struct block_list *bl) } } - ud->state.change_walk_target=0; + ud->state.change_walk_target = 0; if (bl->type == BL_PC) { struct map_session_data *sd = BL_UCAST(BL_PC, bl); @@ -157,15 +211,17 @@ static int unit_walktoxy_sub(struct block_list *bl) } clif->move(ud); - if(ud->walkpath.path_pos>=ud->walkpath.path_len) - i = -1; - else if(ud->walkpath.path[ud->walkpath.path_pos]&1) - i = status->get_speed(bl)*MOVE_DIAGONAL_COST/MOVE_COST; + int timer_delay; + if (ud->walkpath.path_pos >= ud->walkpath.path_len) + timer_delay = -1; + else if ((ud->walkpath.path[ud->walkpath.path_pos] & 1) != 0) + timer_delay = status->get_speed(bl) * MOVE_DIAGONAL_COST / MOVE_COST; else - i = status->get_speed(bl); - if( i > 0) - ud->walktimer = timer->add(timer->gettick()+i,unit->walktoxy_timer,bl->id,i); - return 1; + timer_delay = status->get_speed(bl); + + if (timer_delay > 0) + ud->walktimer = timer->add(timer->gettick() + timer_delay, unit->walk_toxy_timer, bl->id, 0); //TODO: check if unit->walk_toxy_timer uses any intptr data + return 0; } /** @@ -173,417 +229,456 @@ static int unit_walktoxy_sub(struct block_list *bl) * @param tid: Timer ID * @param tick: Unused * @param id: ID of bl to do the action - * @param data: Not used - * @return 1: Success 0: Fail (No valid bl) + * @param data: Unused + * @return 0: success, 1: fail, 2: nullpointer */ -static int unit_step_timer(int tid, int64 tick, int id, intptr_t data) +static int unit_steptimer(int tid, int64 tick, int id, intptr_t data) { - struct block_list *bl; - struct unit_data *ud; - int target_id; - - bl = map->id2bl(id); - - if (!bl || bl->prev == NULL) - return 0; - - ud = unit->bl2ud(bl); - - if(!ud) - return 0; + struct block_list *bl = map->id2bl(id); + if (bl == NULL || bl->prev == NULL) + return 2; + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) + return 2; - if(ud->steptimer != tid) { - ShowError("unit_step_timer mismatch %d != %d\n",ud->steptimer,tid); - return 0; + if (ud->steptimer != tid) { + ShowError("unit_steptimer mismatch %d != %d\n", ud->steptimer, tid); + return 1; } ud->steptimer = INVALID_TIMER; - if(!ud->stepaction) - return 0; + if (!ud->stepaction) + return 1; - //Set to false here because if an error occurs, it should not be executed again + // Set to false here because if an error occurs, it should not be executed again ud->stepaction = false; - if(!ud->target_to) - return 0; + if (ud->target_to == 0) + return 1; - //Flush target_to as it might contain map coordinates which should not be used by other functions - target_id = ud->target_to; + // Flush target_to as it might contain map coordinates which should not be used by other functions + int target_id = ud->target_to; ud->target_to = 0; - //If stepaction is set then we remembered a client request that should be executed on the next step - //Execute request now if target is in attack range - if(ud->stepskill_id && skill->get_inf(ud->stepskill_id) & INF_GROUND_SKILL) { - //Execute ground skill + // If stepaction is set then we remembered a client request that should be executed on the next step + // Execute request now if target is in attack range + if (ud->stepskill_id != 0 && (skill->get_inf(ud->stepskill_id) & INF_GROUND_SKILL) != 0) { + // Execute ground skill struct map_data *md = &map->list[bl->m]; - unit->skilluse_pos(bl, target_id%md->xs, target_id/md->xs, ud->stepskill_id, ud->stepskill_lv); + unit->skilluse_pos(bl, target_id % md->xs, target_id / md->xs, ud->stepskill_id, ud->stepskill_lv); } else { - //If a player has target_id set and target is in range, attempt attack + // If a player has target_id set and target is in range, attempt attack struct block_list *tbl = map->id2bl(target_id); - if (!tbl || !status->check_visibility(bl, tbl)) { - return 0; - } - if(ud->stepskill_id == 0) { - //Execute normal attack - unit->attack(bl, tbl->id, (ud->state.attack_continue) + 2); - } else { - //Execute non-ground skill - unit->skilluse_id(bl, tbl->id, ud->stepskill_id, ud->stepskill_lv); - } + nullpo_retr(2, tbl); + if (status->check_visibility(bl, tbl) == 0) // Target not visible + return 1; + if (ud->stepskill_id == 0) + unit->attack(bl, tbl->id, ud->state.attack_continue + 2); // Execute normal attack + else + unit->skilluse_id(bl, tbl->id, ud->stepskill_id, ud->stepskill_lv); // Execute non-ground skill } - return 1; + return 0; } -static int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) +/** + * Warps homunculus or mercenary towards his master in case he's too far away for 3 seconds. + * @param master_bl: block_list of master + * @param slave_bl: block_list of homunculus/mercenary master owns + * @return 0: success, 1: fail + */ +static int unit_warpto_master(struct block_list *master_bl, struct block_list *slave_bl) { - int i; - int x,y,dx,dy; - unsigned char icewall_walk_block; - uint8 dir; - struct block_list *bl; - struct map_session_data *sd; - struct mob_data *md; - struct unit_data *ud; - struct mercenary_data *mrd; + nullpo_retr(1, master_bl); + nullpo_retr(1, slave_bl); + int64 *masterteleport_timer; + struct homun_data *hd = BL_CAST(BL_HOM, slave_bl); + struct mercenary_data *md = BL_CAST(BL_MER, slave_bl); + + bool check = true; + if (hd != NULL) { + masterteleport_timer = &hd->masterteleport_timer; + check = homun_alive(hd); + } else if (md != NULL) { + masterteleport_timer = &md->masterteleport_timer; + } else { + return 1; + } - bl = map->id2bl(id); - if(bl == NULL) - return 0; - sd = BL_CAST(BL_PC, bl); - md = BL_CAST(BL_MOB, bl); - mrd = BL_CAST(BL_MER, bl); - ud = unit->bl2ud(bl); + if (check && !check_distance_bl(master_bl, slave_bl, MAX_MER_DISTANCE)) { + if (*masterteleport_timer == 0) { + *masterteleport_timer = timer->gettick(); + return 0; + } else if (DIFF_TICK(timer->gettick(), *masterteleport_timer) > 3000) { + unit->warp(slave_bl, master_bl->m, master_bl->x, master_bl->y, CLR_TELEPORT); + } + } + *masterteleport_timer = 0; // resets tick in case he isn't far anymore. - if(ud == NULL) return 0; + return 0; +} + +/** + * Timer for walking to target coordinates or object. + * @param tid: timer id + * @param tick: tick + * @param id: id of bl to do the action + * @param data: unused + * @return 0: success, 1: fail + */ +static int unit_walk_toxy_timer(int tid, int64 tick, int id, intptr_t data) +{ + struct block_list *bl = map->id2bl(id); + if (bl == NULL) + return 1; + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) + return 1; - if(ud->walktimer != tid){ + if (ud->walktimer != tid) { ShowError("unit_walk_timer mismatch %d != %d\n",ud->walktimer,tid); - return 0; + return 1; } + ud->walktimer = INVALID_TIMER; - if (bl->prev == NULL) return 0; // Stop moved because it is missing from the block_list - if(ud->walkpath.path_pos>=ud->walkpath.path_len) - return 0; + if (bl->prev == NULL) // Stop moved because it is missing from the block_list. + return 1; - if(ud->walkpath.path[ud->walkpath.path_pos]>=8) + if (ud->walkpath.path_pos >= ud->walkpath.path_len) return 1; - x = bl->x; - y = bl->y; - dir = ud->walkpath.path[ud->walkpath.path_pos]; + enum unit_dir dir = ud->walkpath.path[ud->walkpath.path_pos]; + Assert_retr(1, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); + int x = bl->x; + int y = bl->y; + ud->dir = dir; - dx = dirx[(int)dir]; - dy = diry[(int)dir]; + int dx = dirx[dir]; + int dy = diry[dir]; - //Get icewall walk block depending on boss mode (players can't be trapped) - if(md && md->status.mode&MD_BOSS) - icewall_walk_block = battle_config.boss_icewall_walk_block; - else if(md) - icewall_walk_block = battle_config.mob_icewall_walk_block; - else - icewall_walk_block = 0; + // Get icewall walk block depending on boss mode (players can't be trapped) + unsigned char icewall_walk_block = 0; + struct mob_data *md = BL_CAST(BL_MOB, bl); + if (md != NULL) { + if ((md->status.mode & MD_BOSS) != 0) + icewall_walk_block = battle_config.boss_icewall_walk_block; + else + icewall_walk_block = battle_config.mob_icewall_walk_block; + } - //Monsters will walk into an icewall from the west and south if they already started walking + // Monsters will walk into an icewall from the west and south if they already started walking if (map->getcell(bl->m, bl, x + dx, y + dy, CELL_CHKNOPASS) && (icewall_walk_block == 0 || !map->getcell(bl->m, bl, x + dx, y + dy, CELL_CHKICEWALL) || dx < 0 || dy < 0)) - return unit->walktoxy_sub(bl); + return unit->walk_toxy_sub(bl); - //Monsters can only leave icewalls to the west and south - //But if movement fails more than icewall_walk_block times, they can ignore this rule - if (md && md->walktoxy_fail_count < icewall_walk_block && map->getcell(bl->m, bl, x, y, CELL_CHKICEWALL) && (dx > 0 || dy > 0)) { - //Needs to be done here so that rudeattack skills are invoked + // Monsters can only leave icewalls to the west and south + // But if movement fails more than icewall_walk_block times, they can ignore this rule + if (md != NULL && md->walktoxy_fail_count < icewall_walk_block && map->getcell(bl->m, bl, x, y, CELL_CHKICEWALL) != 0 && (dx > 0 || dy > 0)) { + // Needs to be done here so that rudeattack skills are invoked md->walktoxy_fail_count++; clif->fixpos(bl); - //Monsters in this situation first use a chase skill, then unlock target and then use an idle skill - if (!(++ud->walk_count%WALK_SKILL_INTERVAL)) + // Monsters in this situation first use a chase skill, then unlock target and then use an idle skill + if ((++ud->walk_count % WALK_SKILL_INTERVAL) == 0) mob->skill_use(md, tick, -1); mob->unlocktarget(md, tick); - if (!(++ud->walk_count%WALK_SKILL_INTERVAL)) + if ((++ud->walk_count % WALK_SKILL_INTERVAL) != 0) mob->skill_use(md, tick, -1); - return 0; + return 1; } + struct map_session_data *sd = BL_CAST(BL_PC, bl); //Refresh view for all those we lose sight - map->foreachinmovearea(clif->outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl); + map->foreachinmovearea(clif->outsight, bl, AREA_SIZE, dx, dy, (sd != NULL ? BL_ALL : BL_PC), bl); x += dx; y += dy; map->moveblock(bl, x, y, tick); - ud->walk_count++; //walked cell counter, to be used for walk-triggered skills. [Skotlex] + ud->walk_count++; // walked cell counter, to be used for walk-triggered skills. [Skotlex] status_change_end(bl, SC_ROLLINGCUTTER, INVALID_TIMER); //If you move, you lose your counters. [malufett] if (bl->x != x || bl->y != y || ud->walktimer != INVALID_TIMER) - return 0; //map->moveblock has altered the object beyond what we expected (moved/warped it) + return 1; // map->moveblock has altered the object beyond what we expected (moved/warped it) ud->walktimer = -2; // arbitrary non-INVALID_TIMER value to make the clif code send walking packets - map->foreachinmovearea(clif->insight, bl, AREA_SIZE, -dx, -dy, sd?BL_ALL:BL_PC, bl); + map->foreachinmovearea(clif->insight, bl, AREA_SIZE, -dx, -dy, (sd != NULL ? BL_ALL : BL_PC), bl); ud->walktimer = INVALID_TIMER; - if(sd) { - if( sd->touching_id ) - npc->touchnext_areanpc(sd,false); + struct mercenary_data *mrd = BL_CAST(BL_MER, bl); + if (sd != NULL) { + if (sd->touching_id != 0) + npc->touchnext_areanpc(sd, false); if (map->getcell(bl->m, bl, x, y, CELL_CHKNPC)) { - npc->touch_areanpc(sd,bl->m,x,y); + npc->touch_areanpc(sd, bl->m, x, y); if (bl->prev == NULL) //Script could have warped char, abort remaining of the function. return 0; - } else + } else { npc->untouch_areanpc(sd, bl->m, x, y); - - if( sd->md ) { // mercenary should be warped after being 3 seconds too far from the master [greenbox] - if( !check_distance_bl(&sd->bl, &sd->md->bl, MAX_MER_DISTANCE) ) { - if (sd->md->masterteleport_timer == 0) - sd->md->masterteleport_timer = timer->gettick(); - else if (DIFF_TICK(timer->gettick(), sd->md->masterteleport_timer) > 3000) { - sd->md->masterteleport_timer = 0; - unit->warp( &sd->md->bl, sd->bl.m, sd->bl.x, sd->bl.y, CLR_TELEPORT ); - } - } else // reset the tick, he is not far anymore - sd->md->masterteleport_timer = 0; - } - if( sd->hd ) { - if( homun_alive(sd->hd) && !check_distance_bl(&sd->bl, &sd->hd->bl, MAX_MER_DISTANCE) ) { - if (sd->hd->masterteleport_timer == 0) - sd->hd->masterteleport_timer = timer->gettick(); - else if (DIFF_TICK(timer->gettick(), sd->hd->masterteleport_timer) > 3000) { - sd->hd->masterteleport_timer = 0; - unit->warp( &sd->hd->bl, sd->bl.m, sd->bl.x, sd->bl.y, CLR_TELEPORT ); - } - } else - sd->hd->masterteleport_timer = 0; } + + if (sd->md != NULL) // mercenary should be warped after being 3 seconds too far from the master [greenbox] + unit->warpto_master(bl, &sd->md->bl); + if (sd->hd != NULL) + unit->warpto_master(bl, &sd->hd->bl); } else if (md) { - //Movement was successful, reset walktoxy_fail_count + // Movement was successful, reset walktoxy_fail_count md->walktoxy_fail_count = 0; - if (map->getcell(bl->m, bl, x, y, CELL_CHKNPC)) { - if( npc->touch_areanpc2(md) ) return 0; // Warped - } else + + if (map->getcell(bl->m, bl, x, y, CELL_CHKNPC) != 0 && npc->touch_areanpc2(md)) + return 0; // Warped + else md->areanpc_id = 0; - if (md->min_chase > md->db->range3) md->min_chase--; - //Walk skills are triggered regardless of target due to the idle-walk mob state. - //But avoid triggering on stop-walk calls. - if (tid != INVALID_TIMER - && !(ud->walk_count%WALK_SKILL_INTERVAL) - && map->list[bl->m].users > 0 - && mob->skill_use(md, tick, -1) - ) { + + if (md->min_chase > md->db->range3) + md->min_chase--; + // Walk skills are triggered regardless of target due to the idle-walk mob state. + // But avoid triggering on stop-walk calls. + if (tid != INVALID_TIMER && (ud->walk_count % WALK_SKILL_INTERVAL) == 0 + && map->list[bl->m].users > 0 && mob->skill_use(md, tick, -1) == 0) { + // Walk skills are supposed to be used while walking if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER) - && md->state.skillstate != MSS_WALK //Walk skills are supposed to be used while walking - ) { - //Skill used, abort walking - clif->fixpos(bl); //Fix position as walk has been canceled. - return 0; + && md->state.skillstate != MSS_WALK) { + // Skill used, abort walking + clif->fixpos(bl); // Fix position as walk has been canceled. + return 1; } - //Resend walk packet for proper Self Destruction display. + // Resend walk packet for proper Self Destruction display. clif->move(ud); } - } - else if( mrd && mrd->master ) - { - if (!check_distance_bl(&mrd->master->bl, bl, MAX_MER_DISTANCE)) - { - // mercenary should be warped after being 3 seconds too far from the master [greenbox] - if (mrd->masterteleport_timer == 0) - { - mrd->masterteleport_timer = timer->gettick(); - } - else if (DIFF_TICK(timer->gettick(), mrd->masterteleport_timer) > 3000) - { - mrd->masterteleport_timer = 0; - unit->warp( bl, mrd->master->bl.m, mrd->master->bl.x, mrd->master->bl.y, CLR_TELEPORT ); - } - } - else - { - mrd->masterteleport_timer = 0; - } + } else if (mrd != NULL && mrd->master != NULL) { + unit->warpto_master(&mrd->master->bl, bl); } - if(tid == INVALID_TIMER) //A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant. + if(tid == INVALID_TIMER) // A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant. return 0; - //If stepaction is set then we remembered a client request that should be executed on the next step - if (ud->stepaction && ud->target_to) { - //Delete old stepaction even if not executed yet, the latest command is what counts - if(ud->steptimer != INVALID_TIMER) { - timer->delete(ud->steptimer, unit->step_timer); + // If stepaction is set then we remembered a client request that should be executed on the next step + if (ud->stepaction && ud->target_to != 0) { + // Delete old stepaction even if not executed yet, the latest command is what counts + if (ud->steptimer != INVALID_TIMER) { + timer->delete(ud->steptimer, unit->steptimer); ud->steptimer = INVALID_TIMER; } - //Delay stepactions by half a step (so they are executed at full step) - if(ud->walkpath.path[ud->walkpath.path_pos]&1) - i = status->get_speed(bl)*14/20; + // Delay stepactions by half a step (so they are executed at full step) + int timer_delay; + if ((ud->walkpath.path[ud->walkpath.path_pos] & 1) != 0) + timer_delay = status->get_speed(bl) * 14 / 20; else - i = status->get_speed(bl)/2; - ud->steptimer = timer->add(tick+i, unit->step_timer, bl->id, 0); + timer_delay = status->get_speed(bl) / 2; + ud->steptimer = timer->add(tick + timer_delay, unit->steptimer, bl->id, 0); } - if(ud->state.change_walk_target) { - if(unit->walktoxy_sub(bl)) { - return 1; - } else { - clif->fixpos(bl); + if (ud->state.change_walk_target) { + if (unit->walk_toxy_sub(bl) == 0) return 0; - } + clif->fixpos(bl); + return 1; } + int timer_delay; ud->walkpath.path_pos++; if(ud->walkpath.path_pos>=ud->walkpath.path_len) - i = -1; - else if(ud->walkpath.path[ud->walkpath.path_pos]&1) - i = status->get_speed(bl)*14/10; + timer_delay = -1; + else if ((ud->walkpath.path[ud->walkpath.path_pos] & 1) != 0) + timer_delay = status->get_speed(bl) * 14 / 10; else - i = status->get_speed(bl); + timer_delay = status->get_speed(bl); - if(i > 0) { - ud->walktimer = timer->add(tick+i,unit->walktoxy_timer,id,i); - if( md && DIFF_TICK(tick,md->dmgtick) < 3000 )//not required not damaged recently + if (timer_delay > 0) { + ud->walktimer = timer->add(tick + timer_delay, unit->walk_toxy_timer, id, 0); + if (md != NULL && DIFF_TICK(tick, md->dmgtick) < 3000) // not required not damaged recently clif->move(ud); - } else if(ud->state.running) { - //Keep trying to run. - if ( !(unit->run(bl, NULL, SC_RUN) || unit->run(bl, sd, SC_WUGDASH)) ) + } else if (ud->state.running != 0) { + // Keep trying to run. + if (!(unit->run(bl, NULL, SC_RUN) || unit->run(bl, sd, SC_WUGDASH))) ud->state.running = 0; - } else if (!ud->stepaction && ud->target_to) { - //Update target trajectory. + } else if (!ud->stepaction && ud->target_to != 0) { + // Update target trajectory. struct block_list *tbl = map->id2bl(ud->target_to); - if (!tbl || !status->check_visibility(bl, tbl)) { - //Cancel chase. + if (tbl == NULL || status->check_visibility(bl, tbl) == 0) { // not visible + // Cancel chase. ud->to_x = bl->x; ud->to_y = bl->y; - if (tbl && bl->type == BL_MOB && mob->warpchase(BL_UCAST(BL_MOB, bl), tbl)) + if (tbl != NULL && bl->type == BL_MOB && mob->warpchase(BL_UCAST(BL_MOB, bl), tbl) != 0) return 0; ud->target_to = 0; - return 0; + return 1; } if (tbl->m == bl->m && check_distance_bl(bl, tbl, ud->chaserange)) { //Reached destination. + ud->target_to = 0; if (ud->state.attack_continue) { //Aegis uses one before every attack, we should //only need this one for syncing purposes. [Skotlex] - ud->target_to = 0; clif->fixpos(bl); unit->attack(bl, tbl->id, ud->state.attack_continue); } - } else { //Update chase-path - unit->walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy|(ud->state.attack_continue? 1 : 0)); + } else { // Update chase-path + unit->walktobl(bl, tbl, ud->chaserange, ud->state.walk_easy | ud->state.attack_continue); return 0; } } else { - //Stopped walking. Update to_x and to_y to current location [Skotlex] + // Stopped walking. Update to_x and to_y to current location [Skotlex] ud->to_x = bl->x; ud->to_y = bl->y; - if (battle_config.official_cell_stack_limit && map->count_oncell(bl->m, x, y, BL_CHAR|BL_NPC, 0x1 | 0x2) > battle_config.official_cell_stack_limit) { - //Walked on occupied cell, call unit_walktoxy again - if(ud->steptimer != INVALID_TIMER) { - //Execute step timer on next step instead - timer->delete(ud->steptimer, unit->step_timer); + if (battle_config.official_cell_stack_limit != 0 && map->count_oncell(bl->m, x, y, BL_CHAR | BL_NPC, 0x1 | 0x2) > battle_config.official_cell_stack_limit) { + // Walked on occupied cell, call unit->walk_toxy again + if (ud->steptimer != INVALID_TIMER) { + // Execute step timer on next step instead + timer->delete(ud->steptimer, unit->steptimer); ud->steptimer = INVALID_TIMER; } - return unit->walktoxy(bl, x, y, 8); + return unit->walk_toxy(bl, x, y, 8); } } return 0; } -static int unit_delay_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) +/** + * Timer for delayed execution of unit->walk_toxy once triggered + * @param tid: Timer ID, unused + * @param tick: Tick, unused + * @param id: ID of block_list to execute the action + * @param data: uint32 data cast to intptr_t with x-coord in lowest 16 bits and y-coord in highest 16 bits + * @return 0: success, 1: failure + */ +static int unit_delay_walk_toxy_timer(int tid, int64 tick, int id, intptr_t data) { struct block_list *bl = map->id2bl(id); - - if (!bl || bl->prev == NULL) - return 0; - unit->walktoxy(bl, (short)((data>>16)&0xffff), (short)(data&0xffff), 0); - return 1; + if (bl == NULL || bl->prev == NULL) + return 1; + short x = (short)GetWord((uint32)data, 0); + short y = (short)GetWord((uint32)data, 1); + unit->walk_toxy(bl, x, y, 0); + return 0; } -//flag parameter: -//&1 -> 1/0 = easy/hard -//&2 -> force walking -//&4 -> Delay walking if the reason you can't walk is the canwalk delay -//&8 -> Search for an unoccupied cell and cancel if none available -static int unit_walktoxy(struct block_list *bl, short x, short y, int flag) +/** + * Makes a unit walk to (x, y) coordinates + * @param bl: block_list of unit to move + * @param x: x-coordinate + * @param y: y-coordinate + * @param flag: flag paramater with following options: + * - `& 1` -> 1/0 = easy / hard + * - `& 2` -> Force walking + * - `& 4` -> Delay walking, if the reason you can't walk is the `canwalk delay` + * - `& 8` -> Search for an unoccupied cell and cancel if none available + * . + * @return 0: success, 1: failure + */ +static int unit_walk_toxy(struct block_list *bl, short x, short y, int flag) { + // TODO: change flag to enum? [skyleo] struct unit_data* ud = NULL; struct status_change* sc = NULL; struct walkpath_data wpd; - nullpo_ret(bl); + nullpo_retr(1, bl); ud = unit->bl2ud(bl); - if( ud == NULL) return 0; + if (ud == NULL) + return 1; - if (battle_config.check_occupied_cells && (flag&8) && !map->closest_freecell(bl->m, bl, &x, &y, BL_CHAR|BL_NPC, 1)) //This might change x and y - return 0; + if ((flag & 8) != 0 && battle_config.check_occupied_cells != 0) { + if (!map->closest_freecell(bl->m, bl, &x, &y, BL_CHAR | BL_NPC, 1)) // This might change x and y + return 1; + } - if (!path->search(&wpd, bl, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS)) // Count walk path cells - return 0; + if (!path->search(&wpd, bl, bl->m, bl->x, bl->y, x, y, flag & 1, CELL_CHKNOPASS)) // Count walk path cells + return 1; + if (bl->type != BL_NPC) { #ifdef OFFICIAL_WALKPATH - if( !path->search_long(NULL, bl, bl->m, bl->x, bl->y, x, y, CELL_CHKNOPASS) // Check if there is an obstacle between - && (wpd.path_len > (battle_config.max_walk_path/17)*14) // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] - && (bl->type != BL_NPC) ) // If type is a NPC, please disregard. - return 0; + // Check if there is an obstacle between + // Official number of walkable cells is 14 if and only if there is an obstacle between. [malufett] + if (!path->search_long(NULL, bl, bl->m, bl->x, bl->y, x, y, CELL_CHKNOPASS) + && (wpd.path_len > (battle_config.max_walk_path / 17) * 14)) + return 1; #endif - if ((wpd.path_len > battle_config.max_walk_path) && (bl->type != BL_NPC)) - return 0; + if (wpd.path_len > battle_config.max_walk_path) + return 1; + } - if (flag&4 && DIFF_TICK(ud->canmove_tick, timer->gettick()) > 0 && - DIFF_TICK(ud->canmove_tick, timer->gettick()) < 2000) { + if ((flag & 4) != 0 && DIFF_TICK(ud->canmove_tick, timer->gettick()) > 0 + && DIFF_TICK(ud->canmove_tick, timer->gettick()) < 2000) { // Delay walking command. [Skotlex] - timer->add(ud->canmove_tick+1, unit->delay_walktoxy_timer, bl->id, (x<<16)|(y&0xFFFF)); - return 1; + timer->add(ud->canmove_tick + 1, unit->delay_walk_toxy_timer, bl->id, + (intptr_t)MakeDWord((uint16)x, (uint16)y)); + return 0; } - if(!(flag&2) && (!(status_get_mode(bl)&MD_CANMOVE) || !unit->can_move(bl))) - return 0; + if ((flag & 2) == 0 && ((status_get_mode(bl) & MD_CANMOVE) == 0 || unit->can_move(bl) == 0)) + return 1; - ud->state.walk_easy = flag&1; + ud->state.walk_easy = flag & 1; ud->to_x = x; ud->to_y = y; unit->stop_attack(bl); //Sets target to 0 + if ((flag & 8) == 0) // Stepaction might be delayed due to occupied cell + unit->stop_stepaction(bl); // unit->walktoxy removes any remembered stepaction and resets ud->target_to sc = status->get_sc(bl); - if( sc ) { - if( sc->data[SC_CONFUSION] || sc->data[SC__CHAOS] ) //Randomize the target position + if (sc != NULL) { + if (sc->data[SC_CONFUSION] != NULL || sc->data[SC__CHAOS] != NULL) // Randomize the target position map->random_dir(bl, &ud->to_x, &ud->to_y); - if( sc->data[SC_COMBOATTACK] ) + if (sc->data[SC_COMBOATTACK] != NULL) status_change_end(bl, SC_COMBOATTACK, INVALID_TIMER); } - if(ud->walktimer != INVALID_TIMER) { + if (ud->walktimer != INVALID_TIMER) { // When you come to the center of the grid because the change of destination while you're walking right now - // Call a function from a timer unit->walktoxy_sub + // Call a function from a timer unit->walk_toxy_sub ud->state.change_walk_target = 1; - return 1; + return 0; } - return unit->walktoxy_sub(bl); + return unit->walk_toxy_sub(bl); } -//To set Mob's CHASE/FOLLOW states (shouldn't be done if there's no path to reach) -static inline void set_mobstate(struct block_list *bl, int flag) +/** + * Sets CHASE / FOLLOW states, in case bl is a mob. + * WARNING: This shouldn't be done if there's no path to reach + * @param bl: block_list of mob + */ +static inline void set_mobstate(struct block_list *bl) { - struct mob_data* md = BL_CAST(BL_MOB,bl); + struct mob_data* md = BL_CAST(BL_MOB, bl); - if( md && flag ) - md->state.skillstate = md->state.aggressive ? MSS_FOLLOW : MSS_RUSH; + if (md != NULL) { + if (md->state.aggressive != 0) + md->state.skillstate = MSS_FOLLOW; + else + md->state.skillstate = MSS_RUSH; + } } -static int unit_walktobl_sub(int tid, int64 tick, int id, intptr_t data) +/** + * Timer used for when a unit can't walk towards its target yet due to it's canmove_tick, + * keeps retrying until it works or target changes. + * @param tid: Timer ID, unused + * @param tick: Tick, unused + * @param id: ID of block_list to execute the action + * @param data: ID of block_list to walk towards + * @return 0: success, 1: failure + */ +static int unit_walktobl_timer(int tid, int64 tick, int id, intptr_t data) { struct block_list *bl = map->id2bl(id); - struct unit_data *ud = bl?unit->bl2ud(bl):NULL; - - if (ud && ud->walktimer == INVALID_TIMER && ud->target == data) { - if (DIFF_TICK(ud->canmove_tick, tick) > 0) //Keep waiting? - timer->add(ud->canmove_tick+1, unit->walktobl_sub, id, data); - else if (unit->can_move(bl)) { - if (unit->walktoxy_sub(bl)) - set_mobstate(bl, ud->state.attack_continue); - } + if (bl == NULL) + return 1; + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) + return 1; + + if (ud->walktimer == INVALID_TIMER && ud->target == data) { + if (DIFF_TICK(ud->canmove_tick, tick) > 0) // Keep waiting? + timer->add(ud->canmove_tick + 1, unit->walktobl_timer, id, data); + else if (unit->can_move(bl) != 0 && unit->walk_toxy_sub(bl) == 0 && ud->state.attack_continue != 0) + set_mobstate(bl); } return 0; } @@ -627,23 +722,27 @@ static int unit_walktobl(struct block_list *bl, struct block_list *tbl, int rang if(ud->walktimer != INVALID_TIMER) { ud->state.change_walk_target = 1; - set_mobstate(bl, flag&2); + if ((flag & 2) != 0) + set_mobstate(bl); return 1; } if (DIFF_TICK(ud->canmove_tick, timer->gettick()) > 0) { //Can't move, wait a bit before invoking the movement. - timer->add(ud->canmove_tick+1, unit->walktobl_sub, bl->id, ud->target); + timer->add(ud->canmove_tick + 1, unit->walktobl_timer, bl->id, ud->target); return 1; } if(!unit->can_move(bl)) return 0; - if (unit->walktoxy_sub(bl)) { - set_mobstate(bl, flag&2); + if (unit->walk_toxy_sub(bl) == 0) { + if ((flag & 2) != 0) + set_mobstate(bl); + return 1; } + return 0; } @@ -661,7 +760,7 @@ static void unit_run_hit(struct block_list *bl, struct status_change *sc, struct lv = sc->data[type]->val1; //If you can't run forward, you must be next to a wall, so bounce back. [Skotlex] if( type == SC_RUN ) - clif->sc_load(bl,bl->id,AREA,SI_TING,0,0,0); + clif->sc_load(bl, bl->id, AREA, status->get_sc_icon(SC_TING), 0, 0, 0); ud = unit->bl2ud(bl); nullpo_retv(ud); @@ -673,7 +772,7 @@ static void unit_run_hit(struct block_list *bl, struct status_change *sc, struct if (lv > 0) skill->blown(bl, bl, skill->get_blewcount(TK_RUN, lv), unit->getdir(bl), 0); clif->fixpos(bl); //Why is a clif->slide (skill->blown) AND a fixpos needed? Ask Aegis. - clif->sc_end(bl, bl->id, AREA, SI_TING); + clif->sc_end(bl, bl->id, AREA, status->get_sc_icon(SC_TING)); } else if (sd) { clif->fixpos(bl); skill->castend_damage_id(bl, &sd->bl, RA_WUGDASH, lv, timer->gettick(), SD_LEVEL); @@ -730,14 +829,14 @@ static bool unit_run(struct block_list *bl, struct map_session_data *sd, enum sc return false; } - if( unit->walktoxy(bl, to_x, to_y, 1) ) + if (unit->walk_toxy(bl, to_x, to_y, 1) == 0) return true; //There must be an obstacle nearby. Attempt walking one cell at a time. do { to_x -= dir_x; to_y -= dir_y; - } while (--i > 0 && !unit->walktoxy(bl, to_x, to_y, 1)); + } while (--i > 0 && unit->walk_toxy(bl, to_x, to_y, 1) != 0); if ( i == 0 ) { unit->run_hit(bl, sc, sd, type); @@ -750,19 +849,21 @@ static bool unit_run(struct block_list *bl, struct map_session_data *sd, enum sc //Makes bl attempt to run dist cells away from target. Uses hard-paths. static int unit_escape(struct block_list *bl, struct block_list *target, short dist) { - uint8 dir; nullpo_ret(bl); - dir = map->calc_dir(target, bl->x, bl->y); + enum unit_dir dir = map->calc_dir(target, bl->x, bl->y); + Assert_retr(1, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); while (dist > 0 && map->getcell(bl->m, bl, bl->x + dist * dirx[dir], bl->y + dist * diry[dir], CELL_CHKNOREACH)) dist--; - return ( dist > 0 && unit->walktoxy(bl, bl->x + dist*dirx[dir], bl->y + dist*diry[dir], 0) ); + if (dist > 0 && unit->walk_toxy(bl, bl->x + dist * dirx[dir], bl->y + dist * diry[dir], 0) == 0) + return 1; + else + return 0; } //Instant warp function. static int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath) { short dx,dy; - uint8 dir; struct unit_data *ud = NULL; struct map_session_data *sd = NULL; @@ -781,7 +882,7 @@ static int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int eas ud->to_x = dst_x; ud->to_y = dst_y; - dir = map->calc_dir(bl, dst_x, dst_y); + enum unit_dir dir = map->calc_dir(bl, dst_x, dst_y); ud->dir = dir; dx = dst_x - bl->x; @@ -805,7 +906,7 @@ static int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int eas } else npc->untouch_areanpc(sd, bl->m, bl->x, bl->y); - if( sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > 0 ) + if (sd->status.pet_id > 0 && sd->pd && sd->pd->pet.intimate > PET_INTIMACY_NONE) { // Check if pet needs to be teleported. [Skotlex] int flag = 0; struct block_list* pbl = &sd->pd->bl; @@ -823,12 +924,18 @@ static int unit_movepos(struct block_list *bl, short dst_x, short dst_y, int eas return 1; } -static int unit_setdir(struct block_list *bl, unsigned char dir) +/** + * Sets the facing direction of a unit + * @param bl: unit to modify + * @param dir: the facing direction @see enum unit_dir + * @return 0: success, 1: failure + */ +static int unit_set_dir(struct block_list *bl, enum unit_dir dir) { - struct unit_data *ud; - nullpo_ret(bl ); - ud = unit->bl2ud(bl); - if (!ud) return 0; + nullpo_retr(1, bl); + struct unit_data *ud = unit->bl2ud(bl); + if (ud == NULL) + return 1; ud->dir = dir; if (bl->type == BL_PC) BL_UCAST(BL_PC, bl)->head_dir = 0; @@ -836,15 +943,20 @@ static int unit_setdir(struct block_list *bl, unsigned char dir) return 0; } -static uint8 unit_getdir(struct block_list *bl) +/** + * Get the facing direction of a unit + * @param bl: unit to request data from + * @return the facing direction @see enum unit_dir + */ +static enum unit_dir unit_getdir(const struct block_list *bl) { - struct unit_data *ud; - nullpo_ret(bl); + nullpo_retr(UNIT_DIR_NORTH, bl); - if( bl->type == BL_NPC ) + if (bl->type == BL_NPC) return BL_UCCAST(BL_NPC, bl)->dir; - ud = unit->bl2ud(bl); - if (!ud) return 0; + const struct unit_data *ud = unit->cbl2ud(bl); + if (ud == NULL) + return UNIT_DIR_NORTH; return ud->dir; } @@ -918,7 +1030,7 @@ static int unit_blown(struct block_list *bl, int dx, int dy, int count, int flag //Warps a unit/ud to a given map/position. //In the case of players, pc->setpos is used. //it respects the no warp flags, so it is safe to call this without doing nowarpto/nowarp checks. -static int unit_warp(struct block_list *bl, short m, short x, short y, clr_type type) +static int unit_warp(struct block_list *bl, short m, short x, short y, enum clr_type type) { struct unit_data *ud; nullpo_ret(bl); @@ -957,7 +1069,7 @@ static int unit_warp(struct block_list *bl, short m, short x, short y, clr_type 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); @@ -1008,7 +1120,7 @@ static int unit_stop_walking(struct block_list *bl, int flag) //timer->delete function does not messes with it. If the function's //behavior changes in the future, this code could break! td = timer->get(ud->walktimer); - timer->delete(ud->walktimer, unit->walktoxy_timer); + timer->delete(ud->walktimer, unit->walk_toxy_timer); ud->walktimer = INVALID_TIMER; ud->state.change_walk_target = 0; tick = timer->gettick(); @@ -1016,7 +1128,7 @@ static int unit_stop_walking(struct block_list *bl, int flag) || (flag&STOPWALKING_FLAG_NEXTCELL && td && DIFF_TICK(td->tick, tick) <= td->data/2) //Enough time has passed to cover half-cell ) { ud->walkpath.path_len = ud->walkpath.path_pos+1; - unit->walktoxy_timer(INVALID_TIMER, tick, bl->id, ud->walkpath.path_pos); + unit->walk_toxy_timer(INVALID_TIMER, tick, bl->id, ud->walkpath.path_pos); } if(flag&STOPWALKING_FLAG_FIXPOS) @@ -1039,11 +1151,17 @@ static int unit_stop_walking(struct block_list *bl, int flag) static int unit_skilluse_id(struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv) { - return unit->skilluse_id2( - src, target_id, skill_id, skill_lv, - skill->cast_fix(src, skill_id, skill_lv), - skill->get_castcancel(skill_id) - ); + int casttime = skill->cast_fix(src, skill_id, skill_lv); + int castcancel = skill->get_castcancel(skill_id); + int ret = unit->skilluse_id2(src, target_id, skill_id, skill_lv, casttime, castcancel); + struct map_session_data *sd = BL_CAST(BL_PC, src); + + if (sd != NULL && ret == 0) + pc->autocast_clear(sd); // Error in unit_skilluse_id2(). + else if (sd != NULL && ret != 0 && skill_id != SA_ABRACADABRA && skill_id != WM_RANDOMIZESPELL) + skill->validate_autocast_data(sd, skill_id, skill_lv); + + return ret; } static int unit_is_walking(struct block_list *bl) @@ -1090,6 +1208,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 )) @@ -1228,7 +1347,7 @@ static int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int } else { unit->stop_walking(bl, STOPWALKING_FLAG_NEXTCELL); if (ud->target) - timer->add(ud->canmove_tick+1, unit->walktobl_sub, bl->id, ud->target); + timer->add(ud->canmove_tick + 1, unit->walktobl_timer, bl->id, ud->target); } } } @@ -1327,6 +1446,12 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill if (src->type==BL_HOM) switch(skill_id) { //Homun-auto-target skills. + case HVAN_CHAOTIC: + target_id = ud->target; // Choose attack target for now + target = map->id2bl(target_id); + if (target != NULL) + break; + FALLTHROUGH // Attacking nothing, choose master as default target instead case HLIF_HEAL: case HLIF_AVOID: case HAMI_DEFENCE: @@ -1409,22 +1534,8 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill } } - if (src->type == BL_HOM) { - // In case of homunuculus, set the sd to the homunculus' master, as needed below - struct block_list *master = battle->get_master(src); - if (master) - sd = map->id2sd(master->id); - } - - if (sd) { - /* temporarily disabled, awaiting for kenpachi to detail this so we can make it work properly */ -#if 0 - if (sd->skillitem != skill_id && !skill->check_condition_castbegin(sd, skill_id, skill_lv)) -#else - if (!skill->check_condition_castbegin(sd, skill_id, skill_lv)) -#endif - return 0; - } + if (sd != NULL && skill->check_condition_castbegin(sd, skill_id, skill_lv) == 0) + return 0; if (src->type == BL_MOB) { const struct mob_data *src_md = BL_UCCAST(BL_MOB, src); @@ -1451,7 +1562,7 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill ud->target_to = target_id; ud->stepskill_id = skill_id; ud->stepskill_lv = skill_lv; - return 0; // Attacking will be handled by unit_walktoxy_timer in this case + return 0; // Attacking will be handled by unit_walk_toxy_timer in this case } //Check range when not using skill on yourself or is a combo-skill during attack @@ -1607,6 +1718,9 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill if (!ud->state.running) //need TK_RUN or WUGDASH handler to be done before that, see bugreport:6026 unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS);// even though this is not how official works but this will do the trick. bugreport:6829 + if (sd != NULL && sd->autocast.itemskill_instant_cast && sd->autocast.type == AUTOCAST_ITEM) + casttime = 0; + // in official this is triggered even if no cast time. clif->useskill(src, src->id, target_id, 0,0, skill_id, skill_lv, casttime); if( casttime > 0 || temp ) @@ -1642,7 +1756,7 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill if( casttime <= 0 ) ud->state.skillcastcancel = 0; - if( !sd || sd->skillitem != skill_id || skill->get_cast(skill_id,skill_lv) ) + if (sd == NULL || sd->autocast.type < AUTOCAST_ABRA || skill->get_cast(skill_id, skill_lv) != 0) ud->canact_tick = tick + casttime + 100; if( sd ) { @@ -1661,7 +1775,7 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill if( casttime > 0 ) { if (src->id != target->id) // self-targeted skills shouldn't show different direction - unit->setdir(src, map->calc_dir(src, target->x, target->y)); + unit->set_dir(src, map->calc_dir(src, target->x, target->y)); ud->skilltimer = timer->add( tick+casttime, skill->castend_id, src->id, 0 ); if (sd && (pc->checkskill(sd, SA_FREECAST) > 0 || skill_id == LG_EXEEDBREAK || (skill->get_inf2(ud->skill_id) & INF2_FREE_CAST_REDUCED) != 0)) status_calc_bl(&sd->bl, SCB_SPEED|SCB_ASPD); @@ -1676,11 +1790,17 @@ static int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill static int unit_skilluse_pos(struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv) { - return unit->skilluse_pos2( - src, skill_x, skill_y, skill_id, skill_lv, - skill->cast_fix(src, skill_id, skill_lv), - skill->get_castcancel(skill_id) - ); + int casttime = skill->cast_fix(src, skill_id, skill_lv); + int castcancel = skill->get_castcancel(skill_id); + int ret = unit->skilluse_pos2(src, skill_x, skill_y, skill_id, skill_lv, casttime, castcancel); + struct map_session_data *sd = BL_CAST(BL_PC, src); + + if (sd != NULL && ret == 0) + pc->autocast_clear(sd); // Error in unit_skilluse_pos2(). + else if (sd != NULL && ret != 0 && skill_id != SA_ABRACADABRA && skill_id != WM_RANDOMIZESPELL) + skill->validate_autocast_data(sd, skill_id, skill_lv); + + return ret; } static int unit_skilluse_pos2(struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel) @@ -1751,7 +1871,7 @@ static int unit_skilluse_pos2(struct block_list *src, short skill_x, short skill ud->target_to = (skill_x + skill_y*md->xs); ud->stepskill_id = skill_id; ud->stepskill_lv = skill_lv; - return 0; // Attacking will be handled by unit_walktoxy_timer in this case + return 0; // Attacking will be handled by unit_walk_toxy_timer in this case } if( skill->get_state(ud->skill_id) == ST_MOVE_ENABLE ) { @@ -1775,7 +1895,7 @@ static int unit_skilluse_pos2(struct block_list *src, short skill_x, short skill } ud->state.skillcastcancel = castcancel&&casttime>0?1:0; - if( !sd || sd->skillitem != skill_id || skill->get_cast(skill_id,skill_lv) ) + if (sd == NULL || sd->autocast.type < AUTOCAST_ABRA || skill->get_cast(skill_id, skill_lv) != 0) ud->canact_tick = tick + casttime + 100; #if 0 if (sd) { @@ -1805,10 +1925,14 @@ static int unit_skilluse_pos2(struct block_list *src, short skill_x, short skill } unit->stop_walking(src, STOPWALKING_FLAG_FIXPOS); + + if (sd != NULL && sd->autocast.itemskill_instant_cast && sd->autocast.type == AUTOCAST_ITEM) + casttime = 0; + // in official this is triggered even if no cast time. clif->useskill(src, src->id, 0, skill_x, skill_y, skill_id, skill_lv, casttime); if( casttime > 0 ) { - unit->setdir(src, map->calc_dir(src, skill_x, skill_y)); + unit->set_dir(src, map->calc_dir(src, skill_x, skill_y)); ud->skilltimer = timer->add( tick+casttime, skill->castend_pos, src->id, 0 ); if ((sd && pc->checkskill(sd, SA_FREECAST) > 0) || skill_id == LG_EXEEDBREAK || (skill->get_inf2(ud->skill_id) & INF2_FREE_CAST_REDUCED) != 0) { status_calc_bl(&sd->bl, SCB_SPEED|SCB_ASPD); @@ -1887,7 +2011,7 @@ static void unit_stop_stepaction(struct block_list *bl) return; //Clear timer - timer->delete(ud->steptimer, unit->step_timer); + timer->delete(ud->steptimer, unit->steptimer); ud->steptimer = INVALID_TIMER; } @@ -1930,8 +2054,10 @@ static int unit_attack(struct block_list *src, int target_id, int continuous) if (src->type == BL_PC) { struct map_session_data *sd = BL_UCAST(BL_PC, src); - if( target->type == BL_NPC ) { // monster npcs [Valaris] - npc->click(sd, BL_UCAST(BL_NPC, target)); // submitted by leinsirk10 [Celest] + if (target->type == BL_NPC) { // monster npcs [Valaris] + if (sd->block_action.npc == 0) { // *pcblock script command + npc->click(sd, BL_UCAST(BL_NPC, target)); // submitted by leinsirk10 [Celest] + } return 0; } if( pc_is90overweight(sd) || pc_isridingwug(sd) ) { // overweight or mounted on warg - stop attacking @@ -1969,7 +2095,7 @@ static int unit_attack(struct block_list *src, int target_id, int continuous) ud->target_to = ud->target; ud->stepskill_id = 0; ud->stepskill_lv = 0; - return 0; // Attacking will be handled by unit_walktoxy_timer in this case + return 0; // Attacking will be handled by unit_walk_toxy_timer in this case } if(DIFF_TICK(ud->attackabletime, timer->gettick()) > 0) @@ -2070,14 +2196,13 @@ static bool unit_can_reach_bl(struct block_list *bl, struct block_list *tbl, int /*========================================== * Calculates position of Pet/Mercenary/Homunculus/Elemental *------------------------------------------*/ -static int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir) +static int unit_calc_pos(struct block_list *bl, int tx, int ty, enum unit_dir dir) { int dx, dy, x, y; struct unit_data *ud = unit->bl2ud(bl); nullpo_ret(ud); - if(dir > 7) - return 1; + Assert_retr(1, dir >= UNIT_DIR_FIRST && dir < UNIT_DIR_MAX); ud->to_x = tx; ud->to_y = ty; @@ -2094,7 +2219,7 @@ static int unit_calc_pos(struct block_list *bl, int tx, int ty, uint8 dir) if (!unit->can_reach_pos(bl, x, y, 0)) { int i; for (i = 0; i < 12; i++) { - int k = rnd()%8; // Pick a Random Dir + enum unit_dir k = rnd() % UNIT_DIR_MAX; // Pick a Random Dir dx = -dirx[k] * 2; dy = -diry[k] * 2; x = tx + dx; @@ -2220,7 +2345,7 @@ static int unit_attack_timer_sub(struct block_list *src, int tid, int64 tick) if(md) { //First attack is always a normal attack if(md->state.skillstate == MSS_ANGRY || md->state.skillstate == MSS_BERSERK) { - if (mob->skill_use(md,tick,-1)) { + if (mob->skill_use(md, tick, -1) == 0) { map->freeblock_unlock(); return 1; } @@ -2260,7 +2385,7 @@ static int unit_attack_timer_sub(struct block_list *src, int tid, int64 tick) } if(ud->state.attack_continue) { - unit->setdir(src, map->calc_dir(src, target->x, target->y)); + unit->set_dir(src, map->calc_dir(src, target->x, target->y)); if( src->type == BL_PC ) pc->update_idle_time(sd, BCIDLE_ATTACK); ud->attacktimer = timer->add(ud->attackabletime,unit->attack_timer,src->id,0); @@ -2419,7 +2544,7 @@ static int unit_changeviewsize(struct block_list *bl, short size) * Otherwise it is assumed bl is being warped. * On-Kill specific stuff is not performed here, look at status->damage for that. *------------------------------------------*/ -static int unit_remove_map(struct block_list *bl, clr_type clrtype, const char *file, int line, const char *func) +static int unit_remove_map(struct block_list *bl, enum clr_type clrtype, const char *file, int line, const char *func) { struct unit_data *ud = unit->bl2ud(bl); struct status_change *sc = status->get_sc(bl); @@ -2605,7 +2730,7 @@ static int unit_remove_map(struct block_list *bl, clr_type clrtype, const char * case BL_PET: { struct pet_data *pd = BL_UCAST(BL_PET, bl); - if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) ) { + if (pd->pet.intimate <= PET_INTIMACY_NONE && !(pd->msd && !pd->msd->state.active)) { //If logging out, this is deleted on unit->free clif->clearunit_area(bl,clrtype); map->delblock(bl); @@ -2665,7 +2790,7 @@ static int unit_remove_map(struct block_list *bl, clr_type clrtype, const char * return 1; } -static void unit_remove_map_pc(struct map_session_data *sd, clr_type clrtype) +static void unit_remove_map_pc(struct map_session_data *sd, enum clr_type clrtype) { nullpo_retv(sd); unit->remove_map(&sd->bl,clrtype,ALC_MARK); @@ -2697,7 +2822,7 @@ static void unit_free_pc(struct map_session_data *sd) * Function to free all related resources to the bl * if unit is on map, it is removed using the clrtype specified *------------------------------------------*/ -static int unit_free(struct block_list *bl, clr_type clrtype) +static int unit_free(struct block_list *bl, enum clr_type clrtype) { struct unit_data *ud = unit->bl2ud( bl ); nullpo_ret(bl); @@ -2819,7 +2944,7 @@ static int unit_free(struct block_list *bl, clr_type clrtype) aFree (pd->loot); pd->loot = NULL; } - if (pd->pet.intimate > 0) { + if (pd->pet.intimate > PET_INTIMACY_NONE) { intif->save_petdata(pd->pet.account_id,&pd->pet); } else { //Remove pet. @@ -2957,10 +3082,10 @@ static int do_init_unit(bool minimal) return 0; timer->add_func_list(unit->attack_timer, "unit_attack_timer"); - timer->add_func_list(unit->walktoxy_timer,"unit_walktoxy_timer"); - timer->add_func_list(unit->walktobl_sub, "unit_walktobl_sub"); - timer->add_func_list(unit->delay_walktoxy_timer,"unit_delay_walktoxy_timer"); - timer->add_func_list(unit->step_timer,"unit_step_timer"); + timer->add_func_list(unit->walk_toxy_timer, "unit_walk_toxy_timer"); + timer->add_func_list(unit->walktobl_timer, "unit_walktobl_timer"); + timer->add_func_list(unit->delay_walk_toxy_timer, "unit_delay_walk_toxy_timer"); + timer->add_func_list(unit->steptimer, "unit_steptimer"); return 0; } @@ -2978,26 +3103,28 @@ void unit_defaults(void) unit->final = do_final_unit; /* */ unit->bl2ud = unit_bl2ud; + unit->cbl2ud = unit_cbl2ud; unit->bl2ud2 = unit_bl2ud2; unit->init_ud = unit_init_ud; unit->attack_timer = unit_attack_timer; - unit->walktoxy_timer = unit_walktoxy_timer; - unit->walktoxy_sub = unit_walktoxy_sub; - unit->delay_walktoxy_timer = unit_delay_walktoxy_timer; - unit->walktoxy = unit_walktoxy; - unit->walktobl_sub = unit_walktobl_sub; + unit->walk_toxy_timer = unit_walk_toxy_timer; + unit->walk_toxy_sub = unit_walk_toxy_sub; + unit->delay_walk_toxy_timer = unit_delay_walk_toxy_timer; + unit->walk_toxy = unit_walk_toxy; + unit->walktobl_timer = unit_walktobl_timer; unit->walktobl = unit_walktobl; unit->run = unit_run; unit->run_hit = unit_run_hit; unit->escape = unit_escape; unit->movepos = unit_movepos; - unit->setdir = unit_setdir; + unit->set_dir = unit_set_dir; unit->getdir = unit_getdir; unit->blown = unit_blown; unit->warp = unit_warp; + unit->warpto_master = unit_warpto_master; unit->stop_walking = unit_stop_walking; unit->skilluse_id = unit_skilluse_id; - unit->step_timer = unit_step_timer; + unit->steptimer = unit_steptimer; unit->stop_stepaction = unit_stop_stepaction; unit->is_walking = unit_is_walking; unit->can_move = unit_can_move; diff --git a/src/map/unit.h b/src/map/unit.h index 0c1c2405c..3f288e0d3 100644 --- a/src/map/unit.h +++ b/src/map/unit.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include "map/clif.h" // clr_type #include "map/path.h" // struct walkpath_data #include "map/skill.h" // 'MAX_SKILLTIMERSKILL, struct skill_timerskill, struct skill_unit_group, struct skill_unit_group_tickset +#include "map/unitdefines.h" // enum unit_dir #include "common/hercules.h" struct map_session_data; @@ -44,6 +45,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,11 +63,12 @@ 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; int64 canmove_tick; - uint8 dir; + enum unit_dir dir; unsigned char walk_count; unsigned char target_count; struct { @@ -100,26 +103,28 @@ struct unit_interface { int (*final) (void); /* */ struct unit_data* (*bl2ud) (struct block_list *bl); + const struct unit_data* (*cbl2ud) (const struct block_list *bl); struct unit_data* (*bl2ud2) (struct block_list *bl); void (*init_ud) (struct unit_data *ud); int (*attack_timer) (int tid, int64 tick, int id, intptr_t data); - int (*walktoxy_timer) (int tid, int64 tick, int id, intptr_t data); - int (*walktoxy_sub) (struct block_list *bl); - int (*delay_walktoxy_timer) (int tid, int64 tick, int id, intptr_t data); - int (*walktoxy) (struct block_list *bl, short x, short y, int flag); - int (*walktobl_sub) (int tid, int64 tick, int id, intptr_t data); + int (*walk_toxy_timer) (int tid, int64 tick, int id, intptr_t data); + int (*walk_toxy_sub) (struct block_list *bl); + int (*delay_walk_toxy_timer) (int tid, int64 tick, int id, intptr_t data); + int (*walk_toxy) (struct block_list *bl, short x, short y, int flag); + int (*walktobl_timer) (int tid, int64 tick, int id, intptr_t data); int (*walktobl) (struct block_list *bl, struct block_list *tbl, int range, int flag); bool (*run) (struct block_list *bl, struct map_session_data *sd, enum sc_type type); void (*run_hit) (struct block_list *bl, struct status_change *sc, struct map_session_data *sd, enum sc_type type); int (*escape) (struct block_list *bl, struct block_list *target, short dist); int (*movepos) (struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath); - int (*setdir) (struct block_list *bl, unsigned char dir); - uint8 (*getdir) (struct block_list *bl); + int (*set_dir) (struct block_list *bl, enum unit_dir dir); + enum unit_dir (*getdir) (const struct block_list *bl); int (*blown) (struct block_list *bl, int dx, int dy, int count, int flag); - int (*warp) (struct block_list *bl, short m, short x, short y, clr_type type); + int (*warp) (struct block_list *bl, short m, short x, short y, enum clr_type type); + int (*warpto_master) (struct block_list *master_bl, struct block_list *slave_bl); int (*stop_walking) (struct block_list *bl, int type); int (*skilluse_id) (struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv); - int (*step_timer) (int tid, int64 tick, int id, intptr_t data); + int (*steptimer) (int tid, int64 tick, int id, intptr_t data); void (*stop_stepaction) (struct block_list *bl); int (*is_walking) (struct block_list *bl); int (*can_move) (struct block_list *bl); @@ -135,17 +140,17 @@ struct unit_interface { int (*cancel_combo) (struct block_list *bl); bool (*can_reach_pos) (struct block_list *bl, int x, int y, int easy); bool (*can_reach_bl) (struct block_list *bl, struct block_list *tbl, int range, int easy, short *x, short *y); - int (*calc_pos) (struct block_list *bl, int tx, int ty, uint8 dir); + int (*calc_pos) (struct block_list *bl, int tx, int ty, enum unit_dir dir); int (*attack_timer_sub) (struct block_list *src, int tid, int64 tick); int (*skillcastcancel) (struct block_list *bl, int type); void (*dataset) (struct block_list *bl); int (*counttargeted) (struct block_list *bl); int (*fixdamage) (struct block_list *src, struct block_list *target, int sdelay, int ddelay, int64 damage, short div, unsigned char type, int64 damage2); int (*changeviewsize) (struct block_list *bl, short size); - int (*remove_map) (struct block_list *bl, clr_type clrtype, const char *file, int line, const char *func); - void (*remove_map_pc) (struct map_session_data *sd, clr_type clrtype); + int (*remove_map) (struct block_list *bl, enum clr_type clrtype, const char *file, int line, const char *func); + void (*remove_map_pc) (struct map_session_data *sd, enum clr_type clrtype); void (*free_pc) (struct map_session_data *sd); - int (*free) (struct block_list *bl, clr_type clrtype); + int (*free) (struct block_list *bl, enum clr_type clrtype); }; #ifdef HERCULES_CORE diff --git a/src/map/unitdefines.h b/src/map/unitdefines.h new file mode 100644 index 000000000..0ee30998c --- /dev/null +++ b/src/map/unitdefines.h @@ -0,0 +1,58 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2012-2019 Hercules Dev Team + * Copyright (C) Athena Dev Teams + * + * 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_UNITDEFINES_H +#define MAP_UNITDEFINES_H + +/** + * Used for directions, @see unit_data.dir + */ +enum unit_dir { + UNIT_DIR_UNDEFINED = -1, + UNIT_DIR_FIRST = 0, + UNIT_DIR_NORTH = 0, + UNIT_DIR_NORTHWEST = 1, + UNIT_DIR_WEST = 2, + UNIT_DIR_SOUTHWEST = 3, + UNIT_DIR_SOUTH = 4, + UNIT_DIR_SOUTHEAST = 5, + UNIT_DIR_EAST = 6, + UNIT_DIR_NORTHEAST = 7, + UNIT_DIR_MAX = 8, + /* IMPORTANT: Changing the order would break the above macros + * and several usages of directions anywhere */ +}; + +/* Returns the opposite of the facing direction */ +#define unit_get_opposite_dir(dir) ( ((dir) + 4) % UNIT_DIR_MAX ) + +/* Returns true when direction is diagonal/combined (ex. UNIT_DIR_NORTHWEST, UNIT_DIR_SOUTHWEST, ...) */ +#define unit_is_diagonal_dir(dir) ( ((dir) % 2) == UNIT_DIR_NORTHWEST ) + +/* Returns true if direction equals val or the opposite direction of val */ +#define unit_is_dir_or_opposite(dir, val) ( ((dir) % 4) == (val) ) + +/* Returns the next direction after 90° CCW on a compass */ +#define unit_get_ccw90_dir(dir) ( ((dir) + 2) % UNIT_DIR_MAX ) + +/* Returns a random diagonal direction */ +#define unit_get_rnd_diagonal_dir() ( UNIT_DIR_NORTHWEST + 2 * (rnd() % 4) ) + +#endif /* MAP_UNITDEFINES_H */ diff --git a/src/map/vending.c b/src/map/vending.c index 692f5f378..4fd009025 100644 --- a/src/map/vending.c +++ b/src/map/vending.c @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/map/vending.h b/src/map/vending.h index c994aad3a..fcf80aef0 100644 --- a/src/map/vending.h +++ b/src/map/vending.h @@ -2,8 +2,8 @@ * This file is part of Hercules. * http://herc.ws - http://github.com/HerculesWS/Hercules * - * Copyright (C) 2012-2018 Hercules Dev Team - * Copyright (C) Athena Dev Teams + * Copyright (C) 2012-2020 Hercules Dev Team + * Copyright (C) Athena Dev Teams * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by |