diff options
-rw-r--r-- | sql-files/main.sql | 2 | ||||
-rw-r--r-- | sql-files/upgrades/2018-06-03--17-16.sql | 24 | ||||
-rw-r--r-- | sql-files/upgrades/index.txt | 1 | ||||
-rw-r--r-- | src/char/char.c | 13 | ||||
-rw-r--r-- | src/common/mmo.h | 2 | ||||
-rw-r--r-- | src/map/achievement.c | 105 | ||||
-rw-r--r-- | src/map/achievement.h | 4 | ||||
-rw-r--r-- | src/map/clif.c | 215 | ||||
-rw-r--r-- | src/map/clif.h | 2 | ||||
-rw-r--r-- | src/map/intif.c | 1 | ||||
-rw-r--r-- | src/map/packets.h | 2 | ||||
-rw-r--r-- | src/map/packets_struct.h | 26 | ||||
-rw-r--r-- | src/map/pc.h | 2 | ||||
-rw-r--r-- | src/map/unit.c | 1 |
14 files changed, 301 insertions, 99 deletions
diff --git a/sql-files/main.sql b/sql-files/main.sql index b80f81f5d..277000df3 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -243,6 +243,7 @@ CREATE TABLE IF NOT EXISTS `char` ( `hotkey_rowshift` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `attendance_count` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `attendance_timer` BIGINT(20) NULL DEFAULT '0', + `title_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`char_id`), UNIQUE KEY `name_key` (`name`), KEY `account_id` (`account_id`), @@ -917,6 +918,7 @@ INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1509835214); -- 2017-11-0 INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1519671456); -- 2018-02-26--15-57.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1520654809); -- 2018-03-10--04-06.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1527964800); -- 2018-06-03--00-10.sql +INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1528026381); -- 2018-06-03--17-16.sql INSERT IGNORE INTO `sql_updates` (`timestamp`) VALUES (1528180320); -- 2018-06-05--12-02.sql -- -- Table structure for table `storage` diff --git a/sql-files/upgrades/2018-06-03--17-16.sql b/sql-files/upgrades/2018-06-03--17-16.sql new file mode 100644 index 000000000..e14ca62ca --- /dev/null +++ b/sql-files/upgrades/2018-06-03--17-16.sql @@ -0,0 +1,24 @@ +#1528026381 + +-- This file is part of Hercules. +-- http://herc.ws - http://github.com/HerculesWS/Hercules +-- +-- Copyright (C) 2018 Hercules Dev Team +-- Copyright (C) Dastgir +-- +-- Hercules is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. + +ALTER TABLE `char` ADD `title_id` INT(11) UNSIGNED NOT NULL DEFAULT '0'; + +INSERT INTO `sql_updates` (`timestamp`, `ignored`) VALUES (1528026381, 'No'); diff --git a/sql-files/upgrades/index.txt b/sql-files/upgrades/index.txt index a578599fd..f6bdada63 100644 --- a/sql-files/upgrades/index.txt +++ b/sql-files/upgrades/index.txt @@ -46,4 +46,5 @@ 2018-02-26--15-57.sql 2018-03-10--04-06.sql 2018-06-03--00-10.sql +2018-06-03--17-16.sql 2018-06-05--12-02.sql diff --git a/src/char/char.c b/src/char/char.c index 321e386ae..54f6ca7d1 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -476,7 +476,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (p->show_equip != cp->show_equip) || (p->allow_party != cp->allow_party) || (p->font != cp->font) || (p->uniqueitem_counter != cp->uniqueitem_counter) || (p->hotkey_rowshift != cp->hotkey_rowshift) || (p->clan_id != cp->clan_id) || (p->last_login != cp->last_login) || (p->attendance_count != cp->attendance_count) || - (p->attendance_timer != cp->attendance_timer) + (p->attendance_timer != cp->attendance_timer) || (p->title_id != cp->title_id) ) { //Save status unsigned int opt = 0; @@ -494,7 +494,8 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," "`delete_date`='%lu',`robe`='%d',`slotchange`='%d', `char_opt`='%u', `font`='%u', `uniqueitem_counter` ='%u'," - "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'" + "`hotkey_rowshift`='%d',`clan_id`='%d',`last_login`='%"PRId64"',`attendance_count`='%d',`attendance_timer`='%"PRId64"'," + "`title_id`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -507,6 +508,7 @@ static int char_mmo_char_tosql(int char_id, struct mmo_charstatus *p) (unsigned long)p->delete_date, // FIXME: platform-dependent size p->look.robe,p->slotchange,opt,p->font,p->uniqueitem_counter, p->hotkey_rowshift,p->clan_id,p->last_login, p->attendance_count, p->attendance_timer, + p->title_id, p->account_id, p->char_id) ) { Sql_ShowDebug(inter->sql_handle); @@ -1077,7 +1079,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," - "`robe`,`slotchange`,`unban_time`,`sex`" + "`robe`,`slotchange`,`unban_time`,`sex`,`title_id`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SQL->StmtExecute(stmt) || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, sizeof p.char_id, NULL, NULL) @@ -1120,6 +1122,7 @@ static int char_mmo_chars_fromsql(struct char_session_data *sd, uint8 *buf) || SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_USHORT, &p.slotchange, sizeof p.slotchange, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_TIME, &unban_time, sizeof unban_time, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 39, SQLDT_ENUM, &sex, sizeof sex, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 40, SQLDT_INT, &p.title_id, sizeof p.title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); @@ -1184,7 +1187,8 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`hair_color`,`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`,`slotchange`," - "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`" + "`char_opt`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`, `attendance_count`, `attendance_timer`," + "`title_id`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) || SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, sizeof char_id) || SQL_ERROR == SQL->StmtExecute(stmt) @@ -1251,6 +1255,7 @@ static int char_mmo_char_fromsql(int char_id, struct mmo_charstatus *p, bool loa || SQL_ERROR == SQL->StmtBindColumn(stmt, 60, SQLDT_INT64, &p->last_login, sizeof p->last_login, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 61, SQLDT_SHORT, &p->attendance_count, sizeof p->attendance_count, NULL, NULL) || SQL_ERROR == SQL->StmtBindColumn(stmt, 62, SQLDT_INT64, &p->attendance_timer, sizeof p->attendance_timer, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 63, SQLDT_INT, &p->title_id, sizeof p->title_id, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SQL->StmtFree(stmt); diff --git a/src/common/mmo.h b/src/common/mmo.h index 69717c972..7e0d915eb 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -705,6 +705,8 @@ struct mmo_charstatus { short attendance_count; unsigned char hotkey_rowshift; + + int32 title_id; // Achievement Title[Dastgir/Hercules] }; typedef enum mail_status { diff --git a/src/map/achievement.c b/src/map/achievement.c index 67807c734..0369b0fb5 100644 --- a/src/map/achievement.c +++ b/src/map/achievement.c @@ -4,6 +4,7 @@ * * Copyright (C) 2017 Hercules Dev Team * Copyright (C) Smokexyz +* Copyright (C) Dastgir * * Hercules is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -967,6 +968,106 @@ static bool achievement_type_requires_criteria(enum achievement_types type) } /** + * Stores all the title ID that has been earned by player + * @param[in] sd pointer to session data. + */ +static void achievement_init_titles(struct map_session_data *sd) +{ + int i; + nullpo_retv(sd); + + VECTOR_INIT(sd->title_ids); + /* Browse through the session's achievement list and gather their values. */ + for (i = 0; i < VECTOR_LENGTH(sd->achievement); i++) { + 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; + + if (a->completed_at > 0 && a->rewarded_at > 0 && ad->rewards.title_id > 0) { + VECTOR_ENSURE(sd->title_ids, 1, 1); + VECTOR_PUSH(sd->title_ids, ad->rewards.title_id); + } + } +} + +/** + * Validates whether player has earned the title. + * @param[in] sd pointer to session data. + * @param[in] title_id Title ID + * @return true, if title has been earned, else false + */ +static bool achievement_check_title(struct map_session_data *sd, int title_id) { + int i; + + nullpo_retr(false, sd); + + if (title_id == 0) + return true; + + for (i = 0; i < VECTOR_LENGTH(sd->title_ids); i++) { + if (VECTOR_INDEX(sd->title_ids, i) == title_id) { + return true; + } + } + + return false; +} + +/** + * Achievement rewards are given to player + * @param sd session data + * @param ad achievement data + */ +static void achievement_get_rewards(struct map_session_data *sd, const struct achievement_data *ad) { + int i = 0; + struct achievement *ach = NULL; + + nullpo_retv(sd); + nullpo_retv(ad); + + if ((ach = achievement->ensure(sd, ad)) == NULL) + return; + + /* Buff */ + if (ad->rewards.bonus != NULL) + script->run(ad->rewards.bonus, 0, sd->bl.id, 0); + + /* Give Items */ + for (i = 0; i < VECTOR_LENGTH(ad->rewards.item); i++) { + struct item it = { 0 }; + int total = 0; + + it.nameid = VECTOR_INDEX(ad->rewards.item, i).id; + total = VECTOR_INDEX(ad->rewards.item, i).amount; + + it.identify = 1; + + //Check if it's stackable. + if (!itemdb->isstackable(it.nameid)) { + int j = 0; + for (j = 0; j < total; ++j) + pc->additem(sd, &it, (it.amount = 1), LOG_TYPE_SCRIPT); + } else { + pc->additem(sd, &it, (it.amount = total), LOG_TYPE_SCRIPT); + } + } + + ach->rewarded_at = time(NULL); + + if (ad->rewards.title_id > 0) { // Add Title + VECTOR_ENSURE(sd->title_ids, 1, 1); + VECTOR_PUSH(sd->title_ids, ad->rewards.title_id); + clif->achievement_send_list(sd->fd, sd); + } else { + clif->achievement_reward_ack(sd->fd, sd, ad); + clif->achievement_send_update(sd->fd, sd, ad); // send update. + } +} + +/** * Parses the Achievement Ranks. * @read db/achievement_rank_db.conf */ @@ -1875,4 +1976,8 @@ void achievement_defaults(void) achievement->validate_achievement_rank = achievement_validate_achievement_rank; /* */ achievement->type_requires_criteria = achievement_type_requires_criteria; + /* */ + achievement->init_titles = achievement_init_titles; + achievement->check_title = achievement_check_title; + achievement->get_rewards = achievement_get_rewards; } diff --git a/src/map/achievement.h b/src/map/achievement.h index f875f3a62..beba120a2 100644 --- a/src/map/achievement.h +++ b/src/map/achievement.h @@ -274,6 +274,10 @@ struct achievement_interface { void (*validate_achievement_rank) (struct map_session_data *sd, int rank); /* */ bool (*type_requires_criteria) (enum achievement_types type); + /* */ + void (*init_titles) (struct map_session_data *sd); + bool (*check_title) (struct map_session_data *sd, int title_id); + void (*get_rewards) (struct map_session_data *sd, const struct achievement_data *ad); }; #ifdef HERCULES_CORE diff --git a/src/map/clif.c b/src/map/clif.c index c71c9d38d..3379369c4 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -8816,17 +8816,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 +8835,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 +8878,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 +8923,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 +8963,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). @@ -20364,38 +20373,7 @@ static void clif_parse_achievement_get_reward(int fd, struct map_session_data *s return; if (achievement->check_complete(sd, ad) && ach->completed_at && ach->rewarded_at == 0) { - int i = 0; - /* Buff */ - if (ad->rewards.bonus != NULL) - script->run(ad->rewards.bonus, 0, sd->bl.id, 0); - - /* Give Items */ - for (i = 0; i < VECTOR_LENGTH(ad->rewards.item); i++) { - struct item it = { 0 }; - int total = 0; - - it.nameid = VECTOR_INDEX(ad->rewards.item, i).id; - total = VECTOR_INDEX(ad->rewards.item, i).amount; - - it.identify = 1; - - //Check if it's stackable. - if (!itemdb->isstackable(it.nameid)) { - int j = 0; - for (j = 0; j < total; ++j) - pc->additem(sd, &it, (it.amount = 1), LOG_TYPE_SCRIPT); - } else { - pc->additem(sd, &it, (it.amount = total), LOG_TYPE_SCRIPT); - } - } - - /* @TODO TitleId */ - - ach->rewarded_at = time(NULL); - - clif->achievement_send_update(fd, sd, ad); // send update. - - clif->achievement_reward_ack(fd, sd, ad); + achievement->get_rewards(sd, ad); } #endif // PACKETVER >= 20141016 } @@ -20419,6 +20397,52 @@ static void clif_achievement_reward_ack(int fd, struct map_session_data *sd, con 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 /*========================================== @@ -22362,6 +22386,9 @@ void clif_defaults(void) 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 diff --git a/src/map/clif.h b/src/map/clif.h index 7a2fbaa68..8cff8a21e 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1203,6 +1203,8 @@ struct clif_interface { void (*achievement_send_update) (int fd, struct map_session_data *sd, const struct achievement_data *ad); void (*pAchievementGetReward) (int fd, struct map_session_data *sd); void (*achievement_reward_ack) (int fd, struct map_session_data *sd, const struct achievement_data *ad); + void (*change_title_ack) (int fd, struct map_session_data *sd, int title_id); + void (*pChangeTitle) (int fd, struct map_session_data *sd); /*------------------------ *- Parse Incoming Packet *------------------------*/ diff --git a/src/map/intif.c b/src/map/intif.c index a5dc9c575..5fafc0913 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -1792,6 +1792,7 @@ static void intif_parse_achievements_load(int fd) VECTOR_PUSH(sd->achievement, t_ach); } + achievement->init_titles(sd); clif->achievement_send_list(fd, sd); sd->achievements_received = true; } diff --git a/src/map/packets.h b/src/map/packets.h index a0459e94c..a8ebccc6b 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -3266,7 +3266,7 @@ packet(0x96e,-1,clif->ackmergeitems); // 2014-09-03aRagexeRE #if PACKETVER >= 20140903 // new packets - packet(0x0a2e,6,clif->pDull/*,XXX*/); // CZ_REQ_CHANGE_TITLE + packet(0x0a2e,6,clif->pChangeTitle); // CZ_REQ_CHANGE_TITLE packet(0x0a2f,7); // ZC_ACK_CHANGE_TITLE // changed packet sizes #endif diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index ef78f76aa..6c08d634a 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -406,6 +406,12 @@ enum packet_headers { #else hominfoType = 0x22e, #endif + reqName = 0x95, +#if PACKETVER >= 20150503 // Confirm this? + reqNameAllType = 0xA30, +#else + reqNameAllType = 0x195, +#endif }; #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute @@ -2626,6 +2632,26 @@ struct packet_achievement_reward_ack { uint32 ach_id; } __attribute__((packed)); +// Name Packet ZC_ACK_REQNAME +struct packet_reqname_ack { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; +} __attribute__((packed)); + +// ZC_ACK_REQNAMEALL / ZC_ACK_REQNAMEALL2 +struct packet_reqnameall_ack { + uint16 packet_id; + int32 gid; + char name[NAME_LENGTH]; + char party_name[NAME_LENGTH]; + char guild_name[NAME_LENGTH]; + char position_name[NAME_LENGTH]; +#if PACKETVER >= 20150503 // Confirm this? + int32 title_id; // Achievement Title +#endif +} __attribute__((packed)); + #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute #pragma pack(pop) #endif // not NetBSD < 6 / Solaris diff --git a/src/map/pc.h b/src/map/pc.h index 9839258e4..84de4d745 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -635,6 +635,8 @@ END_ZEROED_BLOCK; /* Achievement System */ struct char_achievements achievement; bool achievements_received; + // Title + VECTOR_DECL(int) title_ids; }; #define EQP_WEAPON EQP_HAND_R diff --git a/src/map/unit.c b/src/map/unit.c index 91f7e0acd..9174bdccd 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -2768,6 +2768,7 @@ static int unit_free(struct block_list *bl, clr_type clrtype) VECTOR_CLEAR(sd->achievement); // Achievement [Smokexyz/Hercules] VECTOR_CLEAR(sd->storage.item); VECTOR_CLEAR(sd->hatEffectId); + VECTOR_CLEAR(sd->title_ids); // Title [Dastgir/Hercules] sd->storage.received = false; if( sd->quest_log != NULL ) { aFree(sd->quest_log); |