diff options
Diffstat (limited to 'src/map/clif.c')
-rw-r--r-- | src/map/clif.c | 662 |
1 files changed, 543 insertions, 119 deletions
diff --git a/src/map/clif.c b/src/map/clif.c index 82507a84a..fd8a3c783 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -56,13 +56,14 @@ #include "map/trade.h" #include "map/unit.h" #include "map/vending.h" +#include "map/achievement.h" #include "common/HPM.h" #include "common/cbasetypes.h" #include "common/conf.h" #include "common/ers.h" #include "common/grfio.h" #include "common/memmgr.h" -#include "common/mmo.h" // NEW_CARTS +#include "common/mmo.h" // NEW_CARTS, char_achievements #include "common/nullpo.h" #include "common/random.h" #include "common/showmsg.h" @@ -2009,8 +2010,8 @@ static void clif_changemap_airship(struct map_session_data *sd, short m, int x, /// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE). /// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W -/// 0ac7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <zero>.128B -static void clif_changemapserver(struct map_session_data *sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port) +/// 0ac7 <map name>.16B <x>.W <y>.W <ip>.L <port>.W <dns host>.128B +static void clif_changemapserver(struct map_session_data *sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port, char *dnsHost) { int fd; #if PACKETVER >= 20170315 @@ -2028,6 +2029,15 @@ static void clif_changemapserver(struct map_session_data *sd, unsigned short map WFIFOW(fd, 20) = y; WFIFOL(fd, 22) = htonl(ip); WFIFOW(fd, 26) = sockt->ntows(htons(port)); // [!] LE byte order here [!] + +#if PACKETVER >= 20170315 + if (dnsHost != NULL) { + safestrncpy(WFIFOP(fd, 28), dnsHost, 128); + } else { + memset(WFIFOP(fd, 28), 0, 128); + } +#endif + WFIFOSET(fd, packet_len(cmd)); } @@ -2623,6 +2633,22 @@ static void clif_dropitem(struct map_session_data *sd, int n, int amount) WFIFOSET(fd,packet_len(0xaf)); } +static void clif_item_movefailed(struct map_session_data *sd, int n) +{ +#if PACKETVER_MAIN_NUM >= 20161214 || PACKETVER_RE_NUM >= 20161130 || defined(PACKETVER_ZERO) + int fd = sd->fd; + const int len = sizeof(struct PACKET_ZC_INVENTORY_MOVE_FAILED); + WFIFOHEAD(fd, len); + struct PACKET_ZC_INVENTORY_MOVE_FAILED *p = WFIFOP(fd, 0); + p->packetType = 0xaa7; + p->index = n; + p->unknown = 1; + WFIFOSET(fd, len); +#else + clif->dropitem(sd, n, 0); +#endif +} + /// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY). /// 07fa <delete type>.W <index>.W <amount>.W /// delete type: @see enum delitem_reason @@ -3147,12 +3173,13 @@ static void clif_updatestatus(struct map_session_data *sd, int type) WFIFOL(fd,4)=pc_leftside_matk(sd); break; case SP_ZENY: +// [4144] possible send 64 bit value from PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) +// but kro sending 0xb1 packet only. WFIFOW(fd,0)=0xb1; WFIFOL(fd,4)=sd->status.zeny; len = packet_len(0xb1); break; -// [4144] exact version unknown, between 20170405 to 20170913? -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) case SP_BASEEXP: WFIFOW(fd, 0) = 0xacb; WFIFOQ(fd, 4) = sd->status.base_exp; @@ -5496,28 +5523,46 @@ static void clif_skill_poseffect(struct block_list *src, uint16 skill_id, int va static void clif_skill_warppoint(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4) { int fd; + int len; + int mapsCount = 0; + struct PACKET_ZC_WARPLIST *p; nullpo_retv(sd); fd = sd->fd; +#if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) + len = sizeof(struct PACKET_ZC_WARPLIST) + sizeof(struct PACKET_ZC_WARPLIST_sub) * mapsCount; +#else + len = sizeof(struct PACKET_ZC_WARPLIST); +#endif - WFIFOHEAD(fd,packet_len(0x11c)); - WFIFOW(fd,0) = 0x11c; - WFIFOW(fd,2) = skill_id; - memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT); - if (map1 == (unsigned short)-1) strcpy(WFIFOP(fd,4), "Random"); - else // normal map name - if (map1 > 0) mapindex->getmapname_ext(mapindex_id2name(map1), WFIFOP(fd,4)); - if (map2 > 0) mapindex->getmapname_ext(mapindex_id2name(map2), WFIFOP(fd,20)); - if (map3 > 0) mapindex->getmapname_ext(mapindex_id2name(map3), WFIFOP(fd,36)); - if (map4 > 0) mapindex->getmapname_ext(mapindex_id2name(map4), WFIFOP(fd,52)); - WFIFOSET(fd,packet_len(0x11c)); + WFIFOHEAD(fd, len); + p = WFIFOP(fd, 0); + memset(p, 0, len); + p->packetType = skilWarpPointType; + p->skillId = skill_id; + if (map1 == (unsigned short)-1) { + strcpy(p->maps[mapsCount++].map, "Random"); + } else { // normal map name + if (map1 > 0) mapindex->getmapname_ext(mapindex_id2name(map1), p->maps[mapsCount++].map); + } + if (map2 > 0) mapindex->getmapname_ext(mapindex_id2name(map2), p->maps[mapsCount++].map); + if (map3 > 0) mapindex->getmapname_ext(mapindex_id2name(map3), p->maps[mapsCount++].map); + if (map4 > 0) mapindex->getmapname_ext(mapindex_id2name(map4), p->maps[mapsCount++].map); + +#if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) + len = sizeof(struct PACKET_ZC_WARPLIST) + sizeof(struct PACKET_ZC_WARPLIST_sub) * mapsCount; + p->packetLength = len; +#endif + + WFIFOSET(fd, len); sd->menuskill_id = skill_id; - if (skill_id == AL_WARP){ - sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here. + if (skill_id == AL_WARP) { + sd->menuskill_val = (sd->ud.skillx << 16) | sd->ud.skilly; //Store warp position here. sd->state.workinprogress = 3; - }else + } else { sd->menuskill_val = skill_lv; + } } /// Memo message (ZC_ACK_REMEMBER_WARPPOINT). @@ -6070,6 +6115,7 @@ static void clif_map_property_mapall(int mapid, enum map_property property) struct block_list bl; unsigned char buf[16]; + memset(&bl, 0, sizeof(bl)); bl.id = 0; bl.type = BL_NUL; bl.m = mapid; @@ -6150,6 +6196,7 @@ static void clif_wis_message(int fd, const char *nick, const char *mes, int mes_ /// 1 = target character is not logged in /// 2 = ignored by target /// 3 = everyone ignored by target +/// other = target character is not logged in static void clif_wis_end(int fd, int flag) { struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; @@ -6161,7 +6208,7 @@ static void clif_wis_end(int fd, int flag) p.PacketType = wisendType; p.result = (char)flag; #if PACKETVER >= 20131223 - p.unknown = 0; + p.AID = sd->bl.id; #endif clif->send(&p, sizeof(p), &sd->bl, SELF); @@ -6751,7 +6798,7 @@ static void clif_party_member_info(struct party_data *p, struct map_session_data packet.GID = sd->status.char_id; #endif packet.leader = (p->party.member[i].leader) ? 0 : 1; -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) packet.class = sd->status.class; packet.baseLevel = sd->status.base_level; #endif @@ -6802,7 +6849,7 @@ static void clif_party_info(struct party_data *p, struct map_session_data *sd) mapindex->getmapname_ext(mapindex_id2name(m->map), packet->members[c].mapName); packet->members[c].leader = (m->leader) ? 0 : 1; packet->members[c].offline = (m->online) ? 0 : 1; -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) packet->members[c].class = m->class; packet->members[c].baseLevel = m->lv; #endif @@ -6821,8 +6868,7 @@ static void clif_party_info(struct party_data *p, struct map_session_data *sd) /// 0abd <account id>.L <job>.W <level>.W static void clif_party_job_and_level(struct map_session_data *sd) { -// [4144] packet 0xabd added in client in 2017-02-15 because this probably it can works for clients older than 20170502 -#if PACKETVER >= 20170502 +#if PACKETVER_MAIN_NUM >= 20170502 || PACKETVER_RE_NUM >= 20170419 || defined(PACKETVER_ZERO) unsigned char buf[10]; nullpo_retv(sd); @@ -7583,12 +7629,13 @@ static void clif_guild_created(struct map_session_data *sd, int flag) /// mode: @see enum guild_permission static void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g) { - int ps,fd; nullpo_retv(sd); nullpo_retv(g); - fd=sd->fd; - ps=guild->getposition(g,sd); + int fd = sd->fd; + int ps = guild->getposition(g, sd); + Assert_retv(ps != -1); + WFIFOHEAD(fd,packet_len(0x16c)); WFIFOW(fd,0)=0x16c; WFIFOL(fd,2)=g->guild_id; @@ -8065,41 +8112,45 @@ static void clif_guild_inviteack(struct map_session_data *sd, int flag) /// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD). /// 015a <char name>.24B <reason>.40B -static void clif_guild_leave(struct map_session_data *sd, const char *name, const char *mes) +static void clif_guild_leave(struct map_session_data *sd, const char *name, int char_id, const char *mes) { - unsigned char buf[128]; - nullpo_retv(sd); + nullpo_retv(name); + nullpo_retv(mes); - WBUFW(buf, 0)=0x15a; - memcpy(WBUFP(buf, 2),name,NAME_LENGTH); - memcpy(WBUFP(buf,26),mes,40); - clif->send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG); + struct PACKET_ZC_ACK_LEAVE_GUILD p; + p.packetType = guildLeave; +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) + p.GID = char_id; +#else + safestrncpy(&p.name[0], name, NAME_LENGTH); +#endif + safestrncpy(&p.reason[0], mes, 40); + clif->send(&p, sizeof(p), &sd->bl, GUILD_NOBG); } /// Notifies clients of a guild of an expelled member. /// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD) /// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO) -static void clif_guild_expulsion(struct map_session_data *sd, const char *name, const char *mes, int account_id) +static void clif_guild_expulsion(struct map_session_data *sd, const char *name, int char_id, const char *mes, int account_id) { - unsigned char buf[128]; -#if PACKETVER < 20100803 - const unsigned short cmd = 0x15c; -#else - const unsigned short cmd = 0x839; -#endif - nullpo_retv(sd); nullpo_retv(name); nullpo_retv(mes); - WBUFW(buf,0) = cmd; - safestrncpy(WBUFP(buf,2), name, NAME_LENGTH); - safestrncpy(WBUFP(buf,26), mes, 40); + struct PACKET_ZC_ACK_BAN_GUILD p; + p.packetType = guildExpulsion; +#if PACKETVER_MAIN_NUM >= 20161019 || PACKETVER_RE_NUM >= 20160921 || defined(PACKETVER_ZERO) + p.GID = char_id; +#else + safestrncpy(&p.name[0], name, NAME_LENGTH); +#endif + safestrncpy(&p.reason[0], mes, 40); + #if PACKETVER < 20100803 - memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons) + memset(&p.account_name, 0, NAME_LENGTH); // account name (not used for security reasons) #endif - clif->send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG); + clif->send(&p, sizeof(p), &sd->bl, GUILD_NOBG); } /// Guild expulsion list (ZC_BAN_LIST). @@ -8281,6 +8332,49 @@ static void clif_guild_broken(struct map_session_data *sd, int flag) WFIFOSET(fd,packet_len(0x15e)); } +static void clif_guild_position_selected(struct map_session_data *sd) +{ +#if PACKETVER >= 20180801 + clif->guild_set_position(sd); +#else + clif->charnameupdate(sd); +#endif +} + +static void clif_guild_set_position(struct map_session_data *sd) +{ + nullpo_retv(sd); + + int len = sizeof(struct PACKET_ZC_GUILD_POSITION); + const char *name = NULL; + if (sd->status.guild_id > 0) { + struct guild *g = sd->guild; + + nullpo_retv(g); + + int i = 0; + int ps = -1; + ARR_FIND(0, g->max_member, i, g->member[i].account_id == sd->status.account_id && g->member[i].char_id == sd->status.char_id); + if (i < g->max_member) + ps = g->member[i].position; + + if (ps >= 0 && ps < MAX_GUILDPOSITION) { + len += 24; + name = g->position[ps].name; + } + } + + unsigned char buf[sizeof(struct PACKET_ZC_GUILD_POSITION) + NAME_LENGTH]; + struct PACKET_ZC_GUILD_POSITION *p = WBUFP(buf, 0); + p->packetType = 0xafd; + p->packetLength = len; + p->AID = sd->bl.id; + if (name != NULL) + memcpy(&p->position, name, 24); + + clif->send(buf, len, &sd->bl, AREA); +} + /// Displays emotion on an object (ZC_EMOTION). /// 00c0 <id>.L <type>.B /// type: @@ -8816,17 +8910,18 @@ static void clif_refresh(struct map_session_data *sd) /// Updates the object's (bl) name on client. /// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME) /// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL) +/// 0A30 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B <title id>.L (ZC_ACK_REQNAMEALL2) static void clif_charnameack(int fd, struct block_list *bl) { - unsigned char buf[103]; - int cmd = 0x95; + struct packet_reqnameall_ack packet = { 0 }; + int len = sizeof(struct packet_reqnameall_ack); nullpo_retv(bl); - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = bl->id; + packet.packet_id = reqName; + packet.gid = bl->id; - switch( bl->type ) { + switch(bl->type) { case BL_PC: { const struct map_session_data *ssd = BL_UCCAST(BL_PC, bl); @@ -8834,17 +8929,28 @@ static void clif_charnameack(int fd, struct block_list *bl) const struct guild *g = NULL; int ps = -1; + if (ssd->fakename[0] != '\0' || ssd->status.guild_id > 0 || ssd->status.party_id > 0 || ssd->status.title_id > 0) { + packet.packet_id = reqNameAllType; + } + //Requesting your own "shadow" name. [Skotlex] - if (ssd->fd == fd && ssd->disguise != -1) - WBUFL(buf,2) = -bl->id; + if (ssd->fd == fd && ssd->disguise != -1) { + packet.gid = -bl->id; + } if (ssd->fakename[0] != '\0') { - WBUFW(buf, 0) = cmd = 0x195; - memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH); - WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0; + memcpy(packet.name, ssd->fakename, NAME_LENGTH); break; } - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + +#if PACKETVER >= 20150503 + // Title System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; + } +#endif + + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (ssd->status.party_id != 0) { p = party->search(ssd->status.party_id); @@ -8866,47 +8972,41 @@ static void clif_charnameack(int fd, struct block_list *bl) if (p == NULL && g == NULL) break; - WBUFW(buf, 0) = cmd = 0x195; - if (p != NULL) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) { + memcpy(packet.party_name, p->party.name, NAME_LENGTH); + } if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); - } else { //Assume no guild. - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } } break; //[blackhole89] case BL_HOM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_HOM, bl)->homunculus.name, NAME_LENGTH); break; case BL_MER: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_MER, bl)->db->name, NAME_LENGTH); break; case BL_PET: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_PET, bl)->pet.name, NAME_LENGTH); break; case BL_NPC: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_NPC, bl)->name, NAME_LENGTH); break; case BL_MOB: { const struct mob_data *md = BL_UCCAST(BL_MOB, bl); - memcpy(WBUFP(buf,6), md->name, NAME_LENGTH); + memcpy(packet.name, md->name, NAME_LENGTH); if (md->guardian_data && md->guardian_data->g) { - WBUFW(buf, 0) = cmd = 0x195; - WBUFB(buf,30) = 0; - memcpy(WBUFP(buf,54), md->guardian_data->g->name, NAME_LENGTH); - memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH); + packet.packet_id = reqNameAllType; + memcpy(packet.guild_name, md->guardian_data->g->name, NAME_LENGTH); + memcpy(packet.position_name, md->guardian_data->castle->castle_name, NAME_LENGTH); } else if (battle_config.show_mob_info) { char mobhp[50], *str_p = mobhp; - WBUFW(buf, 0) = cmd = 0x195; + packet.packet_id = reqNameAllType; if (battle_config.show_mob_info&4) str_p += sprintf(str_p, "Lv. %d | ", md->level); if (battle_config.show_mob_info&1) @@ -8917,34 +9017,39 @@ static void clif_charnameack(int fd, struct block_list *bl) //can parse it. [Skotlex] if (str_p != mobhp) { *(str_p-3) = '\0'; //Remove trailing space + pipe. - memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH); - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + memcpy(packet.party_name, mobhp, NAME_LENGTH); } } } break; case BL_CHAT: #if 0 //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex] - memcpy(WBUFP(buf,6), BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_CHAT, bl)->title, NAME_LENGTH); break; #endif return; case BL_ELEM: - memcpy(WBUFP(buf,6), BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); + memcpy(packet.name, BL_UCCAST(BL_ELEM, bl)->db->name, NAME_LENGTH); break; default: ShowError("clif_charnameack: bad type %u(%d)\n", bl->type, bl->id); return; } + if (packet.packet_id == reqName) { + len = sizeof(struct packet_reqname_ack); + } + // if no recipient specified just update nearby clients // if no recipient specified just update nearby clients if (fd == 0) { - clif->send(buf, packet_len(cmd), bl, AREA); + clif->send(&packet, len, bl, AREA); } else { - WFIFOHEAD(fd, packet_len(cmd)); - memcpy(WFIFOP(fd, 0), buf, packet_len(cmd)); - WFIFOSET(fd, packet_len(cmd)); + struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL; + if (sd != NULL) { + clif->send(&packet, len, &sd->bl, SELF); + } else { + clif->send(&packet, len, bl, SELF); + } } } @@ -8952,54 +9057,52 @@ static void clif_charnameack(int fd, struct block_list *bl) //Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent. static void clif_charnameupdate(struct map_session_data *ssd) { - unsigned char buf[103]; - int cmd = 0x195, ps = -1; + int ps = -1; struct party_data *p = NULL; struct guild *g = NULL; + struct packet_reqnameall_ack packet = { 0 }; nullpo_retv(ssd); - if( ssd->fakename[0] ) + if (ssd->fakename[0]) return; //No need to update as the party/guild was not displayed anyway. - WBUFW(buf,0) = cmd; - WBUFL(buf,2) = ssd->bl.id; + packet.packet_id = reqNameAllType; + packet.gid = ssd->bl.id; - memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH); + memcpy(packet.name, ssd->status.name, NAME_LENGTH); if (!battle_config.display_party_name) { if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) p = party->search(ssd->status.party_id); - }else{ + } else { if (ssd->status.party_id > 0) p = party->search(ssd->status.party_id); } - if( ssd->status.guild_id > 0 && (g = ssd->guild) != NULL ) - { + if (ssd->status.guild_id > 0 && (g = ssd->guild) != NULL) { int i; ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id); if( i < g->max_member ) ps = g->member[i].position; } - if( p ) - memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH); - else - WBUFB(buf,30) = 0; + if (p != NULL) + memcpy(packet.party_name, p->party.name, NAME_LENGTH); - if( g && ps >= 0 && ps < MAX_GUILDPOSITION ) - { - memcpy(WBUFP(buf,54), g->name,NAME_LENGTH); - memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH); + if (g != NULL && ps >= 0 && ps < MAX_GUILDPOSITION) { + memcpy(packet.guild_name, g->name,NAME_LENGTH); + memcpy(packet.position_name, g->position[ps].name, NAME_LENGTH); } - else - { - WBUFB(buf,54) = 0; - WBUFB(buf,78) = 0; + +#if PACKETVER >= 20150503 + // Achievement System [Dastgir/Hercules] + if (ssd->status.title_id > 0) { + packet.title_id = ssd->status.title_id; } +#endif // Update nearby clients - clif->send(buf, packet_len(cmd), &ssd->bl, AREA); + clif->send(&packet, sizeof(packet), &ssd->bl, AREA); } /// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP). @@ -9289,6 +9392,9 @@ static void clif_viewequip_ack(struct map_session_data *sd, struct map_session_d #endif viewequip_list.headpalette = tsd->vd.hair_color; viewequip_list.bodypalette = tsd->vd.cloth_color; +#if PACKETVER_MAIN_NUM >= 20180801 || PACKETVER_RE_NUM >= 20180801 || PACKETVER_ZERO_NUM >= 20180808 + viewequip_list.body2 = tsd->vd.body_style; +#endif viewequip_list.sex = tsd->vd.sex; clif->send(&viewequip_list, viewequip_list.PacketLength, &sd->bl, SELF); @@ -9395,6 +9501,36 @@ static void clif_msgtable_str(struct map_session_data *sd, enum clif_messages ms } /** + * Displays a format string from msgstringtable.txt with a %s value and color (ZC_FORMATSTRING_MSG). + * + * @param sd The target character. + * @param msg_id msgstringtable message index, 0-based (@see enum clif_messages) + * @param value The value to fill %s. + * @param color The color to use + */ +static void clif_msgtable_str_color(struct map_session_data *sd, enum clif_messages msg_id, const char *value, uint32 color) +{ +#if PACKETVER >= 20160330 + nullpo_retv(sd); + nullpo_retv(value); + + int message_len = (int)strlen(value) + 1; + const int len = sizeof(struct PACKET_ZC_FORMATSTRING_MSG_COLOR) + message_len + 1; + struct PACKET_ZC_FORMATSTRING_MSG_COLOR *p = (struct PACKET_ZC_FORMATSTRING_MSG_COLOR *)aMalloc(len); + + p->PacketType = 0xa6f; + p->PacketLength = len; + p->messageId = msg_id; + p->color = color; + safestrncpy(p->messageString, value, message_len); + p->messageString[message_len] = 0; + + clif->send(p, p->PacketLength, &sd->bl, SELF); + aFree(p); +#endif +} + +/** * Displays a format string from msgstringtable.txt with a color (ZC_MSG_COLOR). * * @param sd The target character. @@ -10952,7 +11088,7 @@ static void clif_parse_DropItem(int fd, struct map_session_data *sd) } //Because the client does not like being ignored. - clif->dropitem(sd, item_index, 0); + clif->item_movefailed(sd, item_index); } static void clif_parse_UseItem(int fd, struct map_session_data *sd) __attribute__((nonnull (2))); @@ -11447,7 +11583,7 @@ static void clif_parse_PutItemToCart(int fd, struct map_session_data *sd) if (!pc_iscarton(sd)) return; if ( (flag = pc->putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4))) ) { - clif->dropitem(sd, RFIFOW(fd,2)-2,0); + clif->item_movefailed(sd, RFIFOW(fd,2)-2); clif->cart_additem_ack(sd,flag == 1?0x0:0x1); } } @@ -14787,6 +14923,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) f_sd->status.friends[i].char_id = sd->status.char_id; memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH); clif->friendslist_reqack(f_sd, sd, 0); + achievement->validate_friend_add(f_sd); // Achievements [Smokexyz/Hercules] if (battle_config.friend_auto_add) { // Also add f_sd to sd's friendlist. @@ -14805,6 +14942,7 @@ static void clif_parse_FriendsListReply(int fd, struct map_session_data *sd) sd->status.friends[i].char_id = f_sd->status.char_id; memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH); clif->friendslist_reqack(sd, f_sd, 0); + achievement->validate_friend_add(sd); // Achievements [Smokexyz/Hercules] } } } @@ -17204,7 +17342,7 @@ static void clif_bg_updatescore(int16 m) struct block_list bl; unsigned char buf[6]; - bl.id = 0; + memset(&bl, 0, sizeof(bl)); bl.type = BL_NUL; bl.m = m; @@ -17410,8 +17548,7 @@ static void clif_displayexp(struct map_session_data *sd, uint64 exp, char type, { int fd; -// [4144] unconfirment exact version can be from 20170405 to 20170913 -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) const int cmd = 0xacc; #else const int cmd = 0x7f6; @@ -17423,8 +17560,7 @@ static void clif_displayexp(struct map_session_data *sd, uint64 exp, char type, WFIFOHEAD(fd, packet_len(cmd)); WFIFOW(fd, 0) = cmd; WFIFOL(fd, 2) = sd->bl.id; -// [4144] unconfirment exact version can be from 20170405 to 20170913 -#if PACKETVER >= 20170830 +#if PACKETVER_MAIN_NUM >= 20170906 || PACKETVER_RE_NUM >= 20170830 || defined(PACKETVER_ZERO) WFIFOQ(fd, 6) = exp; WFIFOW(fd, 14) = type; WFIFOW(fd, 16) = is_quest ? 1 : 0; // Normal exp is shown in yellow, quest exp is shown in purple. @@ -20224,6 +20360,214 @@ static unsigned short clif_parse_cmd_optional(int fd, struct map_session_data *s return cmd; } +/** + * Send all of a session's achievement information to client. + * Called only once on login/char-loading. (PACKET_ZC_ALL_ACH_LIST) + * @packet [out] 0x0A23 <ID>.W <Length>.W <ach_count>.L <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *[]>.P + * @param fd socket descriptor + * @param sd pointer to map_session_data + */ +static void clif_achievement_send_list(int fd, struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + int i = 0, count = 0, curr_exp_tmp = 0; + struct packet_achievement_list p = { 0 }; + + nullpo_retv(sd); + + /* Browse through the session's achievement list and gather their values. */ + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + int j = 0; + struct achievement *a = &VECTOR_INDEX(sd->achievement, i); + const struct achievement_data *ad = NULL; + + /* Sanity check for nonull pointers. */ + if (a == NULL || (ad = achievement->get(a->id)) == NULL) + continue; + + p.ach[count].ach_id = a->id; + + for (j = 0; j < VECTOR_LENGTH(ad->objective); j++) + p.ach[count].objective[j] = a->objective[j]; + + if (a->completed_at) { + p.ach[count].completed = 1; + p.ach[count].completed_at = (uint32) a->completed_at; + p.ach[count].reward = a->rewarded_at ? 1 : 0; + p.total_points += ad->points; + } + + count++; + } + + p.packet_id = achievementListType; + p.packet_len = sizeof(struct ach_list_info) * count + 22; + p.total_achievements = count; + /* Determine Achievement Rank and current exp */ + curr_exp_tmp = p.total_points; + for (i = 0; curr_exp_tmp && i < MAX_ACHIEVEMENT_RANKS && i < VECTOR_LENGTH(achievement->rank_exp); i++) { + if (curr_exp_tmp >= VECTOR_INDEX(achievement->rank_exp, i)) { + curr_exp_tmp -= VECTOR_INDEX(achievement->rank_exp, i); + p.rank++; + + // Validate achievement rank type achievements. + achievement->validate_achievement_rank(sd, p.rank); + } + } + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_exp_tmp; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + /* Send payload */ + clif->send(&p, p.packet_len, &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement information for a single achievement. + * Called on objective progress updates/completion. (PACKET_ZC_ACH_UPDATE) + * @packet [out] 0x0A24 <ID>.W <total_points>.L <rank>.W <current_rank_points>.L <next_rank_points>.L <struct ach_list_info *>.P + * @param fd socket descriptor + * @param sd pointer to struct map_session_data + * @param ad const pointer to struct achievement_data from the achievement db. + */ +static void clif_achievement_send_update(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + struct packet_achievement_update p = { 0 }; + struct achievement *a = NULL; + int i = 0, points = 0, rank = 0, curr_rank_points = 0; + + nullpo_retv(sd); + nullpo_retv(ad); + + /* Get Session Achievement Data */ + if ((a = achievement->ensure(sd, ad)) == NULL) + return; + + /* Get total points, current rank and current rank points from the session. */ + achievement->calculate_totals(sd, &points, NULL, &rank, &curr_rank_points); + + p.packet_id = achievementUpdateType; + + /* Determine Total Achievement Points */ + p.total_points = points; + + /* Determine Achievement Rank */ + p.rank = rank; + + /* Determine Achievement Rank Exp */ + p.current_rank_points = curr_rank_points; + p.next_rank_points = VECTOR_INDEX(achievement->rank_exp, p.rank); + + p.ach.ach_id = ad->id; + p.ach.completed = (uint8) achievement->check_complete(sd, ad); + + for (i = 0; i < VECTOR_LENGTH(ad->objective); i++) + p.ach.objective[i] = a->objective[i]; + + p.ach.completed_at = (uint32) a->completed_at; + + p.ach.reward = a->rewarded_at ? 1 : 0; + + clif->send(&p, packet_len(achievementUpdateType), &sd->bl, SELF); + + /* Validate rank-related achievements */ + achievement->validate_achievement_rank(sd, rank); + +#endif // PACKETVER >= 20141016 +} + +/** + * Parses achievement reward packet from session. + * @packet [in] 0x0A25 <ach_id>.L + * @param fd socket descriptor. + * @param sd ptr to session data. + */ +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_achievement_get_reward(int fd, struct map_session_data *sd) +{ + int ach_id = RFIFOL(fd, 2); + const struct achievement_data *ad = NULL; + struct achievement *ach = NULL; + + if (ach_id <= 0 || (ad = achievement->get(ach_id)) == NULL) + return; + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + if (achievement->check_complete(sd, ad) && ach->completed_at && ach->rewarded_at == 0) { + achievement->get_rewards(sd, ad); + } +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet [out] 0x0A26 <packet_id>.W <received + */ +static void clif_achievement_reward_ack(int fd, struct map_session_data *sd, const struct achievement_data *ad) +{ +#if PACKETVER_MAIN_NUM >= 20150225 || PACKETVER_RE_NUM >= 20141126 || defined(PACKETVER_ZERO) + struct packet_achievement_reward_ack p = { 0 }; + + nullpo_retv(sd); + nullpo_retv(ad); + + p.packet_id = achievementRewardAckType; + p.received = 1; + p.ach_id = ad->id; + + clif->send(&p, packet_len(achievementRewardAckType), &sd->bl, SELF); +#endif // PACKETVER >= 20141016 +} + +/** + * Sends achievement reward collection acknowledgement to the client. + * @packet[in] 0x0A2E <packet_id>.W <title_id>.L + */ +static void clif_parse_change_title(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_change_title(int fd, struct map_session_data *sd) +{ + int title_id = RFIFOL(fd, 2); + + if (title_id == sd->status.title_id) { // Same Title + return; + } else if (title_id < 0) { + title_id = 0; + } + + clif->change_title_ack(fd, sd, title_id); +} + +/** + * [clif_change_title_ack description] + * @packet [out] 0x0A2F <packet_id>.W <Result>.B <title_id>.L + */ +static void clif_change_title_ack(int fd, struct map_session_data *sd, int title_id) +{ +#if PACKETVER >= 20141016 + unsigned char failed = 0; + + if (!achievement->check_title(sd, title_id)) { + clif->message(fd, "Title is not yet earned."); + failed = 1; + } + + sd->status.title_id = title_id; + + WFIFOHEAD(fd, packet_len(0xa2f)); + WFIFOW(fd, 0) = 0xa2f; + WFIFOB(fd, 2) = failed; + WFIFOL(fd, 3) = sd->status.title_id; + WFIFOSET(fd, packet_len(0xa2f)); + + // Update names + clif->charnameack(fd, &sd->bl); + clif->charnameack(0, &sd->bl); +#endif +} +// End of Achievement System + /*========================================== * RoDEX *------------------------------------------*/ @@ -20795,7 +21139,7 @@ static void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) { const struct PACKET_CZ_REQ_OPEN_MAIL *packet = RFIFOP(fd, 0); #if PACKETVER >= 20170419 - rodex->open(sd, RODEX_OPENTYPE_UNSET, packet->Upper_MailID); + rodex->open(sd, RODEX_OPENTYPE_UNSET, packet->char_Upper_MailID); #else rodex->open(sd, packet->opentype, packet->Upper_MailID); #endif @@ -21220,6 +21564,9 @@ static bool clif_stylist_read_db_libconfig_sub(struct config_setting_t *it, int if (itemdb->lookup_const(it, "BoxItemID", &i32)) entry.boxid = i32; + if (libconfig->setting_lookup_bool(it, "AllowDoram", &i32)) + entry.allow_doram = (i32 == 0) ? false : true; + VECTOR_ENSURE(stylist_data[type], 1, 1); VECTOR_PUSH(stylist_data[type], entry); return true; @@ -21236,6 +21583,9 @@ static bool clif_style_change_validate_requirements(struct map_session_data *sd, 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) @@ -21294,13 +21644,32 @@ static void clif_parse_cz_req_style_change(int fd, struct map_session_data *sd) clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); if (p->BottomAccessory > 0) clif->cz_req_style_change_sub(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); -#if PACKETVER_RE_NUM >= 20180718 + clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); + return; +} + +static void clif_parse_cz_req_style_change2(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +static void clif_parse_cz_req_style_change2(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_STYLE_CHANGE2 *p = RP2PTR(fd); + + if (p->HeadStyle > 0) + clif->cz_req_style_change_sub(sd, LOOK_HAIR, p->HeadStyle, false); + if (p->HeadPalette > 0) + clif->cz_req_style_change_sub(sd, LOOK_HAIR_COLOR, p->HeadPalette, false); + if (p->BodyPalette > 0) + clif->cz_req_style_change_sub(sd, LOOK_CLOTHES_COLOR, p->BodyPalette, false); + if (p->TopAccessory > 0) + clif->cz_req_style_change_sub(sd, LOOK_HEAD_TOP, p->TopAccessory, true); + if (p->MidAccessory > 0) + clif->cz_req_style_change_sub(sd, LOOK_HEAD_MID, p->MidAccessory, true); + if (p->BottomAccessory > 0) + clif->cz_req_style_change_sub(sd, LOOK_HEAD_BOTTOM, p->BottomAccessory, true); if (p->BodyStyle > 0) { if (pc->has_second_costume(sd)) { clif->cz_req_style_change_sub(sd, LOOK_BODY2, p->BodyStyle, false); } } -#endif clif->style_change_response(sd, STYLIST_SHOP_SUCCESS); return; } @@ -21340,6 +21709,44 @@ static void clif_style_change_response(struct map_session_data *sd, enum stylist #endif } +static void clif_overweight_percent(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20171108 || PACKETVER_RE_NUM >= 20171025 || PACKETVER_ZERO_NUM >= 20171019 + struct PACKET_ZC_OVERWEIGHT_PERCENT p; + + nullpo_retv(sd); + + p.packetType = 0xade; + p.percent = battle_config.natural_heal_weight_rate; + clif->send(&p, sizeof(p), &sd->bl, SELF); +#endif +} + +static void clif_parse_changeDress(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +/// 0ae8 <packet len>.W +static void clif_parse_changeDress(int fd, struct map_session_data *sd) +{ + const char commandname[] = "changedress"; + char command[sizeof commandname + 3] = ""; // '@' command + ' ' + NUL + + sprintf(command, "%c%s ", atcommand->at_symbol, commandname); + atcommand->exec(fd, sd, command, true); +} + +static void clif_party_dead_notification(struct map_session_data *sd) +{ +#if PACKETVER_MAIN_NUM >= 20170524 || PACKETVER_RE_NUM >= 20170502 || defined(PACKETVER_ZERO) + struct PACKET_ZC_GROUP_ISALIVE p; + + nullpo_retv(sd); + + p.packetType = 0xab2; + p.AID = sd->bl.id; + p.isDead = pc_isdead(sd); + clif->send(&p, sizeof(p), &sd->bl, PARTY_WOS); +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -21681,6 +22088,7 @@ void clif_defaults(void) clif->dropitem = clif_dropitem; clif->delitem = clif_delitem; clif->takeitem = clif_takeitem; + clif->item_movefailed = clif_item_movefailed; clif->item_equip = clif_item_equip; clif->item_normal = clif_item_normal; clif->arrowequip = clif_arrowequip; @@ -21917,6 +22325,7 @@ void clif_defaults(void) clif->msgtable = clif_msgtable; clif->msgtable_num = clif_msgtable_num; clif->msgtable_str = clif_msgtable_str; + clif->msgtable_str_color = clif_msgtable_str_color; clif->msgtable_color = clif_msgtable_color; clif->message = clif_displaymessage; clif->messageln = clif_displaymessage2; @@ -22005,6 +22414,8 @@ void clif_defaults(void) clif->guild_positionnamelist = clif_guild_positionnamelist; clif->guild_positioninfolist = clif_guild_positioninfolist; clif->guild_expulsionlist = clif_guild_expulsionlist; + clif->guild_set_position = clif_guild_set_position; + clif->guild_position_selected = clif_guild_position_selected; clif->validate_emblem = clif_validate_emblem; /* battleground-specific */ clif->bg_hp = clif_bg_hp; @@ -22166,6 +22577,14 @@ void clif_defaults(void) clif->isdisguised = clif_isdisguised; clif->navigate_to = clif_navigate_to; clif->bl_type = clif_bl_type; + /* Achievement System */ + clif->achievement_send_list = clif_achievement_send_list; + clif->achievement_send_update = clif_achievement_send_update; + clif->pAchievementGetReward = clif_parse_achievement_get_reward; + clif->achievement_reward_ack = clif_achievement_reward_ack; + /* Title */ + clif->change_title_ack = clif_change_title_ack; + clif->pChangeTitle = clif_parse_change_title; /*------------------------ *- Parse Incoming Packet @@ -22369,6 +22788,8 @@ void clif_defaults(void) clif->pSkillSelectMenu = clif_parse_SkillSelectMenu; clif->pMoveItem = clif_parse_MoveItem; clif->p_cz_blocking_play_cancel = clif_parse_cz_blocking_play_cancel; + clif->overweight_percent = clif_overweight_percent; + clif->pChangeDress = clif_parse_changeDress; /* dull */ clif->pDull = clif_parse_dull; /* BGQueue */ @@ -22413,6 +22834,7 @@ void clif_defaults(void) clif->pHotkeyRowShift = clif_parse_HotkeyRowShift; clif->dressroom_open = clif_dressroom_open; clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; + /* Achievements [Smokexyz/Hercules] */ clif->get_bl_name = clif_get_bl_name; /* RODEX */ clif->pRodexOpenWriteMail = clif_parse_rodex_open_write_mail; @@ -22452,6 +22874,7 @@ void clif_defaults(void) // -- Hat Effect clif->hat_effect = clif_hat_effect; clif->hat_effect_single = clif_hat_effect_single; + clif->party_dead_notification = clif_party_dead_notification; clif->pAttendanceDB = clif_parse_attendance_db; clif->attendancedb_libconfig_sub = clif_attendancedb_libconfig_sub; @@ -22471,6 +22894,7 @@ void clif_defaults(void) clif->style_change_validate_requirements = clif_style_change_validate_requirements; clif->stylist_send_rodexitem = clif_stylist_send_rodexitem; clif->pReqStyleChange = clif_parse_cz_req_style_change; + clif->pReqStyleChange2 = clif_parse_cz_req_style_change2; clif->cz_req_style_change_sub = clif_cz_req_style_change_sub; clif->style_change_response = clif_style_change_response; |