From 544da439e81ff78ec102b754e16b6cc0a28a6d0a Mon Sep 17 00:00:00 2001 From: KirieZ Date: Sun, 30 Jul 2017 13:45:41 -0300 Subject: Implementation of RoDEX --- src/char/HPMchar.c | 1 + src/char/Makefile.in | 4 +- src/char/char.c | 4 + src/char/char.h | 2 + src/char/int_rodex.c | 536 ++++++++++++++++++++++++++++++++++++++++ src/char/int_rodex.h | 47 ++++ src/char/inter.c | 6 +- src/char/mapif.c | 21 ++ src/char/mapif.h | 9 + src/common/mmo.h | 47 ++++ src/map/HPMmap.c | 1 + src/map/Makefile.in | 4 +- src/map/battle.c | 2 + src/map/battle.h | 3 + src/map/clif.c | 516 +++++++++++++++++++++++++++++++++++++++ src/map/clif.h | 28 +++ src/map/date.c | 14 ++ src/map/date.h | 1 + src/map/intif.c | 232 +++++++++++++++++- src/map/intif.h | 12 + src/map/map.c | 5 + src/map/packets.h | 34 +-- src/map/packets_struct.h | 212 ++++++++++++++++ src/map/pc.c | 3 + src/map/pc.h | 9 + src/map/rodex.c | 621 +++++++++++++++++++++++++++++++++++++++++++++++ src/map/rodex.h | 85 +++++++ src/map/script.c | 181 ++++++++++++++ src/map/status.c | 7 + src/map/status.h | 5 +- 30 files changed, 2628 insertions(+), 24 deletions(-) create mode 100644 src/char/int_rodex.c create mode 100644 src/char/int_rodex.h create mode 100644 src/map/rodex.c create mode 100644 src/map/rodex.h (limited to 'src') diff --git a/src/char/HPMchar.c b/src/char/HPMchar.c index e0f81c61f..3a74f443d 100644 --- a/src/char/HPMchar.c +++ b/src/char/HPMchar.c @@ -36,6 +36,7 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" #include "char/loginif.h" #include "char/mapif.h" diff --git a/src/char/Makefile.in b/src/char/Makefile.in index 7806ce2bb..1a7d067a4 100644 --- a/src/char/Makefile.in +++ b/src/char/Makefile.in @@ -42,11 +42,11 @@ MT19937AR_H = $(MT19937AR_D)/mt19937ar.h CHAR_C = char.c HPMchar.c loginif.c mapif.c geoip.c inter.c int_auction.c int_elemental.c int_guild.c \ int_homun.c int_mail.c int_mercenary.c int_party.c int_pet.c \ - int_quest.c int_storage.c pincode.c + int_quest.c int_rodex.c int_storage.c pincode.c CHAR_OBJ = $(addprefix obj_sql/, $(patsubst %.c,%.o,$(CHAR_C))) CHAR_H = char.h HPMchar.h loginif.h mapif.h geoip.h inter.h int_auction.h int_elemental.h int_guild.h \ int_homun.h int_mail.h int_mercenary.h int_party.h int_pet.h \ - int_quest.h int_storage.h pincode.h + int_quest.h int_rodex.h int_storage.h pincode.h CHAR_PH = HAVE_MYSQL=@HAVE_MYSQL@ diff --git a/src/char/char.c b/src/char/char.c index f66108c00..4687d4d6b 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -34,6 +34,7 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" #include "char/inter.h" #include "char/loginif.h" @@ -97,6 +98,8 @@ char auction_db[256] = "auction"; // Auctions System char friend_db[256] = "friends"; char hotkey_db[256] = "hotkey"; char quest_db[256] = "quest"; +char rodex_db[256] = "rodex_mail"; +char rodex_item_db[256] = "rodex_items"; char homunculus_db[256] = "homunculus"; char skill_homunculus_db[256] = "skill_homunculus"; char mercenary_db[256] = "mercenary"; @@ -6412,6 +6415,7 @@ void char_load_defaults(void) inter_pet_defaults(); inter_quest_defaults(); inter_storage_defaults(); + inter_rodex_defaults(); inter_defaults(); geoip_defaults(); } diff --git a/src/char/char.h b/src/char/char.h index 6b081e536..3689690c7 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -328,6 +328,8 @@ extern char pet_db[256]; extern char mail_db[256]; extern char auction_db[256]; extern char quest_db[256]; +extern char rodex_db[256]; +extern char rodex_item_db[256]; extern char homunculus_db[256]; extern char skill_homunculus_db[256]; extern char mercenary_db[256]; diff --git a/src/char/int_rodex.c b/src/char/int_rodex.c new file mode 100644 index 000000000..12ab54290 --- /dev/null +++ b/src/char/int_rodex.c @@ -0,0 +1,536 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * 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 . + */ +#define HERCULES_CORE + +#include "int_rodex.h" + +#include "char/char.h" +#include "char/inter.h" +#include "char/mapif.h" +#include "common/db.h" +#include "common/memmgr.h" +#include "common/mmo.h" +#include "common/nullpo.h" +#include "common/showmsg.h" +#include "common/socket.h" +#include "common/sql.h" +#include "common/strlib.h" +#include "common/timer.h" + +#include +#include + +struct inter_rodex_interface inter_rodex_s; +struct inter_rodex_interface *inter_rodex; + +// Loads new mails of this char_id/account_id +static int inter_rodex_fromsql(int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails) +{ + int i, count = 0; + struct rodex_message msg = { 0 }; + struct SqlStmt *stmt; + struct SqlStmt *stmt_items; + + nullpo_retr(-1, mails); + + stmt = SQL->StmtMalloc(inter->sql_handle); + + switch (opentype) { + case RODEX_OPENTYPE_MAIL: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE `expire_date` > '%d' AND `receiver_id` = '%d' AND `mail_id` > '%"PRId64"'" + "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), char_id, mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + + case RODEX_OPENTYPE_ACCOUNT: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE " + "`expire_date` > '%d' AND `receiver_accountid` = '%d' AND `mail_id` > '%"PRId64"'" + "ORDER BY `mail_id` ASC", rodex_db, (int)time(NULL), account_id, mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + + case RODEX_OPENTYPE_RETURN: + if (SQL_ERROR == SQL->StmtPrepare(stmt, + "SELECT `mail_id`, `sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`," + "`title`, `body`, `zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`" + "FROM `%s` WHERE (`sender_id` = '%d' AND `expire_date` <= '%d' AND `send_date` + '%d' > '%d' AND `mail_id` > '%"PRId64"')" + "ORDER BY `mail_id` ASC", rodex_db, char_id, (int)time(NULL), 2 * RODEX_EXPIRE, (int)time(NULL), mail_id) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + break; + } + + if (SQL_ERROR == SQL->StmtExecute(stmt) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT64, &msg.id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 1, SQLDT_STRING, &msg.sender_name, sizeof(msg.sender_name), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &msg.sender_id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 3, SQLDT_STRING, &msg.receiver_name, sizeof(msg.receiver_name), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &msg.receiver_id, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_INT, &msg.receiver_accountid, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_STRING, &msg.title, sizeof(msg.title), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_STRING, &msg.body, sizeof(msg.body), NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_INT, &msg.zeny, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_UINT8, &msg.type, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_INT8, &msg.is_read, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 11, SQLDT_INT, &msg.send_date, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 12, SQLDT_INT, &msg.expire_date, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt, 13, SQLDT_INT, &msg.weight, 0, NULL, NULL) + ) { + SqlStmt_ShowDebug(stmt); + SQL->StmtFree(stmt); + return -1; + } + + stmt_items = SQL->StmtMalloc(inter->sql_handle); + if (stmt_items == NULL) { + SQL->StmtFreeResult(stmt); + SQL->StmtFree(stmt); + return -1; + } + + // Read mails + while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) { + struct item it = { 0 }; + + if (msg.type & MAIL_TYPE_ITEM) { + if (SQL_ERROR == SQL->StmtPrepare(stmt_items, "SELECT `nameid`, `amount`, `equip`, `identify`," + "`refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`," + "`opt_idx1`, `opt_val1`, `opt_idx2`, `opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4`," + "`expire_time`, `bound`, `unique_id`" + "FROM `%s` WHERE mail_id = '%"PRId64"' ORDER BY `mail_id` ASC", rodex_item_db, msg.id) + || SQL_ERROR == SQL->StmtExecute(stmt_items) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 0, SQLDT_INT, &it.nameid, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 1, SQLDT_INT, &it.amount, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 2, SQLDT_UINT, &it.equip, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 3, SQLDT_INT8, &it.identify, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 4, SQLDT_INT8, &it.refine, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 5, SQLDT_INT8, &it.attribute, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 6, SQLDT_INT16, &it.card[0], 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 7, SQLDT_INT16, &it.card[1], 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 8, SQLDT_INT16, &it.card[2], 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 9, SQLDT_INT16, &it.card[3], 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 10, SQLDT_INT16, &it.option[0].index, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 11, SQLDT_INT16, &it.option[0].value, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 12, SQLDT_INT16, &it.option[1].index, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 13, SQLDT_INT16, &it.option[1].value, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 14, SQLDT_INT16, &it.option[2].index, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 15, SQLDT_INT16, &it.option[2].value, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 16, SQLDT_INT16, &it.option[3].index, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 17, SQLDT_INT16, &it.option[3].value, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 18, SQLDT_INT16, &it.option[4].index, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 19, SQLDT_INT16, &it.option[4].value, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 20, SQLDT_INT, &it.expire_time, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 21, SQLDT_UINT8, &it.bound, 0, NULL, NULL) + || SQL_ERROR == SQL->StmtBindColumn(stmt_items, 22, SQLDT_UINT64, &it.unique_id, 0, NULL, NULL)) { + SqlStmt_ShowDebug(stmt_items); + } + + for (i = 0; i < RODEX_MAX_ITEM && SQL_SUCCESS == SQL->StmtNextRow(stmt_items); ++i) { + msg.items[i].item = it; + msg.items_count++; + } + } + + if (msg.items_count == 0) { + msg.type &= ~MAIL_TYPE_ITEM; + } + + if (msg.zeny == 0) { + msg.type &= ~MAIL_TYPE_ZENY; + } + + msg.opentype = opentype; +#if PACKETVER < 20160601 + // NPC Message Type isn't supported in old clients + msg.type &= ~MAIL_TYPE_NPC; +#endif + + ++count; + VECTOR_ENSURE(*mails, 1, 1); + VECTOR_PUSH(*mails, msg); + memset(&msg, 0, sizeof(struct rodex_message)); + } + + SQL->StmtFreeResult(stmt); + SQL->StmtFreeResult(stmt_items); + + SQL->StmtFree(stmt); + SQL->StmtFree(stmt_items); + + ShowInfo("rodex load complete from DB - id: %d (total: %d)\n", char_id, count); + return count; +} + +// Checks if user has non-read mails +static bool inter_rodex_hasnew(int char_id, int account_id) +{ + int count = 0; + char *data; + + if (SQL_ERROR == SQL->Query(inter->sql_handle, + "SELECT count(*) FROM `%s` WHERE (" + "(`expire_date` > '%d' AND (`receiver_id` = '%d' OR `receiver_accountid` = '%d')) OR" + "(`sender_id` = '%d' AND `expire_date` <= '%d' AND `send_date` + '%d' > '%d')" + ") AND (`is_read` = 0 OR (`type` > 0 AND `type` != 8))", + rodex_db, (int)time(NULL), char_id, account_id, + char_id, (int)time(NULL), 2 * RODEX_EXPIRE, (int)time(NULL)) + ) { + Sql_ShowDebug(inter->sql_handle); + return -1; + } + + if (SQL_SUCCESS != SQL->NextRow(inter->sql_handle)) + return false; + + SQL->GetData(inter->sql_handle, 0, &data, NULL); + count = atoi(data); + SQL->FreeResult(inter->sql_handle); + + return count > 0; +} + +/// Checks player name and retrieves some data +static bool inter_rodex_checkname(const char *name, int *target_char_id, short *target_class, int *target_level) +{ + char esc_name[NAME_LENGTH * 2 + 1]; + bool found = false; + + nullpo_retr(false, name); + nullpo_retr(false, target_char_id); + nullpo_retr(false, target_class); + nullpo_retr(false, target_level); + + // Try to find the Dest Char by Name + SQL->EscapeStringLen(inter->sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "SELECT `char_id`, `class`, `base_level` FROM `%s` WHERE `name` = '%s'", char_db, esc_name)) { + Sql_ShowDebug(inter->sql_handle); + } else { + if (SQL_SUCCESS == SQL->NextRow(inter->sql_handle)) { + char *data; + SQL->GetData(inter->sql_handle, 0, &data, NULL); *target_char_id = atoi(data); + SQL->GetData(inter->sql_handle, 1, &data, NULL); *target_class = (short)atoi(data); + SQL->GetData(inter->sql_handle, 2, &data, NULL); *target_level = atoi(data); + found = true; + } + } + SQL->FreeResult(inter->sql_handle); + + return found; +} + +/// Stores a single message in the database. +/// Returns the message's ID if successful (or 0 if it fails). +int64 inter_rodex_savemessage(struct rodex_message* msg) +{ + char sender_name[NAME_LENGTH * 2 + 1]; + char receiver_name[NAME_LENGTH * 2 + 1]; + char body[RODEX_BODY_LENGTH * 2 + 1]; + char title[RODEX_TITLE_LENGTH * 2 + 1]; + int i; + + nullpo_retr(false, msg); + + SQL->EscapeStringLen(inter->sql_handle, sender_name, msg->sender_name, strnlen(msg->sender_name, NAME_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, receiver_name, msg->receiver_name, strnlen(msg->receiver_name, NAME_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, body, msg->body, strnlen(msg->body, RODEX_BODY_LENGTH)); + SQL->EscapeStringLen(inter->sql_handle, title, msg->title, strnlen(msg->title, RODEX_TITLE_LENGTH)); + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`sender_name`, `sender_id`, `receiver_name`, `receiver_id`, `receiver_accountid`, `title`, `body`," + "`zeny`, `type`, `is_read`, `send_date`, `expire_date`, `weight`) VALUES " + "('%s', '%d', '%s', '%d', '%d', '%s', '%s', '%"PRId64"', '%d', '%d', '%d', '%d', '%d')", + rodex_db, sender_name, msg->sender_id, receiver_name, msg->receiver_id, msg->receiver_accountid, + title, body, msg->zeny, msg->type, msg->is_read == true ? 1 : 0, msg->send_date, msg->expire_date, msg->weight)) { + Sql_ShowDebug(inter->sql_handle); + return 0; + } + + msg->id = (int64)SQL->LastInsertId(inter->sql_handle); + + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + // Should we use statement here? [KIRIEZ] + struct item *it = &msg->items[i].item; + if (it->nameid == 0) + continue; + + if (SQL_ERROR == SQL->Query(inter->sql_handle, "INSERT INTO `%s` (`mail_id`, `nameid`, `amount`, `equip`, `identify`," + "`refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `opt_idx0`, `opt_val0`, `opt_idx1`, `opt_val1`, `opt_idx2`," + "`opt_val2`, `opt_idx3`, `opt_val3`, `opt_idx4`, `opt_val4`,`expire_time`, `bound`, `unique_id`) VALUES " + "('%"PRId64"', '%d', '%d', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%u', '%"PRIu64"')", + rodex_item_db, msg->id, it->nameid, it->amount, it->equip, it->identify, it->refine, it->attribute, it->card[0], it->card[1], it->card[2], it->card[3], + it->option[0].index, it->option[0].value, it->option[1].index, it->option[1].value, it->option[2].index, it->option[2].value, it->option[3].index, + it->option[3].value, it->option[4].index, it->option[4].value, it->expire_time, it->bound, it->unique_id) + ) { + Sql_ShowDebug(inter->sql_handle); + continue; + } + } + + return msg->id; +} + +/*========================================== + * Inbox Request + *------------------------------------------*/ +void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails) +{ + int per_packet = (UINT16_MAX - 15) / sizeof(struct rodex_message); + int sent = 0; + nullpo_retv(mails); + Assert_retv(char_id > 0); + Assert_retv(count >= 0); + + do { + int i = 15, j, size, limit; + bool is_last = true; + + if (count <= per_packet) { + size = count * sizeof(struct rodex_message) + 15; + limit = count; + is_last = true; + } else { + int to_send = count - sent; + limit = min(to_send, per_packet); + if (limit != to_send) { + is_last = false; + } + size = limit * sizeof(struct rodex_message) + 15; + } + + WFIFOHEAD(fd, size); + WFIFOW(fd, 0) = 0x3895; + WFIFOW(fd, 2) = size; + WFIFOL(fd, 4) = char_id; + WFIFOB(fd, 8) = opentype; + WFIFOB(fd, 9) = flag; + WFIFOB(fd, 10) = is_last; + WFIFOL(fd, 11) = count; + for (j = 0; j < limit; ++j, ++sent, i += sizeof(struct rodex_message)) { + memcpy(WFIFOP(fd, i), &VECTOR_INDEX(*mails, sent), sizeof(struct rodex_message)); + } + WFIFOSET(fd, size); + } while (sent < count); +} + +void mapif_parse_rodex_requestinbox(int fd) +{ + int count; + int char_id = RFIFOL(fd,2); + int account_id = RFIFOL(fd, 6); + int8 flag = RFIFOB(fd, 10); + int8 opentype = RFIFOB(fd, 11); + int64 mail_id = RFIFOQ(fd, 12); + struct rodex_maillist mails = { 0 }; + + VECTOR_INIT(mails); + if (flag == 0) + count = inter_rodex->fromsql(char_id, account_id, opentype, 0, &mails); + else + count = inter_rodex->fromsql(char_id, account_id, opentype, mail_id, &mails); + mapif->rodex_sendinbox(fd, char_id, opentype, flag, count, &mails); + VECTOR_CLEAR(mails); +} + +/*========================================== +* Checks if there are new mails +*------------------------------------------*/ +void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new) +{ + Assert_retv(char_id > 0); + + WFIFOHEAD(fd, 7); + WFIFOW(fd, 0) = 0x3896; + WFIFOL(fd, 2) = char_id; + WFIFOB(fd, 6) = has_new; + WFIFOSET(fd, 7); +} + +void mapif_parse_rodex_checkhasnew(int fd) +{ + int char_id = RFIFOL(fd, 2); + int account_id = RFIFOL(fd, 6); + bool has_new; + + Assert_retv(account_id >= START_ACCOUNT_NUM && account_id <= END_ACCOUNT_NUM); + Assert_retv(char_id >= START_CHAR_NUM); + + has_new = inter_rodex->hasnew(char_id, account_id); + mapif->rodex_sendhasnew(fd, char_id, has_new); +} + +/*========================================== + * Update/Delete mail + *------------------------------------------*/ +void mapif_parse_rodex_updatemail(int fd) +{ + int64 mail_id = RFIFOL(fd, 2); + int8 flag = RFIFOB(fd, 10); + + Assert_retv(mail_id > 0); + Assert_retv(flag >= 0 && flag <= 3); + + switch (flag) { + case 0: // Read + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `is_read` = 1 WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 1: // Get Zeny + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~2) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 2: // Get Items + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "UPDATE `%s` SET `zeny` = 0, `type` = `type` & (~4) WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + + case 3: // Delete Mail + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + if (SQL_ERROR == SQL->Query(inter->sql_handle, "DELETE FROM `%s` WHERE `mail_id` = '%"PRId64"'", rodex_item_db, mail_id)) + Sql_ShowDebug(inter->sql_handle); + break; + } +} + +/*========================================== + * Send Mail + *------------------------------------------*/ +void mapif_rodex_send(int fd, int sender_id, int receiver_id, int receiver_accountid, bool result) +{ + Assert_retv(sender_id >= 0); + Assert_retv(receiver_id + receiver_accountid > 0); + + WFIFOHEAD(fd,15); + WFIFOW(fd,0) = 0x3897; + WFIFOL(fd,2) = sender_id; + WFIFOL(fd,6) = receiver_id; + WFIFOL(fd,10) = receiver_accountid; + WFIFOB(fd,14) = result; + WFIFOSET(fd,15); +} + +void mapif_parse_rodex_send(int fd) +{ + struct rodex_message msg = { 0 }; + + if (RFIFOW(fd,2) != 4 + sizeof(struct rodex_message)) + return; + + memcpy(&msg, RFIFOP(fd,4), sizeof(struct rodex_message)); + if (msg.receiver_id > 0 || msg.receiver_accountid > 0) + msg.id = inter_rodex->savemessage(&msg); + + mapif->rodex_send(fd, msg.sender_id, msg.receiver_id, msg.receiver_accountid, msg.id > 0 ? true : false); +} + +/*------------------------------------------ + * Check Player + *------------------------------------------*/ +void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH]) +{ + nullpo_retv(name); + Assert_retv(reqchar_id > 0); + Assert_retv(target_char_id >= 0); + + WFIFOHEAD(fd, 16 + NAME_LENGTH); + WFIFOW(fd, 0) = 0x3898; + WFIFOL(fd, 2) = reqchar_id; + WFIFOL(fd, 6) = target_char_id; + WFIFOW(fd, 10) = target_class; + WFIFOL(fd, 12) = target_level; + safestrncpy(WFIFOP(fd, 16), name, NAME_LENGTH); + WFIFOSET(fd, 16 + NAME_LENGTH); +} + +void mapif_parse_rodex_checkname(int fd) +{ + int reqchar_id = RFIFOL(fd, 2); + char name[NAME_LENGTH]; + int target_char_id, target_level; + short target_class; + + safestrncpy(name, RFIFOP(fd, 6), NAME_LENGTH); + + if (inter_rodex->checkname(name, &target_char_id, &target_class, &target_level) == true) + mapif->rodex_checkname(fd, reqchar_id, target_char_id, target_class, target_level, name); + else + mapif->rodex_checkname(fd, reqchar_id, 0, 0, 0, name); +} + +/*========================================== + * Packets From Map Server + *------------------------------------------*/ +int inter_rodex_parse_frommap(int fd) +{ + switch(RFIFOW(fd,0)) + { + case 0x3095: mapif->parse_rodex_requestinbox(fd); break; + case 0x3096: mapif->parse_rodex_checkhasnew(fd); break; + case 0x3097: mapif->parse_rodex_updatemail(fd); break; + case 0x3098: mapif->parse_rodex_send(fd); break; + case 0x3099: mapif->parse_rodex_checkname(fd); break; + default: + return 0; + } + return 1; +} + +int inter_rodex_sql_init(void) +{ + return 1; +} + +void inter_rodex_sql_final(void) +{ + return; +} + +void inter_rodex_defaults(void) +{ + inter_rodex = &inter_rodex_s; + + inter_rodex->savemessage = inter_rodex_savemessage; + inter_rodex->parse_frommap = inter_rodex_parse_frommap; + inter_rodex->sql_init = inter_rodex_sql_init; + inter_rodex->sql_final = inter_rodex_sql_final; + inter_rodex->fromsql = inter_rodex_fromsql; + inter_rodex->hasnew = inter_rodex_hasnew; + inter_rodex->checkname = inter_rodex_checkname; +} diff --git a/src/char/int_rodex.h b/src/char/int_rodex.h new file mode 100644 index 000000000..801ebcb89 --- /dev/null +++ b/src/char/int_rodex.h @@ -0,0 +1,47 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * 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 . + */ +#ifndef CHAR_INT_RODEX_H +#define CHAR_INT_RODEX_H + +#include "common/mmo.h" +#include "common/db.h" + +struct item; + +/** + * inter_rodex interface + **/ +struct inter_rodex_interface { + int (*sql_init) (void); + void (*sql_final) (void); + int (*parse_frommap) (int fd); + int (*fromsql) (int char_id, int account_id, int8 opentype, int64 mail_id, struct rodex_maillist *mails); + bool (*hasnew) (int char_id, int account_id); + bool (*checkname) (const char *name, int *target_char_id, short *target_class, int *target_level); + int64 (*savemessage) (struct rodex_message* msg); +}; + +#ifdef HERCULES_CORE +void inter_rodex_defaults(void); +#endif // HERCULES_CORE + +HPShared struct inter_rodex_interface *inter_rodex; + +#endif /* CHAR_INT_RODEX_H */ diff --git a/src/char/inter.c b/src/char/inter.c index cfbc0fd4d..557ee5313 100644 --- a/src/char/inter.c +++ b/src/char/inter.c @@ -33,6 +33,7 @@ #include "char/int_party.h" #include "char/int_pet.h" #include "char/int_quest.h" +#include "char/int_rodex.h" #include "char/int_storage.h" #include "char/mapif.h" #include "common/cbasetypes.h" @@ -76,7 +77,7 @@ int inter_recv_packet_length[] = { 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- - -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator] + -1,10,-1, 6, 0, 20,10,11, -1,6 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator], RoDEX packets }; struct WisData { @@ -974,6 +975,7 @@ int inter_init_sql(const char *file) inter_elemental->sql_init(); inter_mail->sql_init(); inter_auction->sql_init(); + inter_rodex->sql_init(); geoip->init(); inter->msg_config_read("conf/messages.conf", false); @@ -994,6 +996,7 @@ void inter_final(void) inter_elemental->sql_final(); inter_mail->sql_final(); inter_auction->sql_final(); + inter_rodex->sql_final(); geoip->final(true); inter->do_final_msg(); @@ -1416,6 +1419,7 @@ int inter_parse_frommap(int fd) || inter_mail->parse_frommap(fd) || inter_auction->parse_frommap(fd) || inter_quest->parse_frommap(fd) + || inter_rodex->parse_frommap(fd) ) break; else diff --git a/src/char/mapif.c b/src/char/mapif.c index 5fff96ba8..b71171e23 100644 --- a/src/char/mapif.c +++ b/src/char/mapif.c @@ -26,6 +26,7 @@ #include "char/int_auction.h" #include "char/int_guild.h" #include "char/int_homun.h" +#include "char/int_rodex.h" #include "common/cbasetypes.h" #include "common/mmo.h" #include "common/random.h" @@ -182,6 +183,16 @@ void mapif_quest_save_ack(int fd, int char_id, bool success); int mapif_parse_quest_save(int fd); void mapif_send_quests(int fd, int char_id, struct quest *tmp_questlog, int num_quests); int mapif_parse_quest_load(int fd); +/* RoDEX */ +int mapif_parse_rodex_requestinbox(int fd); +void mapif_rodex_sendinbox(int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails); +int mapif_parse_rodex_checkhasnew(int fd); +void mapif_rodex_sendhasnew(int fd, int char_id, bool has_new); +int mapif_parse_rodex_updatemail(int fd); +int mapif_parse_rodex_send(int fd); +void mapif_rodex_send(int fd, int sender_id, int receiver_id, int receiver_accountid, bool result); +int mapif_parse_rodex_checkname(int fd); +void mapif_rodex_checkname(int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH]); int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag); int mapif_save_guild_storage_ack(int fd, int account_id, int guild_id, int fail); int mapif_parse_LoadGuildStorage(int fd); @@ -363,6 +374,16 @@ void mapif_defaults(void) { mapif->parse_quest_save = mapif_parse_quest_save; mapif->send_quests = mapif_send_quests; mapif->parse_quest_load = mapif_parse_quest_load; + /* RoDEX */ + mapif->parse_rodex_requestinbox = mapif_parse_rodex_requestinbox; + mapif->rodex_sendinbox = mapif_rodex_sendinbox; + mapif->parse_rodex_checkhasnew = mapif_parse_rodex_checkhasnew; + mapif->rodex_sendhasnew = mapif_rodex_sendhasnew; + mapif->parse_rodex_updatemail = mapif_parse_rodex_updatemail; + mapif->parse_rodex_send = mapif_parse_rodex_send; + mapif->rodex_send = mapif_rodex_send; + mapif->parse_rodex_checkname = mapif_parse_rodex_checkname; + mapif->rodex_checkname = mapif_rodex_checkname; mapif->load_guild_storage = mapif_load_guild_storage; mapif->save_guild_storage_ack = mapif_save_guild_storage_ack; mapif->parse_LoadGuildStorage = mapif_parse_LoadGuildStorage; diff --git a/src/char/mapif.h b/src/char/mapif.h index 07fbed6c6..353da7ab0 100644 --- a/src/char/mapif.h +++ b/src/char/mapif.h @@ -176,6 +176,15 @@ struct mapif_interface { int (*parse_quest_save) (int fd); void (*send_quests) (int fd, int char_id, struct quest *tmp_questlog, int num_quests); int (*parse_quest_load) (int fd); + int(*parse_rodex_requestinbox) (int fd); + void(*rodex_sendinbox) (int fd, int char_id, int8 opentype, int8 flag, int count, struct rodex_maillist *mails); + int(*parse_rodex_checkhasnew) (int fd); + void(*rodex_sendhasnew) (int fd, int char_id, bool has_new); + int(*parse_rodex_updatemail) (int fd); + int(*parse_rodex_send) (int fd); + void(*rodex_send) (int fd, int sender_id, int receiver_id, int receiver_accountid, bool result); + int(*parse_rodex_checkname) (int fd); + void(*rodex_checkname) (int fd, int reqchar_id, int target_char_id, short target_class, int target_level, char name[NAME_LENGTH]); int (*load_guild_storage) (int fd, int account_id, int guild_id, char flag); int (*save_guild_storage_ack) (int fd, int account_id, int guild_id, int fail); int (*parse_LoadGuildStorage) (int fd); diff --git a/src/common/mmo.h b/src/common/mmo.h index 276e0eb08..9bb9837ab 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -271,6 +271,13 @@ #endif STATIC_ASSERT(MAX_ITEM_OPTIONS <= 5, "This value is limited by the client and database layout and should only be increased if you know the consequences."); +// RoDEX +#define RODEX_TITLE_LENGTH (40 + 1) +#define RODEX_BODY_LENGTH (500 + 1) +#define RODEX_MAX_ITEM (5) +#define RODEX_EXPIRE (1 * 15 * 24 * 60 * 60) +#define RODEX_MAIL_PER_PAGE 7 + // The following system marks a different job ID system used by the map server, // which makes a lot more sense than the normal one. [Skotlex] // These marks the "level" of the job. @@ -809,6 +816,46 @@ enum fame_list_type { RANKTYPE_PK = 3, //Not supported yet }; +struct rodex_message { + int64 id; + int sender_id; + char sender_name[NAME_LENGTH]; + int receiver_id; + int receiver_accountid; + char receiver_name[NAME_LENGTH]; + char title[RODEX_TITLE_LENGTH]; + char body[RODEX_BODY_LENGTH]; + struct { + struct item item; + int idx; + + } items[RODEX_MAX_ITEM]; + int64 zeny; + uint8 type; + int8 opentype; + bool is_read; + bool is_deleted; + int send_date; + int expire_date; + int weight; + int items_count; +}; + +VECTOR_STRUCT_DECL(rodex_maillist, struct rodex_message); + +enum rodex_opentype { + RODEX_OPENTYPE_MAIL = 0, + RODEX_OPENTYPE_ACCOUNT = 1, + RODEX_OPENTYPE_RETURN = 2, +}; + +enum MAIL_TYPE { + MAIL_TYPE_TEXT = 0x0, + MAIL_TYPE_ZENY = 0x2, + MAIL_TYPE_ITEM = 0x4, + MAIL_TYPE_NPC = 0x8 +}; + /** * Guild Basic Information * It is used to request changes via intif_guild_change_basicinfo in map-server and to diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c index 84544976a..381dbf599 100644 --- a/src/map/HPMmap.c +++ b/src/map/HPMmap.c @@ -78,6 +78,7 @@ #include "map/pc_groups.h" #include "map/pet.h" #include "map/quest.h" +#include "map/rodex.h" #include "map/script.h" #include "map/searchstore.h" #include "map/skill.h" diff --git a/src/map/Makefile.in b/src/map/Makefile.in index c01826bfb..55712cc69 100644 --- a/src/map/Makefile.in +++ b/src/map/Makefile.in @@ -44,14 +44,14 @@ MAP_C = atcommand.c battle.c battleground.c buyingstore.c channel.c chat.c \ chrif.c clif.c date.c duel.c elemental.c guild.c homunculus.c HPMmap.c \ instance.c intif.c irc-bot.c itemdb.c log.c mail.c map.c mapreg_sql.c \ mercenary.c mob.c npc.c npc_chat.c party.c path.c pc.c pc_groups.c \ - pet.c quest.c script.c searchstore.c skill.c status.c storage.c \ + pet.c quest.c rodex.c script.c searchstore.c skill.c status.c storage.c \ trade.c unit.c vending.c MAP_OBJ = $(addprefix obj_sql/, $(patsubst %c,%o,$(MAP_C))) MAP_H = atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \ chrif.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \ instance.h intif.h irc-bot.h itemdb.h log.h mail.h map.h mapreg.h \ mercenary.h mob.h npc.h packets.h packets_struct.h party.h path.h \ - pc.h pc_groups.h pet.h quest.h script.h searchstore.h skill.h \ + pc.h pc_groups.h pet.h quest.h rodex.h script.h searchstore.h skill.h \ status.h storage.h trade.h unit.h vending.h MAP_PH = diff --git a/src/map/battle.c b/src/map/battle.c index 57a74bba4..b5ad62407 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -7335,6 +7335,8 @@ static const struct battle_data { { "monster_eye_range_bonus", &battle_config.mob_eye_range_bonus, 0, 0, 10, }, { "prevent_logout_trigger", &battle_config.prevent_logout_trigger, 0xE, 0, 0xF, }, { "boarding_halter_speed", &battle_config.boarding_halter_speed, 25, 0, 100, }, + { "features/rodex", &battle_config.feature_rodex, 1, 0, 1, }, + { "features/rodex_use_accountmail", &battle_config.feature_rodex_use_accountmail, 0, 0, 1, }, }; #ifndef STATS_OPT_OUT /** diff --git a/src/map/battle.h b/src/map/battle.h index a73c6dc44..e6129ca7c 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -555,6 +555,9 @@ struct Battle_Config { int prevent_logout_trigger; int boarding_halter_speed; + + int feature_rodex; + int feature_rodex_use_accountmail; }; /* criteria for battle_config.idletime_critera */ diff --git a/src/map/clif.c b/src/map/clif.c index bb4d53a99..c13a3ea80 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -46,6 +46,7 @@ #include "map/pc.h" #include "map/pet.h" #include "map/quest.h" +#include "map/rodex.h" #include "map/script.h" #include "map/skill.h" #include "map/status.h" @@ -19225,6 +19226,494 @@ unsigned short clif_parse_cmd_optional( int fd, struct map_session_data *sd ) { return cmd; } +/*========================================== + * RoDEX + *------------------------------------------*/ + +void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_open_write_mail(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_OPEN_WRITE_MAIL *rPacket = RFIFOP(fd, 0); + int8 result = (rodex->isenabled() == true) ? 1 : 0; + + clif->rodex_open_write_mail(fd, rPacket->receiveName, result); +} + +void clif_rodex_open_write_mail(int fd, const char *receiver_name, int8 result) +{ +#if PACKETVER >= 20140416 + struct PACKET_ZC_ACK_OPEN_WRITE_MAIL *sPacket = NULL; + + nullpo_retv(receiver_name); + + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexopenwrite; + strncpy(sPacket->receiveName, receiver_name, NAME_LENGTH); + sPacket->result = result; + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_parse_rodex_add_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_add_item(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_ADD_ITEM_TO_MAIL *rPacket = RFIFOP(fd, 0); + int16 idx = rPacket->index - 2; + + rodex->add_item(sd, idx, (int16)rPacket->count); +} + +void clif_rodex_add_item_result(struct map_session_data *sd, int16 idx, int16 amount, int8 result) +{ +#if PACKETVER >= 20140416 + struct PACKET_ZC_ADD_ITEM_TO_MAIL *packet; + int fd, j; + + nullpo_retv(sd); + if (idx < 0 || idx >= MAX_INVENTORY) + result = RODEX_ADD_ITEM_FATAL_ERROR; + + fd = sd->fd; + + WFIFOHEAD(fd, sizeof(*packet)); + packet = WFIFOP(fd, 0); + memset(packet, 0x0, sizeof(*packet)); + packet->PacketType = rodexadditem; + packet->result = result; + + if (result != RODEX_ADD_ITEM_SUCCESS) { //No need to continue building the packet if it failed + WFIFOSET(fd, sizeof(*packet)); + return; + } + + packet->index = idx + 2; + packet->count = amount; + packet->ITID = sd->status.inventory[idx].nameid; + packet->type = itemtype(sd->inventory_data[idx]->type); + packet->IsIdentified = sd->status.inventory[idx].identify ? 1 : 0; + packet->IsDamaged = (sd->status.inventory[idx].attribute & ATTR_BROKEN) != 0 ? 1 : 0; + packet->refiningLevel = sd->status.inventory[idx].refine; + for (j = 0; j < ARRAYLENGTH(packet->slot.card); ++j) + packet->slot.card[j] = sd->status.inventory[idx].card[j]; + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + packet->optionData[j].index = sd->status.inventory[idx].option[j].index; + packet->optionData[j].param = sd->status.inventory[idx].option[j].param; + packet->optionData[j].value = sd->status.inventory[idx].option[j].value; + } + packet->weight = sd->rodex.tmp.weight / 10; + WFIFOSET(fd, sizeof(*packet)); +#endif +} + +void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_remove_item(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_REMOVE_ITEM_MAIL *rPacket = RFIFOP(fd, 0); + int16 idx = rPacket->index - 2; + + rodex->remove_item(sd, idx, (int16)rPacket->cnt); +} + +void clif_rodex_remove_item_result(struct map_session_data *sd, int16 idx, int16 amount) +{ +#if PACKETVER >= 20140521 + struct PACKET_ZC_ACK_REMOVE_ITEM_MAIL *packet; + int fd; + + nullpo_retv(sd); + Assert_retv(idx >= 0 && idx < MAX_INVENTORY); + + fd = sd->fd; + + WFIFOHEAD(fd, sizeof(*packet)); + packet = WFIFOP(fd, 0); + packet->PacketType = rodexremoveitem; + packet->result = (amount < 0) ? 0 : 1; + packet->cnt = (amount < 0) ? 0 : sd->status.inventory[idx].amount - amount; + packet->index = idx + 2; + packet->weight = sd->rodex.tmp.weight / 10; + WFIFOSET(fd, sizeof(*packet)); +#endif +} + +void clif_parse_rodex_checkname(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_checkname(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_CHECKNAME *rPacket = RFIFOP(fd, 0); + int char_id = 0, base_level = 0; + short class = 0; + char name[NAME_LENGTH]; + + safestrncpy(name, rPacket->Name, NAME_LENGTH); + + rodex->check_player(sd, name, &base_level, &char_id, &class); +} + +void clif_rodex_checkname_result(struct map_session_data *sd, int char_id, short class_, int base_level, const char *name) +{ +#if PACKETVER >= 20140521 + struct PACKET_ZC_CHECKNAME *sPacket; + int fd; + + nullpo_retv(sd); + nullpo_retv(name); + + fd = sd->fd; + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexcheckplayer; + if (char_id == 0) { + sPacket->CharId = 0; + WFIFOSET(fd, sizeof(*sPacket)); + return; + } + sPacket->CharId = char_id; + sPacket->Class = class_; + sPacket->BaseLevel = base_level; +#if PACKETVER >= 20160316 + strncpy(sPacket->Name, name, NAME_LENGTH); +#endif + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_send_mail(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_SEND_MAIL *rPacket = RFIFOP(fd, 0); + int8 result; + + if (rPacket->TextcontentsLength + rPacket->Titlelength > rPacket->PacketLength - sizeof(*rPacket)) { + result = RODEX_SEND_MAIL_FATAL_ERROR; + } else if (rPacket->TextcontentsLength > RODEX_BODY_LENGTH || rPacket->Titlelength > RODEX_TITLE_LENGTH) { + result = RODEX_SEND_MAIL_FATAL_ERROR; + } else { + char rname[NAME_LENGTH] = ""; + char title[RODEX_TITLE_LENGTH] = ""; + char body[RODEX_BODY_LENGTH] = ""; + + safestrncpy(rname, rPacket->receiveName, NAME_LENGTH); + safestrncpy(title, rPacket->string, RODEX_TITLE_LENGTH); + safestrncpy(body, &rPacket->string[rPacket->Titlelength], RODEX_BODY_LENGTH); + + result = rodex->send_mail(sd, rname, body, title, rPacket->zeny); + } + + if (result != RODEX_SEND_MAIL_SUCCESS) + clif->rodex_send_mail_result(fd, sd, result); + rodex->clean(sd, 1); +} + +void clif_rodex_send_mail_result(int fd, struct map_session_data *sd, int8 result) +{ +#if PACKETVER >= 20131230 + struct PACKET_ZC_WRITE_MAIL_RESULT *sPacket; + + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexwriteresult; + sPacket->result = result; + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_rodex_send_maillist(int fd, struct map_session_data *sd, int8 open_type, int64 page_start) +{ +#if PACKETVER >= 20131218 + struct PACKET_ZC_MAIL_LIST *packet; + struct maillistinfo *inner; + int16 size = sizeof(*packet); + int8 count = 0; + + nullpo_retv(sd); + + WFIFOHEAD(fd, sizeof(*packet) + (sizeof(*inner) + RODEX_TITLE_LENGTH) * 7); + packet = WFIFOP(fd, 0); + packet->PacketType = ((page_start == (VECTOR_LENGTH(sd->rodex.messages) - 1)) ? rodexmailList : rodexnextpage); + packet->opentype = open_type; + + inner = WFIFOP(fd, size); + + while (page_start >= 0 && count < RODEX_MAIL_PER_PAGE) { + struct rodex_message *msg = &VECTOR_INDEX(sd->rodex.messages, page_start); + --page_start; + + if (msg->is_deleted) + continue; + + inner->MailID = msg->id; + inner->Isread = msg->is_read == true ? 1 : 0; + inner->type = msg->type; + inner->regDateTime = (int)time(NULL) - msg->send_date; + inner->expireDateTime = msg->expire_date - (int)time(NULL); + if (open_type == RODEX_OPENTYPE_RETURN) { + inner->expireDateTime += RODEX_EXPIRE; + } + inner->Titlelength = (int16)strlen(msg->title) + 1; + if (open_type != RODEX_OPENTYPE_RETURN) { + strncpy(inner->SenderName, msg->sender_name, sizeof(msg->sender_name)); + } else { + strncpy(inner->SenderName, msg->receiver_name, sizeof(msg->receiver_name)); + } + strncpy(inner->title, msg->title, inner->Titlelength); + size += sizeof(*inner) + inner->Titlelength; + inner = WFIFOP(fd, size); + ++count; + } + + packet->PacketLength = size; + packet->cnt = count; + packet->IsEnd = page_start > 0 ? 0 : 1; + WFIFOSET(fd, size); +#endif +} + +void clif_rodex_send_refresh(int fd, struct map_session_data *sd, int8 open_type, int count) +{ +#if PACKETVER >= 20131218 + struct PACKET_ZC_MAIL_LIST *packet; + struct maillistinfo *inner; + int16 size = sizeof(*packet); + int i, j; + + nullpo_retv(sd); + + WFIFOHEAD(fd, sizeof(*packet) + (sizeof(*inner) + RODEX_TITLE_LENGTH) * 7); + packet = WFIFOP(fd, 0); + packet->PacketType = rodexmailList; + packet->opentype = open_type; + + inner = WFIFOP(fd, size); + + i = VECTOR_LENGTH(sd->rodex.messages) - 1; + j = count; + while (i >= 0 && j > 0) { + struct rodex_message *msg = &VECTOR_INDEX(sd->rodex.messages, i); + --i; + + if (msg->is_deleted) + continue; + + inner->MailID = msg->id; + inner->Isread = msg->is_read == true ? 1 : 0; + inner->type = msg->type; + inner->regDateTime = (int)time(NULL) - msg->send_date; + inner->expireDateTime = msg->expire_date - (int)time(NULL); + if (open_type == RODEX_OPENTYPE_RETURN) { + inner->expireDateTime += RODEX_EXPIRE; + } + inner->Titlelength = (int16)strlen(msg->title) + 1; + if (open_type != RODEX_OPENTYPE_RETURN) { + strncpy(inner->SenderName, msg->sender_name, sizeof(msg->sender_name)); + } else { + strncpy(inner->SenderName, msg->receiver_name, sizeof(msg->receiver_name)); + } + strncpy(inner->title, msg->title, inner->Titlelength); + size += sizeof(*inner) + inner->Titlelength; + inner = WFIFOP(fd, size); + --j; + } + + packet->PacketLength = size; + packet->cnt = count; + packet->IsEnd = 1; + WFIFOSET(fd, size); +#endif +} + +void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_next_maillist(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_NEXT_MAIL_LIST *packet = RFIFOP(fd, 0); + + rodex->next_page(sd, packet->opentype, packet->Lower_MailID); +} + +void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_read_mail(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_READ_MAIL *rPacket = RFIFOP(fd, 0); + + rodex->read_mail(sd, rPacket->MailID); +} + +void clif_rodex_read_mail(struct map_session_data *sd, int8 opentype, struct rodex_message *msg) +{ +#if PACKETVER >= 20131223 + struct PACKET_ZC_READ_MAIL *sPacket; + struct mail_item *item; + int fd, i, body_len, size; + + nullpo_retv(sd); + nullpo_retv(msg); + + fd = sd->fd; + body_len = (int)strlen(msg->body) + 1; + size = sizeof(*sPacket); + + WFIFOHEAD(fd, sizeof(*sPacket) + body_len + (sizeof(*item) * RODEX_MAX_ITEM)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexread; + sPacket->opentype = opentype; + sPacket->MailID = msg->id; + sPacket->TextcontentsLength = body_len; + sPacket->zeny = msg->zeny; + sPacket->ItemCnt = msg->items_count; + strncpy(WFIFOP(fd, size), msg->body, body_len); + size += body_len; + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + struct item *it = &msg->items[i].item; + int j, k; + + if (it->nameid == 0) { + continue; + } + + item = WFIFOP(fd, size); + memset(item, 0x0, sizeof(*item)); + item->ITID = it->nameid; + item->count = it->amount; + item->type = itemtype(itemdb->search(it->nameid)->type); + item->IsIdentified = it->identify ? 1 : 0; + item->IsDamaged = (it->attribute & ATTR_BROKEN) != 0 ? 1 : 0; + item->refiningLevel = it->refine; + for (k = 0; k < MAX_SLOTS; ++k) { + item->slot.card[k] = it->card[k]; + } + for (j = 0; j < MAX_ITEM_OPTIONS; ++j) { + item->optionData[j].index = it->option[j].index; + item->optionData[j].value = it->option[j].value; + } + + size += sizeof(*item); + } + sPacket->PacketLength = size; + WFIFOSET(fd, size); +#endif +} + +void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_delete_mail(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_DELETE_MAIL *rPacket = RFIFOP(fd, 0); + + rodex->delete_mail(sd, rPacket->MailID); +} + +void clif_rodex_delete_mail(struct map_session_data *sd, int8 opentype, int64 mail_id) +{ +#if PACKETVER >= 20131218 + struct PACKET_ZC_ACK_DELETE_MAIL *sPacket; + int fd; + + nullpo_retv(sd); + + fd = sd->fd; + + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexdelete; + sPacket->opentype = opentype; + sPacket->MailID = mail_id; + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_request_zeny(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_ZENY_FROM_MAIL *rPacket = RFIFOP(fd, 0); + + rodex->get_zeny(sd, rPacket->opentype, rPacket->MailID); +} + +void clif_rodex_request_zeny(struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result) +{ +#if PACKETVER >= 20140409 + struct PACKET_ZC_ACK_ZENY_FROM_MAIL *sPacket; + int fd; + + nullpo_retv(sd); + + fd = sd->fd; + + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexgetzeny; + sPacket->MailID = mail_id; + sPacket->opentype = opentype; + sPacket->result = result; + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_parse_rodex_request_items(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_request_items(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_ITEM_FROM_MAIL *rPacket = RFIFOP(fd, 0); + + rodex->get_items(sd, rPacket->opentype, rPacket->MailID); +} + +void clif_rodex_request_items(struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result) +{ +#if PACKETVER >= 20140409 + struct PACKET_ZC_ACK_ITEM_FROM_MAIL *sPacket; + int fd; + + nullpo_retv(sd); + + fd = sd->fd; + + WFIFOHEAD(fd, sizeof(*sPacket)); + sPacket = WFIFOP(fd, 0); + sPacket->PacketType = rodexgetitem; + sPacket->MailID = mail_id; + sPacket->opentype = opentype; + sPacket->result = result; + WFIFOSET(fd, sizeof(*sPacket)); +#endif +} + +void clif_rodex_icon(int fd, bool show) +{ +#if PACKETVER >= 20140716 + WFIFOHEAD(fd, 3); + WFIFOW(fd, 0) = rodexicon; + WFIFOB(fd, 2) = (show == true ? 1 : 0); + WFIFOSET(fd, 3); +#endif +} + +void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_refresh_maillist(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_REFRESH_MAIL_LIST *packet = RFIFOP(fd, 0); + rodex->refresh(sd, packet->opentype, packet->Upper_MailID); +} + +void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_open_mailbox(int fd, struct map_session_data *sd) +{ + const struct PACKET_CZ_REQ_OPEN_MAIL *packet = RFIFOP(fd, 0); + rodex->open(sd, packet->opentype); + rodex->clean(sd, 1); +} + +void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_close_mailbox(int fd, struct map_session_data *sd) +{ + rodex->clean(sd, 0); + intif->rodex_checkhasnew(sd); +} + +void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd) __attribute__((nonnull(2))); +void clif_parse_rodex_cancel_write_mail(int fd, struct map_session_data *sd) +{ + rodex->clean(sd, 1); +} + /*========================================== * Main client packet processing function *------------------------------------------*/ @@ -20265,4 +20754,31 @@ void clif_defaults(void) { clif->dressroom_open = clif_dressroom_open; clif->pOneClick_ItemIdentify = clif_parse_OneClick_ItemIdentify; clif->get_bl_name = clif_get_bl_name; + /* RODEX */ + clif->pRodexOpenWriteMail = clif_parse_rodex_open_write_mail; + clif->rodex_open_write_mail = clif_rodex_open_write_mail; + clif->pRodexAddItem = clif_parse_rodex_add_item; + clif->rodex_add_item_result = clif_rodex_add_item_result; + clif->pRodexRemoveItem = clif_parse_rodex_remove_item; + clif->rodex_remove_item_result = clif_rodex_remove_item_result; + clif->pRodexSendMail = clif_parse_rodex_send_mail; + clif->rodex_send_mail_result = clif_rodex_send_mail_result; + clif->rodex_send_maillist = clif_rodex_send_maillist; + clif->rodex_send_refresh = clif_rodex_send_refresh; + clif->pRodexReadMail = clif_parse_rodex_read_mail; + clif->rodex_read_mail = clif_rodex_read_mail; + clif->pRodexNextMaillist = clif_parse_rodex_next_maillist; + clif->pRodexCloseMailbox = clif_parse_rodex_close_mailbox; + clif->pRodexCancelWriteMail = clif_parse_rodex_cancel_write_mail; + clif->pRodexOpenMailbox = clif_parse_rodex_open_mailbox; + clif->pRodexCheckName = clif_parse_rodex_checkname; + clif->rodex_checkname_result = clif_rodex_checkname_result; + clif->pRodexDeleteMail = clif_parse_rodex_delete_mail; + clif->rodex_delete_mail = clif_rodex_delete_mail; + clif->pRodexRefreshMaillist = clif_parse_rodex_refresh_maillist; + clif->pRodexRequestZeny = clif_parse_rodex_request_zeny; + clif->rodex_request_zeny = clif_rodex_request_zeny; + clif->pRodexRequestItems = clif_parse_rodex_request_items; + clif->rodex_request_items = clif_rodex_request_items; + clif->rodex_icon = clif_rodex_icon; } diff --git a/src/map/clif.h b/src/map/clif.h index b34be81a3..ac8cc8d35 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -1365,6 +1365,34 @@ struct clif_interface { void (*pSelectCart) (int fd, struct map_session_data *sd); const char *(*get_bl_name) (const struct block_list *bl); + + /* RoDEX */ + void (*pRodexOpenWriteMail) (int fd, struct map_session_data *sd); + void (*rodex_open_write_mail) (int fd, const char *receiver_name, int8 result); + void (*pRodexAddItem) (int fd, struct map_session_data *sd); + void (*rodex_add_item_result) (struct map_session_data *sd, int16 idx, int16 amount, int8 result); + void (*pRodexRemoveItem) (int fd, struct map_session_data *sd); + void (*rodex_remove_item_result) (struct map_session_data *sd, int16 idx, int16 amount); + void (*pRodexSendMail) (int fd, struct map_session_data *sd); + void (*rodex_send_mail_result) (int fd, struct map_session_data *sd, int8 result); + void (*rodex_send_maillist) (int fd, struct map_session_data *sd, int8 open_type, int64 page_start); + void (*rodex_send_refresh) (int fd, struct map_session_data *sd, int8 open_type, int count); + void (*pRodexReadMail) (int fd, struct map_session_data *sd); + void (*rodex_read_mail) (struct map_session_data *sd, int8 opentype, struct rodex_message *msg); + void (*pRodexNextMaillist) (int fd, struct map_session_data *sd); + void (*pRodexCloseMailbox) (int fd, struct map_session_data *sd); + void (*pRodexCancelWriteMail) (int fd, struct map_session_data *sd); + void (*pRodexOpenMailbox) (int fd, struct map_session_data *sd); + void (*pRodexCheckName) (int fd, struct map_session_data *sd); + void (*rodex_checkname_result) (struct map_session_data *sd, int char_id, short class_, int base_level, const char *name); + void (*pRodexDeleteMail) (int fd, struct map_session_data *sd); + void (*rodex_delete_mail) (struct map_session_data *sd, int8 opentype, int64 mail_id); + void (*pRodexRefreshMaillist) (int fd, struct map_session_data *sd); + void (*pRodexRequestZeny) (int fd, struct map_session_data *sd); + void (*rodex_request_zeny) (struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result); + void (*pRodexRequestItems) (int fd, struct map_session_data *sd); + void (*rodex_request_items) (struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result); + void (*rodex_icon) (int fd, bool show); }; #ifdef HERCULES_CORE diff --git a/src/map/date.c b/src/map/date.c index 20ab9fe95..b06ec18d6 100644 --- a/src/map/date.c +++ b/src/map/date.c @@ -84,6 +84,20 @@ int date_get_dayofweek(void) return lt->tm_wday; } +// Returns YYYYMMDD of now +int date_get_date(void) +{ + time_t t; + struct tm * lt; + t = time(NULL); + lt = localtime(&t); + + return + (lt->tm_year + 1900) * 10000 + + (lt->tm_mon + 1) * 100 + + (lt->tm_mday); +} + /*========================================== * Star gladiator related checks *------------------------------------------*/ diff --git a/src/map/date.h b/src/map/date.h index ac0a3a7fa..f4f119ade 100644 --- a/src/map/date.h +++ b/src/map/date.h @@ -32,6 +32,7 @@ int date_get_hour(void); int date_get_min(void); int date_get_sec(void); int date_get_dayofweek(void); +int date_get_date(void); bool is_day_of_sun(void); bool is_day_of_moon(void); diff --git a/src/map/intif.c b/src/map/intif.c index 61d2e3633..de862b4dd 100644 --- a/src/map/intif.c +++ b/src/map/intif.c @@ -38,6 +38,7 @@ #include "map/pc.h" #include "map/pet.h" #include "map/quest.h" +#include "map/rodex.h" #include "map/storage.h" #include "common/memmgr.h" #include "common/nullpo.h" @@ -2399,6 +2400,218 @@ void intif_parse_Itembound_ack(int fd) { gstor->lock = 0; //Unlock now that operation is completed #endif } + +/*========================================== +* RoDEX System +*==========================================*/ + +/*------------------------------------------ + * Mail List + *------------------------------------------*/ + +// Rodex Inbox Request +// char_id: char_id +// account_id: account_id (used by account mail) +// flag: 0 - Open/Refresh ; 1 = Next Page +int intif_rodex_requestinbox(int char_id, int account_id, int8 flag, int8 opentype, int64 mail_id) +{ + if (intif->CheckForCharServer()) + return 0; + + WFIFOHEAD(inter_fd, 20); + WFIFOW(inter_fd, 0) = 0x3095; + WFIFOL(inter_fd, 2) = char_id; + WFIFOL(inter_fd, 6) = account_id; + WFIFOL(inter_fd, 10) = flag; + WFIFOB(inter_fd, 11) = opentype; + WFIFOQ(inter_fd, 12) = mail_id; + WFIFOSET(inter_fd, 20); + + return 0; +} + +void intif_parse_RequestRodexOpenInbox(int fd) +{ + struct map_session_data *sd; + int8 opentype = RFIFOB(fd, 8); + int8 flag = RFIFOB(fd, 9); + int8 is_end = RFIFOB(fd, 10); + int count = RFIFOL(fd, 11); + int i, j; + + sd = map->charid2sd(RFIFOL(fd, 4)); + + if (sd == NULL) // user is not online anymore + return; + + sd->rodex.total = count; + if (RFIFOW(fd, 2) - 15 != sd->rodex.total * sizeof(struct rodex_message)) { + ShowError("intif_parse_RodexInboxOpenReceived: data size mismatch %d != %"PRIuS"\n", RFIFOW(fd, 2) - 15, sd->rodex.total * sizeof(struct rodex_message)); + return; + } + + if (flag == 0) + VECTOR_CLEAR(sd->rodex.messages); + + for (i = 0, j = 15; i < count; ++i, j += sizeof(struct rodex_message)) { + struct rodex_message msg = { 0 }; + VECTOR_ENSURE(sd->rodex.messages, 1, 1); + memcpy(&msg, RFIFOP(fd, j), sizeof(struct rodex_message)); + VECTOR_PUSH(sd->rodex.messages, msg); + } + + if (is_end == true) { + if (flag == 0) + clif->rodex_send_maillist(sd->fd, sd, opentype, VECTOR_LENGTH(sd->rodex.messages) - 1); + else + clif->rodex_send_refresh(sd->fd, sd, opentype, count); + } +} + +/*------------------------------------------ + * Notifications + *------------------------------------------*/ +int intif_rodex_hasnew(struct map_session_data *sd) +{ + nullpo_retr(0, sd); + + if (intif->CheckForCharServer()) + return 0; + + WFIFOHEAD(inter_fd, 10); + WFIFOW(inter_fd, 0) = 0x3096; + WFIFOL(inter_fd, 2) = sd->status.char_id; + WFIFOL(inter_fd, 6) = sd->status.account_id; + WFIFOSET(inter_fd, 10); + + return 0; +} + +void intif_parse_RodexNotifications(int fd) +{ + struct map_session_data *sd; + bool has_messages; + + sd = map->charid2sd(RFIFOL(fd, 2)); + has_messages = RFIFOB(fd, 6); + + if (sd == NULL) // user is not online anymore + return; + + clif->rodex_icon(sd->fd, has_messages); +} + +/*------------------------------------------ + * Update Mail + *------------------------------------------*/ + +/// Updates a mail +/// flag: +/// 0 - user Read +/// 1 - user got Zeny +/// 2 - user got Items +/// 3 - delete +int intif_rodex_updatemail(int64 mail_id, int8 flag) +{ + if (intif->CheckForCharServer()) + return 0; + + WFIFOHEAD(inter_fd, 11); + WFIFOW(inter_fd, 0) = 0x3097; + WFIFOQ(inter_fd, 2) = mail_id; + WFIFOB(inter_fd, 10) = flag; + WFIFOSET(inter_fd, 11); + + return 0; +} + +/*------------------------------------------ + * Send Mail + *------------------------------------------*/ +int intif_rodex_sendmail(struct rodex_message *msg) +{ + if (intif->CheckForCharServer()) + return 0; + + nullpo_retr(0, msg); + + WFIFOHEAD(inter_fd, 4 + sizeof(struct rodex_message)); + WFIFOW(inter_fd, 0) = 0x3098; + WFIFOW(inter_fd, 2) = 4 + sizeof(struct rodex_message); + memcpy(WFIFOP(inter_fd, 4), msg, sizeof(struct rodex_message)); + WFIFOSET(inter_fd, 4 + sizeof(struct rodex_message)); + + return 0; +} + +void intif_parse_RodexSendMail(int fd) +{ + struct map_session_data *ssd = NULL, *rsd = NULL; + int sender_id = RFIFOL(fd, 2); + int receiver_id = RFIFOL(fd, 6); + int receiver_accountid = RFIFOL(fd, 10); + + if (sender_id > 0) + ssd = map->charid2sd(sender_id); + + if (receiver_id > 0) + rsd = map->charid2sd(receiver_id); + else if (receiver_accountid > 0) + rsd = map->id2sd(receiver_accountid); + + rodex->send_mail_result(ssd, rsd, RFIFOL(fd, 14)); +} + +/*------------------------------------------ + * Check Player + *------------------------------------------*/ +int intif_rodex_checkname(struct map_session_data *sd, const char *name) +{ + if (intif->CheckForCharServer()) + return 0; + + nullpo_retr(0, sd); + nullpo_retr(0, name); + + WFIFOHEAD(inter_fd, 6 + NAME_LENGTH); + WFIFOW(inter_fd, 0) = 0x3099; + WFIFOL(inter_fd, 2) = sd->status.char_id; + safestrncpy(WFIFOP(inter_fd, 6), name, NAME_LENGTH); + WFIFOSET(inter_fd, 6 + NAME_LENGTH); + + return 0; +} + +void intif_parse_RodexCheckName(int fd) +{ + struct map_session_data *sd = NULL; + int reqchar_id = RFIFOL(fd, 2); + int target_char_id = RFIFOL(fd, 6); + short target_class = RFIFOW(fd, 10); + int target_level = RFIFOL(fd, 12); + char name[NAME_LENGTH]; + + safestrncpy(name, RFIFOP(inter_fd, 16), NAME_LENGTH); + + if (reqchar_id <= 0) + return; + + sd = map->charid2sd(reqchar_id); + + if (sd == NULL) // User is not online anymore + return; + + if (target_char_id == 0) { + clif->rodex_checkname_result(sd, 0, 0, 0, name); + return; + } + + sd->rodex.tmp.receiver_id = target_char_id; + strncpy(sd->rodex.tmp.receiver_name, name, NAME_LENGTH); + + clif->rodex_checkname_result(sd, target_char_id, target_class, target_level, name); +} + //----------------------------------------------------------------- // Communication from the inter server // Return a 0 (false) if there were any errors. @@ -2509,6 +2722,12 @@ int intif_parse(int fd) case 0x3891: intif->pRecvHomunculusData(fd); break; case 0x3892: intif->pSaveHomunculusOk(fd); break; case 0x3893: intif->pDeleteHomunculusOk(fd); break; + + // RoDEX + case 0x3895: intif->pRequestRodexOpenInbox(fd); break; + case 0x3896: intif->pRodexHasNew(fd); break; + case 0x3897: intif->pRodexSendMail(fd); break; + case 0x3898: intif->pRodexCheckName(fd); break; default: ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0)); return 0; @@ -2534,7 +2753,7 @@ void intif_defaults(void) { -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] 12,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 - -1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] + -1,-1, 7, 3, 0,-1, 7, 15,16 + NAME_LENGTH, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator] / RoDEX [KirieZ] }; intif = &intif_s; @@ -2616,6 +2835,12 @@ void intif_defaults(void) { intif->elemental_request = intif_elemental_request; intif->elemental_delete = intif_elemental_delete; intif->elemental_save = intif_elemental_save; + // RoDEX + intif->rodex_requestinbox = intif_rodex_requestinbox; + intif->rodex_checkhasnew = intif_rodex_hasnew; + intif->rodex_updatemail = intif_rodex_updatemail; + intif->rodex_sendmail = intif_rodex_sendmail; + intif->rodex_checkname = intif_rodex_checkname; /* @accinfo */ intif->request_accinfo = intif_request_accinfo; /* */ @@ -2687,4 +2912,9 @@ void intif_defaults(void) { intif->pRecvHomunculusData = intif_parse_RecvHomunculusData; intif->pSaveHomunculusOk = intif_parse_SaveHomunculusOk; intif->pDeleteHomunculusOk = intif_parse_DeleteHomunculusOk; + /* RoDEX */ + intif->pRequestRodexOpenInbox = intif_parse_RequestRodexOpenInbox; + intif->pRodexHasNew = intif_parse_RodexNotifications; + intif->pRodexSendMail = intif_parse_RodexSendMail; + intif->pRodexCheckName = intif_parse_RodexCheckName; } diff --git a/src/map/intif.h b/src/map/intif.h index 3d6a52440..4bca5f167 100644 --- a/src/map/intif.h +++ b/src/map/intif.h @@ -37,6 +37,7 @@ struct s_elemental; struct s_homunculus; struct s_mercenary; struct s_pet; +struct rodex_message; /** * Defines @@ -131,6 +132,12 @@ struct intif_interface { int (*elemental_request) (int ele_id, int char_id); int (*elemental_delete) (int ele_id); int (*elemental_save) (struct s_elemental *ele); + // RoDEX + int(*rodex_requestinbox) (int char_id, int account_id, int8 flag, int8 opentype, int64 mail_id); + int(*rodex_checkhasnew) (struct map_session_data *sd); + int(*rodex_updatemail) (int64 mail_id, int8 flag); + int(*rodex_sendmail) (struct rodex_message *msg); + int(*rodex_checkname) (struct map_session_data *sd, const char *name); /* @accinfo */ void (*request_accinfo) (int u_fd, int aid, int group_lv, char* query); /* */ @@ -200,6 +207,11 @@ struct intif_interface { void (*pRecvHomunculusData) (int fd); void (*pSaveHomunculusOk) (int fd); void (*pDeleteHomunculusOk) (int fd); + /* RoDEX */ + void(*pRequestRodexOpenInbox) (int fd); + void(*pRodexHasNew) (int fd); + void(*pRodexSendMail) (int fd); + void(*pRodexCheckName) (int fd); }; #ifdef HERCULES_CORE diff --git a/src/map/map.c b/src/map/map.c index 17156f631..f8aebcfa6 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -55,6 +55,7 @@ #include "map/skill.h" #include "map/status.h" #include "map/storage.h" +#include "map/rodex.h" #include "map/trade.h" #include "map/unit.h" #include "common/HPM.h" @@ -1915,6 +1916,7 @@ int map_quit(struct map_session_data *sd) { } npc->script_event(sd, NPCE_LOGOUT); + rodex->clean(sd, 0); //Unit_free handles clearing the player related data, //map->quit handles extra specific data which is related to quitting normally @@ -6012,6 +6014,7 @@ int do_final(void) { elemental->final(); map->list_final(); vending->final(); + rodex->final(); HPM_map_do_final(); @@ -6208,6 +6211,7 @@ void map_load_defaults(void) { path_defaults(); quest_defaults(); npc_chat_defaults(); + rodex_defaults(); } /** * --run-once handler @@ -6525,6 +6529,7 @@ int do_init(int argc, char *argv[]) bg->init(minimal); duel->init(minimal); vending->init(minimal); + rodex->init(minimal); if (map->scriptcheck) { bool failed = map->extra_scripts_count > 0 ? false : true; diff --git a/src/map/packets.h b/src/map/packets.h index 0a774561d..c471e8a6b 100644 --- a/src/map/packets.h +++ b/src/map/packets.h @@ -2858,7 +2858,7 @@ packet(0x96e,-1,clif->ackmergeitems); // new packets packet(0x09e7,2); // ZC_NOTIFY_UNREAD_RODEX packet(0x09e8,18,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX - packet(0x09e9,2,clif->pDull/*,XXX*/); // CZ_CLOSE_RODEXBOX + packet(0x09e9,2,clif->pRodexCloseMailbox); // CZ_CLOSE_RODEXBOX packet(0x09ed,-1); // ZC_ACK_SEND_RODEX packet(0x09ee,-1,clif->pDull/*,XXX*/); // CZ_REQ_NEXT_RODEX // changed packet sizes @@ -2902,13 +2902,13 @@ packet(0x96e,-1,clif->ackmergeitems); // new packets packet(0x09ea,10,clif->pDull/*,XXX*/); // CZ_REQ_READ_RODEX packet(0x09eb,14); // ZC_ACK_READ_RODEX - packet(0x09ef,11,clif->pDull/*,XXX*/); // CZ_REQ_REFRESH_RODEX + packet(0x09ef,11,clif->pRodexRefreshMaillist); // CZ_REQ_REFRESH_RODEX packet(0x09f0,-1); // ZC_ACK_RODEX_LIST - packet(0x09f5,11,clif->pDull/*,XXX*/); // CZ_REQ_DELETE_RODEX + packet(0x09f5,11,clif->pRodexDeleteMail); // CZ_REQ_DELETE_RODEX packet(0x09f6,11); // ZC_ACK_DELETE_RODEX // changed packet sizes packet(0x09e8,10,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX - packet(0x09ee,11,clif->pDull/*,XXX*/); // CZ_REQ_NEXT_RODEX + packet(0x09ee,11,clif->pRodexNextMaillist); // CZ_REQ_NEXT_RODEX #endif // 2013-12-23cRagexe - Yommy @@ -2948,7 +2948,7 @@ packet(0x96e,-1,clif->ackmergeitems); #if PACKETVER >= 20131223 // new packets // changed packet sizes - packet(0x09ea,11,clif->pDull/*,XXX*/); // CZ_REQ_READ_RODEX + packet(0x09ea,11,clif->pRodexReadMail); // CZ_REQ_READ_RODEX packet(0x09eb,24); // ZC_ACK_READ_RODEX #endif @@ -2988,7 +2988,7 @@ packet(0x96e,-1,clif->ackmergeitems); // 2013-12-30aRagexe #if PACKETVER >= 20131230 // new packets - packet(0x09ec,-1,clif->pDull/*,XXX*/); // CZ_REQ_SEND_RODEX + packet(0x09ec,-1,clif->pRodexSendMail); // CZ_REQ_SEND_RODEX packet(0x09ed,3); // ZC_ACK_SEND_RODEX packet(0x09f7,75); // ZC_PROPERTY_HOMUN_2 // changed packet sizes @@ -3110,7 +3110,7 @@ packet(0x96e,-1,clif->ackmergeitems); // new packets packet(0x0a02,4); // ZC_DRESSROOM_OPEN // changed packet sizes - packet(0x09e8,11,clif->pDull/*,XXX*/); // CZ_OPEN_RODEXBOX + packet(0x09e8,11,clif->pRodexOpenMailbox); // CZ_OPEN_RODEXBOX #endif // 2014-02-19aRagexeRE @@ -3204,11 +3204,11 @@ packet(0x96e,-1,clif->ackmergeitems); // 2014-03-26cRagexeRE #if PACKETVER >= 20140326 // changed packet sizes - packet(0x09f1,11,clif->pDull/*,XXX*/); // CZ_REQ_ZENY_FROM_RODEX + packet(0x09f1,11,clif->pRodexRequestZeny); // CZ_REQ_ZENY_FROM_RODEX packet(0x09f2,4); // ZC_ACK_ZENY_FROM_RODEX - packet(0x09f3,11,clif->pDull/*,XXX*/); // CZ_REQ_ITEM_FROM_RODEX + packet(0x09f3,11,clif->pRodexRequestItems); // CZ_REQ_ITEM_FROM_RODEX packet(0x09f4,4); // ZC_ACK_ITEM_FROM_RODEX - packet(0x0a03,2,clif->pDull/*,XXX*/); // CZ_REQ_CANCEL_WRITE_RODEX + packet(0x0a03,2,clif->pRodexCancelWriteMail); // CZ_REQ_CANCEL_WRITE_RODEX packet(0x0a07,6); // ZC_ACK_REMOVE_RODEX_ITEM packet(0x0a08,7,clif->pDull/*,XXX*/); // CZ_REQ_OPEN_WRITE_RODEX #endif @@ -3298,14 +3298,14 @@ packet(0x96e,-1,clif->ackmergeitems); // 2014-04-16aRagexeRE #if PACKETVER >= 20140416 // new packets - packet(0x0a04,6,clif->pDull/*,XXX*/); // CZ_REQ_ADD_ITEM_RODEX + packet(0x0a04,6,clif->pRodexAddItem); // CZ_REQ_ADD_ITEM_RODEX packet(0x0a12,27); // ZC_ACK_OPEN_WRITE_RODEX - packet(0x0a13,2,clif->pDull/*,XXX*/); // CZ_CHECK_RECEIVE_CHARACTER_NAME + packet(0x0a13,2,clif->pRodexCheckName); // CZ_CHECK_RECEIVE_CHARACTER_NAME // changed packet sizes packet(0x0a05,48); // ZC_ACK_ADD_ITEM_RODEX - packet(0x0a06,6,clif->pDull/*,XXX*/); // CZ_REQ_REMOVE_RODEX_ITEM + packet(0x0a06,6,clif->pRodexRemoveItem); // CZ_REQ_REMOVE_RODEX_ITEM packet(0x0a07,7); // ZC_ACK_REMOVE_RODEX_ITEM - packet(0x0a08,26,clif->pDull/*,XXX*/); // CZ_REQ_OPEN_WRITE_RODEX + packet(0x0a08,26,clif->pRodexOpenWriteMail); // CZ_REQ_OPEN_WRITE_RODEX #endif // 2014-04-23aRagexeRE @@ -3313,7 +3313,7 @@ packet(0x96e,-1,clif->ackmergeitems); // new packets packet(0x0a14,6); // ZC_CHECK_RECEIVE_CHARACTER_NAME // changed packet sizes - packet(0x0a13,26,clif->pDull/*,XXX*/); // CZ_CHECK_RECEIVE_CHARACTER_NAME + packet(0x0a13,26,clif->pRodexCheckName); // CZ_CHECK_RECEIVE_CHARACTER_NAME #endif // 2014-04-30aRagexeRE @@ -4818,7 +4818,7 @@ packet(0x96e,-1,clif->ackmergeitems); // new packets packet(0x0a6c,7,clif->pDull/*,XXX*/); packet(0x0a6d,-1); - packet(0x0a6e,-1); + packet(0x0a6e,-1,clif->pRodexSendMail); // CZ_RODEX_SEND_MAIL packet(0x0a6f,-1); // changed packet sizes #endif @@ -4971,7 +4971,7 @@ packet(0x96e,-1,clif->ackmergeitems); packet(0x0a79,-1); packet(0x0a7b,-1); packet(0x0a7c,-1); - packet(0x0a7d,-1); + packet(0x0a7d,-1); // ZC_RODEX_MAILLIST // changed packet sizes #endif diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h index 796ea577c..6e0ddf814 100644 --- a/src/map/packets_struct.h +++ b/src/map/packets_struct.h @@ -307,6 +307,27 @@ enum packet_headers { #else // PACKETVER < 20141022 questListType = 0x2b1, ///< ZC_ALL_QUEST_LIST #endif // PACKETVER >= 20141022 + /* Rodex */ + rodexicon = 0x09E7, + rodexread = 0x09EB, + rodexwriteresult = 0x09ED, + rodexnextpage = 0x09F0, + rodexgetzeny = 0x09F2, + rodexgetitem = 0x09F4, + rodexdelete = 0x09F6, + rodexadditem = 0x0A05, + rodexremoveitem = 0x0A07, + rodexopenwrite = 0x0A12, +#if PACKETVER < 20160600 + rodexmailList = 0x09F0, +#else // PACKETVER >= 20160600 + rodexmailList = 0x0A7D, +#endif +#if PACKETVER < 20160316 + rodexcheckplayer = 0x0A14, +#else // PACKETVER >= 20160316 + rodexcheckplayer = 0x0A51, +#endif }; #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute @@ -1247,6 +1268,197 @@ struct packet_whisper_message { char message[]; } __attribute__((packed)); +/* RoDEX */ +struct PACKET_CZ_ADD_ITEM_TO_MAIL { + int16 PacketType; + int16 index; + int16 count; +} __attribute__((packed)); + +struct PACKET_ZC_ADD_ITEM_TO_MAIL { + int16 PacketType; + int8 result; + int16 index; + int16 count; + uint16 ITID; + int8 type; + int8 IsIdentified; + int8 IsDamaged; + int8 refiningLevel; + struct EQUIPSLOTINFO slot; + struct ItemOptions optionData[MAX_ITEM_OPTIONS]; + int16 weight; + int8 unknow[5]; +} __attribute__((packed)); + +struct mail_item { + int16 count; + uint16 ITID; + int8 IsIdentified; + int8 IsDamaged; + int8 refiningLevel; + struct EQUIPSLOTINFO slot; + int8 unknow1[4]; + int8 type; + int8 unknown[4]; + struct ItemOptions optionData[MAX_ITEM_OPTIONS]; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_OPEN_WRITE_MAIL { + int16 PacketType; + char receiveName[NAME_LENGTH]; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_OPEN_WRITE_MAIL { + int16 PacketType; + char receiveName[NAME_LENGTH]; + int8 result; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_REMOVE_ITEM_MAIL { + int16 PacketType; + int16 index; + uint16 cnt; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_REMOVE_ITEM_MAIL { + int16 PacketType; + int8 result; + int16 index; + uint16 cnt; + int16 weight; +} __attribute__((packed)); + +struct PACKET_CZ_SEND_MAIL { + int16 PacketType; + int16 PacketLength; + char receiveName[24]; + char senderName[24]; + int64 zeny; + int16 Titlelength; + int16 TextcontentsLength; +#if PACKETVER > 20160600 + int32 receiver_char_id; +#endif // PACKETVER > 20160600 + char string[]; +} __attribute__((packed)); + +struct PACKET_ZC_WRITE_MAIL_RESULT { + int16 PacketType; + int8 result; +} __attribute__((packed)); + +struct PACKET_CZ_CHECKNAME { + int16 PacketType; + char Name[24]; +} __attribute__((packed)); + +struct PACKET_ZC_CHECKNAME { + int16 PacketType; + int32 CharId; + int16 Class; + int16 BaseLevel; +#if PACKETVER >= 20160316 + char Name[24]; +#endif +} __attribute__((packed)); + +struct PACKET_ZC_NOTIFY_UNREADMAIL { + int16 PacketType; + char result; +} __attribute__((packed)); + +struct maillistinfo { + int64 MailID; + int8 Isread; + uint8 type; + char SenderName[24]; + int32 regDateTime; + int32 expireDateTime; + int16 Titlelength; + char title[]; +} __attribute__((packed)); + +struct PACKET_ZC_MAIL_LIST { + int16 PacketType; + int16 PacketLength; + int8 opentype; + int8 cnt; + int8 IsEnd; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_NEXT_MAIL_LIST { + int16 PacketType; + int8 opentype; + int64 Lower_MailID; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_OPEN_MAIL { + int16 PacketType; + int8 opentype; + int64 Upper_MailID; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_READ_MAIL { + int16 PacketType; + int8 opentype; + int64 MailID; +} __attribute__((packed)); + +struct PACKET_ZC_READ_MAIL { + int16 PacketType; + int16 PacketLength; + int8 opentype; + int64 MailID; + int16 TextcontentsLength; + int64 zeny; + int8 ItemCnt; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_DELETE_MAIL { + int16 PacketType; + int8 opentype; + int64 MailID; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_DELETE_MAIL { + int16 PacketType; + int8 opentype; + int64 MailID; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_REFRESH_MAIL_LIST { + int16 PacketType; + int8 opentype; + int64 Upper_MailID; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_ZENY_FROM_MAIL { + int16 PacketType; + int64 MailID; + int8 opentype; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_ZENY_FROM_MAIL { + int16 PacketType; + int64 MailID; + int8 opentype; + int8 result; +} __attribute__((packed)); + +struct PACKET_CZ_REQ_ITEM_FROM_MAIL { + int16 PacketType; + int64 MailID; + int8 opentype; +} __attribute__((packed)); + +struct PACKET_ZC_ACK_ITEM_FROM_MAIL { + int16 PacketType; + int64 MailID; + int8 opentype; + int8 result; +} __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.c b/src/map/pc.c index a925b523c..f332feea8 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1165,6 +1165,8 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim } memcpy(&sd->status, st, sizeof(*st)); + memset(&sd->rodex, 0x0, sizeof(sd->rodex)); + VECTOR_INIT(sd->rodex.messages); if (st->sex != sd->status.sex) { clif->authfail_fd(sd->fd, 0); @@ -1517,6 +1519,7 @@ int pc_reg_received(struct map_session_data *sd) intif->Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox intif->request_questlog(sd); + intif->rodex_checkhasnew(sd); if (sd->state.connect_new == 0 && sd->fd) { //Character already loaded map! Gotta trigger LoadEndAck manually. sd->state.connect_new = 1; diff --git a/src/map/pc.h b/src/map/pc.h index 04fd98b24..c94ec6099 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -28,6 +28,7 @@ #include "map/log.h" // struct e_log_pick_type #include "map/map.h" // RC_MAX, ELE_MAX #include "map/pc_groups.h" // GroupSettings +#include "map/rodex.h" #include "map/script.h" // struct reg_db #include "map/searchstore.h" // struct s_search_store_info #include "map/status.h" // enum sc_type, OPTION_* @@ -499,6 +500,14 @@ END_ZEROED_BLOCK; bool changed; // if true, should sync with charserver on next mailbox request } mail; + // RoDEX + struct { + struct rodex_message tmp; + struct rodex_maillist messages; + int total; + bool new_mail; + } rodex; + // Quest log system int num_quests; ///< Number of entries in quest_log int avail_quests; ///< Number of Q_ACTIVE and Q_INACTIVE entries in quest log (index of the first Q_COMPLETE entry) diff --git a/src/map/rodex.c b/src/map/rodex.c new file mode 100644 index 000000000..03191ab22 --- /dev/null +++ b/src/map/rodex.c @@ -0,0 +1,621 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * 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 . + */ +#define HERCULES_CORE + +#include "rodex.h" + +#include "map/battle.h" +#include "map/date.h" +#include "map/intif.h" +#include "map/itemdb.h" +#include "map/pc.h" + +#include "common/nullpo.h" +#include "common/sql.h" +#include "common/memmgr.h" + + +// NOTE : These values are hardcoded into the client +// Cost of each Attached Item +#define ATTACHITEM_COST 2500 +// Percent of Attached Zeny that will be paid as Tax +#define ATTACHZENY_TAX 2 +// Maximun number of messages that can be sent in one day +#define DAILY_MAX_MAILS 100 + +struct rodex_interface rodex_s; +struct rodex_interface *rodex; + +/// Checks if RoDEX System is enabled in the server +/// Returns true if it's enabled, false otherwise +bool rodex_isenabled(void) +{ + if (battle_config.feature_rodex == 1) + return true; + + return false; +} + +/// Checks and refreshes the user daily number of Stamps +/// @param sd : The player who's being checked +void rodex_refresh_stamps(struct map_session_data *sd) +{ + int today = date_get_date(); + nullpo_retv(sd); + + // Note : Weirdly, iRO starts this with maximum messages of the day and decrements + // but our clients starts this at 0 and increments + if (sd->sc.data[SC_DAILYSENDMAILCNT] == NULL) { + sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, today, 0, INFINITE_DURATION); + } else { + int sc_date = sd->sc.data[SC_DAILYSENDMAILCNT]->val1; + if (sc_date != today) { + sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, today, 0, INFINITE_DURATION); + } + } +} + +/// Attaches an item to a message being written +/// @param sd : The player who's writting +/// @param idx : the inventory idx of the item +/// @param amount : Amount of the item to be attached +void rodex_add_item(struct map_session_data *sd, int16 idx, int16 amount) +{ + int i; + bool is_stack = false; + + nullpo_retv(sd); + + if (idx < 0 || idx >= MAX_INVENTORY) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR); + return; + } + + if (amount < 0 || amount > sd->status.inventory[idx].amount) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR); + return; + } + + if (!pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || + !itemdb_canmail(&sd->status.inventory[idx], pc_get_group_level(sd)) || + (sd->status.inventory[idx].bound && !pc_can_give_bound_items(sd))) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NOT_TRADEABLE); + return; + } + + if (itemdb->isstackable(sd->status.inventory[idx].nameid) == 1) { + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + if (sd->rodex.tmp.items[i].idx == idx) { + if (sd->status.inventory[idx].nameid == sd->rodex.tmp.items[i].item.nameid && + sd->status.inventory[idx].unique_id == sd->rodex.tmp.items[i].item.unique_id) { + is_stack = true; + break; + } + } + } + + if (i == RODEX_MAX_ITEM && sd->rodex.tmp.items_count < RODEX_MAX_ITEM) { + ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0); + } + } else if (sd->rodex.tmp.items_count < RODEX_MAX_ITEM) { + ARR_FIND(0, RODEX_MAX_ITEM, i, sd->rodex.tmp.items[i].idx == 0); + } else { + i = RODEX_MAX_ITEM; + } + + if (i == RODEX_MAX_ITEM) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_NO_SPACE); + return; + } + + if (sd->rodex.tmp.items[i].item.amount + amount > sd->status.inventory[idx].amount) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR); + return; + } + + if (sd->rodex.tmp.weight + sd->inventory_data[idx]->weight * amount > RODEX_WEIGHT_LIMIT) { + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_FATAL_ERROR); + return; + } + + sd->rodex.tmp.items[i].idx = idx; + sd->rodex.tmp.weight += sd->inventory_data[idx]->weight * amount; + if (is_stack == false) { + sd->rodex.tmp.items[i].item = sd->status.inventory[idx]; + sd->rodex.tmp.items[i].item.amount = amount; + sd->rodex.tmp.items_count++; + } else { + sd->rodex.tmp.items[i].item.amount += amount; + } + sd->rodex.tmp.type |= MAIL_TYPE_ITEM; + + clif->rodex_add_item_result(sd, idx, amount, RODEX_ADD_ITEM_SUCCESS); +} + +/// Removes an item attached to a message being writen +/// @param sd : The player who's writting the message +/// @param idx : The index of the item +/// @param amount : How much to remove +void rodex_remove_item(struct map_session_data *sd, int16 idx, int16 amount) +{ + int i; + struct item *it; + struct item_data *itd; + + nullpo_retv(sd); + Assert_retv(idx >= 0 && idx < MAX_INVENTORY); + + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + if (sd->rodex.tmp.items[i].idx == idx) + break; + } + + if (i == RODEX_MAX_ITEM) { + clif->rodex_remove_item_result(sd, idx, -1); + return; + } + + it = &sd->rodex.tmp.items[i].item; + + if (amount <= 0 || amount > it->amount) { + clif->rodex_remove_item_result(sd, idx, -1); + return; + } + + itd = itemdb->search(it->nameid); + + if (amount == it->amount) { + sd->rodex.tmp.weight -= itd->weight * amount; + sd->rodex.tmp.items_count--; + if (sd->rodex.tmp.items_count < 1) { + sd->rodex.tmp.type &= ~MAIL_TYPE_ITEM; + } + memset(&sd->rodex.tmp.items[i], 0x0, sizeof(sd->rodex.tmp.items[0])); + clif->rodex_remove_item_result(sd, idx, 0); + return; + } + + it->amount -= amount; + sd->rodex.tmp.weight -= itd->weight * amount; + + clif->rodex_remove_item_result(sd, idx, it->amount); +} + +/// Request if character with given name exists and returns information about him +/// @param sd : The player who's requesting +/// @param name : The name of the character to check +/// @param base_level : Reference to return the character base level, if he exists +/// @param char_id : Reference to return the character id, if he exists +/// @param class : Reference to return the character class id, if he exists +void rodex_check_player(struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class) +{ + intif->rodex_checkname(sd, name); +} + +/// Sends a Mail to an character +/// @param sd : The player who's sending +/// @param receiver_name : The name of the character who's receiving the message +/// @param body : Mail message +/// @param title : Mail Title +/// @param zeny : Amount of zeny attached +/// Returns result code: +/// RODEX_SEND_MAIL_SUCCESS = 0, +/// RODEX_SEND_MAIL_FATAL_ERROR = 1, +/// RODEX_SEND_MAIL_COUNT_ERROR = 2, +/// RODEX_SEND_MAIL_ITEM_ERROR = 3, +/// RODEX_SEND_MAIL_RECEIVER_ERROR = 4 +int rodex_send_mail(struct map_session_data *sd, const char *receiver_name, const char *body, const char *title, int64 zeny) +{ + int i; + int64 total_zeny; + + nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, sd); + nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, receiver_name); + nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, body); + nullpo_retr(RODEX_SEND_MAIL_FATAL_ERROR, title); + + if (zeny < 0) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_FATAL_ERROR; + } + + total_zeny = zeny + sd->rodex.tmp.items_count * ATTACHITEM_COST + (2 * zeny)/100; + + if (strcmp(receiver_name, sd->rodex.tmp.receiver_name) != 0) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_RECEIVER_ERROR; + } + + if (total_zeny > sd->status.zeny || total_zeny < 0) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_FATAL_ERROR; + } + + rodex_refresh_stamps(sd); + + if (sd->sc.data[SC_DAILYSENDMAILCNT] != NULL) { + if (sd->sc.data[SC_DAILYSENDMAILCNT]->val2 >= DAILY_MAX_MAILS) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_COUNT_ERROR; + } + + sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, sd->sc.data[SC_DAILYSENDMAILCNT]->val1, sd->sc.data[SC_DAILYSENDMAILCNT]->val2 + 1, INFINITE_DURATION); + } else { + sc_start2(NULL, &sd->bl, SC_DAILYSENDMAILCNT, 100, date_get_date(), 1, INFINITE_DURATION); + } + + for (i = 0; i < RODEX_MAX_ITEM; i++) { + int16 idx = sd->rodex.tmp.items[i].idx; + + if (sd->rodex.tmp.items[i].item.nameid == 0) + continue; + + if (sd->rodex.tmp.items[i].item.nameid != sd->status.inventory[idx].nameid + || sd->rodex.tmp.items[i].item.unique_id != sd->status.inventory[idx].unique_id + || sd->rodex.tmp.items[i].item.amount > sd->status.inventory[idx].amount + || sd->rodex.tmp.items[i].item.amount < 1) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_ITEM_ERROR; + } + } + + if (total_zeny > 0 && pc->payzeny(sd, (int)total_zeny, LOG_TYPE_MAIL, NULL) != 0) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_FATAL_ERROR; + } + + for (i = 0; i < RODEX_MAX_ITEM; i++) { + int16 idx = sd->rodex.tmp.items[i].idx; + + if (sd->rodex.tmp.items[i].item.nameid == 0) { + continue; + } + + if (pc->delitem(sd, idx, sd->rodex.tmp.items[i].item.amount, 0, DELITEM_NORMAL, LOG_TYPE_MAIL) != 0) { + rodex->clean(sd, 1); + return RODEX_SEND_MAIL_ITEM_ERROR; + } + } + + sd->rodex.tmp.zeny = zeny; + sd->rodex.tmp.is_read = false; + sd->rodex.tmp.is_deleted = false; + sd->rodex.tmp.send_date = (int)time(NULL); + sd->rodex.tmp.expire_date = (int)time(NULL) + RODEX_EXPIRE; + if (strlen(sd->rodex.tmp.body) > 0) + sd->rodex.tmp.type |= MAIL_TYPE_TEXT; + if (sd->rodex.tmp.zeny > 0) + sd->rodex.tmp.type |= MAIL_TYPE_ZENY; + sd->rodex.tmp.sender_id = sd->status.char_id; + strncpy(sd->rodex.tmp.sender_name, sd->status.name, NAME_LENGTH); + strncpy(sd->rodex.tmp.title, title, RODEX_TITLE_LENGTH); + strncpy(sd->rodex.tmp.body, body, RODEX_BODY_LENGTH); + + intif->rodex_sendmail(&sd->rodex.tmp); + return RODEX_SEND_MAIL_SUCCESS; // this will not inform client of the success yet. (see rodex_send_mail_result) +} + +/// The result of a message send, called by char-server +/// @param ssd : Sender's sd +/// @param rsd : Receiver's sd +/// @param result : Message sent (true) or failed (false) +void rodex_send_mail_result(struct map_session_data *ssd, struct map_session_data *rsd, bool result) +{ + if (ssd != NULL) { + rodex->clean(ssd, 1); + if (result == false) { + clif->rodex_send_mail_result(ssd->fd, ssd, RODEX_SEND_MAIL_FATAL_ERROR); + return; + } + + clif->rodex_send_mail_result(ssd->fd, ssd, RODEX_SEND_MAIL_SUCCESS); + } + + if (rsd != NULL) { + clif->rodex_icon(rsd->fd, true); + clif_disp_onlyself(rsd, "You've got a new mail!"); + } + return; +} + +/// Retrieves one message from character +/// @param sd : Character +/// @param mail_id : Mail ID that's being retrieved +/// Returns the message +struct rodex_message *rodex_get_mail(struct map_session_data *sd, int64 mail_id) +{ + int i; + struct rodex_message *msg; + + nullpo_retr(NULL, sd); + + ARR_FIND(0, VECTOR_LENGTH(sd->rodex.messages), i, VECTOR_INDEX(sd->rodex.messages, i).id == mail_id && VECTOR_INDEX(sd->rodex.messages, i).is_deleted != true); + if (i == VECTOR_LENGTH(sd->rodex.messages)) + return NULL; + + msg = &VECTOR_INDEX(sd->rodex.messages, i); + + return msg; +} + +/// Request to read a mail by its ID +/// @param sd : Who's reading +/// @param mail_id : Mail ID to be read +void rodex_read_mail(struct map_session_data *sd, int64 mail_id) +{ + struct rodex_message *msg; + + nullpo_retv(sd); + + msg = rodex->get_mail(sd, mail_id); + nullpo_retv(msg); + + if (msg->is_read == false) { + intif->rodex_updatemail(msg->id, 0); + msg->is_read = true; + } + + clif->rodex_read_mail(sd, msg->opentype, msg); +} + +/// Deletes a mail +/// @param sd : Who's deleting +/// @param mail_id : Mail ID to be deleted +void rodex_delete_mail(struct map_session_data *sd, int64 mail_id) +{ + struct rodex_message *msg; + + nullpo_retv(sd); + + msg = rodex->get_mail(sd, mail_id); + nullpo_retv(msg); + + msg->is_deleted = true; + intif->rodex_updatemail(msg->id, 3); + + clif->rodex_delete_mail(sd, msg->opentype, msg->id); +} + +/// Gets attached zeny +/// @param sd : Who's getting +/// @param mail_id : Mail ID that we're getting zeny from +void rodex_get_zeny(struct map_session_data *sd, int8 opentype, int64 mail_id) +{ + struct rodex_message *msg; + + nullpo_retv(sd); + + msg = rodex->get_mail(sd, mail_id); + + if (msg == NULL) { + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); + return; + } + + if ((int64)sd->status.zeny + msg->zeny > MAX_ZENY) { + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_LIMIT_ERROR); + return; + } + + if (pc->getzeny(sd, (int)msg->zeny, LOG_TYPE_MAIL, NULL) != 0) { + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_FATAL_ERROR); + return; + } + + msg->zeny = 0; + intif->rodex_updatemail(mail_id, 1); + + clif->rodex_request_zeny(sd, opentype, mail_id, RODEX_GET_ZENY_SUCCESS); +} + +/// Gets attached item +/// @param sd : Who's getting +/// @param mail_id : Mail ID that we're getting items from +void rodex_get_items(struct map_session_data *sd, int8 opentype, int64 mail_id) +{ + struct rodex_message *msg; + int weight = 0; + int empty_slots = 0, required_slots; + int i; + + nullpo_retv(sd); + + msg = rodex->get_mail(sd, mail_id); + + if (msg == NULL) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FATAL_ERROR); + return; + } + + if (msg->items_count < 1) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FATAL_ERROR); + return; + } + + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + if (msg->items[i].item.nameid != 0) { + weight += itemdb->search(msg->items[i].item.nameid)->weight * msg->items[i].item.amount; + } + } + + if ((sd->weight + weight > sd->max_weight)) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); + return; + } + + required_slots = msg->items_count; + for (i = 0; i < MAX_INVENTORY; ++i) { + if (sd->status.inventory[i].nameid == 0) { + empty_slots++; + } else if (itemdb->isstackable(sd->status.inventory[i].nameid) == 1) { + int j; + ARR_FIND(0, msg->items_count, j, sd->status.inventory[i].nameid == msg->items[j].item.nameid); + if (j < msg->items_count) { + struct item_data *idata = itemdb->search(sd->status.inventory[i].nameid); + + if ((idata->stack.inventory && sd->status.inventory[i].amount + msg->items[i].item.amount > idata->stack.amount) || + sd->status.inventory[i].amount + msg->items[i].item.amount > MAX_AMOUNT) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); + return; + } + + required_slots--; + } + } + } + + if (empty_slots < required_slots) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); + return; + } + + for (i = 0; i < RODEX_MAX_ITEM; ++i) { + struct item *it = &msg->items[i].item; + + if (it->nameid == 0) { + continue; + } + + if (pc->additem(sd, it, it->amount, LOG_TYPE_MAIL) != 0) { + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEM_FULL_ERROR); + intif->rodex_updatemail(mail_id, 2); + return; + } else { + memset(it, 0x0, sizeof(*it)); + } + } + + intif->rodex_updatemail(mail_id, 2); + + clif->rodex_request_items(sd, opentype, mail_id, RODEX_GET_ITEMS_SUCCESS); +} + +/// Cleans user's RoDEX related data +/// - should be called everytime we're going to stop using rodex in this character +/// @param sd : Target to clean +/// @param flag : +/// 0 - clear everything +/// 1 - clear tmp only +void rodex_clean(struct map_session_data *sd, int8 flag) +{ + nullpo_retv(sd); + + if (flag == 0) + VECTOR_CLEAR(sd->rodex.messages); + + memset(&sd->rodex.tmp, 0x0, sizeof(sd->rodex.tmp)); +} + +/// User request to open rodex, load mails from char-server +/// @param sd : Who's requesting +/// @param open_type : Box Type (see RODEX_OPENTYPE) +void rodex_open(struct map_session_data *sd, int8 open_type) +{ + nullpo_retv(sd); + if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false) + open_type = RODEX_OPENTYPE_MAIL; + + intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 0, open_type, 0); +} + +/// User request to read next page of mails +/// @param sd : Who's requesting +/// @param open_type : Box Type (see RODEX_OPENTYPE) +/// @param last_mail_id : The last mail from the current page +void rodex_next_page(struct map_session_data *sd, int8 open_type, int64 last_mail_id) +{ + int64 msg_count, page_start = 0; + nullpo_retv(sd); + + if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false) { + // Should not happen + open_type = RODEX_OPENTYPE_MAIL; + rodex->open(sd, open_type); + return; + } + + msg_count = VECTOR_LENGTH(sd->rodex.messages); + + if (last_mail_id > 0) { + // Find where the page starts + ARR_FIND(0, msg_count, page_start, VECTOR_INDEX(sd->rodex.messages, page_start).id == last_mail_id); + if (page_start > 0 && page_start < msg_count) { + --page_start; // Valid page, get first item of next page + } else { + page_start = msg_count - 1; // Should not happen, invalid lower_id given + } + clif->rodex_send_maillist(sd->fd, sd, open_type, page_start); + } +} + +/// User's request to refresh his mail box +/// @param sd : Who's requesting +/// @param open_type : Box Type (See RODEX_OPENTYPE) +/// @param first_mail_id : The first mail id known by client, currently unused +void rodex_refresh(struct map_session_data *sd, int8 open_type, int64 first_mail_id) +{ + nullpo_retv(sd); + if (open_type == RODEX_OPENTYPE_ACCOUNT && battle_config.feature_rodex_use_accountmail == false) + open_type = RODEX_OPENTYPE_MAIL; + + // Some clients sends the first mail id it currently has and expects to receive + // a list of newer mails, other clients sends first mail id as 0 and expects + // to receive the first page (as if opening the box) + if (first_mail_id > 0) { + intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 1, open_type, first_mail_id); + } else { + intif->rodex_requestinbox(sd->status.char_id, sd->status.account_id, 0, open_type, first_mail_id); + } +} + +void do_init_rodex(bool minimal) +{ + if (minimal) + return; +} + +void do_final_rodex(void) +{ + +} + +void rodex_defaults(void) +{ + rodex = &rodex_s; + + rodex->init = do_init_rodex; + rodex->final = do_final_rodex; + + rodex->open = rodex_open; + rodex->next_page = rodex_next_page; + rodex->refresh = rodex_refresh; + rodex->isenabled = rodex_isenabled; + rodex->add_item = rodex_add_item; + rodex->remove_item = rodex_remove_item; + rodex->check_player = rodex_check_player; + rodex->send_mail = rodex_send_mail; + rodex->send_mail_result = rodex_send_mail_result; + rodex->get_mail = rodex_get_mail; + rodex->read_mail = rodex_read_mail; + rodex->delete_mail = rodex_delete_mail; + rodex->get_zeny = rodex_get_zeny; + rodex->get_items = rodex_get_items; + rodex->clean = rodex_clean; +} diff --git a/src/map/rodex.h b/src/map/rodex.h new file mode 100644 index 000000000..18ecd91d6 --- /dev/null +++ b/src/map/rodex.h @@ -0,0 +1,85 @@ +/** + * This file is part of Hercules. + * http://herc.ws - http://github.com/HerculesWS/Hercules + * + * Copyright (C) 2017 Hercules Dev Team + * + * 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 . + */ +#ifndef MAP_RODEX_H +#define MAP_RODEX_H + +#include "common/mmo.h" +#include "common/hercules.h" + +#define RODEX_WEIGHT_LIMIT (2000 * 10) + +struct rodex_message; + +enum rodex_add_item { + RODEX_ADD_ITEM_SUCCESS = 0, + RODEX_ADD_ITEM_WEIGHT_ERROR = 1, + RODEX_ADD_ITEM_FATAL_ERROR = 2, + RODEX_ADD_ITEM_NO_SPACE = 3, + RODEX_ADD_ITEM_NOT_TRADEABLE = 4, +}; + +enum rodex_send_mail { + RODEX_SEND_MAIL_SUCCESS = 0, + RODEX_SEND_MAIL_FATAL_ERROR = 1, + RODEX_SEND_MAIL_COUNT_ERROR = 2, + RODEX_SEND_MAIL_ITEM_ERROR = 3, + RODEX_SEND_MAIL_RECEIVER_ERROR = 4 +}; + +enum rodex_get_zeny { + RODEX_GET_ZENY_SUCCESS = 0, + RODEX_GET_ZENY_FATAL_ERROR = 1, + RODEX_GET_ZENY_LIMIT_ERROR = 2 +}; + +enum rodex_get_items { + RODEX_GET_ITEMS_SUCCESS = 0, + RODEX_GET_ITEM_FATAL_ERROR = 1, + RODEX_GET_ITEM_FULL_ERROR = 2, +}; + +struct rodex_interface { + void (*init) (bool minimal); + void (*final) (void); + + bool (*isenabled) (void); + void (*open) (struct map_session_data *sd, int8 open_type); + void (*next_page) (struct map_session_data *sd, int8 open_type, int64 last_mail_id); + void (*refresh) (struct map_session_data *sd, int8 open_type, int64 first_mail_id); + void (*add_item) (struct map_session_data *sd, int16 idx, int16 amount); + void (*remove_item) (struct map_session_data *sd, int16 idx, int16 amount); + void (*check_player) (struct map_session_data *sd, const char *name, int *base_level, int *char_id, short *class); + int (*send_mail) (struct map_session_data *sd, const char *receiver_name, const char *body, const char *title, int64 zeny); + void (*send_mail_result) (struct map_session_data *ssd, struct map_session_data *rsd, bool result); + struct rodex_message *(*get_mail) (struct map_session_data *sd, int64 mail_id); + void (*read_mail) (struct map_session_data *sd, int64 mail_id); + void (*get_zeny) (struct map_session_data *sd, int8 opentype, int64 mail_id); + void (*get_items) (struct map_session_data *sd, int8 opentype, int64 mail_id); + void (*delete_mail) (struct map_session_data *sd, int64 mail_id); + void (*clean) (struct map_session_data *sd, int8 flag); +}; + +#ifdef HERCULES_CORE +void rodex_defaults(void); +#endif + +HPShared struct rodex_interface *rodex; + +#endif diff --git a/src/map/script.c b/src/map/script.c index 31af59891..8722fcdbe 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -23372,6 +23372,181 @@ BUILDIN(navigateto) #endif } +bool rodex_sendmail_sub(struct script_state* st, struct rodex_message *msg) +{ + const char *sender_name, *title, *body; + + if (!strcmp(script->getfuncname(st), "rodex_sendmail_acc2")) + msg->receiver_accountid = script_getnum(st, 2); + else + msg->receiver_id = script_getnum(st, 2); + + sender_name = script_getstr(st, 3); + if (strlen(sender_name) >= NAME_LENGTH) { + ShowError("script:rodex_sendmail: Sender name must not be bigger than %d!\n", NAME_LENGTH - 1); + return false; + } + safestrncpy(msg->sender_name, sender_name, NAME_LENGTH); + + title = script_getstr(st, 4); + if (strlen(title) >= RODEX_TITLE_LENGTH) { + ShowError("script:rodex_sendmail: Mail Title must not be bigger than %d!\n", RODEX_TITLE_LENGTH - 1); + return false; + } + safestrncpy(msg->title, title, RODEX_TITLE_LENGTH); + + body = script_getstr(st, 5); + if (strlen(body) >= MAIL_BODY_LENGTH) { + ShowError("script:rodex_sendmail: Mail Message must not be bigger than %d!\n", RODEX_BODY_LENGTH - 1); + return false; + } + safestrncpy(msg->body, body, MAIL_BODY_LENGTH); + + if (script_hasdata(st, 6)) { + msg->zeny = script_getnum(st, 6); + if (msg->zeny < 0 || msg->zeny > MAX_ZENY) { + ShowError("script:rodex_sendmail: Invalid Zeny value %"PRId64"!\n", msg->zeny); + return false; + } + } + + return true; +} + +BUILDIN(rodex_sendmail) +{ + struct rodex_message msg = { 0 }; + int item_count = 0, i = 0, param = 7; + + // Common parameters - sender/message/zeny + if (rodex_sendmail_sub(st, &msg) == false) + return false; + + // Item list + while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) { + struct item_data *idata; + + if (!script_hasdata(st, param + 1)) { + ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1)); + return false; + } + + ++item_count; + if (data_isstring(script_getdata(st, param)) == false) { + int itemid = script_getnum(st, param); + + if (itemdb->exists(itemid) == false) { + ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid); + return false; + } + + idata = itemdb->search(itemid); + } + else { + ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1)); + return false; + } + + msg.items[i].item.nameid = idata->nameid; + msg.items[i].item.amount = script_getnum(st, (param + 1)); + msg.items[i].item.identify = 1; + + ++i; + param += 2; + } + msg.items_count = item_count; + + msg.type = MAIL_TYPE_NPC; + if (msg.zeny > 0) + msg.type |= MAIL_TYPE_ZENY; + if (msg.items_count > 0) + msg.type |= MAIL_TYPE_ITEM; + msg.send_date = (int)time(NULL); + msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; + + intif->rodex_sendmail(&msg); + + return true; +} + +BUILDIN(rodex_sendmail2) +{ + struct rodex_message msg = { 0 }; + int item_count = 0, i = 0, param = 7; + + // Common parameters - sender/message/zeny + if (rodex_sendmail_sub(st, &msg) == false) + return false; + + // Item list + while (i < RODEX_MAX_ITEM && script_hasdata(st, param)) { + struct item_data *idata; + int j; + + // Tests + if (!script_hasdata(st, param + 1)) { + ShowError("script:rodex_sendmail: Missing Item %d amount!\n", (i + 1)); + return false; + } + if (!script_hasdata(st, param + 2)) { + ShowError("script:rodex_sendmail: Missing Item %d refine!\n", (i + 1)); + return false; + } + if (!script_hasdata(st, param + 3)) { + ShowError("script:rodex_sendmail: Missing Item %d attribute!\n", (i + 1)); + return false; + } + for (j = 0; j < MAX_SLOTS; ++j) { + if (!script_hasdata(st, param + 4 + j)) { + ShowError("script:rodex_sendmail: Missing Item %d card %d!\n", (i + 1), j); + return false; + } + } + + // Set data to message + ++item_count; + if (data_isstring(script_getdata(st, param)) == false) { + int itemid = script_getnum(st, param); + + if (itemdb->exists(itemid) == false) { + ShowError("script:rodex_sendmail: Unknown item ID %d.\n", itemid); + return false; + } + + idata = itemdb->search(itemid); + } else { + ShowError("script:rodex_sendmail: Item %d must be passed as Number.\n", (i + 1)); + return false; + } + + msg.items[i].item.nameid = idata->nameid; + msg.items[i].item.amount = script_getnum(st, (param + 1)); + msg.items[i].item.refine = script_getnum(st, (param + 2)); + msg.items[i].item.attribute = script_getnum(st, (param + 3)); + msg.items[i].item.identify = 1; + + for (j = 0; j < MAX_SLOTS; ++j) { + msg.items[i].item.card[j] = script_getnum(st, param + 4 + j); + } + + ++i; + param += 4 + MAX_SLOTS; + } + msg.items_count = item_count; + + msg.type = MAIL_TYPE_NPC; + if (msg.zeny > 0) + msg.type |= MAIL_TYPE_ZENY; + if (msg.items_count > 0) + msg.type |= MAIL_TYPE_ITEM; + msg.send_date = (int)time(NULL); + msg.expire_date = (int)time(NULL) + RODEX_EXPIRE; + + intif->rodex_sendmail(&msg); + + return true; +} + /** * Adds a built-in script function. * @@ -24071,6 +24246,12 @@ void script_parse_builtin(void) { BUILDIN_DEF(showscript, "s?"), BUILDIN_DEF(mergeitem,""), BUILDIN_DEF(getcalendartime, "ii??"), + + // -- RoDEX + BUILDIN_DEF(rodex_sendmail, "isss???????????"), + BUILDIN_DEF2(rodex_sendmail, "rodex_sendmail_acc", "isss???????????"), + BUILDIN_DEF(rodex_sendmail2, "isss?????????????????????????????????????????"), + BUILDIN_DEF2(rodex_sendmail2, "rodex_sendmail_acc2", "isss?????????????????????????????????????????"), BUILDIN_DEF(_,"s"), BUILDIN_DEF2(_, "_$", "s"), }; diff --git a/src/map/status.c b/src/map/status.c index 166b33e6f..c98857736 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1019,6 +1019,9 @@ void initChangeTables(void) // Summoner status->dbs->IconChangeTable[SC_SPRITEMABLE] = SI_SPRITEMABLE; + // RoDEX + status->dbs->IconChangeTable[SC_DAILYSENDMAILCNT] = SI_DAILYSENDMAILCNT; + // Other SC which are not necessarily associated to skills. status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION1] |= SCB_ASPD; status->dbs->ChangeFlagTable[SC_ATTHASTE_POTION2] |= SCB_ASPD; @@ -8246,6 +8249,7 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t case SC_TURNKICK_READY: case SC_DODGE_READY: case SC_PUSH_CART: + case SC_DAILYSENDMAILCNT: tick = INFINITE_DURATION; break; @@ -9865,6 +9869,9 @@ int status_get_val_flag(enum sc_type type) case SC_OVERLAPEXPUP: val_flag |= 1; break; + case SC_DAILYSENDMAILCNT: + val_flag |= 1 | 2; + break; } return val_flag; } diff --git a/src/map/status.h b/src/map/status.h index 3c0a87175..0482c7cbd 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -842,6 +842,9 @@ typedef enum sc_type { SC_FRESHSHRIMP, SC_DRESS_UP, + + // Rodex + SC_DAILYSENDMAILCNT = 824, #ifndef SC_MAX SC_MAX, //Automatically updated max, used in for's to check we are within bounds. #endif @@ -1691,7 +1694,7 @@ enum si_type { //SI_SHOW_NPCHPBAR = 821, SI_FLOWERSMOKE = 822, SI_FSTONE = 823, - //SI_DAILYSENDMAILCNT = 824, + SI_DAILYSENDMAILCNT = 824, //SI_QSCARABA = 825, SI_LJOSALFAR = 826, //SI_PAD_READER_KNIGHT = 827, -- cgit v1.2.3-60-g2f50