diff options
Diffstat (limited to 'src/map/clif.c')
-rw-r--r-- | src/map/clif.c | 944 |
1 files changed, 752 insertions, 192 deletions
diff --git a/src/map/clif.c b/src/map/clif.c index 2a2d87ccc..76625f0ba 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -56,13 +56,14 @@ #include "map/trade.h" #include "map/unit.h" #include "map/vending.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/ers.h" #include "common/grfio.h" #include "common/memmgr.h" -#include "common/mmo.h" // NEW_CARTS +#include "common/mmo.h" // NEW_CARTS, char_achievements #include "common/nullpo.h" #include "common/random.h" #include "common/showmsg.h" @@ -85,8 +86,8 @@ static struct s_packet_db packet_db[MAX_PACKET_DB + 1]; /* re-usable */ static struct packet_itemlist_normal itemlist_normal; static struct packet_itemlist_equip itemlist_equip; -static struct packet_storelist_normal storelist_normal; -static struct packet_storelist_equip storelist_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; @@ -2009,8 +2010,8 @@ static void clif_changemap_airship(struct map_session_data *sd, short m, int x, /// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE). /// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W -/// 0ac7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <zero>.128B -static void clif_changemapserver(struct map_session_data *sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port) +/// 0ac7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <dns host>.128B +static void clif_changemapserver(struct map_session_data *sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port, char *dnsHost) { int fd; #if PACKETVER >= 20170315 @@ -2028,6 +2029,15 @@ static void clif_changemapserver(struct map_session_data *sd, unsigned short map WFIFOW(fd, 20) = y; WFIFOL(fd, 22) = htonl(ip); WFIFOW(fd, 26) = sockt->ntows(htons(port)); // [!] LE byte order here [!] + +#if PACKETVER >= 20170315 + if (dnsHost != NULL) { + safestrncpy(WFIFOP(fd, 28), dnsHost, 128); + } else { + memset(WFIFOP(fd, 28), 0, 128); + } +#endif + WFIFOSET(fd, packet_len(cmd)); } @@ -2623,6 +2633,22 @@ static void clif_dropitem(struct map_session_data *sd, int n, int amount) WFIFOSET(fd,packet_len(0xaf)); } +static void clif_item_movefailed(struct map_session_data *sd, int n) +{ +#if PACKETVER_MAIN_NUM >= 20161214 || PACKETVER_RE_NUM >= 20161130 || defined(PACKETVER_ZERO) + int fd = sd->fd; + const int len = sizeof(struct PACKET_ZC_INVENTORY_MOVE_FAILED); + WFIFOHEAD(fd, len); + struct PACKET_ZC_INVENTORY_MOVE_FAILED *p = WFIFOP(fd, 0); + p->packetType = 0xaa7; + p->index = n + 2; + p->unknown = 1; + WFIFOSET(fd, len); +#else + clif->dropitem(sd, n, 0); +#endif +} + /// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY). /// 07fa <delete type>.W <index>.W <amount>.W /// delete type: @see enum delitem_reason @@ -2761,7 +2787,18 @@ static void clif_item_normal(short idx, struct NORMALITEM_INFO *p, struct item * #endif } -static void clif_inventorylist(struct map_session_data *sd) +static void clif_inventoryList(struct map_session_data *sd) +{ +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + clif->inventoryStart(sd, INVTYPE_INVENTORY, ""); +#endif + clif->inventoryItems(sd, INVTYPE_INVENTORY); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + clif->inventoryEnd(sd, INVTYPE_INVENTORY); +#endif +} + +static void clif_inventoryItems(struct map_session_data *sd, enum inventory_type type) { int i, normal = 0, equip = 0; @@ -2776,9 +2813,12 @@ static void clif_inventorylist(struct map_session_data *sd) clif->item_normal(i+2,&itemlist_normal.list[normal++],&sd->status.inventory[i],sd->inventory_data[i]); } - if( normal ) { - itemlist_normal.PacketType = inventorylistnormalType; - itemlist_normal.PacketLength = 4 + (sizeof(struct NORMALITEM_INFO) * normal); + if (normal) { + itemlist_normal.PacketType = inventorylistnormalType; + itemlist_normal.PacketLength = (sizeof(itemlist_normal) - sizeof(itemlist_normal.list)) + (sizeof(struct NORMALITEM_INFO) * normal); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + itemlist_normal.invType = type; +#endif clif->send(&itemlist_normal, itemlist_normal.PacketLength, &sd->bl, SELF); } @@ -2788,7 +2828,10 @@ static void clif_inventorylist(struct map_session_data *sd) if( equip ) { itemlist_equip.PacketType = inventorylistequipType; - itemlist_equip.PacketLength = 4 + (sizeof(struct EQUIPITEM_INFO) * equip); + itemlist_equip.PacketLength = (sizeof(itemlist_equip) - sizeof(itemlist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + itemlist_equip.invType = type; +#endif clif->send(&itemlist_equip, itemlist_equip.PacketLength, &sd->bl, SELF); } @@ -2804,8 +2847,20 @@ static void clif_inventorylist(struct map_session_data *sd) #endif } +static void clif_equipList(struct map_session_data *sd) +{ +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + clif->inventoryStart(sd, INVTYPE_INVENTORY, ""); + clif->inventoryItems(sd, INVTYPE_INVENTORY); + clif->inventoryEnd(sd, INVTYPE_INVENTORY); +#else + // [4144] for old packet version it send only equipment. this is bug? + clif->equipItems(sd, INVTYPE_INVENTORY); +#endif +} + //Required when items break/get-repaired. Only sends equippable item list. -static void clif_equiplist(struct map_session_data *sd) +static void clif_equipItems(struct map_session_data *sd, enum inventory_type type) { int i, equip = 0; @@ -2818,9 +2873,12 @@ static void clif_equiplist(struct map_session_data *sd) clif->item_equip(i+2,&itemlist_equip.list[equip++],&sd->status.inventory[i],sd->inventory_data[i],pc->equippoint(sd,i)); } - if( equip ) { - itemlist_equip.PacketType = inventorylistequipType; - itemlist_equip.PacketLength = 4 + (sizeof(struct EQUIPITEM_INFO) * equip); + if (equip) { + itemlist_equip.PacketType = inventorylistequipType; + itemlist_equip.PacketLength = (sizeof(itemlist_equip) - sizeof(itemlist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + itemlist_equip.invType = type; +#endif clif->send(&itemlist_equip, itemlist_equip.PacketLength, &sd->bl, SELF); } @@ -2837,13 +2895,74 @@ static void clif_equiplist(struct map_session_data *sd) #endif } -static void clif_storagelist(struct map_session_data *sd, struct item *items, int items_length) +static void clif_storageList(struct map_session_data *sd, struct item *items, int items_length) { - int i = 0; - struct item_data *id; + nullpo_retv(sd); + + clif->inventoryStart(sd, INVTYPE_STORAGE, "Storage"); + if (sd->storage.aggregate > 0) + clif->storageItems(sd, INVTYPE_STORAGE, items, items_length); + clif->inventoryEnd(sd, INVTYPE_STORAGE); +} + +static void clif_guildStorageList(struct map_session_data *sd, struct item *items, int items_length) +{ + clif->inventoryStart(sd, INVTYPE_GUILD_STORAGE, "Guild storage"); + clif->storageItems(sd, INVTYPE_GUILD_STORAGE, items, items_length); + clif->inventoryEnd(sd, INVTYPE_GUILD_STORAGE); +} +static void clif_inventoryStart(struct map_session_data *sd, enum inventory_type type, const char *name) +{ +#if PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 + nullpo_retv(sd); + nullpo_retv(name); + + char buf[sizeof(struct ZC_INVENTORY_START) + 24]; + memset(buf, 0, sizeof(buf)); + struct ZC_INVENTORY_START *p = (struct ZC_INVENTORY_START *)buf; + p->packetType = 0xb08; +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + p->invType = type; +#endif +#if PACKETVER_RE_NUM >= 20180919 || PACKETVER_ZERO_NUM >= 20180919 + int strLen = (int)safestrnlen(name, 24); + if (strLen > 24) + strLen = 24; + const int len = sizeof(struct ZC_INVENTORY_START) + strLen; + p->packetLength = len; + safestrncpy(p->name, name, strLen); +#else + const int len = sizeof(struct ZC_INVENTORY_START); + safestrncpy(p->name, name, NAME_LENGTH); +#endif + clif->send(p, len, &sd->bl, SELF); +#endif +} + +static void clif_inventoryEnd(struct map_session_data *sd, enum inventory_type type) +{ +#if PACKETVER_RE_NUM >= 20180829 || PACKETVER_ZERO_NUM >= 20180919 + nullpo_retv(sd); + + struct ZC_INVENTORY_END p; + p.packetType = 0xb0b; +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + p.invType = type; +#endif + p.flag = 0; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + +static void clif_storageItems(struct map_session_data *sd, enum inventory_type type, struct item *items, int items_length) +{ nullpo_retv(sd); nullpo_retv(items); + + int i = 0; + struct item_data *id; + do { int normal = 0, equip = 0, k = 0; @@ -2861,10 +2980,13 @@ static void clif_storagelist(struct map_session_data *sd, struct item *items, in } if( normal ) { - storelist_normal.PacketType = storagelistnormalType; + storelist_normal.PacketType = storageListNormalType; storelist_normal.PacketLength = ( sizeof( storelist_normal ) - sizeof( storelist_normal.list ) ) + (sizeof(struct NORMALITEM_INFO) * normal); -#if PACKETVER >= 20120925 +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + storelist_normal.invType = type; +#endif +#if PACKETVER >= 20120925 && PACKETVER_RE_NUM < 20180829 && PACKETVER_ZERO_NUM < 20180919 safestrncpy(storelist_normal.name, "Storage", NAME_LENGTH); #endif @@ -2872,10 +2994,13 @@ static void clif_storagelist(struct map_session_data *sd, struct item *items, in } if( equip ) { - storelist_equip.PacketType = storagelistequipType; + storelist_equip.PacketType = storageListEquipType; storelist_equip.PacketLength = ( sizeof( storelist_equip ) - sizeof( storelist_equip.list ) ) + (sizeof(struct EQUIPITEM_INFO) * equip); -#if PACKETVER >= 20120925 +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + storelist_equip.invType = type; +#endif +#if PACKETVER >= 20120925 && PACKETVER_RE_NUM < 20180829 && PACKETVER_ZERO_NUM < 20180919 safestrncpy(storelist_equip.name, "Storage", NAME_LENGTH); #endif @@ -2886,7 +3011,18 @@ static void clif_storagelist(struct map_session_data *sd, struct item *items, in } -static void clif_cartlist(struct map_session_data *sd) +static void clif_cartList(struct map_session_data *sd) +{ +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + clif->inventoryStart(sd, INVTYPE_CART, ""); +#endif + clif->cartItems(sd, INVTYPE_CART); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + clif->inventoryEnd(sd, INVTYPE_CART); +#endif +} + +static void clif_cartItems(struct map_session_data *sd, enum inventory_type type) { int i, normal = 0, equip = 0; struct item_data *id; @@ -2904,16 +3040,22 @@ static void clif_cartlist(struct map_session_data *sd) clif->item_normal(i+2,&itemlist_normal.list[normal++],&sd->status.cart[i],id); } - if( normal ) { - itemlist_normal.PacketType = cartlistnormalType; - itemlist_normal.PacketLength = 4 + (sizeof(struct NORMALITEM_INFO) * normal); + if (normal) { + itemlist_normal.PacketType = cartlistnormalType; + itemlist_normal.PacketLength = (sizeof(itemlist_normal) - sizeof(itemlist_normal.list)) + (sizeof(struct NORMALITEM_INFO) * normal); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + itemlist_normal.invType = type; +#endif clif->send(&itemlist_normal, itemlist_normal.PacketLength, &sd->bl, SELF); } - if( equip ) { - itemlist_equip.PacketType = cartlistequipType; - itemlist_equip.PacketLength = 4 + (sizeof(struct EQUIPITEM_INFO) * equip); + if (equip) { + itemlist_equip.PacketType = cartlistequipType; + itemlist_equip.PacketLength = (sizeof(itemlist_equip) - sizeof(itemlist_equip.list)) + (sizeof(struct EQUIPITEM_INFO) * equip); +#if PACKETVER_RE_NUM >= 20180912 || PACKETVER_ZERO_NUM >= 20180919 + itemlist_equip.invType = type; +#endif clif->send(&itemlist_equip, itemlist_equip.PacketLength, &sd->bl, SELF); } @@ -3147,12 +3289,13 @@ static void clif_updatestatus(struct map_session_data *sd, int type) WFIFOL(fd,4)=pc_leftside_matk(sd); break; case SP_ZENY: +// [4144] possible send 64 bit value from PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) +// but kro sending 0xb1 packet only. WFIFOW(fd,0)=0xb1; WFIFOL(fd,4)=sd->status.zeny; len = packet_len(0xb1); break; -// [4144] exact version unknown, between 20170405 to 20170913? -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) case SP_BASEEXP: WFIFOW(fd, 0) = 0xacb; WFIFOQ(fd, 4) = sd->status.base_exp; @@ -5496,28 +5639,46 @@ static void clif_skill_poseffect(struct block_list *src, uint16 skill_id, int va static void clif_skill_warppoint(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4) { int fd; + int len; + int mapsCount = 0; + struct PACKET_ZC_WARPLIST *p; 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; +#else + len = sizeof(struct PACKET_ZC_WARPLIST); +#endif - WFIFOHEAD(fd,packet_len(0x11c)); - WFIFOW(fd,0) = 0x11c; - WFIFOW(fd,2) = skill_id; - memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT); - if (map1 == (unsigned short)-1) strcpy(WFIFOP(fd,4), "Random"); - else // normal map name - if (map1 > 0) mapindex->getmapname_ext(mapindex_id2name(map1), WFIFOP(fd,4)); - if (map2 > 0) mapindex->getmapname_ext(mapindex_id2name(map2), WFIFOP(fd,20)); - if (map3 > 0) mapindex->getmapname_ext(mapindex_id2name(map3), WFIFOP(fd,36)); - if (map4 > 0) mapindex->getmapname_ext(mapindex_id2name(map4), WFIFOP(fd,52)); - WFIFOSET(fd,packet_len(0x11c)); + WFIFOHEAD(fd, len); + p = WFIFOP(fd, 0); + memset(p, 0, len); + p->packetType = skilWarpPointType; + p->skillId = skill_id; + if (map1 == (unsigned short)-1) { + strcpy(p->maps[mapsCount++].map, "Random"); + } else { // normal map name + if (map1 > 0) mapindex->getmapname_ext(mapindex_id2name(map1), p->maps[mapsCount++].map); + } + if (map2 > 0) mapindex->getmapname_ext(mapindex_id2name(map2), p->maps[mapsCount++].map); + if (map3 > 0) mapindex->getmapname_ext(mapindex_id2name(map3), p->maps[mapsCount++].map); + if (map4 > 0) mapindex->getmapname_ext(mapindex_id2name(map4), p->maps[mapsCount++].map); + +#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; + p->packetLength = len; +#endif + + WFIFOSET(fd, len); sd->menuskill_id = skill_id; - if (skill_id == AL_WARP){ - sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here. + if (skill_id == AL_WARP) { + sd->menuskill_val = (sd->ud.skillx << 16) | sd->ud.skilly; //Store warp position here. sd->state.workinprogress = 3; - }else + } else { sd->menuskill_val = skill_lv; + } } /// Memo message (ZC_ACK_REMEMBER_WARPPOINT). @@ -6070,6 +6231,7 @@ static void clif_map_property_mapall(int mapid, enum map_property property) struct block_list bl; unsigned char buf[16]; + memset(&bl, 0, sizeof(bl)); bl.id = 0; bl.type = BL_NUL; bl.m = mapid; @@ -6150,6 +6312,7 @@ static void clif_wis_message(int fd, const char *nick, const char *mes, int mes_ /// 1 = target character is not logged in /// 2 = ignored by target /// 3 = everyone ignored by target +/// other = target character is not logged in static void clif_wis_end(int fd, int flag) { struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; @@ -6161,7 +6324,7 @@ static void clif_wis_end(int fd, int flag) p.PacketType = wisendType; p.result = (char)flag; #if PACKETVER >= 20131223 - p.unknown = 0; + p.AID = sd->bl.id; #endif clif->send(&p, sizeof(p), &sd->bl, SELF); @@ -6751,7 +6914,7 @@ static void clif_party_member_info(struct party_data *p, struct map_session_data packet.GID = sd->status.char_id; #endif packet.leader = (p->party.member[i].leader) ? 0 : 1; -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) packet.class = sd->status.class; packet.baseLevel = sd->status.base_level; #endif @@ -6802,7 +6965,7 @@ static void clif_party_info(struct party_data *p, struct map_session_data *sd) mapindex->getmapname_ext(mapindex_id2name(m->map), packet->members[c].mapName); packet->members[c].leader = (m->leader) ? 0 : 1; packet->members[c].offline = (m->online) ? 0 : 1; -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) packet->members[c].class = m->class; packet->members[c].baseLevel = m->lv; #endif @@ -6821,8 +6984,7 @@ static void clif_party_info(struct party_data *p, struct map_session_data *sd) /// 0abd <account id>.L <job>.W <level>.W static void clif_party_job_and_level(struct map_session_data *sd) { -// [4144] packet 0xabd added in client in 2017-02-15 because this probably it can works for clients older than 20170502 -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) unsigned char buf[10]; nullpo_retv(sd); @@ -7244,6 +7406,7 @@ static void clif_sendegg(struct map_session_data *sd) /// 3 = accessory /// 4 = performance (data = 1~3: normal, 4: special) /// 5 = hairstyle +/// 6 = close egg selection ui and update egg in inventory (PACKETVER >= 20180704) /// /// If sd is null, the update is sent to nearby objects, otherwise it is sent only to that player. static void clif_send_petdata(struct map_session_data *sd, struct pet_data *pd, int type, int param) @@ -7524,22 +7687,16 @@ static void clif_mvp_item(struct map_session_data *sd, int nameid) /// 010b <exp>.L static void clif_mvp_exp(struct map_session_data *sd, unsigned int exp) { -#if PACKETVER >= 20131223 // Kro removed this packet [Napster] - if (battle_config.mvp_exp_reward_message) { - char e_msg[CHAT_SIZE_MAX]; - sprintf(e_msg, msg_txt(855), exp); - clif->messagecolor_self(sd->fd, COLOR_CYAN, e_msg); // Congratulations! You are the MVP! Your reward EXP Points are %u !! - } -#else +#if PACKETVER_RE_NUM >= 20080827 || PACKETVER_MAIN_NUM >= 20090401 || defined(PACKETVER_ZERO) int fd; nullpo_retv(sd); - fd=sd->fd; - WFIFOHEAD(fd,packet_len(0x10b)); - WFIFOW(fd,0)=0x10b; - WFIFOL(fd,2)=cap_value(exp,0,INT32_MAX); - WFIFOSET(fd,packet_len(0x10b)); + fd = sd->fd; + WFIFOHEAD(fd, packet_len(0x10b)); + WFIFOW(fd, 0) = 0x10b; + WFIFOL(fd, 2) = cap_value(exp, 0, INT32_MAX); + WFIFOSET(fd, packet_len(0x10b)); #endif } @@ -7582,12 +7739,13 @@ static void clif_guild_created(struct map_session_data *sd, int flag) /// mode: @see enum guild_permission static void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g) { - int ps,fd; nullpo_retv(sd); nullpo_retv(g); - fd=sd->fd; - ps=guild->getposition(g,sd); + int fd = sd->fd; + int ps = guild->getposition(g, sd); + Assert_retv(ps != -1); + WFIFOHEAD(fd,packet_len(0x16c)); WFIFOW(fd,0)=0x16c; WFIFOL(fd,2)=g->guild_id; @@ -8064,41 +8222,45 @@ static void clif_guild_inviteack(struct map_session_data *sd, int flag) /// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD). /// 015a <char name>.24B <reason>.40B -static void clif_guild_leave(struct map_session_data *sd, const char *name, const char *mes) +static void clif_guild_leave(struct map_session_data *sd, const char *name, int char_id, const char *mes) { - unsigned char buf[128]; - nullpo_retv(sd); + nullpo_retv(name); + nullpo_retv(mes); - WBUFW(buf, 0)=0x15a; - memcpy(WBUFP(buf, 2),name,NAME_LENGTH); - memcpy(WBUFP(buf,26),mes,40); - clif->send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG); + struct PACKET_ZC_ACK_LEAVE_GUILD p; + p.packetType = guildLeave; +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) + p.GID = char_id; +#else + safestrncpy(&p.name[0], name, NAME_LENGTH); +#endif + safestrncpy(&p.reason[0], mes, 40); + clif->send(&p, sizeof(p), &sd->bl, GUILD_NOBG); } /// Notifies clients of a guild of an expelled member. /// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD) /// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO) -static void clif_guild_expulsion(struct map_session_data *sd, const char *name, const char *mes, int account_id) +static void clif_guild_expulsion(struct map_session_data *sd, const char *name, int char_id, const char *mes, int account_id) { - unsigned char buf[128]; -#if PACKETVER < 20100803 - const unsigned short cmd = 0x15c; -#else - const unsigned short cmd = 0x839; -#endif - nullpo_retv(sd); nullpo_retv(name); nullpo_retv(mes); - WBUFW(buf,0) = cmd; - safestrncpy(WBUFP(buf,2), name, NAME_LENGTH); - safestrncpy(WBUFP(buf,26), mes, 40); + struct PACKET_ZC_ACK_BAN_GUILD p; + p.packetType = guildExpulsion; +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) + p.GID = char_id; +#else + safestrncpy(&p.name[0], name, NAME_LENGTH); +#endif + safestrncpy(&p.reason[0], mes, 40); + #if PACKETVER < 20100803 - memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons) + memset(&p.account_name, 0, NAME_LENGTH); // account name (not used for security reasons) #endif - clif->send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG); + clif->send(&p, sizeof(p), &sd->bl, GUILD_NOBG); } /// Guild expulsion list (ZC_BAN_LIST). @@ -8280,6 +8442,49 @@ static void clif_guild_broken(struct map_session_data *sd, int flag) WFIFOSET(fd,packet_len(0x15e)); } +static void clif_guild_position_selected(struct map_session_data *sd) +{ +#if PACKETVER >= 20180801 + clif->guild_set_position(sd); +#else + clif->charnameupdate(sd); +#endif +} + +static void clif_guild_set_position(struct map_session_data *sd) +{ + nullpo_retv(sd); + + int len = sizeof(struct PACKET_ZC_GUILD_POSITION); + const char *name = NULL; + if (sd->status.guild_id > 0) { + struct guild *g = sd->guild; + + nullpo_retv(g); + + int i = 0; + int ps = -1; + ARR_FIND(0, g->max_member, i, g->member[i].account_id == sd->status.account_id && g->member[i].char_id == sd->status.char_id); + if (i < g->max_member) + ps = g->member[i].position; + + if (ps >= 0 && ps < MAX_GUILDPOSITION) { + len += 24; + name = g->position[ps].name; + } + } + + unsigned char buf[sizeof(struct PACKET_ZC_GUILD_POSITION) + NAME_LENGTH]; + struct PACKET_ZC_GUILD_POSITION *p = WBUFP(buf, 0); + p->packetType = 0xafd; + p->packetLength = len; + p->AID = sd->bl.id; + if (name != NULL) + memcpy(&p->position, name, 24); + + clif->send(buf, len, &sd->bl, AREA); +} + /// Displays emotion on an object (ZC_EMOTION). /// 00c0 <id>.L <type>.B /// type: @@ -8730,8 +8935,8 @@ static void clif_refresh_storagewindow(struct map_session_data *sd) if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) { if (sd->storage.aggregate > 0) { storage->sortitem(VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); - clif->storagelist(sd, VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); } + clif->storageList(sd, VECTOR_DATA(sd->storage.item), VECTOR_LENGTH(sd->storage.item)); clif->updatestorageamount(sd, sd->storage.aggregate, MAX_STORAGE); } // Notify the client that the gstorage is open otherwise it will @@ -8743,7 +8948,7 @@ static void clif_refresh_storagewindow(struct map_session_data *sd) intif->request_guild_storage(sd->status.account_id,sd->status.guild_id); } else { storage->sortitem(gstor->items, ARRAYLENGTH(gstor->items)); - clif->storagelist(sd, gstor->items, ARRAYLENGTH(gstor->items)); + clif->guildStorageList(sd, gstor->items, ARRAYLENGTH(gstor->items)); clif->updatestorageamount(sd, gstor->storage_amount, MAX_GUILD_STORAGE); } } @@ -8755,9 +8960,9 @@ static void clif_refresh(struct map_session_data *sd) nullpo_retv(sd); clif->changemap(sd,sd->bl.m,sd->bl.x,sd->bl.y); - clif->inventorylist(sd); + clif->inventoryList(sd); if(pc_iscarton(sd)) { - clif->cartlist(sd); + clif->cartList(sd); clif->updatestatus(sd,SP_CARTINFO); } clif->updatestatus(sd,SP_WEIGHT); @@ -8815,17 +9020,18 @@ static void clif_refresh(struct map_session_data *sd) /// Updates the object's (bl) name on client. /// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME) /// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL) +/// 0A30 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B <title id>.L (ZC_ACK_REQNAMEALL2) static void clif_charnameack(int fd, struct block_list *bl) { - unsigned char buf[103]; - int cmd = 0x95; + struct packet_reqnameall_ack packet = { 0 }; + int len = sizeof(struct packet_reqnameall_ack); nullpo_retv(bl); - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = bl->id; + packet.packet_id = reqName; + packet.gid = bl->id; - switch( bl->type ) { + switch(bl->type) { case BL_PC: { const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); @@ -8833,17 +9039,28 @@ static void clif_charnameack(int fd, struct block_list *bl) const struct guild *g = NULL; int ps = -1; + if (ssd->fakename[0] != '\0' || ssd->status.guild_id > 0 || ssd->status.party_id > 0 || ssd->status.title_id > 0) { + packet.packet_id = reqNameAllType; + } + //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise != -1) - WBUFL(buf,2) = -bl->id; + if (ssd->fd == fd && ssd->disguise != -1) { + packet.gid = -bl->id; + } if (ssd->fakename[0] != '\0') { - WBUFW(buf, 0) = cmd = 0x195; - memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH); - WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0; + memcpy(packet.name, ssd->fakename, NAME_LENGTH); break; } - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + +#if PACKETVER >= 20150503 + // Title System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; + } +#endif + + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (ssd->status.party_id != 0) { p = party->search(ssd->status.party_id); @@ -8865,47 +9082,41 @@ static void clif_charnameack(int fd, struct block_list *bl) if (p == NULL && g == NULL) break; - WBUFW(buf, 0) = cmd = 0x195; - if (p != NULL) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) { + memcpy(packet.party_name, p->party.name, NAME_LENGTH); + } if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); - } else { //Assume no guild. - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } } break; //[blackhole89] case BL_HOM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); break; case BL_MER: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); break; case BL_PET: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); break; case BL_NPC: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); break; case BL_MOB: { const struct mob_data *md = BL_UCCAST(BL_MOB, bl); - memcpy(WBUFP(buf,6), md->name, NAME_LENGTH); + memcpy(packet.name, md->name, NAME_LENGTH); if (md->guardian_data && md->guardian_data->g) { - WBUFW(buf, 0) = cmd = 0x195; - WBUFB(buf,30) = 0; - memcpy(WBUFP(buf,54), md->guardian_data->g->name, NAME_LENGTH); - memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH); + packet.packet_id = reqNameAllType; + memcpy(packet.guild_name, md->guardian_data->g->name, NAME_LENGTH); + memcpy(packet.position_name, md->guardian_data->castle->castle_name, NAME_LENGTH); } else if (battle_config.show_mob_info) { char mobhp[50], *str_p = mobhp; - WBUFW(buf, 0) = cmd = 0x195; + packet.packet_id = reqNameAllType; if (battle_config.show_mob_info&4) str_p += sprintf(str_p, "Lv. %d | ", md->level); if (battle_config.show_mob_info&1) @@ -8916,34 +9127,39 @@ static void clif_charnameack(int fd, struct block_list *bl) //can parse it. [Skotlex] if (str_p != mobhp) { *(str_p-3) = '\0'; //Remove trailing space + pipe. - memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH); - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.party_name, mobhp, NAME_LENGTH); } } } break; case BL_CHAT: #if 0 //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex] - memcpy(WBUFP(buf,6), BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); break; #endif return; case BL_ELEM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); break; default: ShowError("clif_charnameack: bad type %u(%d)\n", bl->type, bl->id); return; } + if (packet.packet_id == reqName) { + len = sizeof(struct packet_reqname_ack); + } + // if no recipient specified just update nearby clients // if no recipient specified just update nearby clients if (fd == 0) { - clif->send(buf, packet_len(cmd), bl, AREA); + clif->send(&packet, len, bl, AREA); } else { - WFIFOHEAD(fd, packet_len(cmd)); - memcpy(WFIFOP(fd, 0), buf, packet_len(cmd)); - WFIFOSET(fd, packet_len(cmd)); + struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; + if (sd != NULL) { + clif->send(&packet, len, &sd->bl, SELF); + } else { + clif->send(&packet, len, bl, SELF); + } } } @@ -8951,54 +9167,52 @@ static void clif_charnameack(int fd, struct block_list *bl) //Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent. static void clif_charnameupdate(struct map_session_data *ssd) { - unsigned char buf[103]; - int cmd = 0x195, ps = -1; + int ps = -1; struct party_data *p = NULL; struct guild *g = NULL; + struct packet_reqnameall_ack packet = { 0 }; nullpo_retv(ssd); - if( ssd->fakename[0] ) + if (ssd->fakename[0]) return; //No need to update as the party/guild was not displayed anyway. - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = ssd->bl.id; + packet.packet_id = reqNameAllType; + packet.gid = ssd->bl.id; - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (!battle_config.display_party_name) { if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) p = party->search(ssd->status.party_id); - }else{ + } else { if (ssd->status.party_id > 0) p = party->search(ssd->status.party_id); } - if( ssd->status.guild_id > 0 && (g = ssd->guild) != NULL ) - { + if (ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) { int i; ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); if( i < g->max_member ) ps = g->member[i].position; } - if( p ) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) + memcpy(packet.party_name, p->party.name, NAME_LENGTH); - if( g && ps >= 0 && ps < MAX_GUILDPOSITION ) - { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); + if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } - else - { - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + +#if PACKETVER >= 20150503 + // Achievement System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; } +#endif // Update nearby clients - clif->send(buf, packet_len(cmd), &ssd->bl, AREA); + clif->send(&packet, sizeof(packet), &ssd->bl, AREA); } /// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP). @@ -9288,6 +9502,9 @@ static void clif_viewequip_ack(struct map_session_data *sd, struct map_session_d #endif viewequip_list.headpalette = tsd->vd.hair_color; viewequip_list.bodypalette = tsd->vd.cloth_color; +#if PACKETVER_MAIN_NUM >= 20180801 || PACKETVER_RE_NUM >= 20180801 || PACKETVER_ZERO_NUM >= 20180808 + viewequip_list.body2 = tsd->vd.body_style; +#endif viewequip_list.sex = tsd->vd.sex; clif->send(&viewequip_list, viewequip_list.PacketLength, &sd->bl, SELF); @@ -9394,6 +9611,36 @@ static void clif_msgtable_str(struct map_session_data *sd, enum clif_messages ms } /** + * Displays a format string from msgstringtable.txt with a %s value and color (ZC_FORMATSTRING_MSG). + * + * @param sd The target character. + * @param msg_id msgstringtable message index, 0-based (@see enum clif_messages) + * @param value The value to fill %s. + * @param color The color to use + */ +static void clif_msgtable_str_color(struct map_session_data *sd, enum clif_messages msg_id, const char *value, uint32 color) +{ +#if PACKETVER >= 20160330 + nullpo_retv(sd); + nullpo_retv(value); + + int message_len = (int)strlen(value) + 1; + const int len = sizeof(struct PACKET_ZC_FORMATSTRING_MSG_COLOR) + message_len + 1; + struct PACKET_ZC_FORMATSTRING_MSG_COLOR *p = (struct PACKET_ZC_FORMATSTRING_MSG_COLOR *)aMalloc(len); + + p->PacketType = 0xa6f; + p->PacketLength = len; + p->messageId = msg_id; + p->color = color; + safestrncpy(p->messageString, value, message_len); + p->messageString[message_len] = 0; + + clif->send(p, p->PacketLength, &sd->bl, SELF); + aFree(p); +#endif +} + +/** * Displays a format string from msgstringtable.txt with a color (ZC_MSG_COLOR). * * @param sd The target character. @@ -9747,11 +9994,11 @@ static void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) // Send character inventory to the client. // call this before pc->checkitem() so that the client isn't called to delete a non-existent item. - clif->inventorylist(sd); + clif->inventoryList(sd); // Send the cart inventory, counts & weight to the client. if(pc_iscarton(sd)) { - clif->cartlist(sd); + clif->cartList(sd); clif->updatestatus(sd, SP_CARTINFO); } @@ -10754,7 +11001,7 @@ static void clif_parse_WisMessage(int fd, struct map_session_data *sd) // Lordalfa - Paperboy - To whisper NPC commands // //-------------------------------------------------------// if (target[0] && (strncasecmp(target,"NPC:",4) == 0) && (strlen(target) > 4)) { - const char *str = target+4; //Skip the NPC: string part. + char *str = target + 4; // Skip the NPC: string part. struct npc_data *nd; if ((nd = npc->name2id(str))) { char split_data[NUM_WHISPER_VAR][CHAT_SIZE_MAX]; @@ -11446,7 +11693,7 @@ static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) if (!pc_iscarton(sd)) return; if ( (flag = pc->putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4))) ) { - clif->dropitem(sd, RFIFOW(fd,2)-2,0); + clif->item_movefailed(sd, RFIFOW(fd,2)-2); clif->cart_additem_ack(sd,flag == 1?0x0:0x1); } } @@ -14786,6 +15033,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) f_sd->status.friends[i].char_id = sd->status.char_id; memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH); clif->friendslist_reqack(f_sd, sd, 0); + achievement->validate_friend_add(f_sd); // Achievements [Smokexyz/Hercules] if (battle_config.friend_auto_add) { // Also add f_sd to sd's friendlist. @@ -14804,6 +15052,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) sd->status.friends[i].char_id = f_sd->status.char_id; memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH); clif->friendslist_reqack(sd, f_sd, 0); + achievement->validate_friend_add(sd); // Achievements [Smokexyz/Hercules] } } } @@ -15154,7 +15403,7 @@ static void clif_parse_FeelSaveOk(int fd, struct map_session_data *sd) sd->feel_map[i].index = map_id2index(sd->bl.m); sd->feel_map[i].m = sd->bl.m; - pc_setglobalreg(sd,script->add_str(pc->sg_info[i].feel_var),sd->feel_map[i].index); + pc_setglobalreg(sd,script->add_variable(pc->sg_info[i].feel_var),sd->feel_map[i].index); #if 0 // Are these really needed? Shouldn't they show up automatically from the feel save packet? clif_misceffect2(&sd->bl, 0x1b0); @@ -16717,7 +16966,7 @@ static void clif_quest_update_objective(struct map_session_data *sd, struct ques qi = quest->db(qd->quest_id); Assert_retv(qi->objectives_count < MAX_QUEST_OBJECTIVES); - + len = sizeof(struct packet_quest_update_header) + MAX_QUEST_OBJECTIVES * sizeof(struct packet_quest_update_hunt); // >= than the actual length @@ -16730,7 +16979,7 @@ static void clif_quest_update_objective(struct map_session_data *sd, struct ques for (i = 0; i < qi->objectives_count; i++) { real_len += sizeof(packet->objectives[i]); - + packet->objectives[i].questID = qd->quest_id; #if PACKETVER >= 20150513 packet->objectives[i].huntIdent = (qd->quest_id * 1000) + i; @@ -16760,7 +17009,7 @@ static void clif_quest_notify_objective(struct map_session_data *sd, struct ques qi = quest->db(qd->quest_id); Assert_retv(qi->objectives_count < MAX_QUEST_OBJECTIVES); - + len = sizeof(struct packet_quest_hunt_info) + MAX_QUEST_OBJECTIVES * sizeof(struct packet_quest_hunt_info_sub); // >= than the actual length @@ -16772,7 +17021,7 @@ static void clif_quest_notify_objective(struct map_session_data *sd, struct ques for (i = 0; i < qi->objectives_count; i++) { real_len += sizeof(packet->info[i]); - + packet->info[i].questID = qd->quest_id; packet->info[i].mob_id = qi->objectives[i].mob; packet->info[i].maxCount = qi->objectives[i].count; @@ -17203,7 +17452,7 @@ static void clif_bg_updatescore(int16 m) struct block_list bl; unsigned char buf[6]; - bl.id = 0; + memset(&bl, 0, sizeof(bl)); bl.type = BL_NUL; bl.m = m; @@ -17409,8 +17658,7 @@ static void clif_displayexp(struct map_session_data *sd, uint64 exp, char type, { int fd; -// [4144] unconfirment exact version can be from 20170405 to 20170913 -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) const int cmd = 0xacc; #else const int cmd = 0x7f6; @@ -17422,8 +17670,7 @@ static void clif_displayexp(struct map_session_data *sd, uint64 exp, char type, WFIFOHEAD(fd, packet_len(cmd)); WFIFOW(fd, 0) = cmd; WFIFOL(fd, 2) = sd->bl.id; -// [4144] unconfirment exact version can be from 20170405 to 20170913 -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) WFIFOQ(fd, 6) = exp; WFIFOW(fd, 14) = type; WFIFOW(fd, 16) = is_quest ? 1 : 0; // Normal exp is shown in yellow, quest exp is shown in purple. @@ -18703,12 +18950,12 @@ static void clif_parse_CashShopBuy(int fd, struct map_session_data *sd) struct item item_tmp; int k, get_count; int ret = 0; - + get_count = qty; if (!itemdb->isstackable2(data)) get_count = 1; - + 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"); @@ -19509,9 +19756,9 @@ static void clif_parse_RouletteOpen(int fd, struct map_session_data *sd) p.Step = sd->roulette.stage - 1; p.Idx = (char)sd->roulette.prizeIdx; p.AdditionItemID = -1; /** TODO **/ - p.BronzePoint = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")); - p.GoldPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteGold")); - p.SilverPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")); + p.BronzePoint = pc_readglobalreg(sd, script->add_variable("TmpRouletteBronze")); + p.GoldPoint = pc_readglobalreg(sd, script->add_variable("TmpRouletteGold")); + p.SilverPoint = pc_readglobalreg(sd, script->add_variable("TmpRouletteSilver")); clif->send(&p,sizeof(p), &sd->bl, SELF); #endif @@ -19530,7 +19777,7 @@ static void clif_parse_RouletteInfo(int fd, struct map_session_data *sd) } p.PacketType = rouletteinfoackType; - p.PacketLength = 8 + (42 * 8); + p.PacketLength = sizeof(p); p.RouletteSerial = 1; for(i = 0; i < MAX_ROULETTE_LEVEL; i++) { @@ -19578,21 +19825,21 @@ static void clif_parse_RouletteGenerate(int fd, struct map_session_data *sd) stage = sd->roulette.stage = 0; if( stage == 0 ) { - if( pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) <= 0 && - pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) < 10 && - pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) < 10 ) + if( pc_readglobalreg(sd, script->add_variable("TmpRouletteBronze")) <= 0 && + pc_readglobalreg(sd, script->add_variable("TmpRouletteSilver")) < 10 && + pc_readglobalreg(sd, script->add_variable("TmpRouletteGold")) < 10 ) result = GENERATE_ROULETTE_NO_ENOUGH_POINT; } if( result == GENERATE_ROULETTE_SUCCESS ) { if( stage == 0 ) { - if( pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) > 0 ) { - pc_setglobalreg(sd, script->add_str("TmpRouletteBronze"), pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) - 1); - } else if( pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) > 9 ) { - pc_setglobalreg(sd, script->add_str("TmpRouletteSilver"), pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) - 10); + if( pc_readglobalreg(sd, script->add_variable("TmpRouletteBronze")) > 0 ) { + pc_setglobalreg(sd, script->add_variable("TmpRouletteBronze"), pc_readglobalreg(sd, script->add_variable("TmpRouletteBronze")) - 1); + } else if( pc_readglobalreg(sd, script->add_variable("TmpRouletteSilver")) > 9 ) { + pc_setglobalreg(sd, script->add_variable("TmpRouletteSilver"), pc_readglobalreg(sd, script->add_variable("TmpRouletteSilver")) - 10); stage = sd->roulette.stage = 2; - } else if( pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) > 9 ) { - pc_setglobalreg(sd, script->add_str("TmpRouletteGold"), pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) - 10); + } else if( pc_readglobalreg(sd, script->add_variable("TmpRouletteGold")) > 9 ) { + pc_setglobalreg(sd, script->add_variable("TmpRouletteGold"), pc_readglobalreg(sd, script->add_variable("TmpRouletteGold")) - 10); stage = sd->roulette.stage = 4; } } @@ -19774,9 +20021,9 @@ static void clif_roulette_generate_ack(struct map_session_data *sd, unsigned cha p.Step = stage; p.Idx = prizeIdx; p.AdditionItemID = bonusItemID; - p.RemainBronze = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")); - p.RemainGold = pc_readglobalreg(sd, script->add_str("TmpRouletteGold")); - p.RemainSilver = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")); + p.RemainBronze = pc_readglobalreg(sd, script->add_variable("TmpRouletteBronze")); + p.RemainGold = pc_readglobalreg(sd, script->add_variable("TmpRouletteGold")); + p.RemainSilver = pc_readglobalreg(sd, script->add_variable("TmpRouletteSilver")); clif->send(&p,sizeof(p), &sd->bl, SELF); #endif @@ -20223,6 +20470,214 @@ static unsigned short clif_parse_cmd_optional(int fd, struct map_session_data *s return cmd; } +/** + * Send all of a session's achievement information to client. + * Called only once on login/char-loading. (PACKET_ZC_ALL_ACH_LIST) + * @packet [out] 0x0A23 <ID>.W <Length>.W <ach_count>.L <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *[]>.P + * @param fd socket descriptor + * @param sd pointer to map_session_data + */ +static void clif_achievement_send_list(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + int i = 0, count = 0, curr_exp_tmp = 0; + struct packet_achievement_list p = { 0 }; + + nullpo_retv(sd); + + /* Browse through the session's achievement list and gather their values. */ + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + int j = 0; + struct achievement *a = &VECTOR_INDEX(sd->achievement, i); + const struct achievement_data *ad = NULL; + + /* Sanity check for nonull pointers. */ + if (a == NULL || (ad = achievement->get(a->id)) == NULL) + continue; + + p.ach[count].ach_id = a->id; + + for (j = 0; j < VECTOR_LENGTH(ad->objective); j++) + p.ach[count].objective[j] = a->objective[j]; + + if (a->completed_at) { + p.ach[count].completed = 1; + p.ach[count].completed_at = (uint32) a->completed_at; + p.ach[count].reward = a->rewarded_at ? 1 : 0; + p.total_points += ad->points; + } + + count++; + } + + p.packet_id = achievementListType; + p.packet_len = sizeof(struct ach_list_info) * count + 22; + p.total_achievements = count; + /* Determine Achievement Rank and current exp */ + curr_exp_tmp = p.total_points; + for (i = 0; curr_exp_tmp && i < MAX_ACHIEVEMENT_RANKS && i < VECTOR_LENGTH(achievement->rank_exp); i++) { + if (curr_exp_tmp >= VECTOR_INDEX(achievement->rank_exp, i)) { + curr_exp_tmp -= VECTOR_INDEX(achievement->rank_exp, i); + p.rank++; + + // Validate achievement rank type achievements. + achievement->validate_achievement_rank(sd, p.rank); + } + } + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_exp_tmp; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + /* Send payload */ + clif->send(&p, p.packet_len, &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement information for a single achievement. + * Called on objective progress updates/completion. (PACKET_ZC_ACH_UPDATE) + * @packet [out] 0x0A24 <ID>.W <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *>.P + * @param fd socket descriptor + * @param sd pointer to struct map_session_data + * @param ad const pointer to struct achievement_data from the achievement db. + */ +static void clif_achievement_send_update(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + struct packet_achievement_update p = { 0 }; + struct achievement *a = NULL; + int i = 0, points = 0, rank = 0, curr_rank_points = 0; + + nullpo_retv(sd); + nullpo_retv(ad); + + /* Get Session Achievement Data */ + if ((a = achievement->ensure(sd, ad)) == NULL) + return; + + /* Get total points, current rank and current rank points from the session. */ + achievement->calculate_totals(sd, &points, NULL, &rank, &curr_rank_points); + + p.packet_id = achievementUpdateType; + + /* Determine Total Achievement Points */ + p.total_points = points; + + /* Determine Achievement Rank */ + p.rank = rank; + + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_rank_points; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + + p.ach.ach_id = ad->id; + p.ach.completed = (uint8) achievement->check_complete(sd, ad); + + for (i = 0; i < VECTOR_LENGTH(ad->objective); i++) + p.ach.objective[i] = a->objective[i]; + + p.ach.completed_at = (uint32) a->completed_at; + + p.ach.reward = a->rewarded_at ? 1 : 0; + + clif->send(&p, packet_len(achievementUpdateType), &sd->bl, SELF); + + /* Validate rank-related achievements */ + achievement->validate_achievement_rank(sd, rank); + +#endif // PACKETVER >= 20141016 +} + +/** + * Parses achievement reward packet from session. + * @packet [in] 0x0A25 <ach_id>.L + * @param fd socket descriptor. + * @param sd ptr to session data. + */ +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) +{ + int ach_id = RFIFOL(fd, 2); + const struct achievement_data *ad = NULL; + struct achievement *ach = NULL; + + if (ach_id <= 0 || (ad = achievement->get(ach_id)) == NULL) + return; + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + if (achievement->check_complete(sd, ad) && ach->completed_at && ach->rewarded_at == 0) { + achievement->get_rewards(sd, ad); + } +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet [out] 0x0A26 <packet_id>.W <received + */ +static void clif_achievement_reward_ack(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + struct packet_achievement_reward_ack p = { 0 }; + + nullpo_retv(sd); + nullpo_retv(ad); + + p.packet_id = achievementRewardAckType; + p.received = 1; + p.ach_id = ad->id; + + clif->send(&p, packet_len(achievementRewardAckType), &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet[in] 0x0A2E <packet_id>.W <title_id>.L + */ +static void clif_parse_change_title(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_change_title(int fd, struct map_session_data *sd) +{ + int title_id = RFIFOL(fd, 2); + + if (title_id == sd->status.title_id) { // Same Title + return; + } else if (title_id < 0) { + title_id = 0; + } + + clif->change_title_ack(fd, sd, title_id); +} + +/** + * [clif_change_title_ack description] + * @packet [out] 0x0A2F <packet_id>.W <Result>.B <title_id>.L + */ +static void clif_change_title_ack(int fd, struct map_session_data *sd, int title_id) +{ +#if PACKETVER >= 20141016 + unsigned char failed = 0; + + if (!achievement->check_title(sd, title_id)) { + clif->message(fd, "Title is not yet earned."); + failed = 1; + } + + sd->status.title_id = title_id; + + WFIFOHEAD(fd, packet_len(0xa2f)); + WFIFOW(fd, 0) = 0xa2f; + WFIFOB(fd, 2) = failed; + WFIFOL(fd, 3) = sd->status.title_id; + WFIFOSET(fd, packet_len(0xa2f)); + + // Update names + clif->charnameack(fd, &sd->bl); + clif->charnameack(0, &sd->bl); +#endif +} +// End of Achievement System + /*========================================== * RoDEX *------------------------------------------*/ @@ -20489,7 +20944,7 @@ static void clif_rodex_send_mails_all(int fd, struct map_session_data *sd, int64 nullpo_retv(sd); mailsSize = VECTOR_LENGTH(sd->rodex.messages); - + if (mail_id > 0) ARR_FIND(0, VECTOR_LENGTH(sd->rodex.messages), j, (VECTOR_INDEX(sd->rodex.messages, j)).id == mail_id); @@ -20794,7 +21249,7 @@ static void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) { const struct PACKET_CZ_REQ_OPEN_MAIL *packet = RFIFOP(fd, 0); #if PACKETVER >= 20170419 - rodex->open(sd, RODEX_OPENTYPE_UNSET, packet->Upper_MailID); + rodex->open(sd, RODEX_OPENTYPE_UNSET, packet->char_Upper_MailID); #else rodex->open(sd, packet->opentype, packet->Upper_MailID); #endif @@ -21111,8 +21566,8 @@ static void clif_parse_private_airship_request(int fd, struct map_session_data * safestrncpy(evname, "private_airship::OnAirShipRequest", EVENT_NAME_LENGTH); if ((ev = strdb_get(npc->ev_db, evname))) { - pc->setregstr(sd, script->add_str("@mapname$"), p->mapName); - pc->setreg(sd, script->add_str("@itemid"), p->ItemID); + pc->setregstr(sd, script->add_variable("@mapname$"), p->mapName); + pc->setreg(sd, script->add_variable("@itemid"), p->ItemID); script->run_npc(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id); } else { ShowError("clif_parse_private_airship_request: event '%s' not found, operation failed.\n", evname); @@ -21198,7 +21653,7 @@ static bool clif_stylist_read_db_libconfig_sub(struct config_setting_t *it, int 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) { + 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; } @@ -21219,6 +21674,9 @@ static bool clif_stylist_read_db_libconfig_sub(struct config_setting_t *it, int 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; @@ -21235,7 +21693,10 @@ static bool clif_style_change_validate_requirements(struct map_session_data *sd, entry = &VECTOR_INDEX(stylist_data[type], idx); - if (entry->id != 0) { + 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; @@ -21293,7 +21754,32 @@ static void clif_parse_cz_req_style_change(int fd, struct map_session_data *sd) clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); if (p->BottomAccessory > 0) clif->cz_req_style_change_sub(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); + clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); + return; +} + +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) +{ + 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); + if (p->HeadPalette > 0) + clif->cz_req_style_change_sub(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); + if (p->BodyPalette > 0) + clif->cz_req_style_change_sub(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); + if (p->TopAccessory > 0) + clif->cz_req_style_change_sub(sd, LOOK_HEAD_TOP, p->TopAccessory, true); + if (p->MidAccessory > 0) + clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); + if (p->BottomAccessory > 0) + clif->cz_req_style_change_sub(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); + } + } clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); return; } @@ -21333,6 +21819,55 @@ static void clif_style_change_response(struct map_session_data *sd, enum stylist #endif } +static void clif_overweight_percent(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20171108 || PACKETVER_RE_NUM >= 20171025 || PACKETVER_ZERO_NUM >= 20171019 + struct PACKET_ZC_OVERWEIGHT_PERCENT p; + + nullpo_retv(sd); + + p.packetType = 0xade; + p.percent = battle_config.natural_heal_weight_rate; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + +static void clif_parse_changeDress(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +/// 0ae8 <packet len>.W +static void clif_parse_changeDress(int fd, struct map_session_data *sd) +{ + const char commandname[] = "changedress"; + char command[sizeof commandname + 3] = ""; // '@' command + ' ' + NUL + + sprintf(command, "%c%s ", atcommand->at_symbol, commandname); + atcommand->exec(fd, sd, command, true); +} + +static void clif_party_dead_notification(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) + struct PACKET_ZC_GROUP_ISALIVE p; + + nullpo_retv(sd); + + p.packetType = 0xab2; + p.AID = sd->bl.id; + p.isDead = pc_isdead(sd); + clif->send(&p, sizeof(p), &sd->bl, PARTY_WOS); +#endif +} + +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) +{ + const struct PACKET_CZ_MEMORIALDUNGEON_COMMAND *p = RP2PTR(fd); + + switch (p->command) { + case COMMAND_MEMORIALDUNGEON_DESTROY_FORCE: + instance->force_destroy(sd); + } +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -21674,6 +22209,7 @@ void clif_defaults(void) clif->dropitem = clif_dropitem; clif->delitem = clif_delitem; clif->takeitem = clif_takeitem; + clif->item_movefailed = clif_item_movefailed; clif->item_equip = clif_item_equip; clif->item_normal = clif_item_normal; clif->arrowequip = clif_arrowequip; @@ -21782,9 +22318,12 @@ void clif_defaults(void) clif->combo_delay = clif_combo_delay; clif->status_change = clif_status_change; clif->insert_card = clif_insert_card; - clif->inventorylist = clif_inventorylist; - clif->equiplist = clif_equiplist; - clif->cartlist = clif_cartlist; + clif->inventoryList = clif_inventoryList; + clif->inventoryItems = clif_inventoryItems; + clif->equipList = clif_equipList; + clif->equipItems = clif_equipItems; + clif->cartList = clif_cartList; + clif->cartItems = clif_cartItems; clif->favorite_item = clif_favorite_item; clif->clearcart = clif_clearcart; clif->item_identify_list = clif_item_identify_list; @@ -21910,6 +22449,7 @@ void clif_defaults(void) clif->msgtable = clif_msgtable; clif->msgtable_num = clif_msgtable_num; clif->msgtable_str = clif_msgtable_str; + clif->msgtable_str_color = clif_msgtable_str_color; clif->msgtable_color = clif_msgtable_color; clif->message = clif_displaymessage; clif->messageln = clif_displaymessage2; @@ -21939,7 +22479,11 @@ void clif_defaults(void) clif->openvendingAck = clif_openvendingAck; clif->vendingreport = clif_vendingreport; /* storage handling */ - clif->storagelist = clif_storagelist; + clif->storageList = clif_storageList; + clif->guildStorageList = clif_guildStorageList; + clif->storageItems = clif_storageItems; + clif->inventoryStart = clif_inventoryStart; + clif->inventoryEnd = clif_inventoryEnd; clif->updatestorageamount = clif_updatestorageamount; clif->storageitemadded = clif_storageitemadded; clif->storageitemremoved = clif_storageitemremoved; @@ -21998,6 +22542,8 @@ void clif_defaults(void) clif->guild_positionnamelist = clif_guild_positionnamelist; clif->guild_positioninfolist = clif_guild_positioninfolist; clif->guild_expulsionlist = clif_guild_expulsionlist; + clif->guild_set_position = clif_guild_set_position; + clif->guild_position_selected = clif_guild_position_selected; clif->validate_emblem = clif_validate_emblem; /* battleground-specific */ clif->bg_hp = clif_bg_hp; @@ -22159,6 +22705,14 @@ void clif_defaults(void) clif->isdisguised = clif_isdisguised; clif->navigate_to = clif_navigate_to; clif->bl_type = clif_bl_type; + /* Achievement System */ + clif->achievement_send_list = clif_achievement_send_list; + clif->achievement_send_update = clif_achievement_send_update; + clif->pAchievementGetReward = clif_parse_achievement_get_reward; + clif->achievement_reward_ack = clif_achievement_reward_ack; + /* Title */ + clif->change_title_ack = clif_change_title_ack; + clif->pChangeTitle = clif_parse_change_title; /*------------------------ *- Parse Incoming Packet @@ -22362,6 +22916,8 @@ void clif_defaults(void) clif->pSkillSelectMenu = clif_parse_SkillSelectMenu; clif->pMoveItem = clif_parse_MoveItem; clif->p_cz_blocking_play_cancel = clif_parse_cz_blocking_play_cancel; + clif->overweight_percent = clif_overweight_percent; + clif->pChangeDress = clif_parse_changeDress; /* dull */ clif->pDull = clif_parse_dull; /* BGQueue */ @@ -22406,6 +22962,7 @@ void clif_defaults(void) clif->pHotkeyRowShift = clif_parse_HotkeyRowShift; clif->dressroom_open = clif_dressroom_open; clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; + /* Achievements [Smokexyz/Hercules] */ clif->get_bl_name = clif_get_bl_name; /* RODEX */ clif->pRodexOpenWriteMail = clif_parse_rodex_open_write_mail; @@ -22445,6 +23002,7 @@ void clif_defaults(void) // -- Hat Effect clif->hat_effect = clif_hat_effect; clif->hat_effect_single = clif_hat_effect_single; + clif->party_dead_notification = clif_party_dead_notification; clif->pAttendanceDB = clif_parse_attendance_db; clif->attendancedb_libconfig_sub = clif_attendancedb_libconfig_sub; @@ -22464,6 +23022,7 @@ void clif_defaults(void) 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->style_change_response = clif_style_change_response; @@ -22471,4 +23030,5 @@ void clif_defaults(void) clif->pPetEvolution = clif_parse_pet_evolution; clif->petEvolutionResult = clif_pet_evolution_result; + clif->pMemorialDungeonCommand = clif_parse_memorial_dungeon_command; } |