summaryrefslogtreecommitdiff
path: root/src/map/clif.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/clif.c')
-rw-r--r--src/map/clif.c471
1 files changed, 434 insertions, 37 deletions
diff --git a/src/map/clif.c b/src/map/clif.c
index cf5edd0e0..60f4b18c0 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -1539,6 +1539,7 @@ bool clif_spawn(struct block_list *bl)
clif->spiritcharm(sd);
if (sd->status.look.robe != 0)
clif->refreshlook(bl, bl->id, LOOK_ROBE, sd->status.look.robe, AREA);
+ clif->hat_effect(bl, NULL, AREA);
}
break;
case BL_MOB:
@@ -4395,6 +4396,7 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl) {
clif->sendbgemblem_single(sd->fd,tsd);
if (tsd->status.look.robe != 0)
clif->refreshlook(&sd->bl, bl->id, LOOK_ROBE, tsd->status.look.robe, SELF);
+ clif->hat_effect(bl, &sd->bl, SELF);
}
break;
case BL_MER: // Devotion Effects
@@ -5466,6 +5468,7 @@ void clif_skill_warppoint(struct map_session_data* sd, uint16 skill_id, uint16 s
/// 0 = "Saved location as a Memo Point for Warp skill." in color 0xFFFF00 (cyan)
/// 1 = "Skill Level is not high enough." in color 0x0000FF (red)
/// 2 = "You haven't learned Warp." in color 0x0000FF (red)
+/// 3 = "Cannot save location as a Memo Point at current location." in color 0x0000FF (red)
///
/// @param sd Who receives the message
/// @param type What message
@@ -5488,6 +5491,7 @@ void clif_skill_memomessage(struct map_session_data* sd, int type)
/// 0 = "Unable to Teleport in this area" in color 0xFFFF00 (cyan)
/// 1 = "Saved point cannot be memorized." in color 0x0000FF (red)
/// 2 = "This skill cannot be used within this area." in color 0xFFFF00 (cyan)
+/// 3 = "This item cannot be used within this area." in color 0xFFFF00 (cyan)
///
/// @param sd Who receives the message
/// @param type What message
@@ -6092,7 +6096,7 @@ void clif_wis_end(int fd, int flag) {
void clif_solved_charname(int fd, int charid, const char* name)
{
nullpo_retv(name);
-#if !defined(PACKETVER_ZERO) && (PACKETVER >= 20180307 || (defined(PACKETVER_RE) && PACKETVER >= 20180221))
+#if PACKETVER_MAIN_NUM >= 20180307 || PACKETVER_RE_NUM >= 20180221 || PACKETVER_ZERO_NUM >= 20180328
WFIFOHEAD(fd, packet_len(0x0af7));
WFIFOW(fd, 0) = 0xaf7;
if (*name == 0) {
@@ -7715,7 +7719,7 @@ void clif_guild_memberlist(struct map_session_data *sd)
memset(WFIFOP(fd, c * size + 34), 0, 50); //[Ind] - This is displayed in the 'note' column but being you can't edit it it's sent empty.
memcpy(WFIFOP(fd, c * size + 84), m->name, NAME_LENGTH);
#else
- WFIFOL(fd, c * size + 34) = 0; // [4144] this is member last login time. But in hercules it not present.
+ WFIFOL(fd, c * size + 34) = m->last_login; // [Megasantos] - Shows last date online
#endif
c++;
}
@@ -9230,6 +9234,56 @@ void clif_msgtable_skill(struct map_session_data* sd, uint16 skill_id, int msg_i
}
/**
+* Displays a format string from msgstringtable.txt with a %s value (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.
+*/
+void clif_msgtable_str(struct map_session_data *sd, uint16 msg_id, const char *value)
+{
+ int message_len;
+ int len;
+ struct PACKET_ZC_FORMATSTRING_MSG *p;
+
+ nullpo_retv(sd);
+ nullpo_retv(value);
+
+ message_len = (int)strlen(value) + 1;
+ len = sizeof(*p) + message_len + 1;
+
+ p = (struct PACKET_ZC_FORMATSTRING_MSG *)aMalloc(len);
+ p->PacketType = 0x2c2;
+ p->PacketLength = len;
+ p->MessageId = msg_id;
+ safestrncpy(p->MessageString, value, message_len);
+ p->MessageString[message_len] = 0;
+
+ clif->send(p, p->PacketLength, &sd->bl, SELF);
+ aFree(p);
+}
+
+/**
+* Displays a format string from msgstringtable.txt with a color (ZC_MSG_COLOR).
+*
+* @param sd The target character.
+* @param msg_id msgstringtable message index, 0-based (@see enum clif_messages)
+* @param color The color to use
+*/
+void clif_msgtable_color(struct map_session_data *sd, uint16 msg_id, uint32 color)
+{
+ struct PACKET_ZC_MSG_COLOR p;
+
+ nullpo_retv(sd);
+
+ p.PacketType = 0x9cd;
+ p.MessageId = msg_id;
+ p.MessageColor = RGB2BGR(color);
+
+ clif->send(&p, sizeof(p), &sd->bl, SELF);
+}
+
+/**
* Validates and processes a global/guild/party message packet.
*
* @param[in] sd The source character.
@@ -9756,6 +9810,13 @@ void clif_parse_LoadEndAck(int fd, struct map_session_data *sd) {
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);
+#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) )
@@ -14218,10 +14279,8 @@ void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int cha
WFIFOL(fd, 2) = sd->status.friends[i].account_id;
WFIFOL(fd, 6) = sd->status.friends[i].char_id;
WFIFOB(fd, 10) = !online; //Yeah, a 1 here means "logged off", go figure...
-#ifndef PACKETVER_ZERO
-#if PACKETVER >= 20180307 || (defined(PACKETVER_RE) && PACKETVER >= 20180221)
+#if PACKETVER_MAIN_NUM >= 20180307 || PACKETVER_RE_NUM >= 20180221
memcpy(WFIFOP(fd, 11), sd->status.friends[i].name, NAME_LENGTH);
-#endif
#endif // PACKETVER_ZERO
WFIFOSET(fd, packet_len(0x206));
@@ -14245,7 +14304,7 @@ void clif_friendslist_send(struct map_session_data *sd)
{
int i = 0, n, fd = sd->fd;
-#if !defined(PACKETVER_ZERO) && (PACKETVER >= 20180307 || (defined(PACKETVER_RE) && PACKETVER >= 20180221))
+#if PACKETVER_MAIN_NUM >= 20180307 || PACKETVER_RE_NUM >= 20180221
const int offset = 8;
#else
const int offset = 32;
@@ -14257,7 +14316,7 @@ void clif_friendslist_send(struct map_session_data *sd)
for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++) {
WFIFOL(fd, 4 + offset * i + 0) = sd->status.friends[i].account_id;
WFIFOL(fd, 4 + offset * i + 4) = sd->status.friends[i].char_id;
-#if !(!defined(PACKETVER_ZERO) && (PACKETVER >= 20180307 || (defined(PACKETVER_RE) && PACKETVER >= 20180221)))
+#if !(PACKETVER_MAIN_NUM >= 20180307 || PACKETVER_RE_NUM >= 20180221)
memcpy(WFIFOP(fd, 4 + offset * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
#endif
}
@@ -16126,6 +16185,7 @@ void clif_parse_PartyTick(int fd, struct map_session_data* sd)
/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
/// 097a <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <mob_id>.L <killed>.W <total>.W <mob name>.24B }*count }*num
+/// 09f8 <packet len>.W <num>.L { <quest id>.L <active>.B <remaining time>.L <time>.L <count>.W { <hunt identification>.L <mob type>.L <mob_id>.L <min level>.L <max level>.L <killed>.W <total>.W <mob name>.24B }*count }*num
void clif_quest_send_list(struct map_session_data *sd)
{
int i, len, real_len;
@@ -16164,8 +16224,16 @@ void clif_quest_send_list(struct map_session_data *sd)
real_len += sizeof(info->objectives[j]);
mob_data = mob->db(qi->objectives[j].mob);
-
+#if PACKETVER >= 20150513
+ info->objectives[j].huntIdent = (sd->quest_log[i].quest_id * 1000) + j;
+ info->objectives[j].mobType = 0; // Info Needed
+#endif
info->objectives[j].mob_id = qi->objectives[j].mob;
+#if PACKETVER >= 20150513
+ // Info Needed
+ info->objectives[j].levelMin = 0;
+ info->objectives[j].levelMax = 0;
+#endif
info->objectives[j].huntCount = sd->quest_log[i].count[j];
info->objectives[j].maxCount = qi->objectives[j].count;
safestrncpy(info->objectives[j].mobName, mob_data->jname, sizeof(info->objectives[j].mobName));
@@ -16213,33 +16281,53 @@ void clif_quest_send_mission(struct map_session_data *sd)
/// Notification about a new quest (ZC_ADD_QUEST).
/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
+/// 09f9 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <hunt identification>.L <mob type>.L <mob id>.L <min level>.L <max level>.L <mob count>.W <mob name>.24B }*3
void clif_quest_add(struct map_session_data *sd, struct quest *qd)
{
- int fd;
- int i;
+ int i, len;
+ uint8 *buf = NULL;
+ struct packet_quest_add_header *packet = NULL;
struct quest_db *qi;
nullpo_retv(sd);
nullpo_retv(qd);
- fd = sd->fd;
+
qi = quest->db(qd->quest_id);
- WFIFOHEAD(fd, packet_len(0x2b3));
- WFIFOW(fd, 0) = 0x2b3;
- WFIFOL(fd, 2) = qd->quest_id;
- WFIFOB(fd, 6) = qd->state;
- WFIFOB(fd, 7) = qd->time - qi->time;
- WFIFOL(fd, 11) = qd->time;
- WFIFOW(fd, 15) = qi->objectives_count;
+ Assert_retv(qi->objectives_count < MAX_QUEST_OBJECTIVES);
+
+ len = sizeof(struct packet_quest_add_header)
+ + MAX_QUEST_OBJECTIVES * sizeof(struct packet_quest_hunt_sub); // >= than the actual length
+
+ buf = aCalloc(1, len);
+ packet = (struct packet_quest_add_header *)WBUFP(buf, 0);
+
+ packet->PacketType = questAddType;
+ packet->questID = qd->quest_id;
+ packet->active = qd->state;
+ packet->quest_svrTime = qd->time - qi->time;
+ packet->quest_endTime = qd->time;
+ packet->count = qi->objectives_count;
for (i = 0; i < qi->objectives_count; i++) {
struct mob_db *monster;
- WFIFOL(fd, i*30+17) = qi->objectives[i].mob;
- WFIFOW(fd, i*30+21) = qd->count[i];
+
monster = mob->db(qi->objectives[i].mob);
- memcpy(WFIFOP(fd, i*30+23), monster->jname, NAME_LENGTH);
- }
- WFIFOSET(fd, packet_len(0x2b3));
+#if PACKETVER >= 20150513
+ packet->objectives[i].huntIdent = (qd->quest_id * 1000) + i;
+ packet->objectives[i].mobType = 0; // Info Needed
+#endif
+ packet->objectives[i].mob_id = qi->objectives[i].mob;
+#if PACKETVER >= 20150513
+ // Info Needed
+ packet->objectives[i].levelMin = 0;
+ packet->objectives[i].levelMax = 0;
+#endif
+ packet->objectives[i].huntCount = qd->count[i];
+ memcpy(packet->objectives[i].mobName, monster->jname, NAME_LENGTH);
+ }
+ clif->send(buf, len, &sd->bl, SELF);
+ aFree(buf);
}
/// Notification about a quest being removed (ZC_DEL_QUEST).
@@ -16257,32 +16345,84 @@ void clif_quest_delete(struct map_session_data *sd, int quest_id) {
/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
+/// 09fa <packet len>.W <mobs>.W { <quest id>.L <hunt identification>.L <total count>.W <current count>.W }*3
void clif_quest_update_objective(struct map_session_data *sd, struct quest *qd)
{
- int fd;
- int i;
+ int i, len, real_len;
+ uint8 *buf = NULL;
+ struct packet_quest_update_header *packet = NULL;
struct quest_db *qi;
- int len;
nullpo_retv(sd);
nullpo_retv(qd);
- fd = sd->fd;
+
qi = quest->db(qd->quest_id);
- len = qi->objectives_count * 12 + 6;
+ Assert_retv(qi->objectives_count < MAX_QUEST_OBJECTIVES);
+
+ len = sizeof(struct packet_quest_update_header)
+ + MAX_QUEST_OBJECTIVES * sizeof(struct packet_quest_update_hunt); // >= than the actual length
- WFIFOHEAD(fd, len);
- WFIFOW(fd, 0) = 0x2b5;
- WFIFOW(fd, 2) = len;
- WFIFOW(fd, 4) = qi->objectives_count;
+ buf = aCalloc(1, len);
+ packet = (struct packet_quest_update_header *)WBUFP(buf, 0);
+ real_len = sizeof(*packet);
+
+ packet->PacketType = questUpdateType;
+ packet->count = qi->objectives_count;
for (i = 0; i < qi->objectives_count; i++) {
- WFIFOL(fd, i*12+6) = qd->quest_id;
- WFIFOL(fd, i*12+10) = qi->objectives[i].mob;
- WFIFOW(fd, i*12+14) = qi->objectives[i].count;
- WFIFOW(fd, i*12+16) = qd->count[i];
+ real_len += sizeof(packet->objectives[i]);
+
+ packet->objectives[i].questID = qd->quest_id;
+#if PACKETVER >= 20150513
+ packet->objectives[i].huntIdent = (qd->quest_id * 1000) + i;
+#else
+ packet->objectives[i].mob_id = qi->objectives[i].mob;
+#endif
+ packet->objectives[i].maxCount = qi->objectives[i].count;
+ packet->objectives[i].count = qd->count[i];
}
+ packet->PacketLength = real_len;
+ clif->send(buf, real_len, &sd->bl, SELF);
+ aFree(buf);
+}
- WFIFOSET(fd, len);
+/// Notification of an hunting mission counter just after quest is added (ZC_HUNTING_QUEST_INFO).
+/// 08fe <packet len>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
+void clif_quest_notify_objective(struct map_session_data *sd, struct quest *qd)
+{
+#if PACKETVER >= 20150513
+ int i, len, real_len;
+ uint8 *buf = NULL;
+ struct packet_quest_hunt_info *packet = NULL;
+ struct quest_db *qi;
+
+ nullpo_retv(sd);
+ nullpo_retv(qd);
+
+ qi = quest->db(qd->quest_id);
+ Assert_retv(qi->objectives_count < MAX_QUEST_OBJECTIVES);
+
+ len = sizeof(struct packet_quest_hunt_info)
+ + MAX_QUEST_OBJECTIVES * sizeof(struct packet_quest_hunt_info_sub); // >= than the actual length
+
+ buf = aCalloc(1, len);
+ packet = (struct packet_quest_hunt_info *)WBUFP(buf, 0);
+ real_len = sizeof(*packet);
+
+ packet->PacketType = questUpdateType2;
+
+ for (i = 0; i < qi->objectives_count; i++) {
+ real_len += sizeof(packet->info[i]);
+
+ packet->info[i].questID = qd->quest_id;
+ packet->info[i].mob_id = qi->objectives[i].mob;
+ packet->info[i].maxCount = qi->objectives[i].count;
+ packet->info[i].count = qd->count[i];
+ }
+ packet->PacketLength = real_len;
+ clif->send(buf, real_len, &sd->bl, SELF);
+ aFree(buf);
+#endif
}
void clif_parse_questStateAck(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
@@ -20228,6 +20368,248 @@ void clif_skill_scale(struct block_list *bl, int src_id, int x, int y, uint16 sk
#endif
}
+/// Send hat effects to the client (ZC_HAT_EFFECT).
+/// 0A3B <Length>.W <AID>.L <Status>.B { <HatEffectId>.W }
+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;
+
+ nullpo_retv(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+
+ nullpo_retv(sd);
+
+ len = 9 + VECTOR_LENGTH(sd->hatEffectId) * 2;
+
+ buf = (unsigned char*)aMalloc(len);
+
+ WBUFW(buf, 0) = 0xa3b;
+ WBUFW(buf, 2) = len;
+ WBUFL(buf, 4) = bl->id;
+ WBUFB(buf, 8) = 1;
+
+ for( i = 0; i < VECTOR_LENGTH(sd->hatEffectId); i++ ){
+ WBUFW(buf, 9 + i * 2) = VECTOR_INDEX(sd->hatEffectId, i);
+ }
+
+ if (tbl != NULL) {
+ clif->send(buf, len, tbl, target);
+ } else {
+ clif->send(buf, len, bl, target);
+ }
+
+ aFree(buf);
+#endif
+}
+
+void clif_hat_effect_single(struct block_list *bl, uint16 effectId, bool enable){
+#if PACKETVER >= 20150422
+ unsigned char buf[13];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0xa3b;
+ WBUFW(buf,2) = 13;
+ WBUFL(buf,4) = bl->id;
+ WBUFB(buf,8) = enable;
+ WBUFL(buf,9) = effectId;
+
+ clif_send(buf, 13, bl, AREA);
+#endif
+}
+
+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
+ int i = 0;
+
+ if (!libconfig->load_file(&attendance_conf, config_filename))
+ return false;
+ attendance = libconfig->lookup(&attendance_conf, "attendance_db");
+
+ VECTOR_CLEAR(clif->attendance_data);
+
+ while ((it = libconfig->setting_get_elem(attendance, i++))) {
+ clif->attendancedb_libconfig_sub(it, i, config_filename);
+ }
+
+ libconfig->destroy(&attendance_conf);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", i, config_filename);
+ return true;
+}
+
+bool clif_attendancedb_libconfig_sub(struct config_setting_t *it, int n, const char *source)
+{
+ struct attendance_entry entry = { 0 };
+ int i32 = 0;
+
+ nullpo_ret(it);
+ nullpo_ret(source);
+
+ if (!itemdb->lookup_const(it, "ItemID", &i32) || i32 < 0) {
+ ShowWarning("clif_attendancedb_libconfig_sub: unknown item %d, entry #%d, skipping.\n", i32, n);
+ return false;
+ }
+ entry.nameid = i32;
+
+ if (!libconfig->setting_lookup_int(it, "Amount", &i32) || i32 < 1) {
+ ShowWarning("clif_attendancedb_libconfig_sub: invalid amount %d, entry #%d, skipping.\n", i32, n);
+ return false;
+ }
+ entry.qty = i32;
+
+ VECTOR_ENSURE(clif->attendance_data, 1, 1);
+ VECTOR_PUSH(clif->attendance_data, entry);
+ return true;
+}
+
+bool clif_attendance_timediff(struct map_session_data *sd)
+{
+ int64 timediff;
+
+ nullpo_retr(false, sd);
+
+ timediff = (time(NULL) / (60 * 60 * 24)) - (sd->status.attendance_timer / (60 * 60 * 24));
+
+ if (timediff <= 0)
+ return false;
+ return true;
+}
+time_t clif_attendance_getendtime(void)
+{
+ time_t timestamp;
+ struct tm tmtime = { 0 };
+ int year = 0, month = 0, day = 0;
+ char timestring[9];
+
+ sprintf(timestring, "%8d", battle_config.feature_attendance_endtime);
+ sscanf(timestring, "%4d%2d%2d", &year, &month, &day);
+
+ tmtime.tm_year = year - 1900;
+ tmtime.tm_mon = month - 1;
+ tmtime.tm_mday = day;
+
+ timestamp = mktime(&tmtime);
+
+ return timestamp;
+}
+
+void clif_parse_open_ui_request(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_open_ui_request(int fd, struct map_session_data *sd)
+{
+
+ const struct PACKET_CZ_OPEN_UI *p = RP2PTR(fd);
+
+ if (clif->attendance_getendtime() < time(NULL)) {
+ clif->msgtable_color(sd, MSG_ATTENDANCE_UNAVAILABLE, COLOR_RED);
+ return;
+ }
+
+ if (battle_config.feature_enable_attendance_system != 1)
+ return;
+
+ clif->open_ui(sd, p->UIType);
+}
+
+void clif_open_ui(struct map_session_data *sd, int8 UIType)
+{
+#if PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404
+ int claimed = 0;
+ struct PACKET_ZC_OPEN_UI p;
+
+ nullpo_retv(sd);
+
+ p.PacketType = 0xAE2;
+ switch (UIType) {
+ case 5: // client will send 5 for the request but requires to receive ATTENDANCE_UI (7) to open the correct ui.
+ if (clif->attendance_timediff(sd) != true)
+ ++claimed;
+ else if (sd->status.attendance_count >= VECTOR_LENGTH(clif->attendance_data))
+ sd->status.attendance_count = 0;
+ p.UIType = ATTENDANCE_UI;
+ p.data = sd->status.attendance_count * 10 + claimed;
+ break;
+ default:
+ ShowWarning("clif_open_ui: Requested UI (%d) is not implemented yet.\n", UIType);
+ return;
+ }
+
+ clif->send(&p, sizeof(p), &sd->bl, SELF);
+#else
+ ShowWarning("Attendance System available only for PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404.\n");
+#endif
+}
+
+void clif_parse_attendance_reward_request(int fd, struct map_session_data *sd) __attribute__((nonnull(2)));
+void clif_parse_attendance_reward_request(int fd, struct map_session_data *sd)
+{
+#if PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404
+
+ struct rodex_message msg = { 0 };
+ struct attendance_entry *entry;
+ int attendance_count;
+ char title[RODEX_TITLE_LENGTH], body[MAIL_BODY_LENGTH];
+
+ if (clif->attendance_getendtime() < time(NULL)) {
+ clif->msgtable_color(sd, MSG_ATTENDANCE_UNAVAILABLE, COLOR_RED);
+ return;
+ }
+
+ if (battle_config.feature_enable_attendance_system != 1)
+ return;
+
+ if (clif->attendance_timediff(sd) != true)
+ return;
+
+ if (sd->status.attendance_count >= VECTOR_LENGTH(clif->attendance_data))
+ sd->status.attendance_count = 0;
+
+ attendance_count = sd->status.attendance_count;
+ ++sd->status.attendance_count;
+ sd->status.attendance_timer = time(NULL);
+
+ msg.receiver_id = sd->status.char_id;
+ sprintf(title, msg_txt(545), attendance_count + 1);
+ sprintf(body, msg_txt(545), attendance_count + 1);
+
+ entry = &VECTOR_INDEX(clif->attendance_data, attendance_count);
+ msg.items[0].item.nameid = entry->nameid;
+ msg.items[0].item.amount = entry->qty;
+ msg.items[0].item.identify = 1;
+ msg.type = MAIL_TYPE_NPC | MAIL_TYPE_ITEM;
+
+ safestrncpy(msg.sender_name, msg_txt(544), NAME_LENGTH);
+ safestrncpy(msg.title, title, RODEX_TITLE_LENGTH);
+ safestrncpy(msg.body, body, MAIL_BODY_LENGTH);
+ msg.send_date = (int)time(NULL);
+ msg.expire_date = (int)time(NULL) + RODEX_EXPIRE;
+
+ intif->rodex_sendmail(&msg);
+ clif->ui_action(sd, 0, sd->status.attendance_count);
+#else
+ ShowWarning("Attendance System available only for PACKETVER_RE_NUM >= 20180307 || PACKETVER_MAIN_NUM >= 20180404.\n");
+#endif
+}
+
+void clif_ui_action(struct map_session_data *sd, int32 UIType, int32 data)
+{
+
+ struct PACKET_ZC_UI_ACTION p;
+
+ nullpo_retv(sd);
+
+ p.PacketType = 0xAF0;
+ p.UIType = UIType;
+ p.data = data;
+
+ clif->send(&p, sizeof(p), &sd->bl, SELF);
+}
/*==========================================
* Main client packet processing function
*------------------------------------------*/
@@ -20790,6 +21172,8 @@ void clif_defaults(void) {
clif->msgtable_skill = clif_msgtable_skill;
clif->msgtable = clif_msgtable;
clif->msgtable_num = clif_msgtable_num;
+ clif->msgtable_str = clif_msgtable_str;
+ clif->msgtable_color = clif_msgtable_color;
clif->message = clif_displaymessage;
clif->messageln = clif_displaymessage2;
clif->messages = clif_displaymessage_sprintf;
@@ -20924,6 +21308,7 @@ void clif_defaults(void) {
clif->quest_delete = clif_quest_delete;
clif->quest_update_status = clif_quest_update_status;
clif->quest_update_objective = clif_quest_update_objective;
+ clif->quest_notify_objective = clif_quest_notify_objective;
clif->quest_show_event = clif_quest_show_event;
/* mail-related */
clif->mail_window = clif_Mail_window;
@@ -21318,4 +21703,16 @@ void clif_defaults(void) {
clif->clan_leave = clif_clan_leave;
clif->clan_message = clif_clan_message;
clif->pClanMessage = clif_parse_ClanMessage;
+ // -- Hat Effect
+ clif->hat_effect = clif_hat_effect;
+ clif->hat_effect_single = clif_hat_effect_single;
+
+ clif->pAttendanceDB = clif_parse_attendance_db;
+ clif->attendancedb_libconfig_sub = clif_attendancedb_libconfig_sub;
+ clif->attendance_timediff = clif_attendance_timediff;
+ clif->attendance_getendtime = clif_attendance_getendtime;
+ clif->pOpenUIRequest = clif_parse_open_ui_request;
+ clif->open_ui = clif_open_ui;
+ clif->pAttendanceRewardRequest = clif_parse_attendance_reward_request;
+ clif->ui_action = clif_ui_action;
}