summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql-files/main.sql2
-rw-r--r--sql-files/upgrades/2018-06-03--17-16.sql24
-rw-r--r--sql-files/upgrades/index.txt1
-rw-r--r--src/char/char.c13
-rw-r--r--src/common/mmo.h2
-rw-r--r--src/map/achievement.c105
-rw-r--r--src/map/achievement.h4
-rw-r--r--src/map/clif.c215
-rw-r--r--src/map/clif.h2
-rw-r--r--src/map/intif.c1
-rw-r--r--src/map/packets.h2
-rw-r--r--src/map/packets_struct.h26
-rw-r--r--src/map/pc.h2
-rw-r--r--src/map/unit.c1
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);