diff options
Diffstat (limited to 'src/map/clif.c')
-rw-r--r-- | src/map/clif.c | 4691 |
1 files changed, 3348 insertions, 1343 deletions
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; } |