Basic RODEX support (#2186)
Adds basic support for the new mail UI RODEX. Fixes #1567 Thanks to @RagnarokNova, @Atemo, @aleos89 and everyone else that helped me with this. Additionally I would like to thank @jezznar and @hazimjauhari90 for their good testing in the pull request.
This commit is contained in:
parent
76786e2977
commit
58776da1ac
@ -134,6 +134,29 @@ cashshop_show_points: no
|
||||
// 2 = Yes, when there are unread mails
|
||||
mail_show_status: 0
|
||||
|
||||
// Amount of mails a user can send a day.
|
||||
// Default: 100
|
||||
// 0 = Unlimited
|
||||
mail_daily_count: 100
|
||||
|
||||
// Fee for transferring zeny via mail (Note 2)
|
||||
// NOTE: this rate is hardcoded in the client, you need to diff your client accordingly if you change this value.
|
||||
// Default: 2(2%)
|
||||
// 0 = No fee
|
||||
mail_zeny_fee: 2
|
||||
|
||||
// Amount of zeny to pay for each attached item
|
||||
// NOTE: this fee is hardcoded in the client, you need to diff your client accordingly if you change this value.
|
||||
// Default: 2500
|
||||
// 0 = No fee
|
||||
mail_attachment_price: 2500
|
||||
|
||||
// Maximum weight in total that can be attached to a mail
|
||||
// NOTE: this limit is hardcoded in the client, you need to diff your client accordingly if you change this value.
|
||||
// Default: 2000
|
||||
// 0 = No weight limit
|
||||
mail_attachment_weight: 2000
|
||||
|
||||
// Is monster transformation disabled during Guild Wars?
|
||||
// If set to yes, monster transforming is automatically removed/disabled when enterting castles during WoE times
|
||||
mon_trans_disable_in_gvg: no
|
||||
|
@ -261,4 +261,19 @@ default_map_y: 191
|
||||
// Default: 14
|
||||
clan_remove_inactive_days: 14
|
||||
|
||||
//===================================
|
||||
// RODEX
|
||||
//===================================
|
||||
// After how many days should mails be returned to their sender?
|
||||
// 0: never return them
|
||||
// X: return them after X days
|
||||
// Default: 14
|
||||
mail_return_days: 14
|
||||
|
||||
// How many days after a mail was returned to it's sender should it be deleted completely?
|
||||
// 0: never delete them
|
||||
// X: delete them X days after they were returned
|
||||
// Default: 14
|
||||
mail_delete_days: 14
|
||||
|
||||
import: conf/import/char_conf.txt
|
||||
|
@ -108,6 +108,7 @@ party_db: party
|
||||
pet_db: pet
|
||||
friend_db: friends
|
||||
mail_db: mail
|
||||
mail_attachment_db: mail_attachments
|
||||
auction_db: auction
|
||||
quest_db: quest
|
||||
homunculus_db: homunculus
|
||||
|
@ -2497,31 +2497,31 @@ packet_keys: 0x62C86D09,0x75944F17,0x112C133D // [YomRawr]
|
||||
|
||||
// RODEX Mail system
|
||||
0x09E7,3 // ZC_NOTIFY_UNREADMAIL
|
||||
0x09E8,11,dull,0 // CZ_OPEN_MAILBOX
|
||||
0x09E8,11,mailrefresh,2:3 // CZ_OPEN_MAILBOX
|
||||
0x09E9,2,dull,0 // CZ_CLOSE_MAILBOX
|
||||
0x09EA,11,dull,0 // CZ_REQ_READ_MAIL
|
||||
0x09EA,11,mailread,2:3 // CZ_REQ_READ_MAIL
|
||||
0x09EB,-1 // ZC_ACK_READ_MAIL
|
||||
0x09EC,-1,dull,0 // CZ_REQ_WRITE_MAIL
|
||||
0x09EC,-1,mailsend,2:4:28:52:60:62:64 // CZ_REQ_WRITE_MAIL
|
||||
0x09ED,3 // ZC_ACK_WRITE_MAIL
|
||||
0x09EE,11,dull,0 // CZ_REQ_NEXT_MAIL_LIST
|
||||
0x09EF,11,dull,0 // CZ_REQ_REFRESH_MAIL_LIST
|
||||
0x09EE,11,mailrefresh,2:3 // CZ_REQ_NEXT_MAIL_LIST
|
||||
0x09EF,11,mailrefresh,2:3 // CZ_REQ_REFRESH_MAIL_LIST
|
||||
0x09F0,-1 // ZC_ACK_MAIL_LIST
|
||||
0x09F1,11,dull,0 // CZ_REQ_ZENY_FROM_MAIL
|
||||
0x09F1,11,mailgetattach,0 // CZ_REQ_ZENY_FROM_MAIL
|
||||
0x09F2,12 // ZC_ACK_ZENY_FROM_MAIL
|
||||
0x09F3,11,dull,0 // CZ_REQ_ITEM_FROM_MAIL
|
||||
0x09F3,11,mailgetattach,0 // CZ_REQ_ITEM_FROM_MAIL
|
||||
0x09F4,12 // ZC_ACK_ITEM_FROM_MAIL
|
||||
0x09F5,11,dull,0 // CZ_REQ_DELETE_MAIL
|
||||
0x09F5,11,maildelete,0 // CZ_REQ_DELETE_MAIL
|
||||
0x09F6,11 // ZC_ACK_DELETE_MAIL
|
||||
0x0A03,2,dull,0 // CZ_REQ_CANCEL_WRITE_MAIL
|
||||
0x0A04,6,dull,0 // CZ_REQ_ADD_ITEM_TO_MAIL
|
||||
0x0A03,2,mailcancel,0 // CZ_REQ_CANCEL_WRITE_MAIL
|
||||
0x0A04,6,mailsetattach,2:4 // CZ_REQ_ADD_ITEM_TO_MAIL
|
||||
0x0A05,53 // ZC_ACK_ADD_ITEM_TO_MAIL
|
||||
0x0A06,6,dull,0 // CZ_REQ_REMOVE_ITEM_MAIL
|
||||
0x0A06,6,mailwinopen,2:4 // CZ_REQ_REMOVE_ITEM_MAIL
|
||||
0x0A07,9 // ZC_ACK_REMOVE_ITEM_MAIL
|
||||
0x0A08,26,dull,0 // CZ_REQ_OPEN_WRITE_MAIL
|
||||
0x0A08,26,mailbegin,0 // CZ_REQ_OPEN_WRITE_MAIL
|
||||
0x0A12,27 // ZC_ACK_OPEN_WRITE_MAIL
|
||||
0x0A32,2 // ZC_OPEN_RODEX_THROUGH_NPC_ONLY
|
||||
0x0A13,26,dull,0 // CZ_CHECK_RECEIVE_CHARACTER_NAME
|
||||
0x0A13,26,mailreceiver,2 // CZ_CHECK_RECEIVE_CHARACTER_NAME
|
||||
0x0A14,10 // ZC_CHECK_RECEIVE_CHARACTER_NAME
|
||||
0x0A32,2 // ZC_OPEN_RODEX_THROUGH_NPC_ONLY
|
||||
|
||||
// New EquipPackets Support
|
||||
0x0A09,45 // ZC_ADD_EXCHANGE_ITEM3
|
||||
@ -2657,6 +2657,15 @@ packet_keys: 0x4C17382A,0x7ED174C9,0x29961E4F // [Winnie]
|
||||
0x088D,5,hommenu,2:4
|
||||
0x0940,36,storagepassword,2:4:20
|
||||
|
||||
// 2016-03-02bRagexe
|
||||
0x0A51,34
|
||||
|
||||
// 2016-03-30aRagexe
|
||||
0x0A6E,-1,mailsend,2:4:28:52:60:62:64:68 // CZ_REQ_WRITE_MAIL2
|
||||
|
||||
// 2016-06-01aRagexe
|
||||
0x0A7D,-1
|
||||
|
||||
// 2017-05-24aRagexeRE
|
||||
0x0A43,85
|
||||
0x0A44,-1
|
||||
|
@ -1006,13 +1006,14 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
|
||||
0x3048
|
||||
Type: ZI
|
||||
Structure: <cmd>.W <cid>.L <flag>.B
|
||||
index: 0,2,6
|
||||
len: 7
|
||||
Structure: <cmd>.W <cid>.L <flag>.B <mail_type>.B
|
||||
index: 0,2,6,7
|
||||
len: 8
|
||||
parameter:
|
||||
- cmd : packet identification (0x3048)
|
||||
- cid
|
||||
- flag
|
||||
- mail_type
|
||||
desc:
|
||||
- Inbox request
|
||||
|
||||
@ -1029,13 +1030,14 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
|
||||
0x304a
|
||||
Type: ZI
|
||||
Structure: <cmd>.W <cid>.L <mail_id>.L
|
||||
index: 0,2,6
|
||||
len: 10
|
||||
Structure: <cmd>.W <cid>.L <mail_id>.L <attachment_type>.B
|
||||
index: 0,2,6,10
|
||||
len: 11
|
||||
parameter:
|
||||
- cmd : packet identification (0x304a)
|
||||
- cid
|
||||
- mail_id
|
||||
- attachment_type
|
||||
desc:
|
||||
- Mail get attachment
|
||||
|
||||
@ -1076,6 +1078,18 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
desc:
|
||||
- Mail send
|
||||
|
||||
0x304e
|
||||
Type: ZI
|
||||
Structure: <cmd>.W <cid>.L <name>.24B
|
||||
index: 0,2,6
|
||||
len: 30
|
||||
parameter:
|
||||
- cmd : packet identification (0x304e)
|
||||
- cid
|
||||
- name
|
||||
desc:
|
||||
- Checks if a character with the given name exists.
|
||||
|
||||
0x3050
|
||||
Type: ZI
|
||||
Structure: <cmd>.W <len>.W <cid>.L <type>.W <price>.L <page>.W <searchtext>.?B
|
||||
@ -1966,14 +1980,15 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
|
||||
0x3848
|
||||
Type: IZ
|
||||
Structure: <cmd>.W <size>.W <char_id>.L <flag>.B <md>.?B
|
||||
index: 0,2,4,8,9
|
||||
len: variable: 9+md
|
||||
Structure: <cmd>.W <size>.W <char_id>.L <flag>.B <mail_type>.B <md>.?B
|
||||
index: 0,2,4,8,9,10
|
||||
len: variable: 10+md
|
||||
parameter:
|
||||
- cmd : packet identification (0x3848)
|
||||
- size
|
||||
- char_id
|
||||
- flag
|
||||
- mail_type
|
||||
- md : Mail
|
||||
desc:
|
||||
- A player request for mail inbox
|
||||
@ -2044,6 +2059,21 @@ Currently the max packet size is 0xFFFF (see 'WFIFOSET()' in 'src/common/socket.
|
||||
desc:
|
||||
- Mail sent status (to player if the sender is player and online)
|
||||
|
||||
0x384e
|
||||
Type: IZ
|
||||
Structure: <cmd>.W <cid_sender>.L <cid_receiver>.L <class>.W <level>.W <name>.24B
|
||||
index: 0,2,6,10,12,14
|
||||
len: 38
|
||||
parameter:
|
||||
- cmd : packet identification (0x384e)
|
||||
- cid_sender
|
||||
- cid_receiver
|
||||
- class
|
||||
- level
|
||||
- name
|
||||
desc:
|
||||
- Mail receiver's character data(character id, job, level and name)
|
||||
|
||||
0x3850
|
||||
Type: IZ
|
||||
Structure: <cmd>.W <size>.W <char_id>.L <count>.W <pages>.W <auction_data>.?B
|
||||
|
@ -6097,6 +6097,12 @@ while hidden then revealing.... you can wonder around =P
|
||||
|
||||
---------------------------------------
|
||||
|
||||
*unloadnpc "<NPC object name>";
|
||||
|
||||
This command will fully unload a NPC object and all of it's duplicates.
|
||||
|
||||
---------------------------------------
|
||||
|
||||
*doevent "<NPC object name>::<event label>";
|
||||
|
||||
This command will start a new execution thread in a specified NPC object at the
|
||||
|
@ -137,3 +137,14 @@ rachel,122,146,0 duplicate(MailBox) Post Box#ra 888
|
||||
// Veins
|
||||
//============================================================
|
||||
veins,218,123,0 duplicate(MailBox) Post Box#ve 888
|
||||
|
||||
// RODEX makes these NPCs useless
|
||||
- script RodexMailBoxInit -1,{
|
||||
end;
|
||||
|
||||
OnInit:
|
||||
if( PACKETVER >= 20150513 ){
|
||||
unloadnpc "MailBox";
|
||||
}
|
||||
end;
|
||||
}
|
||||
|
@ -716,35 +716,46 @@ CREATE TABLE IF NOT EXISTS `mail` (
|
||||
`time` int(11) unsigned NOT NULL default '0',
|
||||
`status` tinyint(2) NOT NULL default '0',
|
||||
`zeny` int(11) unsigned NOT NULL default '0',
|
||||
`nameid` smallint(5) unsigned NOT NULL default '0',
|
||||
`amount` int(11) unsigned NOT NULL default '0',
|
||||
`refine` tinyint(3) unsigned NOT NULL default '0',
|
||||
`attribute` tinyint(4) unsigned NOT NULL default '0',
|
||||
`identify` smallint(6) NOT NULL default '0',
|
||||
`card0` smallint(5) unsigned NOT NULL default '0',
|
||||
`card1` smallint(5) unsigned NOT NULL default '0',
|
||||
`card2` smallint(5) unsigned NOT NULL default '0',
|
||||
`card3` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_id0` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_val0` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_parm0` tinyint(3) unsigned NOT NULL default '0',
|
||||
`option_id1` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_val1` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_parm1` tinyint(3) unsigned NOT NULL default '0',
|
||||
`option_id2` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_val2` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_parm2` tinyint(3) unsigned NOT NULL default '0',
|
||||
`option_id3` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_val3` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_parm3` tinyint(3) unsigned NOT NULL default '0',
|
||||
`option_id4` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_val4` smallint(5) unsigned NOT NULL default '0',
|
||||
`option_parm4` tinyint(3) unsigned NOT NULL default '0',
|
||||
`unique_id` bigint(20) unsigned NOT NULL default '0',
|
||||
`bound` tinyint(1) unsigned NOT NULL default '0',
|
||||
`type` smallint(5) NOT NULL default '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=MyISAM;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for `mail_attachments`
|
||||
-- ----------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mail_attachments` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`index` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`nameid` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`amount` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`refine` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`attribute` tinyint(4) unsigned NOT NULL DEFAULT '0',
|
||||
`identify` smallint(6) NOT NULL DEFAULT '0',
|
||||
`card0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm0` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm1` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm2` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm3` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id4` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val4` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm4` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`unique_id` bigint(20) unsigned NOT NULL DEFAULT '0',
|
||||
`bound` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`,`index`)
|
||||
) ENGINE=MyISAM;
|
||||
|
||||
--
|
||||
-- Table structure for table `mapreg`
|
||||
--
|
||||
|
76
sql-files/upgrades/upgrade_20170620.sql
Normal file
76
sql-files/upgrades/upgrade_20170620.sql
Normal file
@ -0,0 +1,76 @@
|
||||
-- ----------------------------
|
||||
-- Table structure for `mail_attachments`
|
||||
-- ----------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mail_attachments` (
|
||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`index` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`nameid` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`amount` int(11) unsigned NOT NULL DEFAULT '0',
|
||||
`refine` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`attribute` tinyint(4) unsigned NOT NULL DEFAULT '0',
|
||||
`identify` smallint(6) NOT NULL DEFAULT '0',
|
||||
`card0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`card3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val0` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm0` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val1` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm1` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val2` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm2` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val3` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm3` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`option_id4` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_val4` smallint(5) unsigned NOT NULL DEFAULT '0',
|
||||
`option_parm4` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`unique_id` bigint(20) unsigned NOT NULL DEFAULT '0',
|
||||
`bound` tinyint(1) unsigned NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`,`index`)
|
||||
) ENGINE=MyISAM;
|
||||
|
||||
insert into `mail_attachments`
|
||||
(`id`,`index`,`nameid`,`amount`,`refine`,`attribute`,`identify`,`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`unique_id`,`bound`)
|
||||
select
|
||||
`id`,'0',`nameid`,`amount`,`refine`,`attribute`,`identify`,`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`unique_id`,`bound`
|
||||
from `mail`
|
||||
where `nameid` <> 0;
|
||||
|
||||
alter table `mail`
|
||||
drop column `nameid`,
|
||||
drop column `amount`,
|
||||
drop column `refine`,
|
||||
drop column `attribute`,
|
||||
drop column `identify`,
|
||||
drop column `card0`,
|
||||
drop column `card1`,
|
||||
drop column `card2`,
|
||||
drop column `card3`,
|
||||
drop column `option_id0`,
|
||||
drop column `option_val0`,
|
||||
drop column `option_parm0`,
|
||||
drop column `option_id1`,
|
||||
drop column `option_val1`,
|
||||
drop column `option_parm1`,
|
||||
drop column `option_id2`,
|
||||
drop column `option_val2`,
|
||||
drop column `option_parm2`,
|
||||
drop column `option_id3`,
|
||||
drop column `option_val3`,
|
||||
drop column `option_parm3`,
|
||||
drop column `option_id4`,
|
||||
drop column `option_val4`,
|
||||
drop column `option_parm4`,
|
||||
drop column `unique_id`,
|
||||
drop column `bound`;
|
||||
|
||||
alter table `mail`
|
||||
modify `message` varchar(500) NOT NULL default '';
|
||||
|
||||
ALTER TABLE `mail`
|
||||
ADD COLUMN `type` smallint(5) NOT NULL default '0';
|
@ -20,6 +20,7 @@
|
||||
#include "../common/cli.h"
|
||||
#include "int_guild.h"
|
||||
#include "int_homun.h"
|
||||
#include "int_mail.h"
|
||||
#include "int_mercenary.h"
|
||||
#include "int_elemental.h"
|
||||
#include "int_party.h"
|
||||
@ -1649,6 +1650,10 @@ int char_delete_char_sql(uint32 char_id){
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `char_id`='%d'", schema_config.skill_db, char_id) )
|
||||
Sql_ShowDebug(sql_handle);
|
||||
|
||||
/* delete mail attachments (only received) */
|
||||
if (SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` IN ( SELECT `id` FROM `%s` WHERE `dest_id`='%d' )", schema_config.mail_attachment_db, schema_config.mail_db, char_id))
|
||||
Sql_ShowDebug(sql_handle);
|
||||
|
||||
/* delete mails (only received) */
|
||||
if (SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `dest_id`='%d'", schema_config.mail_db, char_id))
|
||||
Sql_ShowDebug(sql_handle);
|
||||
@ -2228,7 +2233,7 @@ bool char_checkdb(void){
|
||||
schema_config.auction_db, schema_config.quest_db, schema_config.homunculus_db, schema_config.skill_homunculus_db,
|
||||
schema_config.mercenary_db, schema_config.mercenary_owner_db,
|
||||
schema_config.elemental_db, schema_config.ragsrvinfo_db, schema_config.skillcooldown_db, schema_config.bonus_script_db,
|
||||
schema_config.clan_table, schema_config.clan_alliance_table
|
||||
schema_config.clan_table, schema_config.clan_alliance_table, schema_config.mail_attachment_db
|
||||
};
|
||||
ShowInfo("Start checking DB integrity\n");
|
||||
for (i=0; i<ARRAYLENGTH(sqltable); i++){ //check if they all exist and we can acces them in sql-server
|
||||
@ -2365,12 +2370,19 @@ bool char_checkdb(void){
|
||||
}
|
||||
//checking mail_db
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,"
|
||||
"`title`,`message`,`time`,`status`,`zeny`,`nameid`,`amount`,`refine`,`attribute`,`identify`,"
|
||||
"`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`unique_id`, `bound`"
|
||||
"`title`,`message`,`time`,`status`,`zeny`,`type`"
|
||||
" FROM `%s` LIMIT 1;", schema_config.mail_db) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
return false;
|
||||
}
|
||||
//checking mail_attachment_db
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`index`,`nameid`,`amount`,`refine`,`attribute`,`identify`,"
|
||||
"`card0`,`card1`,`card2`,`card3`,`option_id0`,`option_val0`,`option_parm0`,`option_id1`,`option_val1`,`option_parm1`,`option_id2`,`option_val2`,`option_parm2`,`option_id3`,`option_val3`,`option_parm3`,`option_id4`,`option_val4`,`option_parm4`,`unique_id`, `bound`"
|
||||
" FROM `%s` LIMIT 1;", schema_config.mail_attachment_db) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
//checking auction_db
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `auction_id`,`seller_id`,`seller_name`,`buyer_id`,`buyer_name`,"
|
||||
"`price`,`buynow`,`hours`,`timestamp`,`nameid`,`item_name`,`type`,`refine`,`attribute`,`card0`,`card1`,"
|
||||
@ -2522,6 +2534,8 @@ void char_sql_config_read(const char* cfgName) {
|
||||
safestrncpy(schema_config.pet_db, w2, sizeof(schema_config.pet_db));
|
||||
else if(!strcmpi(w1,"mail_db"))
|
||||
safestrncpy(schema_config.mail_db, w2, sizeof(schema_config.mail_db));
|
||||
else if (!strcmpi(w1, "mail_attachment_db"))
|
||||
safestrncpy(schema_config.mail_attachment_db, w2, sizeof(schema_config.mail_attachment_db));
|
||||
else if(!strcmpi(w1,"auction_db"))
|
||||
safestrncpy(schema_config.auction_db, w2, sizeof(schema_config.auction_db));
|
||||
else if(!strcmpi(w1,"friend_db"))
|
||||
@ -2699,6 +2713,8 @@ void char_set_defaults(){
|
||||
charserv_config.default_map_y = 191;
|
||||
|
||||
charserv_config.clan_remove_inactive_days = 14;
|
||||
charserv_config.mail_return_days = 14;
|
||||
charserv_config.mail_delete_days = 14;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2973,6 +2989,10 @@ bool char_config_read(const char* cfgName, bool normal){
|
||||
charserv_config.default_map_y = atoi(w2);
|
||||
} else if (strcmpi(w1, "clan_remove_inactive_days") == 0) {
|
||||
charserv_config.clan_remove_inactive_days = atoi(w2);
|
||||
} else if (strcmpi(w1, "mail_return_days") == 0) {
|
||||
charserv_config.mail_return_days = atoi(w2);
|
||||
} else if (strcmpi(w1, "mail_delete_days") == 0) {
|
||||
charserv_config.mail_delete_days = atoi(w2);
|
||||
} else if (strcmpi(w1, "import") == 0) {
|
||||
char_config_read(w2, normal);
|
||||
}
|
||||
@ -3147,6 +3167,14 @@ int do_init(int argc, char **argv)
|
||||
add_timer_func_list(char_clan_member_cleanup, "clan_member_cleanup");
|
||||
add_timer_interval(gettick() + 1000, char_clan_member_cleanup, 0, 0, 60 * 60 * 1000); // every 60 minutes
|
||||
|
||||
// periodically check if mails need to be returned to their sender
|
||||
add_timer_func_list(mail_return_timer, "mail_return_timer");
|
||||
add_timer_interval(gettick() + 1000, mail_return_timer, 0, 0, 5 * 60 * 1000); // every 5 minutes
|
||||
|
||||
// periodically check if mails need to be deleted completely
|
||||
add_timer_func_list(mail_delete_timer, "mail_delete_timer");
|
||||
add_timer_interval(gettick() + 1000, mail_delete_timer, 0, 0, 5 * 60 * 1000); // every 5 minutes
|
||||
|
||||
//check db tables
|
||||
if(charserv_config.char_check_db && char_checkdb() == 0){
|
||||
ShowFatalError("char : A tables is missing in sql-server, please fix it, see (sql-files main.sql for structure) \n");
|
||||
|
@ -58,6 +58,7 @@ struct Schema_Config {
|
||||
char party_db[DB_NAME_LEN];
|
||||
char pet_db[DB_NAME_LEN];
|
||||
char mail_db[DB_NAME_LEN]; // MAIL SYSTEM
|
||||
char mail_attachment_db[DB_NAME_LEN];
|
||||
char auction_db[DB_NAME_LEN]; // Auctions System
|
||||
char friend_db[DB_NAME_LEN];
|
||||
char hotkey_db[DB_NAME_LEN];
|
||||
@ -167,6 +168,8 @@ struct CharServ_Config {
|
||||
unsigned short default_map_y;
|
||||
|
||||
int clan_remove_inactive_days;
|
||||
int mail_return_days;
|
||||
int mail_delete_days;
|
||||
};
|
||||
extern struct CharServ_Config charserv_config;
|
||||
|
||||
|
@ -154,12 +154,12 @@ static int auction_end_timer(int tid, unsigned int tick, int id, intptr_t data)
|
||||
{
|
||||
if( auction->buyer_id )
|
||||
{
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(202), 0, &auction->item);
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(202), 0, &auction->item, 1);
|
||||
mapif_Auction_message(auction->buyer_id, 6); // You have won the auction
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(203), auction->price, NULL);
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(203), auction->price, NULL, 0);
|
||||
}
|
||||
else
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(204), 0, &auction->item);
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(204), 0, &auction->item, 1);
|
||||
|
||||
ShowInfo("Auction End: id %u.\n", auction->auction_id);
|
||||
|
||||
@ -376,7 +376,7 @@ static void mapif_parse_Auction_cancel(int fd)
|
||||
return;
|
||||
}
|
||||
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(205), 0, &auction->item);
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(205), 0, &auction->item, 1);
|
||||
auction_delete(auction);
|
||||
|
||||
mapif_Auction_cancel(fd, char_id, 0); // The auction has been canceled
|
||||
@ -415,9 +415,9 @@ static void mapif_parse_Auction_close(int fd)
|
||||
}
|
||||
|
||||
// Send Money to Seller
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(206), auction->price, NULL);
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(206), auction->price, NULL, 0);
|
||||
// Send Item to Buyer
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(207), 0, &auction->item);
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(207), 0, &auction->item, 1);
|
||||
mapif_Auction_message(auction->buyer_id, 6); // You have won the auction
|
||||
auction_delete(auction);
|
||||
|
||||
@ -456,11 +456,11 @@ static void mapif_parse_Auction_bid(int fd)
|
||||
{ // Send Money back to the previous Buyer
|
||||
if( auction->buyer_id != char_id )
|
||||
{
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(208), auction->price, NULL);
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(208), auction->price, NULL, 0);
|
||||
mapif_Auction_message(auction->buyer_id, 7); // You have failed to win the auction
|
||||
}
|
||||
else
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(209), auction->price, NULL);
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(209), auction->price, NULL, 0);
|
||||
}
|
||||
|
||||
auction->buyer_id = char_id;
|
||||
@ -471,9 +471,9 @@ static void mapif_parse_Auction_bid(int fd)
|
||||
{ // Automatic won the auction
|
||||
mapif_Auction_bid(fd, char_id, bid - auction->buynow, 1); // You have successfully bid in the auction
|
||||
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(210), 0, &auction->item);
|
||||
mail_sendmail(0, msg_txt(200), auction->buyer_id, auction->buyer_name, msg_txt(201), msg_txt(210), 0, &auction->item, 1);
|
||||
mapif_Auction_message(char_id, 6); // You have won the auction
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(211), auction->buynow, NULL);
|
||||
mail_sendmail(0, msg_txt(200), auction->seller_id, auction->seller_name, msg_txt(201), msg_txt(211), auction->buynow, NULL, 0);
|
||||
|
||||
auction_delete(auction);
|
||||
return;
|
||||
|
@ -12,87 +12,47 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static bool mail_loadmessage(int mail_id, struct mail_message* msg);
|
||||
static void mapif_Mail_return(int fd, uint32 char_id, int mail_id);
|
||||
static void mapif_Mail_delete(int fd, uint32 char_id, int mail_id, bool deleted);
|
||||
|
||||
static int mail_fromsql(uint32 char_id, struct mail_data* md)
|
||||
{
|
||||
int i, j;
|
||||
struct mail_message *msg;
|
||||
int i;
|
||||
char *data;
|
||||
StringBuf buf;
|
||||
|
||||
memset(md, 0, sizeof(struct mail_data));
|
||||
md->amount = 0;
|
||||
md->full = false;
|
||||
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_AppendStr(&buf, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,"
|
||||
"`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`,`bound`");
|
||||
for (i = 0; i < MAX_SLOTS; i++)
|
||||
StringBuf_Printf(&buf, ",`card%d`", i);
|
||||
for (i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d`", i);
|
||||
StringBuf_Printf(&buf, ", `option_val%d`", i);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d`", i);
|
||||
}
|
||||
// I keep the `status` < 3 just in case someone forget to apply the sqlfix
|
||||
StringBuf_Printf(&buf, " FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d",
|
||||
schema_config.mail_db, char_id, MAIL_MAX_INBOX + 1);
|
||||
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
|
||||
// First we prefill the msg ids
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id` FROM `%s` WHERE `dest_id`='%d' AND `status` < 3 ORDER BY `id` LIMIT %d", schema_config.mail_db, char_id, MAIL_MAX_INBOX + 1) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
|
||||
StringBuf_Destroy(&buf);
|
||||
|
||||
for (i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i )
|
||||
{
|
||||
struct item *item;
|
||||
|
||||
msg = &md->msg[i];
|
||||
Sql_GetData(sql_handle, 0, &data, NULL); msg->id = atoi(data);
|
||||
Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(msg->send_name, data, NAME_LENGTH);
|
||||
Sql_GetData(sql_handle, 2, &data, NULL); msg->send_id = strtoul(data, NULL, 10);
|
||||
Sql_GetData(sql_handle, 3, &data, NULL); safestrncpy(msg->dest_name, data, NAME_LENGTH);
|
||||
Sql_GetData(sql_handle, 4, &data, NULL); msg->dest_id = strtoul(data, NULL, 10);
|
||||
Sql_GetData(sql_handle, 5, &data, NULL); safestrncpy(msg->title, data, MAIL_TITLE_LENGTH);
|
||||
Sql_GetData(sql_handle, 6, &data, NULL); safestrncpy(msg->body, data, MAIL_BODY_LENGTH);
|
||||
Sql_GetData(sql_handle, 7, &data, NULL); msg->timestamp = atoi(data); //strtoull ?
|
||||
Sql_GetData(sql_handle, 8, &data, NULL); msg->status = (mail_status)atoi(data);
|
||||
Sql_GetData(sql_handle, 9, &data, NULL); msg->zeny = strtoul(data, NULL, 10);
|
||||
item = &msg->item;
|
||||
Sql_GetData(sql_handle,10, &data, NULL); item->amount = (short)atoi(data);
|
||||
Sql_GetData(sql_handle,11, &data, NULL); item->nameid = atoi(data);
|
||||
Sql_GetData(sql_handle,12, &data, NULL); item->refine = atoi(data);
|
||||
Sql_GetData(sql_handle,13, &data, NULL); item->attribute = atoi(data);
|
||||
Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data);
|
||||
Sql_GetData(sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
|
||||
Sql_GetData(sql_handle,16, &data, NULL); item->bound = atoi(data);
|
||||
item->expire_time = 0;
|
||||
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
{
|
||||
Sql_GetData(sql_handle, 17 + j, &data, NULL);
|
||||
item->card[j] = atoi(data);
|
||||
}
|
||||
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; j++) {
|
||||
Sql_GetData(sql_handle, 17 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
item->option[j].id = atoi(data);
|
||||
Sql_GetData(sql_handle, 18 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
item->option[j].value = atoi(data);
|
||||
Sql_GetData(sql_handle, 19 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
item->option[j].param = atoi(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
md->full = ( Sql_NumRows(sql_handle) > MAIL_MAX_INBOX );
|
||||
md->full = (Sql_NumRows(sql_handle) > MAIL_MAX_INBOX);
|
||||
|
||||
for( i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
|
||||
Sql_GetData(sql_handle, 0, &data, NULL); md->msg[i].id = atoi(data);
|
||||
}
|
||||
|
||||
md->amount = i;
|
||||
Sql_FreeResult(sql_handle);
|
||||
|
||||
// Now we load them
|
||||
for( i = 0; i < md->amount; i++ ){
|
||||
if( !mail_loadmessage( md->msg[i].id, &md->msg[i] ) ){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
md->unchecked = 0;
|
||||
md->unread = 0;
|
||||
for (i = 0; i < md->amount; i++)
|
||||
{
|
||||
msg = &md->msg[i];
|
||||
struct mail_message *msg = &md->msg[i];
|
||||
|
||||
if( msg->status == MAIL_NEW )
|
||||
{
|
||||
if ( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `status` = '%d' WHERE `id` = '%d'", schema_config.mail_db, MAIL_UNREAD, msg->id) )
|
||||
@ -115,27 +75,13 @@ int mail_savemessage(struct mail_message* msg)
|
||||
{
|
||||
StringBuf buf;
|
||||
SqlStmt* stmt;
|
||||
int j;
|
||||
int i, j;
|
||||
bool found = false;
|
||||
|
||||
// build message save query
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`, `bound`", schema_config.mail_db);
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
StringBuf_Printf(&buf, ", `card%d`", j);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_val%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d`", j);
|
||||
}
|
||||
StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d', '%hu', '%d', '%d', '%d', '%"PRIu64"', '%d'",
|
||||
msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->item.amount, msg->item.nameid, msg->item.refine, msg->item.attribute, msg->item.identify, msg->item.unique_id, msg->item.bound);
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
StringBuf_Printf(&buf, ", '%hu'", msg->item.card[j]);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item.option[j].id);
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item.option[j].value);
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item.option[j].param);
|
||||
}
|
||||
StringBuf_Printf(&buf, "INSERT INTO `%s` (`send_name`, `send_id`, `dest_name`, `dest_id`, `title`, `message`, `time`, `status`, `zeny`,`type`", schema_config.mail_db);
|
||||
StringBuf_Printf(&buf, ") VALUES (?, '%d', ?, '%d', ?, ?, '%lu', '%d', '%d', '%d'", msg->send_id, msg->dest_id, (unsigned long)msg->timestamp, msg->status, msg->zeny, msg->type);
|
||||
StringBuf_AppendStr(&buf, ")");
|
||||
|
||||
// prepare and execute query
|
||||
@ -148,11 +94,50 @@ int mail_savemessage(struct mail_message* msg)
|
||||
|| SQL_SUCCESS != SqlStmt_Execute(stmt) )
|
||||
{
|
||||
SqlStmt_ShowDebug(stmt);
|
||||
msg->id = 0;
|
||||
StringBuf_Destroy(&buf);
|
||||
return msg->id = 0;
|
||||
} else
|
||||
msg->id = (int)SqlStmt_LastInsertId(stmt);
|
||||
|
||||
SqlStmt_Free(stmt);
|
||||
|
||||
StringBuf_Clear(&buf);
|
||||
StringBuf_Printf(&buf,"INSERT INTO `%s` (`id`, `index`, `amount`, `nameid`, `refine`, `attribute`, `identify`, `unique_id`, `bound`", schema_config.mail_attachment_db);
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
StringBuf_Printf(&buf, ", `card%d`", j);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_val%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d`", j);
|
||||
}
|
||||
StringBuf_AppendStr(&buf, ") VALUES ");
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
// skip empty and already matched entries
|
||||
if( msg->item[i].nameid == 0 )
|
||||
continue;
|
||||
|
||||
if( found ){
|
||||
StringBuf_AppendStr(&buf, ",");
|
||||
}else{
|
||||
found = true;
|
||||
}
|
||||
|
||||
StringBuf_Printf(&buf, "('%"PRIu64"', '%hu', '%d', '%hu', '%d', '%d', '%d', '%"PRIu64"', '%d'", (uint64)msg->id, i, msg->item[i].amount, msg->item[i].nameid, msg->item[i].refine, msg->item[i].attribute, msg->item[i].identify, msg->item[i].unique_id, msg->item[i].bound);
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
StringBuf_Printf(&buf, ", '%hu'", msg->item[i].card[j]);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].id);
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].value);
|
||||
StringBuf_Printf(&buf, ", '%d'", msg->item[i].option[j].param);
|
||||
}
|
||||
StringBuf_AppendStr(&buf, ")");
|
||||
}
|
||||
|
||||
if( found && SQL_ERROR == Sql_QueryStr(sql_handle, StringBuf_Value(&buf)) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
}
|
||||
|
||||
StringBuf_Destroy(&buf);
|
||||
|
||||
return msg->id;
|
||||
@ -162,33 +147,16 @@ int mail_savemessage(struct mail_message* msg)
|
||||
/// Returns true if the operation succeeds (or false if it fails).
|
||||
static bool mail_loadmessage(int mail_id, struct mail_message* msg)
|
||||
{
|
||||
int j;
|
||||
int i, j;
|
||||
StringBuf buf;
|
||||
char* data;
|
||||
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_AppendStr(&buf, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,"
|
||||
"`zeny`,`amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`,`bound`");
|
||||
for( j = 0; j < MAX_SLOTS; j++ )
|
||||
StringBuf_Printf(&buf, ",`card%d`", j);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_val%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d`", j);
|
||||
}
|
||||
StringBuf_Printf(&buf, " FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id);
|
||||
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf))
|
||||
|| SQL_SUCCESS != Sql_NextRow(sql_handle) )
|
||||
{
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`send_name`,`send_id`,`dest_name`,`dest_id`,`title`,`message`,`time`,`status`,`zeny`,`type` FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id )
|
||||
|| SQL_SUCCESS != Sql_NextRow(sql_handle) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
Sql_FreeResult(sql_handle);
|
||||
StringBuf_Destroy(&buf);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
char* data;
|
||||
|
||||
}else{
|
||||
Sql_GetData(sql_handle, 0, &data, NULL); msg->id = atoi(data);
|
||||
Sql_GetData(sql_handle, 1, &data, NULL); safestrncpy(msg->send_name, data, NAME_LENGTH);
|
||||
Sql_GetData(sql_handle, 2, &data, NULL); msg->send_id = atoi(data);
|
||||
@ -199,27 +167,60 @@ static bool mail_loadmessage(int mail_id, struct mail_message* msg)
|
||||
Sql_GetData(sql_handle, 7, &data, NULL); msg->timestamp = atoi(data);
|
||||
Sql_GetData(sql_handle, 8, &data, NULL); msg->status = (mail_status)atoi(data);
|
||||
Sql_GetData(sql_handle, 9, &data, NULL); msg->zeny = atoi(data);
|
||||
Sql_GetData(sql_handle,10, &data, NULL); msg->item.amount = (short)atoi(data);
|
||||
Sql_GetData(sql_handle,11, &data, NULL); msg->item.nameid = atoi(data);
|
||||
Sql_GetData(sql_handle,12, &data, NULL); msg->item.refine = atoi(data);
|
||||
Sql_GetData(sql_handle,13, &data, NULL); msg->item.attribute = atoi(data);
|
||||
Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data);
|
||||
Sql_GetData(sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10);
|
||||
Sql_GetData(sql_handle,16, &data, NULL); msg->item.bound = atoi(data);
|
||||
msg->item.expire_time = 0;
|
||||
Sql_GetData(sql_handle,10, &data, NULL); msg->type = atoi(data);
|
||||
|
||||
for( j = 0; j < MAX_SLOTS; j++ )
|
||||
{
|
||||
Sql_GetData(sql_handle,17 + j, &data, NULL);
|
||||
msg->item.card[j] = atoi(data);
|
||||
if( msg->type == MAIL_INBOX_NORMAL && charserv_config.mail_return_days > 0 ){
|
||||
msg->scheduled_deletion = msg->timestamp + charserv_config.mail_return_days * 24 * 60 * 60;
|
||||
}else if( msg->type == MAIL_INBOX_RETURNED && charserv_config.mail_delete_days > 0 ){
|
||||
msg->scheduled_deletion = msg->timestamp + charserv_config.mail_delete_days * 24 * 60 * 60;
|
||||
}else{
|
||||
msg->scheduled_deletion = 0;
|
||||
}
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; j++) {
|
||||
Sql_GetData(sql_handle, 17 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
msg->item.option[j].id = atoi(data);
|
||||
Sql_GetData(sql_handle, 18 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
msg->item.option[j].value = atoi(data);
|
||||
Sql_GetData(sql_handle, 19 + MAX_SLOTS + j * 3, &data, NULL);
|
||||
msg->item.option[j].param = atoi(data);
|
||||
|
||||
Sql_FreeResult(sql_handle);
|
||||
}
|
||||
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_AppendStr(&buf, "SELECT `amount`,`nameid`,`refine`,`attribute`,`identify`,`unique_id`,`bound`");
|
||||
for (j = 0; j < MAX_SLOTS; j++)
|
||||
StringBuf_Printf(&buf, ",`card%d`", j);
|
||||
for (j = 0; j < MAX_ITEM_RDM_OPT; ++j) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_val%d`", j);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d`", j);
|
||||
}
|
||||
StringBuf_Printf(&buf, " FROM `%s`", schema_config.mail_attachment_db);
|
||||
StringBuf_Printf(&buf, " WHERE `id` = '%d'", mail_id);
|
||||
StringBuf_AppendStr(&buf, " ORDER BY `index` ASC");
|
||||
StringBuf_Printf(&buf, " LIMIT %d", MAIL_MAX_ITEM);
|
||||
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
Sql_FreeResult(sql_handle);
|
||||
StringBuf_Destroy(&buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(msg->item, 0, sizeof(struct item) * MAIL_MAX_ITEM);
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
|
||||
Sql_GetData(sql_handle,0, &data, NULL); msg->item[i].amount = (short)atoi(data);
|
||||
Sql_GetData(sql_handle,1, &data, NULL); msg->item[i].nameid = atoi(data);
|
||||
Sql_GetData(sql_handle,2, &data, NULL); msg->item[i].refine = atoi(data);
|
||||
Sql_GetData(sql_handle,3, &data, NULL); msg->item[i].attribute = atoi(data);
|
||||
Sql_GetData(sql_handle,4, &data, NULL); msg->item[i].identify = atoi(data);
|
||||
Sql_GetData(sql_handle,5, &data, NULL); msg->item[i].unique_id = strtoull(data, NULL, 10);
|
||||
Sql_GetData(sql_handle,6, &data, NULL); msg->item[i].bound = atoi(data);
|
||||
msg->item[i].expire_time = 0;
|
||||
|
||||
for( j = 0; j < MAX_SLOTS; j++ ){
|
||||
Sql_GetData(sql_handle,7 + j, &data, NULL); msg->item[i].card[j] = atoi(data);
|
||||
}
|
||||
|
||||
for( j = 0; j < MAX_ITEM_RDM_OPT; j++ ){
|
||||
Sql_GetData(sql_handle, 7 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].id = atoi(data);
|
||||
Sql_GetData(sql_handle, 8 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].value = atoi(data);
|
||||
Sql_GetData(sql_handle, 9 + MAX_SLOTS + j * 3, &data, NULL); msg->item[i].option[j].param = atoi(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,27 +230,95 @@ static bool mail_loadmessage(int mail_id, struct mail_message* msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mail_timer_sub( int limit, enum mail_inbox_type type ){
|
||||
struct{
|
||||
int mail_id;
|
||||
int char_id;
|
||||
int account_id;
|
||||
}mails[MAIL_MAX_INBOX];
|
||||
int i, map_fd;
|
||||
char* data;
|
||||
struct online_char_data* character;
|
||||
|
||||
if( limit <= 0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(mails, 0, sizeof(mails));
|
||||
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `id`,`char_id`,`account_id` FROM `%s` `m` INNER JOIN `%s` `c` ON `c`.`char_id`=`m`.`dest_id` WHERE `type` = '%d' AND `time` <= UNIX_TIMESTAMP( NOW() - INTERVAL %d DAY ) ORDER BY `id` LIMIT %d", schema_config.mail_db, schema_config.char_db, type, limit, MAIL_MAX_INBOX + 1) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( Sql_NumRows(sql_handle) <= 0 ){
|
||||
return 0;
|
||||
}
|
||||
|
||||
for( i = 0; i < MAIL_MAX_INBOX && SQL_SUCCESS == Sql_NextRow(sql_handle); i++ ){
|
||||
Sql_GetData(sql_handle, 0, &data, NULL); mails[i].mail_id = atoi(data);
|
||||
Sql_GetData(sql_handle, 1, &data, NULL); mails[i].char_id = atoi(data);
|
||||
Sql_GetData(sql_handle, 2, &data, NULL); mails[i].account_id = atoi(data);
|
||||
}
|
||||
|
||||
Sql_FreeResult(sql_handle);
|
||||
|
||||
for( i = 0; i < MAIL_MAX_INBOX; i++ ){
|
||||
if( mails[i].mail_id == 0 ){
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for online players
|
||||
if( ( character = (struct online_char_data*)idb_get(char_get_onlinedb(), mails[i].account_id) ) != NULL && character->server >= 0 ){
|
||||
map_fd = map_server[character->server].fd;
|
||||
}else{
|
||||
map_fd = 0;
|
||||
}
|
||||
|
||||
if( type == MAIL_INBOX_NORMAL ){
|
||||
mapif_Mail_return( 0, mails[i].char_id, mails[i].mail_id );
|
||||
mapif_Mail_delete( map_fd, mails[i].char_id, mails[i].mail_id, true );
|
||||
}else if( type == MAIL_INBOX_RETURNED ){
|
||||
mapif_Mail_delete( map_fd, mails[i].char_id, mails[i].mail_id, false );
|
||||
}else{
|
||||
// Should not happen
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mail_return_timer( int tid, unsigned int tick, int id, intptr_t ptr ){
|
||||
return mail_timer_sub( charserv_config.mail_return_days, MAIL_INBOX_NORMAL );
|
||||
}
|
||||
|
||||
int mail_delete_timer( int tid, unsigned int tick, int id, intptr_t data ){
|
||||
return mail_timer_sub( charserv_config.mail_delete_days, MAIL_INBOX_RETURNED );
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
* Client Inbox Request
|
||||
*------------------------------------------*/
|
||||
static void mapif_Mail_sendinbox(int fd, uint32 char_id, unsigned char flag)
|
||||
static void mapif_Mail_sendinbox(int fd, uint32 char_id, unsigned char flag, enum mail_inbox_type type)
|
||||
{
|
||||
struct mail_data md;
|
||||
mail_fromsql(char_id, &md);
|
||||
|
||||
//FIXME: dumping the whole structure like this is unsafe [ultramage]
|
||||
WFIFOHEAD(fd, sizeof(md) + 9);
|
||||
WFIFOHEAD(fd, sizeof(md) + 10);
|
||||
WFIFOW(fd,0) = 0x3848;
|
||||
WFIFOW(fd,2) = sizeof(md) + 9;
|
||||
WFIFOW(fd,2) = sizeof(md) + 10;
|
||||
WFIFOL(fd,4) = char_id;
|
||||
WFIFOB(fd,8) = flag;
|
||||
memcpy(WFIFOP(fd,9),&md,sizeof(md));
|
||||
WFIFOB(fd,9) = type;
|
||||
memcpy(WFIFOP(fd,10),&md,sizeof(md));
|
||||
WFIFOSET(fd,WFIFOW(fd,2));
|
||||
}
|
||||
|
||||
static void mapif_parse_Mail_requestinbox(int fd)
|
||||
{
|
||||
mapif_Mail_sendinbox(fd, RFIFOL(fd,2), RFIFOB(fd,6));
|
||||
mapif_Mail_sendinbox(fd, RFIFOL(fd,2), RFIFOB(fd,6), RFIFOB(fd,7));
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
@ -265,38 +334,23 @@ static void mapif_parse_Mail_read(int fd)
|
||||
/*==========================================
|
||||
* Client Attachment Request
|
||||
*------------------------------------------*/
|
||||
static bool mail_DeleteAttach(int mail_id)
|
||||
{
|
||||
StringBuf buf;
|
||||
int i;
|
||||
|
||||
StringBuf_Init(&buf);
|
||||
StringBuf_Printf(&buf, "UPDATE `%s` SET `zeny` = '0', `nameid` = '0', `amount` = '0', `refine` = '0', `attribute` = '0', `identify` = '0'", schema_config.mail_db);
|
||||
for (i = 0; i < MAX_SLOTS; i++)
|
||||
StringBuf_Printf(&buf, ", `card%d` = '0'", i);
|
||||
for (i = 0; i < MAX_ITEM_RDM_OPT; ++i) {
|
||||
StringBuf_Printf(&buf, ", `option_id%d` = 0", i);
|
||||
StringBuf_Printf(&buf, ", `option_val%d` = 0", i);
|
||||
StringBuf_Printf(&buf, ", `option_parm%d` = 0", i);
|
||||
}
|
||||
StringBuf_Printf(&buf, " WHERE `id` = '%d'", mail_id);
|
||||
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, StringBuf_Value(&buf)) )
|
||||
{
|
||||
static bool mail_DeleteAttach(int mail_id){
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id ) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
StringBuf_Destroy(&buf);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuf_Destroy(&buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mapif_Mail_getattach(int fd, uint32 char_id, int mail_id)
|
||||
static void mapif_Mail_getattach(int fd, uint32 char_id, int mail_id, int type)
|
||||
{
|
||||
struct mail_message msg;
|
||||
|
||||
if( ( type&MAIL_ATT_ALL ) == 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( !mail_loadmessage(mail_id, &msg) )
|
||||
return;
|
||||
|
||||
@ -306,36 +360,77 @@ static void mapif_Mail_getattach(int fd, uint32 char_id, int mail_id)
|
||||
if( msg.status != MAIL_READ )
|
||||
return;
|
||||
|
||||
if( (msg.item.nameid < 1 || msg.item.amount < 1) && msg.zeny < 1 )
|
||||
if( type & MAIL_ATT_ZENY ){
|
||||
if( msg.zeny > 0 ){
|
||||
if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `zeny` = 0 WHERE `id` = '%d'", schema_config.mail_db, mail_id ) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
type &= ~MAIL_ATT_ZENY;
|
||||
}
|
||||
}
|
||||
|
||||
if( type & MAIL_ATT_ITEM ){
|
||||
int i;
|
||||
|
||||
ARR_FIND(0, MAIL_MAX_ITEM, i, msg.item[i].nameid > 0 && msg.item[i].amount > 0);
|
||||
|
||||
// No item was found
|
||||
if( i == MAIL_MAX_ITEM ){
|
||||
type &= ~MAIL_ATT_ITEM;
|
||||
}else{
|
||||
if( !mail_DeleteAttach(mail_id) ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( type == MAIL_ATT_NONE )
|
||||
return; // No Attachment
|
||||
|
||||
if( !mail_DeleteAttach(mail_id) )
|
||||
return;
|
||||
|
||||
WFIFOHEAD(fd, sizeof(struct item) + 12);
|
||||
WFIFOHEAD(fd, sizeof(struct item)*MAIL_MAX_ITEM + 16);
|
||||
WFIFOW(fd,0) = 0x384a;
|
||||
WFIFOW(fd,2) = sizeof(struct item) + 12;
|
||||
WFIFOW(fd,2) = sizeof(struct item)*MAIL_MAX_ITEM + 16;
|
||||
WFIFOL(fd,4) = char_id;
|
||||
WFIFOL(fd,8) = (msg.zeny > 0)?msg.zeny:0;
|
||||
memcpy(WFIFOP(fd,12), &msg.item, sizeof(struct item));
|
||||
WFIFOL(fd,8) = mail_id;
|
||||
if( type & MAIL_ATT_ZENY ){
|
||||
WFIFOL(fd,12) = msg.zeny;
|
||||
}else{
|
||||
WFIFOL(fd, 12) = 0;
|
||||
}
|
||||
if( type & MAIL_ATT_ITEM ){
|
||||
memcpy(WFIFOP(fd, 16), &msg.item, sizeof(struct item)*MAIL_MAX_ITEM);
|
||||
}else{
|
||||
memset(WFIFOP(fd, 16), 0, sizeof(struct item)*MAIL_MAX_ITEM);
|
||||
}
|
||||
WFIFOSET(fd,WFIFOW(fd,2));
|
||||
}
|
||||
|
||||
static void mapif_parse_Mail_getattach(int fd)
|
||||
{
|
||||
mapif_Mail_getattach(fd, RFIFOL(fd,2), RFIFOL(fd,6));
|
||||
mapif_Mail_getattach(fd, RFIFOL(fd,2), RFIFOL(fd,6),RFIFOB(fd,10));
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
* Delete Mail
|
||||
*------------------------------------------*/
|
||||
static void mapif_Mail_delete(int fd, uint32 char_id, int mail_id)
|
||||
static void mapif_Mail_delete(int fd, uint32 char_id, int mail_id, bool deleted)
|
||||
{
|
||||
bool failed = false;
|
||||
if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) )
|
||||
{
|
||||
Sql_ShowDebug(sql_handle);
|
||||
failed = true;
|
||||
|
||||
if( !deleted ){
|
||||
if ( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) ||
|
||||
SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id) )
|
||||
{
|
||||
Sql_ShowDebug(sql_handle);
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only if the request came from a map-server and was not timer triggered for an offline character
|
||||
if( fd <= 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
WFIFOHEAD(fd,11);
|
||||
@ -348,7 +443,7 @@ static void mapif_Mail_delete(int fd, uint32 char_id, int mail_id)
|
||||
|
||||
static void mapif_parse_Mail_delete(int fd)
|
||||
{
|
||||
mapif_Mail_delete(fd, RFIFOL(fd,2), RFIFOL(fd,6));
|
||||
mapif_Mail_delete(fd, RFIFOL(fd,2), RFIFOL(fd,6), false);
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
@ -356,7 +451,7 @@ static void mapif_parse_Mail_delete(int fd)
|
||||
*------------------------------------------*/
|
||||
void mapif_Mail_new(struct mail_message *msg)
|
||||
{
|
||||
unsigned char buf[74];
|
||||
unsigned char buf[75];
|
||||
|
||||
if( !msg || !msg->id )
|
||||
return;
|
||||
@ -366,7 +461,8 @@ void mapif_Mail_new(struct mail_message *msg)
|
||||
WBUFL(buf,6) = msg->id;
|
||||
memcpy(WBUFP(buf,10), msg->send_name, NAME_LENGTH);
|
||||
memcpy(WBUFP(buf,34), msg->title, MAIL_TITLE_LENGTH);
|
||||
chmapif_sendall(buf, 74);
|
||||
WBUFB(buf,74) = msg->type;
|
||||
chmapif_sendall(buf, 75);
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
@ -381,7 +477,8 @@ static void mapif_Mail_return(int fd, uint32 char_id, int mail_id)
|
||||
{
|
||||
if( msg.dest_id != char_id)
|
||||
return;
|
||||
else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id) )
|
||||
else if( SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_db, mail_id)
|
||||
|| SQL_ERROR == Sql_Query(sql_handle, "DELETE FROM `%s` WHERE `id` = '%d'", schema_config.mail_attachment_db, mail_id) )
|
||||
Sql_ShowDebug(sql_handle);
|
||||
else
|
||||
{
|
||||
@ -398,6 +495,7 @@ static void mapif_Mail_return(int fd, uint32 char_id, int mail_id)
|
||||
safestrncpy(msg.title, temp_, MAIL_TITLE_LENGTH);
|
||||
|
||||
msg.status = MAIL_NEW;
|
||||
msg.type = MAIL_INBOX_RETURNED;
|
||||
msg.timestamp = time(NULL);
|
||||
|
||||
new_mail = mail_savemessage(&msg);
|
||||
@ -405,6 +503,11 @@ static void mapif_Mail_return(int fd, uint32 char_id, int mail_id)
|
||||
}
|
||||
}
|
||||
|
||||
// Only if the request came from a map-server and was not timer triggered for an offline character
|
||||
if( fd <= 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
WFIFOHEAD(fd,11);
|
||||
WFIFOW(fd,0) = 0x384c;
|
||||
WFIFOL(fd,2) = char_id;
|
||||
@ -452,12 +555,18 @@ static void mapif_parse_Mail_send(int fd)
|
||||
if ( SQL_SUCCESS == Sql_NextRow(sql_handle) )
|
||||
{
|
||||
char *data;
|
||||
#if PACKETVER < 20150513
|
||||
Sql_GetData(sql_handle, 0, &data, NULL);
|
||||
if (atoi(data) != account_id)
|
||||
{ // Cannot send mail to char in the same account
|
||||
Sql_GetData(sql_handle, 1, &data, NULL);
|
||||
msg.dest_id = atoi(data);
|
||||
}
|
||||
#else
|
||||
// In RODEX you can even send mails to yourself
|
||||
Sql_GetData(sql_handle, 1, &data, NULL);
|
||||
msg.dest_id = atoi(data);
|
||||
#endif
|
||||
}
|
||||
Sql_FreeResult(sql_handle);
|
||||
msg.status = MAIL_NEW;
|
||||
@ -469,7 +578,7 @@ static void mapif_parse_Mail_send(int fd)
|
||||
mapif_Mail_new(&msg); // notify recipient
|
||||
}
|
||||
|
||||
void mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item)
|
||||
void mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item, int amount)
|
||||
{
|
||||
struct mail_message msg;
|
||||
memset(&msg, 0, sizeof(struct mail_message));
|
||||
@ -481,15 +590,56 @@ void mail_sendmail(int send_id, const char* send_name, int dest_id, const char*
|
||||
safestrncpy(msg.title, title, MAIL_TITLE_LENGTH);
|
||||
safestrncpy(msg.body, body, MAIL_BODY_LENGTH);
|
||||
msg.zeny = zeny;
|
||||
if( item != NULL )
|
||||
memcpy(&msg.item, item, sizeof(struct item));
|
||||
if( item != NULL ){
|
||||
int i;
|
||||
|
||||
for( i = 0; i < amount && i < MAIL_MAX_ITEM; i++ ){
|
||||
memcpy(&msg.item[i], &item[i], sizeof(struct item));
|
||||
}
|
||||
}
|
||||
|
||||
msg.timestamp = time(NULL);
|
||||
msg.type = MAIL_INBOX_NORMAL;
|
||||
|
||||
mail_savemessage(&msg);
|
||||
mapif_Mail_new(&msg);
|
||||
}
|
||||
|
||||
static void mapif_Mail_receiver_send( int fd, int requesting_char_id, int char_id, int class_, int base_level, const char* name ){
|
||||
WFIFOHEAD(fd,38);
|
||||
WFIFOW(fd,0) = 0x384e;
|
||||
WFIFOL(fd,2) = requesting_char_id;
|
||||
WFIFOL(fd,6) = char_id;
|
||||
WFIFOW(fd,10) = class_;
|
||||
WFIFOW(fd,12) = base_level;
|
||||
strncpy(WFIFOCP(fd, 14), name, NAME_LENGTH);
|
||||
WFIFOSET(fd,38);
|
||||
}
|
||||
|
||||
static void mapif_parse_Mail_receiver_check( int fd ){
|
||||
char name[NAME_LENGTH], esc_name[NAME_LENGTH * 2 + 1];
|
||||
uint32 char_id = 0;
|
||||
uint16 class_ = 0, base_level = 0;
|
||||
|
||||
safestrncpy( name, RFIFOCP(fd, 6), NAME_LENGTH );
|
||||
|
||||
// Try to find the Dest Char by Name
|
||||
Sql_EscapeStringLen( sql_handle, esc_name, name, strnlen( name, NAME_LENGTH ) );
|
||||
|
||||
if( SQL_ERROR == Sql_Query( sql_handle, "SELECT `char_id`,`class`,`base_level` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name ) ){
|
||||
Sql_ShowDebug(sql_handle);
|
||||
}else if( SQL_SUCCESS == Sql_NextRow(sql_handle) ){
|
||||
char *data;
|
||||
|
||||
Sql_GetData(sql_handle, 0, &data, NULL); char_id = atoi(data);
|
||||
Sql_GetData(sql_handle, 1, &data, NULL); class_ = atoi(data);
|
||||
Sql_GetData(sql_handle, 2, &data, NULL); base_level = atoi(data);
|
||||
}
|
||||
|
||||
Sql_FreeResult(sql_handle);
|
||||
mapif_Mail_receiver_send( fd, RFIFOL(fd, 2), char_id, class_, base_level, name );
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
* Packets From Map Server
|
||||
*------------------------------------------*/
|
||||
@ -503,6 +653,7 @@ int inter_mail_parse_frommap(int fd)
|
||||
case 0x304b: mapif_parse_Mail_delete(fd); break;
|
||||
case 0x304c: mapif_parse_Mail_return(fd); break;
|
||||
case 0x304d: mapif_parse_Mail_send(fd); break;
|
||||
case 0x304e: mapif_parse_Mail_receiver_check(fd); break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,8 +4,11 @@
|
||||
#ifndef _INT_MAIL_SQL_H_
|
||||
#define _INT_MAIL_SQL_H_
|
||||
|
||||
int mail_return_timer( int tid, unsigned int tick, int id, intptr_t data );
|
||||
int mail_delete_timer( int tid, unsigned int tick, int id, intptr_t data );
|
||||
|
||||
int inter_mail_parse_frommap(int fd);
|
||||
void mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item);
|
||||
void mail_sendmail(int send_id, const char* send_name, int dest_id, const char* dest_name, const char* title, const char* body, int zeny, struct item *item, int amount);
|
||||
|
||||
int inter_mail_sql_init(void);
|
||||
void inter_mail_sql_final(void);
|
||||
|
@ -49,7 +49,7 @@ int inter_recv_packet_length[] = {
|
||||
6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010-
|
||||
-1,10,-1,14, 15+NAME_LENGTH,19, 6,-1, 14,14, 6, 0, 0, 0, 0, 0, // 3020- Party
|
||||
-1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
|
||||
-1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
|
||||
-1, 9, 0, 0, 0, 0, 0, 0, 8, 6,11,10, 10,-1,6+NAME_LENGTH, 0, // 3040-
|
||||
-1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus]
|
||||
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]
|
||||
|
@ -140,7 +140,14 @@
|
||||
//Mail System
|
||||
#define MAIL_MAX_INBOX 30
|
||||
#define MAIL_TITLE_LENGTH 40
|
||||
#if PACKETVER < 20150513
|
||||
#define MAIL_BODY_LENGTH 200
|
||||
#define MAIL_MAX_ITEM 1
|
||||
#else
|
||||
#define MAIL_BODY_LENGTH 500
|
||||
#define MAIL_MAX_ITEM 5
|
||||
#define MAIL_PAGE_SIZE 7
|
||||
#endif
|
||||
|
||||
//Mercenary System
|
||||
#define MC_SKILLBASE 8201
|
||||
@ -500,6 +507,19 @@ typedef enum mail_status {
|
||||
MAIL_READ,
|
||||
} mail_status;
|
||||
|
||||
enum mail_inbox_type {
|
||||
MAIL_INBOX_NORMAL = 0,
|
||||
MAIL_INBOX_ACCOUNT,
|
||||
MAIL_INBOX_RETURNED
|
||||
};
|
||||
|
||||
enum mail_attachment_type {
|
||||
MAIL_ATT_NONE = 0,
|
||||
MAIL_ATT_ZENY = 1,
|
||||
MAIL_ATT_ITEM = 2,
|
||||
MAIL_ATT_ALL = MAIL_ATT_ZENY | MAIL_ATT_ITEM
|
||||
};
|
||||
|
||||
struct mail_message {
|
||||
int id;
|
||||
uint32 send_id; //hold char_id of sender
|
||||
@ -508,12 +528,14 @@ struct mail_message {
|
||||
char dest_name[NAME_LENGTH]; //receiver nickname
|
||||
char title[MAIL_TITLE_LENGTH];
|
||||
char body[MAIL_BODY_LENGTH];
|
||||
int type; // enum mail_inbox_type
|
||||
time_t scheduled_deletion;
|
||||
|
||||
mail_status status;
|
||||
time_t timestamp; // marks when the message was sent
|
||||
|
||||
uint32 zeny;
|
||||
struct item item;
|
||||
struct item item[MAIL_MAX_ITEM];
|
||||
};
|
||||
|
||||
struct mail_data {
|
||||
|
@ -8407,6 +8407,10 @@ static const struct _battle_data {
|
||||
{ "dispel_song", &battle_config.dispel_song, 0, 0, 1, },
|
||||
{ "guild_maprespawn_clones", &battle_config.guild_maprespawn_clones, 0, 0, 1, },
|
||||
{ "hide_fav_sell", &battle_config.hide_fav_sell, 0, 0, 1, },
|
||||
{ "mail_daily_count", &battle_config.mail_daily_count, 100, 0, INT32_MAX, },
|
||||
{ "mail_zeny_fee", &battle_config.mail_zeny_fee, 2, 0, 100, },
|
||||
{ "mail_attachment_price", &battle_config.mail_attachment_price, 2500, 0, INT32_MAX, },
|
||||
{ "mail_attachment_weight", &battle_config.mail_attachment_weight, 2000, 0, INT32_MAX, },
|
||||
|
||||
#include "../custom/battle_config_init.inc"
|
||||
};
|
||||
|
@ -618,6 +618,10 @@ extern struct Battle_Config
|
||||
int dispel_song; //Can songs be dispelled?
|
||||
int guild_maprespawn_clones; // Should clones be killed by maprespawnguildid?
|
||||
int hide_fav_sell;
|
||||
int mail_daily_count;
|
||||
int mail_zeny_fee;
|
||||
int mail_attachment_price;
|
||||
int mail_attachment_weight;
|
||||
|
||||
#include "../custom/battle_config_struct.inc"
|
||||
} battle_config;
|
||||
|
758
src/map/clif.c
758
src/map/clif.c
@ -72,6 +72,15 @@ static unsigned int clif_cryptKey[3]; // Used keys
|
||||
static unsigned short clif_parse_cmd(int fd, struct map_session_data *sd);
|
||||
static bool clif_session_isValid(struct map_session_data *sd);
|
||||
|
||||
#if PACKETVER >= 20150513
|
||||
enum mail_type {
|
||||
MAIL_TYPE_TEXT = 0x0,
|
||||
MAIL_TYPE_ZENY = 0x2,
|
||||
MAIL_TYPE_ITEM = 0x4,
|
||||
MAIL_TYPE_NPC = 0x8
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Converts item type to display it on client if necessary.
|
||||
* @param nameid: Item ID
|
||||
* @return item type. For IT_PETEGG will be displayed as IT_WEAPON. If Shadow Weapon of IT_SHADOWGEAR as IT_WEAPON and else as IT_ARMOR
|
||||
@ -10538,6 +10547,12 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
|
||||
pc_show_questinfo_reinit(sd);
|
||||
pc_show_questinfo(sd);
|
||||
|
||||
#if PACKETVER >= 20150513
|
||||
if( sd->mail.inbox.unread ){
|
||||
clif_Mail_new(sd, 0, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
sd->state.connect_new = 0;
|
||||
sd->state.changemap = false;
|
||||
}
|
||||
@ -11438,6 +11453,8 @@ void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
|
||||
#endif
|
||||
if (pc_cant_act2(sd) || sd->npc_id)
|
||||
return;
|
||||
if( sd->state.mail_writing )
|
||||
return;
|
||||
|
||||
bl = map_id2bl(RFIFOL(fd,info->pos[0]));
|
||||
//type = RFIFOB(fd,info->pos[1]);
|
||||
@ -11681,10 +11698,24 @@ void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
|
||||
if(!sd->chatID && pc_cant_act(sd))
|
||||
return; //You can trade while in a chatroom.
|
||||
|
||||
// @noask [LuzZza]
|
||||
if(t_sd && t_sd->state.noask) {
|
||||
clif_noask_sub(sd, t_sd, 0);
|
||||
return;
|
||||
if(t_sd){
|
||||
// @noask [LuzZza]
|
||||
if(t_sd->state.noask) {
|
||||
clif_noask_sub(sd, t_sd, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (t_sd->state.mail_writing) {
|
||||
int old = sd->trade_partner;
|
||||
|
||||
// Fake trading
|
||||
sd->trade_partner = t_sd->status.account_id;
|
||||
clif_tradestart(sd, 5);
|
||||
// Restore old state
|
||||
sd->trade_partner = old;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 1 && pc_checkskill(sd, SU_BASIC_SKILL) < 1) {
|
||||
@ -14759,62 +14790,144 @@ void clif_parse_Check(int fd, struct map_session_data *sd)
|
||||
/// By Zephyrus
|
||||
///
|
||||
|
||||
/// Notification about the result of adding an item to mail (ZC_ACK_MAIL_ADD_ITEM).
|
||||
/// 0255 <index>.W <result>.B
|
||||
/// Notification about the result of adding an item to mail
|
||||
/// 0255 <index>.W <result>.B (ZC_ACK_MAIL_ADD_ITEM)
|
||||
/// result:
|
||||
/// 0 = success
|
||||
/// 1 = failure
|
||||
void clif_Mail_setattachment(int fd, int index, uint8 flag)
|
||||
/// 0a05 <result>.B <index>.W <amount>.W <item id>.W <item type>.B <identified>.B <damaged>.B <refine>.B (ZC_ACK_ADD_ITEM_TO_MAIL)
|
||||
/// { <card id>.W }*4 { <option index>.W <option value>.W <option parameter>.B }*5
|
||||
/// result:
|
||||
/// 0 = success
|
||||
/// 1 = over weight
|
||||
/// 2 = fatal error
|
||||
void clif_Mail_setattachment(struct map_session_data* sd, int index, int amount, uint8 flag)
|
||||
{
|
||||
int fd = sd->fd;
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
WFIFOHEAD(fd,packet_len(0x255));
|
||||
WFIFOW(fd,0) = 0x255;
|
||||
WFIFOW(fd,2) = index;
|
||||
WFIFOB(fd,4) = flag;
|
||||
WFIFOSET(fd,packet_len(0x255));
|
||||
#else
|
||||
struct item* item = &sd->inventory.u.items_inventory[index-2];
|
||||
int i, total = 0;
|
||||
|
||||
WFIFOHEAD(fd, 53);
|
||||
WFIFOW(fd, 0) = 0xa05;
|
||||
WFIFOB(fd, 2) = flag;
|
||||
WFIFOW(fd, 3) = index;
|
||||
WFIFOW(fd, 5) = amount;
|
||||
WFIFOW(fd, 7) = item->nameid;
|
||||
WFIFOB(fd, 9) = itemtype(item->nameid);
|
||||
WFIFOB(fd, 10) = item->identify;
|
||||
WFIFOB(fd, 11) = item->attribute;
|
||||
WFIFOB(fd, 12) = item->refine;
|
||||
clif_addcards(WFIFOP(fd,13), item);
|
||||
clif_add_random_options(WFIFOP(fd,21), item);
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
if( sd->mail.item[i].nameid == 0 ){
|
||||
break;
|
||||
}
|
||||
|
||||
total += sd->mail.item[i].amount * ( sd->inventory_data[sd->mail.item[i].index]->weight / 10 );
|
||||
}
|
||||
|
||||
WFIFOW(fd, 46) = total;
|
||||
// 5 empty unknown bytes
|
||||
memset(WFIFOP(fd, 48), 0, 5);
|
||||
WFIFOSET(fd, 53);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Notification about the result of retrieving a mail attachment (ZC_MAIL_REQ_GET_ITEM).
|
||||
/// 0245 <result>.B
|
||||
/// Notification about the result of retrieving a mail attachment
|
||||
/// 0245 <result>.B (ZC_MAIL_REQ_GET_ITEM)
|
||||
/// result:
|
||||
/// 0 = success
|
||||
/// 1 = failure
|
||||
/// 2 = too many items
|
||||
void clif_Mail_getattachment(int fd, uint8 result)
|
||||
{
|
||||
/// 09f2 <mail id>.Q <mail tab>.B <result>.B (ZC_ACK_ZENY_FROM_MAIL)
|
||||
/// 09f4 <mail id>.Q <mail tab>.B <result>.B (ZC_ACK_ITEM_FROM_MAIL)
|
||||
void clif_mail_getattachment(struct map_session_data* sd, struct mail_message *msg, uint8 result, enum mail_attachment_type type) {
|
||||
int fd = sd->fd;
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
WFIFOHEAD(fd,packet_len(0x245));
|
||||
WFIFOW(fd,0) = 0x245;
|
||||
WFIFOB(fd,2) = result;
|
||||
WFIFOSET(fd,packet_len(0x245));
|
||||
#else
|
||||
switch( type ){
|
||||
case MAIL_ATT_ITEM:
|
||||
case MAIL_ATT_ZENY:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
WFIFOHEAD(fd, 12);
|
||||
WFIFOW(fd, 0) = type == MAIL_ATT_ZENY ? 0x9f2 : 0x9f4;
|
||||
WFIFOQ(fd, 2) = msg->id;
|
||||
WFIFOB(fd, 10) = msg->type;
|
||||
WFIFOB(fd, 11) = result;
|
||||
WFIFOSET(fd, 12);
|
||||
|
||||
clif_Mail_refreshinbox( sd, msg->type, 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Notification about the result of sending a mail (ZC_MAIL_REQ_SEND).
|
||||
/// 0249 <result>.B
|
||||
/// Notification about the result of sending a mail
|
||||
/// 0249 <result>.B (ZC_MAIL_REQ_SEND)
|
||||
/// result:
|
||||
/// 0 = success
|
||||
/// 1 = recipinent does not exist
|
||||
void clif_Mail_send(int fd, bool fail)
|
||||
{
|
||||
/// 09ed <result>.B (ZC_ACK_WRITE_MAIL)
|
||||
void clif_Mail_send(struct map_session_data* sd, enum mail_send_result result){
|
||||
int fd = sd->fd;
|
||||
#if PACKETVER < 20150513
|
||||
WFIFOHEAD(fd,packet_len(0x249));
|
||||
WFIFOW(fd,0) = 0x249;
|
||||
WFIFOB(fd,2) = fail;
|
||||
WFIFOB(fd,2) = result != WRITE_MAIL_SUCCESS;
|
||||
WFIFOSET(fd,packet_len(0x249));
|
||||
#else
|
||||
WFIFOHEAD(fd, 3);
|
||||
WFIFOW(fd, 0) = 0x9ed;
|
||||
WFIFOB(fd, 2) = result;
|
||||
WFIFOSET(fd, 3);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Notification about the result of deleting a mail (ZC_ACK_MAIL_DELETE).
|
||||
/// 0257 <mail id>.L <result>.W
|
||||
/// Notification about the result of deleting a mail.
|
||||
/// 0257 <mail id>.L <result>.W (ZC_ACK_MAIL_DELETE)
|
||||
/// result:
|
||||
/// 0 = success
|
||||
/// 1 = failure
|
||||
void clif_Mail_delete(int fd, int mail_id, short fail)
|
||||
{
|
||||
// 09f6 <mail tab>.B <mail id>.Q (ZC_ACK_DELETE_MAIL)
|
||||
void clif_mail_delete( struct map_session_data* sd, struct mail_message *msg, bool success ){
|
||||
int fd = sd->fd;
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
WFIFOHEAD(fd, packet_len(0x257));
|
||||
WFIFOW(fd,0) = 0x257;
|
||||
WFIFOL(fd,2) = mail_id;
|
||||
WFIFOW(fd,6) = fail;
|
||||
WFIFOL(fd,2) = msg->id;
|
||||
WFIFOW(fd,6) = !success;
|
||||
WFIFOSET(fd, packet_len(0x257));
|
||||
#else
|
||||
// This is only a success notification
|
||||
if( success ){
|
||||
WFIFOHEAD(fd, 11);
|
||||
WFIFOW(fd, 0) = 0x9f6;
|
||||
WFIFOB(fd, 2) = msg->type;
|
||||
WFIFOQ(fd, 3) = msg->id;
|
||||
WFIFOSET(fd, 11);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -14832,20 +14945,26 @@ void clif_Mail_return(int fd, int mail_id, short fail)
|
||||
WFIFOSET(fd,packet_len(0x274));
|
||||
}
|
||||
|
||||
|
||||
/// Notification about new mail (ZC_MAIL_RECEIVE).
|
||||
/// 024a <mail id>.L <title>.40B <sender>.24B
|
||||
void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title)
|
||||
{
|
||||
/// Notification about new mail.
|
||||
/// 024a <mail id>.L <title>.40B <sender>.24B (ZC_MAIL_RECEIVE)
|
||||
/// 09e7 <result>.B (ZC_NOTIFY_UNREADMAIL)
|
||||
void clif_Mail_new(struct map_session_data* sd, int mail_id, const char *sender, const char *title){
|
||||
int fd = sd->fd;
|
||||
#if PACKETVER < 20150513
|
||||
WFIFOHEAD(fd,packet_len(0x24a));
|
||||
WFIFOW(fd,0) = 0x24a;
|
||||
WFIFOL(fd,2) = mail_id;
|
||||
safestrncpy(WFIFOCP(fd,6), title, MAIL_TITLE_LENGTH);
|
||||
safestrncpy(WFIFOCP(fd,46), sender, NAME_LENGTH);
|
||||
WFIFOSET(fd,packet_len(0x24a));
|
||||
#else
|
||||
WFIFOHEAD(fd,3);
|
||||
WFIFOW(fd,0) = 0x9e7;
|
||||
WFIFOB(fd,2) = sd->mail.inbox.unread > 0 || sd->mail.inbox.unchecked > 0; // unread
|
||||
WFIFOSET(fd,3);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Opens/closes the mail window (ZC_MAIL_WINDOWS).
|
||||
/// 0260 <type>.L
|
||||
/// type:
|
||||
@ -14859,14 +14978,17 @@ void clif_Mail_window(int fd, int flag)
|
||||
WFIFOSET(fd,packet_len(0x260));
|
||||
}
|
||||
|
||||
|
||||
/// Lists mails stored in inbox (ZC_MAIL_REQ_GET_LIST).
|
||||
/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount
|
||||
/// Lists mails stored in inbox.
|
||||
/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount (ZC_MAIL_REQ_GET_LIST)
|
||||
/// read:
|
||||
/// 0 = unread
|
||||
/// 1 = read
|
||||
void clif_Mail_refreshinbox(struct map_session_data *sd)
|
||||
{
|
||||
/// 09f0 <packet len>.W <type>.B <amount>.B <last page>.B (ZC_ACK_MAIL_LIST)
|
||||
/// { <mail id>.Q <read>.B <type>.B <sender>.24B <received>.L <expires>.L <title length>.W <title>.?B }*
|
||||
/// 0a7d <packet len>.W <type>.B <amount>.B <last page>.B (ZC_ACK_MAIL_LIST2)
|
||||
/// { <mail id>.Q <read>.B <type>.B <sender>.24B <received>.L <expires>.L <title length>.W <title>.?B }*
|
||||
void clif_Mail_refreshinbox(struct map_session_data *sd,enum mail_inbox_type type,int64 mailID){
|
||||
#if PACKETVER < 20150513
|
||||
int fd = sd->fd;
|
||||
struct mail_data *md = &sd->mail.inbox;
|
||||
struct mail_message *msg;
|
||||
@ -14898,29 +15020,176 @@ void clif_Mail_refreshinbox(struct map_session_data *sd)
|
||||
safesnprintf(output,sizeof(output),"Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX);
|
||||
clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF);
|
||||
}
|
||||
#else
|
||||
int fd = sd->fd;
|
||||
struct mail_data *md = &sd->mail.inbox;
|
||||
struct mail_message *msg;
|
||||
int i, j, k, offset, titleLength;
|
||||
uint8 mailType, amount, remaining;
|
||||
uint32 now = (uint32)time(NULL);
|
||||
#if PACKETVER >= 20160601
|
||||
int cmd = 0xa7d;
|
||||
#else
|
||||
int cmd = 0x9f0;
|
||||
#endif
|
||||
|
||||
if( battle_config.mail_daily_count ){
|
||||
mail_refresh_remaining_amount(sd);
|
||||
}
|
||||
|
||||
// If a starting mail id was sent
|
||||
if( mailID != 0 ){
|
||||
ARR_FIND( 0, md->amount, i, md->msg[i].id == mailID );
|
||||
|
||||
// Unknown mail
|
||||
if( i == md->amount ){
|
||||
// Ignore the request for now
|
||||
return; // TODO: Should we just show the first page instead?
|
||||
}
|
||||
|
||||
// It was actually the oldest/first mail, there is no further page
|
||||
if( i == 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
// We actually want the next/older mail
|
||||
i -= 1;
|
||||
}else{
|
||||
i = md->amount;
|
||||
}
|
||||
|
||||
// Count the remaining mails from the starting mail or the beginning
|
||||
// Only count mails of the target type and those that should not have been deleted already
|
||||
for( j = i, remaining = 0; j >= 0; j-- ){
|
||||
msg = &md->msg[j];
|
||||
|
||||
if (msg->id < 1)
|
||||
continue;
|
||||
if (msg->type != type)
|
||||
continue;
|
||||
if (msg->scheduled_deletion > 0 && msg->scheduled_deletion <= now)
|
||||
continue;
|
||||
|
||||
remaining++;
|
||||
}
|
||||
|
||||
if( remaining > MAIL_PAGE_SIZE ){
|
||||
amount = MAIL_PAGE_SIZE;
|
||||
}else{
|
||||
amount = remaining;
|
||||
}
|
||||
|
||||
WFIFOHEAD(fd, 7 + ((44 + MAIL_TITLE_LENGTH) * amount));
|
||||
WFIFOW(fd, 0) = cmd;
|
||||
WFIFOB(fd, 4) = type;
|
||||
WFIFOB(fd, 5) = amount;
|
||||
WFIFOB(fd, 6) = ( remaining < MAIL_PAGE_SIZE ); // last page
|
||||
|
||||
for( offset = 7, amount = 0; i >= 0; i-- ){
|
||||
msg = &md->msg[i];
|
||||
|
||||
if (msg->id < 1)
|
||||
continue;
|
||||
if (msg->type != type)
|
||||
continue;
|
||||
if (msg->scheduled_deletion > 0 && msg->scheduled_deletion <= now)
|
||||
continue;
|
||||
|
||||
WFIFOQ(fd, offset + 0) = (uint64)msg->id;
|
||||
WFIFOB(fd, offset + 8) = (msg->status != MAIL_UNREAD);
|
||||
|
||||
mailType = MAIL_TYPE_TEXT; // Normal letter
|
||||
|
||||
if( msg->zeny > 0 ){
|
||||
mailType |= MAIL_TYPE_ZENY; // Mail contains zeny
|
||||
}
|
||||
|
||||
for( k = 0; k < MAIL_MAX_ITEM; k++ ){
|
||||
if( msg->item[k].nameid > 0 ){
|
||||
mailType |= MAIL_TYPE_ITEM; // Mail contains items
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If it came from an npc?
|
||||
//mailType |= MAIL_TYPE_NPC;
|
||||
|
||||
WFIFOB(fd, offset + 9) = mailType;
|
||||
safestrncpy(WFIFOCP(fd, offset + 10), msg->send_name, NAME_LENGTH);
|
||||
|
||||
// How much time has passed since you received the mail
|
||||
WFIFOL(fd, offset + 34 ) = now - (uint32)msg->timestamp;
|
||||
|
||||
// If automatic return/deletion of mails is enabled, notify the client when it will kick in
|
||||
if( msg->scheduled_deletion > 0 ){
|
||||
WFIFOL(fd, offset + 38) = (uint32)msg->scheduled_deletion - now;
|
||||
}else{
|
||||
// Fake the scheduled deletion to one year in the future
|
||||
// Sadly the client always displays the scheduled deletion after 24 hours no matter how high this value gets [Lemongrass]
|
||||
WFIFOL(fd, offset + 38) = 365 * 24 * 60 * 60;
|
||||
}
|
||||
|
||||
WFIFOW(fd, offset + 42) = titleLength = (int16)(strlen(msg->title) + 1);
|
||||
safestrncpy(WFIFOCP(fd, offset + 44), msg->title, titleLength);
|
||||
|
||||
offset += 44 + titleLength;
|
||||
}
|
||||
WFIFOW(fd, 2) = (int16)offset;
|
||||
WFIFOSET(fd, offset);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Mail inbox list request (CZ_MAIL_GET_LIST).
|
||||
/// 023f
|
||||
void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd)
|
||||
{
|
||||
/// Mail inbox list request.
|
||||
/// 023f (CZ_MAIL_GET_LIST)
|
||||
/// 09e8 <mail tab>.B <mail id>.Q (CZ_OPEN_MAILBOX)
|
||||
/// 09ee <mail tab>.B <mail id>.Q (CZ_REQ_NEXT_MAIL_LIST)
|
||||
/// 09ef <mail tab>.B <mail id>.Q (CZ_REQ_REFRESH_MAIL_LIST)
|
||||
void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd){
|
||||
#if PACKETVER < 20150513
|
||||
struct mail_data* md = &sd->mail.inbox;
|
||||
|
||||
if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) )
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1);
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1, MAIL_INBOX_NORMAL);
|
||||
else
|
||||
clif_Mail_refreshinbox(sd);
|
||||
clif_Mail_refreshinbox(sd, MAIL_INBOX_NORMAL,0);
|
||||
|
||||
mail_removeitem(sd, 0);
|
||||
mail_removezeny(sd, 0);
|
||||
mail_removeitem(sd, 0, sd->mail.item[0].index, sd->mail.item[0].amount);
|
||||
mail_removezeny(sd, false);
|
||||
#else
|
||||
uint8 openType = RFIFOB(fd, 2);
|
||||
uint64 mailId = RFIFOQ(fd, 3);
|
||||
|
||||
switch( openType ){
|
||||
case MAIL_INBOX_NORMAL:
|
||||
case MAIL_INBOX_ACCOUNT:
|
||||
case MAIL_INBOX_RETURNED:
|
||||
break;
|
||||
default:
|
||||
// Unknown type: ignore request
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->mail.changed || RFIFOW(fd,0) == 0x9ef ){
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1, openType);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is not a next page request
|
||||
if( RFIFOW(fd,0) != 0x9ee ){
|
||||
mailId = 0;
|
||||
}
|
||||
|
||||
clif_Mail_refreshinbox(sd,(enum mail_inbox_type)openType,mailId);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Opens a mail (ZC_MAIL_REQ_OPEN).
|
||||
/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L
|
||||
/// Opens a mail
|
||||
/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L (ZC_MAIL_REQ_OPEN)
|
||||
/// <amount>.L <name id>.W <item type>.W <identified>.B <damaged>.B <refine>.B
|
||||
/// <card1>.W <card2>.W <card3>.W <card4>.W <message>.?B
|
||||
/// 09eb <packet len>.W <type>.B <mail id>.Q <message length>.W <zeny>.Q <item count>.B (ZC_ACK_READ_MAIL)
|
||||
/// { }*n
|
||||
// TODO: Packet description => for repeated block
|
||||
void clif_Mail_read(struct map_session_data *sd, int mail_id)
|
||||
{
|
||||
int i, fd = sd->fd;
|
||||
@ -14932,15 +15201,21 @@ void clif_Mail_read(struct map_session_data *sd, int mail_id)
|
||||
return;
|
||||
} else {
|
||||
struct mail_message *msg = &sd->mail.inbox.msg[i];
|
||||
struct item *item = &msg->item;
|
||||
struct item *item;
|
||||
struct item_data *data;
|
||||
int msg_len = strlen(msg->body), len;
|
||||
int msg_len = strlen(msg->body), len, count = 0;
|
||||
#if PACKETVER >= 20150513
|
||||
int offset, j, itemsize;
|
||||
#endif
|
||||
|
||||
if( msg_len == 0 ) {
|
||||
strcpy(msg->body, "(no message)");
|
||||
strcpy(msg->body, "(no message)"); // TODO: confirm for RODEX
|
||||
msg_len = strlen(msg->body);
|
||||
}
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
item = &msg->item[0];
|
||||
|
||||
len = 101 + msg_len;
|
||||
|
||||
WFIFOHEAD(fd,len);
|
||||
@ -14969,21 +15244,72 @@ void clif_Mail_read(struct map_session_data *sd, int mail_id)
|
||||
WFIFOB(fd,99) = (unsigned char)msg_len;
|
||||
safestrncpy(WFIFOCP(fd,100), msg->body, msg_len + 1);
|
||||
WFIFOSET(fd,len);
|
||||
#else
|
||||
// Count the attached items
|
||||
ARR_FIND( 0, MAIL_MAX_ITEM, count, msg->item[count].nameid == 0 );
|
||||
|
||||
msg_len += 1; // Zero Termination
|
||||
|
||||
itemsize = 24 + 5 * MAX_ITEM_RDM_OPT;
|
||||
len = 24 + msg_len+1 + itemsize * count;
|
||||
|
||||
WFIFOHEAD(fd, len);
|
||||
WFIFOW(fd, 0) = 0x9eb;
|
||||
WFIFOW(fd, 2) = len;
|
||||
WFIFOB(fd, 4) = msg->type;
|
||||
WFIFOQ(fd, 5) = msg->id;
|
||||
WFIFOW(fd, 13) = msg_len;
|
||||
WFIFOQ(fd, 15) = msg->zeny;
|
||||
WFIFOB(fd, 23) = (uint8)count; // item count
|
||||
safestrncpy(WFIFOCP(fd, 24), msg->body, msg_len);
|
||||
|
||||
offset = 24 + msg_len;
|
||||
|
||||
for (j = 0; j < MAIL_MAX_ITEM; j++) {
|
||||
item = &msg->item[j];
|
||||
|
||||
if (item->nameid > 0 && item->amount > 0 && (data = itemdb_exists(item->nameid)) != NULL) {
|
||||
WFIFOW(fd, offset) = item->amount;
|
||||
WFIFOW(fd, offset + 2) = (data->view_id) ? data->view_id : item->nameid;
|
||||
WFIFOB(fd, offset + 4) = item->identify;
|
||||
WFIFOB(fd, offset + 5) = item->attribute;
|
||||
WFIFOB(fd, offset + 6) = item->refine;
|
||||
clif_addcards(WFIFOP(fd, offset + 7), item);
|
||||
// 4B unsigned long location
|
||||
WFIFOB(fd, offset + 15 + 4) = data->type;
|
||||
// 2B unsigned short wItemSpriteNumber
|
||||
//WFIFOW(fd, offset + 15 + 5) = data->view_id;
|
||||
// 2B unsigned short bindOnEquipType
|
||||
clif_add_random_options(WFIFOP(fd, offset + 15 + 9 ), item);
|
||||
offset += itemsize;
|
||||
}
|
||||
}
|
||||
|
||||
WFIFOSET(fd, len);
|
||||
#endif
|
||||
|
||||
if (msg->status == MAIL_UNREAD) {
|
||||
msg->status = MAIL_READ;
|
||||
intif_Mail_read(mail_id);
|
||||
clif_parse_Mail_refreshinbox(fd, sd);
|
||||
|
||||
sd->mail.inbox.unread--;
|
||||
clif_Mail_new(sd, 0, msg->send_name, msg->title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Request to open a mail (CZ_MAIL_OPEN).
|
||||
/// 0241 <mail id>.L
|
||||
void clif_parse_Mail_read(int fd, struct map_session_data *sd)
|
||||
{
|
||||
/// Request to open a mail.
|
||||
/// 0241 <mail id>.L (CZ_MAIL_OPEN)
|
||||
/// 09ea <mail tab>.B <mail id>.Q (CZ_REQ_READ_MAIL)
|
||||
void clif_parse_Mail_read(int fd, struct map_session_data *sd){
|
||||
#if PACKETVER < 20150513
|
||||
int mail_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
||||
#else
|
||||
uint8 openType = RFIFOB(fd, 2);
|
||||
int mail_id = (int)RFIFOQ(fd, 3);
|
||||
#endif
|
||||
|
||||
if( mail_id <= 0 )
|
||||
return;
|
||||
@ -14993,14 +15319,91 @@ void clif_parse_Mail_read(int fd, struct map_session_data *sd)
|
||||
clif_Mail_read(sd, mail_id);
|
||||
}
|
||||
|
||||
/// Allow a player to begin writing a mail
|
||||
/// 0a12 <receiver>.24B <success>.B (ZC_ACK_OPEN_WRITE_MAIL)
|
||||
void clif_send_Mail_beginwrite_ack( struct map_session_data *sd, char* name, bool success ){
|
||||
int fd = sd->fd;
|
||||
|
||||
/// Request to receive mail's attachment (CZ_MAIL_GET_ITEM).
|
||||
/// 0244 <mail id>.L
|
||||
void clif_parse_Mail_getattach(int fd, struct map_session_data *sd)
|
||||
{
|
||||
int mail_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
||||
WFIFOHEAD(fd, 27);
|
||||
WFIFOW(fd, 0) = 0xa12;
|
||||
safestrncpy(WFIFOCP(fd, 2), name, NAME_LENGTH);
|
||||
WFIFOB(fd, 26) = success;
|
||||
WFIFOSET(fd, 27);
|
||||
}
|
||||
|
||||
/// Request to start writing a mail
|
||||
/// 0a08 <receiver>.24B (CZ_REQ_OPEN_WRITE_MAIL)
|
||||
void clif_parse_Mail_beginwrite( int fd, struct map_session_data *sd ){
|
||||
char name[NAME_LENGTH];
|
||||
|
||||
safestrncpy(name, RFIFOCP(fd, 2), NAME_LENGTH);
|
||||
|
||||
if( sd->state.storage_flag || sd->state.mail_writing || sd->trade_partner ){
|
||||
clif_send_Mail_beginwrite_ack(sd, name, false);
|
||||
return;
|
||||
}
|
||||
|
||||
mail_clear(sd);
|
||||
|
||||
sd->state.mail_writing = true;
|
||||
clif_send_Mail_beginwrite_ack(sd, name,true);
|
||||
}
|
||||
|
||||
/// Notification that the client cancelled writing a mail
|
||||
/// 0a03 (CZ_REQ_CANCEL_WRITE_MAIL)
|
||||
void clif_parse_Mail_cancelwrite( int fd, struct map_session_data *sd ){
|
||||
sd->state.mail_writing = false;
|
||||
}
|
||||
|
||||
/// Give the client information about the recipient, if available
|
||||
/// 0a14 <char id>.L <class>.W <base level>.W (ZC_CHECK_RECEIVE_CHARACTER_NAME)
|
||||
/// 0a51 <char id>.L <class>.W <base level>.W <name>.24B (ZC_CHECK_RECEIVE_CHARACTER_NAME2)
|
||||
void clif_Mail_Receiver_Ack( struct map_session_data* sd, uint32 char_id, short class_, uint32 level, const char* name ){
|
||||
int fd = sd->fd;
|
||||
#if PACKETVER <= 20160302
|
||||
int cmd = 0xa14;
|
||||
#else
|
||||
int cmd = 0xa51;
|
||||
#endif
|
||||
|
||||
WFIFOHEAD(fd, packet_len(cmd));
|
||||
WFIFOW(fd, 0) = cmd;
|
||||
WFIFOL(fd, 2) = char_id;
|
||||
WFIFOW(fd, 6) = class_;
|
||||
WFIFOW(fd, 8) = level;
|
||||
#if PACKETVER >= 20160302
|
||||
strncpy(WFIFOCP(fd, 10), name, NAME_LENGTH);
|
||||
#endif
|
||||
WFIFOSET(fd, packet_len(cmd));
|
||||
}
|
||||
|
||||
/// Request information about the recipient
|
||||
/// 0a13 <name>.24B (CZ_CHECK_RECEIVE_CHARACTER_NAME)
|
||||
void clif_parse_Mail_Receiver_Check(int fd, struct map_session_data *sd) {
|
||||
static char name[NAME_LENGTH];
|
||||
|
||||
safestrncpy(name, RFIFOCP(fd, 2), NAME_LENGTH);
|
||||
|
||||
intif_mail_checkreceiver(sd, name);
|
||||
}
|
||||
|
||||
/// Request to receive mail's attachment.
|
||||
/// 0244 <mail id>.L (CZ_MAIL_GET_ITEM)
|
||||
/// 09f1 <mail id>.Q <mail tab>.B (CZ_REQ_ZENY_FROM_MAIL)
|
||||
/// 09f3 <mail id>.Q <mail tab>.B (CZ_REQ_ITEM_FROM_MAIL)
|
||||
void clif_parse_Mail_getattach( int fd, struct map_session_data *sd ){
|
||||
int i;
|
||||
bool fail = false;
|
||||
struct mail_message* msg;
|
||||
#if PACKETVER < 20150513
|
||||
int mail_id = RFIFOL(fd, packet_db[sd->packet_ver][RFIFOW(fd, 0)].pos[0]);
|
||||
int attachment = MAIL_ATT_ALL;
|
||||
#else
|
||||
uint16 packet_id = RFIFOW(fd, 0);
|
||||
int mail_id = (int)RFIFOQ(fd, 2);
|
||||
//int openType = RFIFOB(fd, 10);
|
||||
int attachment = packet_id == 0x9f1 ? MAIL_ATT_ZENY : packet_id == 0x9f3 ? MAIL_ATT_ITEM : MAIL_ATT_NONE;
|
||||
#endif
|
||||
|
||||
if( !chrif_isconnected() )
|
||||
return;
|
||||
@ -15013,54 +15416,89 @@ void clif_parse_Mail_getattach(int fd, struct map_session_data *sd)
|
||||
if( i == MAIL_MAX_INBOX )
|
||||
return;
|
||||
|
||||
if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) )
|
||||
return;
|
||||
msg = &sd->mail.inbox.msg[i];
|
||||
|
||||
if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY ) {
|
||||
clif_Mail_getattachment(fd, 1); //too many zeny
|
||||
return;
|
||||
if( attachment&MAIL_ATT_ZENY && msg->zeny < 1 ){
|
||||
attachment &= ~MAIL_ATT_ZENY;
|
||||
}
|
||||
|
||||
if( attachment&MAIL_ATT_ITEM ){
|
||||
ARR_FIND(0, MAIL_MAX_ITEM, i, msg->item[i].nameid > 0 || msg->item[i].amount > 0);
|
||||
|
||||
if( sd->mail.inbox.msg[i].item.nameid > 0 ) {
|
||||
struct item_data *data;
|
||||
unsigned int weight;
|
||||
|
||||
if ((data = itemdb_exists(sd->mail.inbox.msg[i].item.nameid)) == NULL)
|
||||
return;
|
||||
|
||||
switch( pc_checkadditem(sd, data->nameid, sd->mail.inbox.msg[i].item.amount) ) {
|
||||
case CHKADDITEM_NEW:
|
||||
fail = ( pc_inventoryblank(sd) == 0 );
|
||||
break;
|
||||
case CHKADDITEM_OVERAMOUNT:
|
||||
fail = true;
|
||||
}
|
||||
|
||||
if( fail ) {
|
||||
clif_Mail_getattachment(fd, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
weight = data->weight * sd->mail.inbox.msg[i].item.amount;
|
||||
if( sd->weight + weight > sd->max_weight ) {
|
||||
clif_Mail_getattachment(fd, 2);
|
||||
return;
|
||||
// No items were found
|
||||
if( i == MAIL_MAX_ITEM ){
|
||||
attachment &= ~MAIL_ATT_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
sd->mail.inbox.msg[i].zeny = 0;
|
||||
memset(&sd->mail.inbox.msg[i].item, 0, sizeof(struct item));
|
||||
// Either no attachment requested at all or there are no zeny or items in the mail
|
||||
if( attachment == MAIL_ATT_NONE ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( attachment&MAIL_ATT_ZENY ){
|
||||
if( msg->zeny + sd->status.zeny > MAX_ZENY ){
|
||||
clif_mail_getattachment(sd, msg, 1, MAIL_ATT_ZENY); //too many zeny
|
||||
return;
|
||||
}else{
|
||||
// To make sure another request fails
|
||||
msg->zeny = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( attachment&MAIL_ATT_ITEM ){
|
||||
int new_ = 0, totalweight = 0;
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
struct item* item = &msg->item[i];
|
||||
|
||||
if( item->nameid > 0 && item->amount > 0 ){
|
||||
struct item_data *data;
|
||||
|
||||
if((data = itemdb_exists(item->nameid)) == NULL)
|
||||
continue;
|
||||
|
||||
switch( pc_checkadditem(sd, item->nameid, item->amount) ){
|
||||
case CHKADDITEM_NEW:
|
||||
new_++;
|
||||
break;
|
||||
case CHKADDITEM_OVERAMOUNT:
|
||||
clif_mail_getattachment(sd, msg, 2, MAIL_ATT_ITEM);
|
||||
return;
|
||||
}
|
||||
|
||||
totalweight += data->weight * item->amount;
|
||||
}
|
||||
}
|
||||
|
||||
if( ( totalweight + sd->weight ) > sd->max_weight ){
|
||||
clif_mail_getattachment(sd, msg, 2, MAIL_ATT_ITEM);
|
||||
return;
|
||||
}else if( pc_inventoryblank(sd) < new_ ){
|
||||
clif_mail_getattachment(sd, msg, 2, MAIL_ATT_ITEM);
|
||||
return;
|
||||
}
|
||||
|
||||
// To make sure another request fails
|
||||
memset(msg->item, 0, MAIL_MAX_ITEM*sizeof(struct item));
|
||||
}
|
||||
|
||||
intif_mail_getattach(sd,msg,attachment);
|
||||
clif_Mail_read(sd, mail_id);
|
||||
|
||||
intif_Mail_getattach(sd->status.char_id, mail_id);
|
||||
}
|
||||
|
||||
|
||||
/// Request to delete a mail (CZ_MAIL_DELETE).
|
||||
/// 0243 <mail id>.L
|
||||
/// Request to delete a mail.
|
||||
/// 0243 <mail id>.L (CZ_MAIL_DELETE)
|
||||
/// 09f5 <mail tab>.B <mail id>.Q (CZ_REQ_DELETE_MAIL)
|
||||
void clif_parse_Mail_delete(int fd, struct map_session_data *sd){
|
||||
#if PACKETVER < 20150513
|
||||
int mail_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
||||
int i;
|
||||
#else
|
||||
int openType = RFIFOB(fd, 2);
|
||||
int mail_id = (int)RFIFOQ(fd, 3);
|
||||
#endif
|
||||
int i, j;
|
||||
|
||||
if( !chrif_isconnected() )
|
||||
return;
|
||||
@ -15073,12 +15511,24 @@ void clif_parse_Mail_delete(int fd, struct map_session_data *sd){
|
||||
if (i < MAIL_MAX_INBOX) {
|
||||
struct mail_message *msg = &sd->mail.inbox.msg[i];
|
||||
|
||||
if( (msg->item.nameid > 0 && msg->item.amount > 0) || msg->zeny > 0 ) {// can't delete mail without removing attachment first
|
||||
clif_Mail_delete(sd->fd, mail_id, 1);
|
||||
// can't delete mail without removing zeny first
|
||||
if( msg->zeny > 0 ){
|
||||
clif_mail_delete(sd, msg, false);
|
||||
return;
|
||||
}
|
||||
|
||||
intif_Mail_delete(sd->status.char_id, mail_id);
|
||||
// can't delete mail without removing attachment(s) first
|
||||
for( j = 0; j < MAIL_MAX_ITEM; j++ ){
|
||||
if( msg->item[j].nameid > 0 && msg->item[j].amount > 0 ){
|
||||
clif_mail_delete(sd, msg, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( intif_Mail_delete(sd->status.char_id, mail_id) && msg->status == MAIL_UNREAD ){
|
||||
sd->mail.inbox.unread--;
|
||||
clif_Mail_new(sd,0,NULL,NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15103,12 +15553,17 @@ void clif_parse_Mail_return(int fd, struct map_session_data *sd){
|
||||
}
|
||||
|
||||
|
||||
/// Request to add an item or Zeny to mail (CZ_MAIL_ADD_ITEM).
|
||||
/// 0247 <index>.W <amount>.L
|
||||
/// Request to add an item or Zeny to mail.
|
||||
/// 0247 <index>.W <amount>.L (CZ_MAIL_ADD_ITEM)
|
||||
/// 0a04 <index>.W <amount>.W (CZ_REQ_ADD_ITEM_TO_MAIL)
|
||||
void clif_parse_Mail_setattach(int fd, struct map_session_data *sd){
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
int idx = RFIFOW(fd,info->pos[0]);
|
||||
#if PACKETVER < 20150513
|
||||
int amount = RFIFOL(fd,info->pos[1]);
|
||||
#else
|
||||
int amount = RFIFOW(fd,info->pos[1]);
|
||||
#endif
|
||||
unsigned char flag;
|
||||
|
||||
if( !chrif_isconnected() )
|
||||
@ -15117,31 +15572,54 @@ void clif_parse_Mail_setattach(int fd, struct map_session_data *sd){
|
||||
return;
|
||||
|
||||
flag = mail_setitem(sd, idx, amount);
|
||||
clif_Mail_setattachment(fd,idx,!flag);
|
||||
|
||||
clif_Mail_setattachment(sd,idx,amount,flag);
|
||||
}
|
||||
|
||||
/// Remove an item from a mail
|
||||
/// 0a07 <result>.B <index>.W <amount>.W <weight>.W
|
||||
void clif_mail_removeitem( struct map_session_data* sd, bool success, int index, int amount ){
|
||||
int fd = sd->fd;
|
||||
|
||||
/// Request to reset mail item and/or Zeny (CZ_MAIL_RESET_ITEM).
|
||||
/// 0246 <type>.W
|
||||
WFIFOHEAD(fd, 9);
|
||||
WFIFOW(fd, 0) = 0xa07;
|
||||
WFIFOB(fd, 2) = success;
|
||||
WFIFOW(fd, 3) = index;
|
||||
WFIFOW(fd, 5) = amount;
|
||||
WFIFOW(fd, 7) = 0; // TODO: which weight? item weight? removed weight? remaining weight?
|
||||
WFIFOSET(fd, 9);
|
||||
}
|
||||
|
||||
/// Request to reset mail item and/or Zeny
|
||||
/// 0246 <type>.W (CZ_MAIL_RESET_ITEM)
|
||||
/// type:
|
||||
/// 0 = reset all
|
||||
/// 1 = remove item
|
||||
/// 2 = remove zeny
|
||||
/// 0a06 <index>.W <amount>.W (CZ_REQ_REMOVE_ITEM_MAIL)
|
||||
void clif_parse_Mail_winopen(int fd, struct map_session_data *sd)
|
||||
{
|
||||
#if PACKETVER < 20150513
|
||||
int type = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
|
||||
|
||||
if (type == 0 || type == 1)
|
||||
mail_removeitem(sd, 0);
|
||||
mail_removeitem(sd, 0, sd->mail.item[0].index, sd->mail.item[0].amount);
|
||||
if (type == 0 || type == 2)
|
||||
mail_removezeny(sd, 0);
|
||||
mail_removezeny(sd, false);
|
||||
#else
|
||||
uint16 index = RFIFOW(fd, 2);
|
||||
uint16 count = RFIFOW(fd, 4);
|
||||
|
||||
mail_removeitem(sd,0,index,count);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// Request to send mail (CZ_MAIL_SEND).
|
||||
/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B
|
||||
void clif_parse_Mail_send(int fd, struct map_session_data *sd)
|
||||
{
|
||||
/// Request to send mail
|
||||
/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B (CZ_MAIL_SEND)
|
||||
/// 09ec <packet len>.W <recipient>.24B <sender>.24B <zeny>.Q <title length>.W <body length>.W <title>.?B <body>.?B (CZ_REQ_WRITE_MAIL)
|
||||
/// 0a6e <packet len>.W <recipient>.24B <sender>.24B <zeny>.Q <title length>.W <body length>.W <char id>.L <title>.?B <body>.?B (CZ_REQ_WRITE_MAIL2)
|
||||
void clif_parse_Mail_send(int fd, struct map_session_data *sd){
|
||||
#if PACKETVER < 20150513
|
||||
struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
|
||||
|
||||
if( !chrif_isconnected() )
|
||||
@ -15153,6 +15631,59 @@ void clif_parse_Mail_send(int fd, struct map_session_data *sd)
|
||||
}
|
||||
|
||||
mail_send(sd, RFIFOCP(fd,info->pos[1]), RFIFOCP(fd,info->pos[2]), RFIFOCP(fd,info->pos[4]), RFIFOB(fd,info->pos[3]));
|
||||
#else
|
||||
unsigned short length;
|
||||
static char receiver[NAME_LENGTH];
|
||||
static char sender[NAME_LENGTH];
|
||||
char *title;
|
||||
char *text;
|
||||
uint64 zeny;
|
||||
uint16 titleLength;
|
||||
uint16 textLength;
|
||||
|
||||
length = RFIFOW(fd, 2);
|
||||
|
||||
if( length < 0x3e ){
|
||||
ShowWarning("Too short...\n");
|
||||
clif_Mail_send(sd, WRITE_MAIL_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Forged request without a begin writing packet?
|
||||
if( !sd->state.mail_writing ){
|
||||
return; // Ignore it
|
||||
}
|
||||
|
||||
safestrncpy(receiver, RFIFOCP(fd, 4), NAME_LENGTH);
|
||||
safestrncpy(sender, RFIFOCP(fd, 28), NAME_LENGTH);
|
||||
zeny = RFIFOQ(fd, 52);
|
||||
titleLength = RFIFOW(fd, 60);
|
||||
textLength = RFIFOW(fd, 62);
|
||||
|
||||
title = (char*)aMalloc(titleLength);
|
||||
text = (char*)aMalloc(textLength);
|
||||
|
||||
#if PACKETVER <= 20160330
|
||||
safestrncpy(title, RFIFOCP(fd, 64), titleLength);
|
||||
safestrncpy(text, RFIFOCP(fd, 64 + titleLength), textLength);
|
||||
#else
|
||||
// 64 = <char id>.L
|
||||
safestrncpy(title, RFIFOCP(fd, 68), titleLength);
|
||||
safestrncpy(text, RFIFOCP(fd, 68 + titleLength), textLength);
|
||||
#endif
|
||||
|
||||
if( zeny > 0 ){
|
||||
if( mail_setitem(sd,0,(uint32)zeny) != MAIL_ATTACH_SUCCESS ){
|
||||
clif_Mail_send(sd,WRITE_MAIL_FAILED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mail_send(sd, receiver, title, text, textLength);
|
||||
|
||||
aFree(title);
|
||||
aFree(text);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -19661,20 +20192,20 @@ void packetdb_readdb(bool reload)
|
||||
0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 23, 17, 0, 0,102, 0,
|
||||
0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, -1, -1, -1, 0, 7,
|
||||
0, 0, 0, 0, 0, 18, 22, 3, 11, 0, 11, -1, 0, 3, 11, 0,
|
||||
0, 11, 12, 11, 0, 0, 0, 75, -1,143, 0, 0, 0, -1, -1, -1,
|
||||
-1, 11, 12, 11, 0, 0, 0, 75, -1,143, 0, 0, 0, -1, -1, -1,
|
||||
//#0x0A00
|
||||
#if PACKETVER >= 20141022
|
||||
269, 3, 4, 2, 6, 49, 6, 9, 26, 45, 47, 47, 56, -1, 14, -1,
|
||||
#else
|
||||
269, 0, 0, 2, 6, 48, 6, 9, 26, 45, 47, 47, 56, -1, 14, 0,
|
||||
#endif
|
||||
-1, 0, 0, 26, 0, 0, 0, 0, 14, 2, 23, 2, -1, 2, 3, 2,
|
||||
-1, 0, 0, 26, 10, 0, 0, 0, 14, 2, 23, 2, -1, 2, 3, 2,
|
||||
21, 3, 5, 0, 66, 0, 0, 8, 3, 0, 0, -1, 0, -1, 0, 0,
|
||||
106, 0, 0, 0, 0, 4, 0, 59, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
//#0x0A40
|
||||
0, 0, 0, 85, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
//#0x0A80
|
||||
0, 0, 0, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@ -19851,6 +20382,9 @@ void packetdb_readdb(bool reload)
|
||||
{clif_parse_Mail_setattach,"mailsetattach"},
|
||||
{clif_parse_Mail_winopen,"mailwinopen"},
|
||||
{clif_parse_Mail_send,"mailsend"},
|
||||
{clif_parse_Mail_beginwrite,"mailbegin"},
|
||||
{clif_parse_Mail_cancelwrite,"mailcancel"},
|
||||
{clif_parse_Mail_Receiver_Check,"mailreceiver"},
|
||||
// AUCTION SYSTEM
|
||||
{clif_parse_Auction_search,"auctionsearch"},
|
||||
{clif_parse_Auction_buysell,"auctionbuysell"},
|
||||
|
@ -866,14 +866,25 @@ void do_init_clif(void);
|
||||
void do_final_clif(void);
|
||||
|
||||
// MAIL SYSTEM
|
||||
enum mail_send_result{
|
||||
WRITE_MAIL_SUCCESS = 0x0,
|
||||
WRITE_MAIL_FAILED = 0x1,
|
||||
WRITE_MAIL_FAILED_CNT = 0x2,
|
||||
WRITE_MAIL_FAILED_ITEM = 0x3,
|
||||
WRITE_MAIL_FAILED_CHECK_CHARACTER_NAME = 0x4,
|
||||
WRITE_MAIL_FAILED_WHISPEREXREGISTER = 0x5,
|
||||
};
|
||||
|
||||
void clif_Mail_window(int fd, int flag);
|
||||
void clif_Mail_read(struct map_session_data *sd, int mail_id);
|
||||
void clif_Mail_delete(int fd, int mail_id, short fail);
|
||||
void clif_mail_delete(struct map_session_data* sd, struct mail_message *msg, bool success);
|
||||
void clif_Mail_return(int fd, int mail_id, short fail);
|
||||
void clif_Mail_send(int fd, bool fail);
|
||||
void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title);
|
||||
void clif_Mail_refreshinbox(struct map_session_data *sd);
|
||||
void clif_Mail_getattachment(int fd, uint8 flag);
|
||||
void clif_Mail_send(struct map_session_data* sd, enum mail_send_result result);
|
||||
void clif_Mail_new(struct map_session_data* sd, int mail_id, const char *sender, const char *title);
|
||||
void clif_Mail_refreshinbox(struct map_session_data *sd,enum mail_inbox_type type,int64 mailID);
|
||||
void clif_mail_getattachment(struct map_session_data* sd, struct mail_message *msg, uint8 result, enum mail_attachment_type type);
|
||||
void clif_Mail_Receiver_Ack(struct map_session_data* sd, uint32 char_id, short class_, uint32 level, const char* name);
|
||||
void clif_mail_removeitem(struct map_session_data* sd, bool success, int index, int amount);
|
||||
// AUCTION SYSTEM
|
||||
void clif_Auction_openwindow(struct map_session_data *sd);
|
||||
void clif_Auction_results(struct map_session_data *sd, short count, short pages, uint8 *buf);
|
||||
|
@ -52,6 +52,7 @@ int date_get_year(void);
|
||||
enum e_month date_get_month(void);
|
||||
int date_get_dayofmonth(void);
|
||||
enum e_dayofweek date_get_dayofweek(void);
|
||||
int date_get_dayofyear(void);
|
||||
int date_get_day(void);
|
||||
int date_get_hour(void);
|
||||
int date_get_min(void);
|
||||
|
123
src/map/intif.c
123
src/map/intif.c
@ -32,7 +32,7 @@ static const int packet_len_table[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -1,11, 0, 0, 0, 0, 0, 0, //0x3810
|
||||
39,-1,15,15, 15+NAME_LENGTH,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820
|
||||
10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
|
||||
-1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
|
||||
-1, 0, 0,14, 0, 0, 0, 0, -1,75,-1,11, 11,-1, 38, 0, //0x3840
|
||||
-1,-1, 7, 7, 7,11, 8,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari]
|
||||
-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]
|
||||
@ -2087,16 +2087,17 @@ int intif_quest_save(struct map_session_data *sd)
|
||||
* @param flag 0 Update Inbox | 1 OpenMail
|
||||
* @return 0=errur, 1=msg_sent
|
||||
*/
|
||||
int intif_Mail_requestinbox(uint32 char_id, unsigned char flag)
|
||||
int intif_Mail_requestinbox(uint32 char_id, unsigned char flag, enum mail_inbox_type type)
|
||||
{
|
||||
if (CheckForCharServer())
|
||||
return 0;
|
||||
|
||||
WFIFOHEAD(inter_fd,7);
|
||||
WFIFOHEAD(inter_fd,8);
|
||||
WFIFOW(inter_fd,0) = 0x3048;
|
||||
WFIFOL(inter_fd,2) = char_id;
|
||||
WFIFOB(inter_fd,6) = flag;
|
||||
WFIFOSET(inter_fd,7);
|
||||
WFIFOB(inter_fd,7) = type;
|
||||
WFIFOSET(inter_fd,8);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2120,24 +2121,29 @@ int intif_parse_Mail_inboxreceived(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (RFIFOW(fd,2) - 9 != sizeof(struct mail_data))
|
||||
if (RFIFOW(fd,2) - 10 != sizeof(struct mail_data))
|
||||
{
|
||||
ShowError("intif_parse_Mail_inboxreceived: data size error %d %d\n", RFIFOW(fd,2) - 9, sizeof(struct mail_data));
|
||||
ShowError("intif_parse_Mail_inboxreceived: data size error %d %d\n", RFIFOW(fd,2) - 10, sizeof(struct mail_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
//FIXME: this operation is not safe [ultramage]
|
||||
memcpy(&sd->mail.inbox, RFIFOP(fd,9), sizeof(struct mail_data));
|
||||
memcpy(&sd->mail.inbox, RFIFOP(fd,10), sizeof(struct mail_data));
|
||||
sd->mail.changed = false; // cache is now in sync
|
||||
|
||||
if (flag)
|
||||
clif_Mail_refreshinbox(sd);
|
||||
else if( battle_config.mail_show_status && ( battle_config.mail_show_status == 1 || sd->mail.inbox.unread ) )
|
||||
if (flag){
|
||||
#if PACKETVER >= 20150513
|
||||
// Refresh top right icon
|
||||
clif_Mail_new(sd, 0, NULL, NULL);
|
||||
#endif
|
||||
clif_Mail_refreshinbox(sd,RFIFOB(fd,9),0);
|
||||
}else if( battle_config.mail_show_status && ( battle_config.mail_show_status == 1 || sd->mail.inbox.unread ) )
|
||||
{
|
||||
char output[128];
|
||||
sprintf(output, msg_txt(sd,510), sd->mail.inbox.unchecked, sd->mail.inbox.unread + sd->mail.inbox.unchecked);
|
||||
clif_messagecolor(&sd->bl, color_table[COLOR_LIGHT_GREEN], output, false, SELF);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -2165,18 +2171,18 @@ int intif_Mail_read(int mail_id)
|
||||
* @param mail_id : Mail identification
|
||||
* @return 0=error, 1=msg sent
|
||||
*/
|
||||
int intif_Mail_getattach(uint32 char_id, int mail_id)
|
||||
{
|
||||
bool intif_mail_getattach( struct map_session_data* sd, struct mail_message *msg, enum mail_attachment_type type){
|
||||
if (CheckForCharServer())
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
WFIFOHEAD(inter_fd,10);
|
||||
WFIFOHEAD(inter_fd,11);
|
||||
WFIFOW(inter_fd,0) = 0x304a;
|
||||
WFIFOL(inter_fd,2) = char_id;
|
||||
WFIFOL(inter_fd,6) = mail_id;
|
||||
WFIFOSET(inter_fd, 10);
|
||||
WFIFOL(inter_fd,2) = sd->status.char_id;
|
||||
WFIFOL(inter_fd,6) = msg->id;
|
||||
WFIFOB(inter_fd,10) = (uint8)type;
|
||||
WFIFOSET(inter_fd, 11);
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2187,8 +2193,14 @@ int intif_Mail_getattach(uint32 char_id, int mail_id)
|
||||
int intif_parse_Mail_getattach(int fd)
|
||||
{
|
||||
struct map_session_data *sd;
|
||||
struct item item;
|
||||
int zeny = RFIFOL(fd,8);
|
||||
struct item item[MAIL_MAX_ITEM];
|
||||
int i, mail_id, zeny;
|
||||
|
||||
if (RFIFOW(fd, 2) - 16 != sizeof(struct item)*MAIL_MAX_ITEM)
|
||||
{
|
||||
ShowError("intif_parse_Mail_getattach: data size error %d %d\n", RFIFOW(fd, 2) - 16, sizeof(struct item));
|
||||
return 0;
|
||||
}
|
||||
|
||||
sd = map_charid2sd( RFIFOL(fd,4) );
|
||||
|
||||
@ -2198,15 +2210,17 @@ int intif_parse_Mail_getattach(int fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (RFIFOW(fd,2) - 12 != sizeof(struct item))
|
||||
{
|
||||
ShowError("intif_parse_Mail_getattach: data size error %d %d\n", RFIFOW(fd,2) - 16, sizeof(struct item));
|
||||
mail_id = RFIFOL(fd, 8);
|
||||
|
||||
ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
|
||||
if (i == MAIL_MAX_INBOX)
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&item, RFIFOP(fd,12), sizeof(struct item));
|
||||
zeny = RFIFOL(fd, 12);
|
||||
|
||||
mail_getattachment(sd, zeny, &item);
|
||||
memcpy(item, RFIFOP(fd,16), sizeof(struct item)*MAIL_MAX_ITEM);
|
||||
|
||||
mail_getattachment(sd, &sd->mail.inbox.msg[i], zeny, item);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -2240,6 +2254,7 @@ int intif_parse_Mail_delete(int fd)
|
||||
uint32 char_id = RFIFOL(fd,2);
|
||||
int mail_id = RFIFOL(fd,6);
|
||||
bool failed = RFIFOB(fd,10);
|
||||
enum mail_inbox_type type;
|
||||
|
||||
struct map_session_data *sd = map_charid2sd(char_id);
|
||||
if (sd == NULL)
|
||||
@ -2254,15 +2269,16 @@ int intif_parse_Mail_delete(int fd)
|
||||
ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
|
||||
if( i < MAIL_MAX_INBOX )
|
||||
{
|
||||
clif_mail_delete(sd, &sd->mail.inbox.msg[i], !failed);
|
||||
type = sd->mail.inbox.msg[i].type;
|
||||
memset(&sd->mail.inbox.msg[i], 0, sizeof(struct mail_message));
|
||||
sd->mail.inbox.amount--;
|
||||
}
|
||||
|
||||
if( sd->mail.inbox.full )
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1); // Free space is available for new mails
|
||||
if( sd->mail.inbox.full || sd->mail.inbox.unchecked > 0 )
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1, type); // Free space is available for new mails
|
||||
}
|
||||
|
||||
clif_Mail_delete(sd->fd, mail_id, failed);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -2300,6 +2316,7 @@ int intif_parse_Mail_return(int fd)
|
||||
struct map_session_data *sd = map_charid2sd(RFIFOL(fd,2));
|
||||
int mail_id = RFIFOL(fd,6);
|
||||
short fail = RFIFOB(fd,10);
|
||||
enum mail_inbox_type type;
|
||||
|
||||
if( sd == NULL )
|
||||
{
|
||||
@ -2313,12 +2330,13 @@ int intif_parse_Mail_return(int fd)
|
||||
ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
|
||||
if( i < MAIL_MAX_INBOX )
|
||||
{
|
||||
type = sd->mail.inbox.msg[i].type;
|
||||
memset(&sd->mail.inbox.msg[i], 0, sizeof(struct mail_message));
|
||||
sd->mail.inbox.amount--;
|
||||
}
|
||||
|
||||
if( sd->mail.inbox.full )
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1); // Free space is available for new mails
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1, type); // Free space is available for new mails
|
||||
}
|
||||
|
||||
clif_Mail_return(sd->fd, mail_id, fail);
|
||||
@ -2379,7 +2397,7 @@ static void intif_parse_Mail_send(int fd)
|
||||
mail_deliveryfail(sd, &msg);
|
||||
else
|
||||
{
|
||||
clif_Mail_send(sd->fd, false);
|
||||
clif_Mail_send(sd, WRITE_MAIL_SUCCESS);
|
||||
if( save_settings&CHARSAVE_MAIL )
|
||||
chrif_save(sd, CSAVE_INVENTORY);
|
||||
}
|
||||
@ -2399,9 +2417,47 @@ static void intif_parse_Mail_new(int fd)
|
||||
|
||||
if( sd == NULL )
|
||||
return;
|
||||
|
||||
sd->mail.changed = true;
|
||||
clif_Mail_new(sd->fd, mail_id, sender_name, title);
|
||||
sd->mail.inbox.unread++;
|
||||
clif_Mail_new(sd, mail_id, sender_name, title);
|
||||
#if PACKETVER >= 20150513
|
||||
// Make sure the window gets refreshed when its open
|
||||
intif_Mail_requestinbox(sd->status.char_id, 1, RFIFOB(fd,74));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void intif_parse_Mail_receiver( int fd ){
|
||||
struct map_session_data *sd;
|
||||
|
||||
sd = map_charid2sd( RFIFOL( fd, 2 ) );
|
||||
|
||||
// Only f the player is online
|
||||
if( sd ){
|
||||
clif_Mail_Receiver_Ack( sd, RFIFOL( fd, 6 ), RFIFOW( fd, 10 ), RFIFOW( fd, 12 ), RFIFOCP( fd, 14 ) );
|
||||
}
|
||||
}
|
||||
|
||||
bool intif_mail_checkreceiver( struct map_session_data* sd, char* name ){
|
||||
struct map_session_data *tsd;
|
||||
|
||||
tsd = map_nick2sd( name, false );
|
||||
|
||||
// If the target player is online on this map-server
|
||||
if( tsd != NULL ){
|
||||
clif_Mail_Receiver_Ack( sd, tsd->status.char_id, tsd->status.class_, tsd->status.base_level, name );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( CheckForCharServer() )
|
||||
return false;
|
||||
|
||||
WFIFOHEAD(inter_fd, 6 + NAME_LENGTH);
|
||||
WFIFOW(inter_fd, 0) = 0x304e;
|
||||
WFIFOL(inter_fd, 2) = sd->status.char_id;
|
||||
safestrncpy(WFIFOCP(inter_fd, 6), name, NAME_LENGTH);
|
||||
WFIFOSET(inter_fd, 6 + NAME_LENGTH);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
@ -3525,6 +3581,7 @@ int intif_parse(int fd)
|
||||
case 0x384b: intif_parse_Mail_delete(fd); break;
|
||||
case 0x384c: intif_parse_Mail_return(fd); break;
|
||||
case 0x384d: intif_parse_Mail_send(fd); break;
|
||||
case 0x384e: intif_parse_Mail_receiver(fd); break;
|
||||
|
||||
// Auction System
|
||||
case 0x3850: intif_parse_Auction_results(fd); break;
|
||||
|
@ -89,12 +89,13 @@ int intif_mercenary_delete(int merc_id);
|
||||
int intif_mercenary_save(struct s_mercenary *merc);
|
||||
|
||||
// MAIL SYSTEM
|
||||
int intif_Mail_requestinbox(uint32 char_id, unsigned char flag);
|
||||
int intif_Mail_requestinbox(uint32 char_id, unsigned char flag, enum mail_inbox_type type);
|
||||
int intif_Mail_read(int mail_id);
|
||||
int intif_Mail_getattach(uint32 char_id, int mail_id);
|
||||
bool intif_mail_getattach( struct map_session_data* sd, struct mail_message *msg, enum mail_attachment_type type );
|
||||
int intif_Mail_delete(uint32 char_id, int mail_id);
|
||||
int intif_Mail_return(uint32 char_id, int mail_id);
|
||||
int intif_Mail_send(uint32 account_id, struct mail_message *msg);
|
||||
bool intif_mail_checkreceiver(struct map_session_data* sd, char* name);
|
||||
// AUCTION SYSTEM
|
||||
int intif_Auction_requestlist(uint32 char_id, short type, int price, const char* searchtext, short page);
|
||||
int intif_Auction_register(struct auction_data *auction);
|
||||
|
313
src/map/mail.c
313
src/map/mail.c
@ -11,48 +11,98 @@
|
||||
#include "clif.h"
|
||||
#include "pc.h"
|
||||
#include "intif.h"
|
||||
#include "date.h" // date_get_dayofyear
|
||||
|
||||
void mail_clear(struct map_session_data *sd)
|
||||
{
|
||||
sd->mail.nameid = 0;
|
||||
sd->mail.index = 0;
|
||||
sd->mail.amount = 0;
|
||||
int i;
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
sd->mail.item[i].nameid = 0;
|
||||
sd->mail.item[i].index = 0;
|
||||
sd->mail.item[i].amount = 0;
|
||||
}
|
||||
sd->mail.zeny = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int mail_removeitem(struct map_session_data *sd, short flag)
|
||||
int mail_removeitem(struct map_session_data *sd, short flag, int idx, int amount)
|
||||
{
|
||||
int i;
|
||||
|
||||
nullpo_ret(sd);
|
||||
|
||||
if( sd->mail.amount )
|
||||
{
|
||||
if (flag) // Item send
|
||||
pc_delitem(sd, sd->mail.index, sd->mail.amount, 1, 0, LOG_TYPE_MAIL);
|
||||
else
|
||||
clif_additem(sd, sd->mail.index, sd->mail.amount, 0);
|
||||
idx -= 2;
|
||||
|
||||
if( idx < 0 || idx >= MAX_INVENTORY )
|
||||
return false;
|
||||
if( amount <= 0 || amount > sd->inventory.u.items_inventory[idx].amount )
|
||||
return false;
|
||||
|
||||
ARR_FIND(0, MAIL_MAX_ITEM, i, sd->mail.item[i].index == idx && sd->mail.item[i].nameid > 0);
|
||||
|
||||
if( i == MAIL_MAX_ITEM ){
|
||||
return false;
|
||||
}
|
||||
|
||||
if( flag ){
|
||||
if( battle_config.mail_attachment_price > 0 ){
|
||||
if( pc_payzeny( sd, battle_config.mail_attachment_price, LOG_TYPE_MAIL, NULL ) ){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
// With client update packet
|
||||
pc_delitem(sd, idx, amount, 1, 0, LOG_TYPE_MAIL);
|
||||
#else
|
||||
// RODEX refreshes the client inventory from the ACK packet
|
||||
pc_delitem(sd, idx, amount, 0, 0, LOG_TYPE_MAIL);
|
||||
#endif
|
||||
}else{
|
||||
for( ; i < MAIL_MAX_ITEM-1; i++ ){
|
||||
if (sd->mail.item[i + 1].nameid == 0)
|
||||
break;
|
||||
sd->mail.item[i].index = sd->mail.item[i+1].index;
|
||||
sd->mail.item[i].nameid = sd->mail.item[i+1].nameid;
|
||||
sd->mail.item[i].amount = sd->mail.item[i+1].amount;
|
||||
}
|
||||
|
||||
for( ; i < MAIL_MAX_ITEM; i++ ){
|
||||
sd->mail.item[i].index = 0;
|
||||
sd->mail.item[i].nameid = 0;
|
||||
sd->mail.item[i].amount = 0;
|
||||
}
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
clif_additem(sd, idx, amount, 0);
|
||||
#else
|
||||
clif_mail_removeitem(sd, true, idx + 2, amount);
|
||||
#endif
|
||||
}
|
||||
|
||||
sd->mail.nameid = 0;
|
||||
sd->mail.index = 0;
|
||||
sd->mail.amount = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mail_removezeny(struct map_session_data *sd, short flag)
|
||||
{
|
||||
nullpo_ret(sd);
|
||||
bool mail_removezeny( struct map_session_data *sd, bool flag ){
|
||||
nullpo_retr( false, sd );
|
||||
|
||||
if (flag && sd->mail.zeny > 0)
|
||||
{ //Zeny send
|
||||
pc_payzeny(sd,sd->mail.zeny,LOG_TYPE_MAIL, NULL);
|
||||
if( sd->mail.zeny > 0 ){
|
||||
//Zeny send
|
||||
if( flag ){
|
||||
if( pc_payzeny( sd, sd->mail.zeny + sd->mail.zeny * battle_config.mail_zeny_fee / 100, LOG_TYPE_MAIL, NULL ) ){
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
// Update is called by pc_payzeny, so only call it in the else condition
|
||||
clif_updatestatus(sd, SP_ZENY);
|
||||
}
|
||||
}
|
||||
if (sd->mail.zeny > 0)
|
||||
clif_updatestatus(sd, SP_ZENY);
|
||||
|
||||
sd->mail.zeny = 0;
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,92 +110,175 @@ int mail_removezeny(struct map_session_data *sd, short flag)
|
||||
* @param sd : player attaching the content
|
||||
* @param idx 0 - Zeny; >= 2 - Inventory item
|
||||
* @param amount : amout of zeny or number of item
|
||||
* @return True if item/zeny can be set, False if failed
|
||||
* @return see enum mail_attach_result in mail.h
|
||||
*/
|
||||
bool mail_setitem(struct map_session_data *sd, short idx, uint32 amount) {
|
||||
|
||||
enum mail_attach_result mail_setitem(struct map_session_data *sd, short idx, uint32 amount) {
|
||||
if( pc_istrading(sd) )
|
||||
return false;
|
||||
return MAIL_ATTACH_ERROR;
|
||||
|
||||
if( idx == 0 ) { // Zeny Transfer
|
||||
if( !pc_can_give_items(sd) )
|
||||
return false;
|
||||
return MAIL_ATTACH_UNTRADEABLE;
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
if( amount > sd->status.zeny )
|
||||
amount = sd->status.zeny;
|
||||
amount = sd->status.zeny; // TODO: confirm this behavior for old mail system
|
||||
#else
|
||||
if( ( amount + battle_config.mail_zeny_fee / 100 * amount ) > sd->status.zeny )
|
||||
return MAIL_ATTACH_ERROR;
|
||||
#endif
|
||||
|
||||
sd->mail.zeny = amount;
|
||||
// clif_updatestatus(sd, SP_ZENY);
|
||||
return true;
|
||||
return MAIL_ATTACH_SUCCESS;
|
||||
} else { // Item Transfer
|
||||
int i, j, total = 0;
|
||||
|
||||
idx -= 2;
|
||||
mail_removeitem(sd, 0);
|
||||
|
||||
if( idx < 0 || idx >= MAX_INVENTORY )
|
||||
return false;
|
||||
return MAIL_ATTACH_ERROR;
|
||||
|
||||
#if PACKETVER < 20150513
|
||||
i = 0;
|
||||
// Remove existing item
|
||||
mail_removeitem(sd, 0, sd->mail.item[i].index + 2, sd->mail.item[i].amount);
|
||||
#else
|
||||
ARR_FIND(0, MAIL_MAX_ITEM, i, sd->mail.item[i].index == idx && sd->mail.item[i].nameid > 0 );
|
||||
|
||||
// The same item had already been added to the mail
|
||||
if( i < MAIL_MAX_ITEM ){
|
||||
// Check if it is stackable
|
||||
if( !itemdb_isstackable(sd->mail.item[i].nameid) ){
|
||||
return MAIL_ATTACH_ERROR;
|
||||
}
|
||||
|
||||
// Check if it exceeds the total amount
|
||||
if( ( amount + sd->mail.item[i].amount ) > sd->inventory.u.items_inventory[idx].amount ){
|
||||
return MAIL_ATTACH_ERROR;
|
||||
}
|
||||
|
||||
// Check if it exceeds the total weight
|
||||
if( battle_config.mail_attachment_weight ){
|
||||
for( j = 0; j < i; j++ ){
|
||||
total += sd->mail.item[j].amount * ( sd->inventory_data[sd->mail.item[j].index]->weight / 10 );
|
||||
}
|
||||
|
||||
total += amount * sd->inventory_data[idx]->weight / 10;
|
||||
|
||||
if( total > battle_config.mail_attachment_weight ){
|
||||
return MAIL_ATTACH_WEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
sd->mail.item[i].amount += amount;
|
||||
|
||||
return MAIL_ATTACH_SUCCESS;
|
||||
}else{
|
||||
ARR_FIND(0, MAIL_MAX_ITEM, i, sd->mail.item[i].nameid == 0);
|
||||
|
||||
if( i == MAIL_MAX_ITEM ){
|
||||
return MAIL_ATTACH_SPACE;
|
||||
}
|
||||
|
||||
// Check if it exceeds the total weight
|
||||
if( battle_config.mail_attachment_weight ){
|
||||
for( j = 0; j < i; j++ ){
|
||||
total += sd->mail.item[j].amount * ( sd->inventory_data[sd->mail.item[j].index]->weight / 10 );
|
||||
}
|
||||
|
||||
total += amount * sd->inventory_data[idx]->weight / 10;
|
||||
|
||||
if( total > battle_config.mail_attachment_weight ){
|
||||
return MAIL_ATTACH_WEIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( amount > sd->inventory.u.items_inventory[idx].amount )
|
||||
return false;
|
||||
return MAIL_ATTACH_ERROR;
|
||||
if( !pc_can_give_items(sd) || sd->inventory.u.items_inventory[idx].expire_time
|
||||
|| !itemdb_available(sd->inventory.u.items_inventory[idx].nameid)
|
||||
|| !itemdb_canmail(&sd->inventory.u.items_inventory[idx],pc_get_group_level(sd))
|
||||
|| (sd->inventory.u.items_inventory[idx].bound && !pc_can_give_bounded_items(sd)) )
|
||||
return false;
|
||||
return MAIL_ATTACH_UNTRADEABLE;
|
||||
|
||||
sd->mail.index = idx;
|
||||
sd->mail.nameid = sd->inventory.u.items_inventory[idx].nameid;
|
||||
sd->mail.amount = amount;
|
||||
return true;
|
||||
sd->mail.item[i].index = idx;
|
||||
sd->mail.item[i].nameid = sd->inventory.u.items_inventory[idx].nameid;
|
||||
sd->mail.item[i].amount = amount;
|
||||
return MAIL_ATTACH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
bool mail_setattachment(struct map_session_data *sd, struct mail_message *msg)
|
||||
{
|
||||
int n;
|
||||
int i, amount;
|
||||
|
||||
nullpo_retr(false,sd);
|
||||
nullpo_retr(false,msg);
|
||||
|
||||
if( sd->mail.zeny < 0 || sd->mail.zeny > sd->status.zeny )
|
||||
return false;
|
||||
for( i = 0, amount = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
int index = sd->mail.item[i].index;
|
||||
|
||||
n = sd->mail.index;
|
||||
if( sd->mail.amount )
|
||||
{
|
||||
if( sd->inventory.u.items_inventory[n].nameid != sd->mail.nameid )
|
||||
if( sd->mail.item[i].nameid == 0 || sd->mail.item[i].amount == 0 ){
|
||||
memset(&msg->item[i], 0x00, sizeof(struct item));
|
||||
continue;
|
||||
}
|
||||
|
||||
amount++;
|
||||
|
||||
if( sd->inventory.u.items_inventory[index].nameid != sd->mail.item[i].nameid )
|
||||
return false;
|
||||
|
||||
if( sd->inventory.u.items_inventory[n].amount < sd->mail.amount )
|
||||
if( sd->inventory.u.items_inventory[index].amount < sd->mail.item[i].amount )
|
||||
return false;
|
||||
|
||||
if( sd->weight > sd->max_weight )
|
||||
if( sd->weight > sd->max_weight ) // TODO: Why check something weird like this here?
|
||||
return false;
|
||||
|
||||
memcpy(&msg->item, &sd->inventory.u.items_inventory[n], sizeof(struct item));
|
||||
msg->item.amount = sd->mail.amount;
|
||||
memcpy(&msg->item[i], &sd->inventory.u.items_inventory[index], sizeof(struct item));
|
||||
msg->item[i].amount = sd->mail.item[i].amount;
|
||||
}
|
||||
else
|
||||
memset(&msg->item, 0x00, sizeof(struct item));
|
||||
|
||||
if( sd->mail.zeny < 0 || ( sd->mail.zeny + sd->mail.zeny * battle_config.mail_zeny_fee / 100 + amount * battle_config.mail_attachment_price ) > sd->status.zeny )
|
||||
return false;
|
||||
|
||||
msg->zeny = sd->mail.zeny;
|
||||
|
||||
// Removes the attachment from sender
|
||||
mail_removeitem(sd,1);
|
||||
mail_removezeny(sd,1);
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
if( sd->mail.item[i].nameid == 0 || sd->mail.item[i].amount == 0 ){
|
||||
// Exit the loop on the first empty entry
|
||||
break;
|
||||
}
|
||||
|
||||
mail_removeitem(sd,1,sd->mail.item[i].index + 2,sd->mail.item[i].amount);
|
||||
}
|
||||
mail_removezeny(sd,true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void mail_getattachment(struct map_session_data* sd, int zeny, struct item* item)
|
||||
{
|
||||
if( item->nameid > 0 && item->amount > 0 )
|
||||
{
|
||||
pc_additem(sd, item, item->amount, LOG_TYPE_MAIL);
|
||||
clif_Mail_getattachment(sd->fd, 0);
|
||||
void mail_getattachment(struct map_session_data* sd, struct mail_message* msg, int zeny, struct item* item){
|
||||
int i;
|
||||
bool item_received = false;
|
||||
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
if( item->nameid > 0 && item->amount > 0 ){
|
||||
pc_additem(sd, &item[i], item[i].amount, LOG_TYPE_MAIL);
|
||||
item_received = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( zeny > 0 )
|
||||
{ //Zeny receive
|
||||
if( item_received ){
|
||||
clif_mail_getattachment( sd, msg, 0, MAIL_ATT_ITEM );
|
||||
}
|
||||
|
||||
// Zeny receive
|
||||
if( zeny > 0 ){
|
||||
pc_getzeny(sd, zeny,LOG_TYPE_MAIL, NULL);
|
||||
clif_mail_getattachment( sd, msg, 0, MAIL_ATT_ZENY );
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,33 +294,37 @@ int mail_openmail(struct map_session_data *sd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void mail_deliveryfail(struct map_session_data *sd, struct mail_message *msg)
|
||||
{
|
||||
void mail_deliveryfail(struct map_session_data *sd, struct mail_message *msg){
|
||||
int i, zeny = 0;
|
||||
|
||||
nullpo_retv(sd);
|
||||
nullpo_retv(msg);
|
||||
|
||||
if( msg->item.amount > 0 )
|
||||
{
|
||||
// Item receive (due to failure)
|
||||
pc_additem(sd, &msg->item, msg->item.amount, LOG_TYPE_MAIL);
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
if( msg->item[i].amount > 0 ){
|
||||
// Item receive (due to failure)
|
||||
pc_additem(sd, &msg->item[i], msg->item[i].amount, LOG_TYPE_MAIL);
|
||||
zeny += battle_config.mail_attachment_price;
|
||||
}
|
||||
}
|
||||
|
||||
if( msg->zeny > 0 )
|
||||
{
|
||||
pc_getzeny(sd,msg->zeny,LOG_TYPE_MAIL, NULL); //Zeny receive (due to failure)
|
||||
if( msg->zeny > 0 ){
|
||||
pc_getzeny(sd,msg->zeny + msg->zeny*battle_config.mail_zeny_fee/100 + zeny,LOG_TYPE_MAIL, NULL); //Zeny receive (due to failure)
|
||||
}
|
||||
|
||||
clif_Mail_send(sd->fd, true);
|
||||
clif_Mail_send(sd, WRITE_MAIL_FAILED);
|
||||
}
|
||||
|
||||
// This function only check if the mail operations are valid
|
||||
bool mail_invalid_operation(struct map_session_data *sd)
|
||||
{
|
||||
#if PACKETVER < 20150513
|
||||
if( !map[sd->bl.m].flag.town && !pc_can_use_command(sd, "mail", COMMAND_ATCOMMAND) )
|
||||
{
|
||||
ShowWarning("clif_parse_Mail: char '%s' trying to do invalid mail operations.\n", sd->status.name);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -210,17 +347,33 @@ void mail_send(struct map_session_data *sd, const char *dest_name, const char *t
|
||||
|
||||
if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 ) {
|
||||
clif_displaymessage(sd->fd,msg_txt(sd,675)); //"Cannot send mails too fast!!."
|
||||
clif_Mail_send(sd->fd, true); // fail
|
||||
clif_Mail_send(sd, WRITE_MAIL_FAILED); // fail
|
||||
return;
|
||||
}
|
||||
|
||||
if( battle_config.mail_daily_count ){
|
||||
mail_refresh_remaining_amount(sd);
|
||||
|
||||
// After calling mail_refresh_remaining_amount the status should always be there
|
||||
if( sd->sc.data[SC_DAILYSENDMAILCNT] == NULL || sd->sc.data[SC_DAILYSENDMAILCNT]->val2 >= battle_config.mail_daily_count ){
|
||||
clif_Mail_send(sd, WRITE_MAIL_FAILED_CNT);
|
||||
return;
|
||||
}else{
|
||||
sc_start2( &sd->bl, &sd->bl, SC_DAILYSENDMAILCNT, 100, date_get_dayofyear(), sd->sc.data[SC_DAILYSENDMAILCNT]->val2 + 1, -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( body_len > MAIL_BODY_LENGTH )
|
||||
body_len = MAIL_BODY_LENGTH;
|
||||
|
||||
if( !mail_setattachment(sd, &msg) ) { // Invalid Append condition
|
||||
clif_Mail_send(sd->fd, true); // fail
|
||||
mail_removeitem(sd,0);
|
||||
mail_removezeny(sd,0);
|
||||
int i;
|
||||
|
||||
clif_Mail_send(sd, WRITE_MAIL_FAILED); // fail
|
||||
for( i = 0; i < MAIL_MAX_ITEM; i++ ){
|
||||
mail_removeitem(sd,0,sd->mail.item[i].index + 2, sd->mail.item[i].amount);
|
||||
}
|
||||
mail_removezeny(sd,false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -230,6 +383,7 @@ void mail_send(struct map_session_data *sd, const char *dest_name, const char *t
|
||||
safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH);
|
||||
safestrncpy(msg.dest_name, (char*)dest_name, NAME_LENGTH);
|
||||
safestrncpy(msg.title, (char*)title, MAIL_TITLE_LENGTH);
|
||||
msg.type = MAIL_INBOX_NORMAL;
|
||||
|
||||
if (msg.title[0] == '\0') {
|
||||
return; // Message has no length and somehow client verification was skipped.
|
||||
@ -246,3 +400,14 @@ void mail_send(struct map_session_data *sd, const char *dest_name, const char *t
|
||||
|
||||
sd->cansendmail_tick = gettick() + battle_config.mail_delay; // Flood Protection
|
||||
}
|
||||
|
||||
void mail_refresh_remaining_amount( struct map_session_data* sd ){
|
||||
int doy = date_get_dayofyear();
|
||||
|
||||
nullpo_retv(sd);
|
||||
|
||||
// If it was not yet started or it was started on another day
|
||||
if( sd->sc.data[SC_DAILYSENDMAILCNT] == NULL || sd->sc.data[SC_DAILYSENDMAILCNT]->val1 != doy ){
|
||||
sc_start2( &sd->bl, &sd->bl, SC_DAILYSENDMAILCNT, 100, doy, 0, -1 );
|
||||
}
|
||||
}
|
||||
|
@ -6,15 +6,31 @@
|
||||
|
||||
#include "../common/mmo.h"
|
||||
|
||||
enum mail_attach_result {
|
||||
MAIL_ATTACH_SUCCESS = 0,
|
||||
#if PACKETVER >= 20150513
|
||||
MAIL_ATTACH_WEIGHT = 1,
|
||||
MAIL_ATTACH_ERROR = 2,
|
||||
MAIL_ATTACH_SPACE = 3,
|
||||
MAIL_ATTACH_UNTRADEABLE = 4
|
||||
#else
|
||||
MAIL_ATTACH_WEIGHT = 1,
|
||||
MAIL_ATTACH_ERROR = 1,
|
||||
MAIL_ATTACH_SPACE = 1,
|
||||
MAIL_ATTACH_UNTRADEABLE = 1
|
||||
#endif
|
||||
};
|
||||
|
||||
void mail_clear(struct map_session_data *sd);
|
||||
int mail_removeitem(struct map_session_data *sd, short flag);
|
||||
int mail_removezeny(struct map_session_data *sd, short flag);
|
||||
bool mail_setitem(struct map_session_data *sd, short idx, uint32 amount);
|
||||
int mail_removeitem(struct map_session_data *sd, short flag, int idx, int amount);
|
||||
bool mail_removezeny(struct map_session_data *sd, bool flag);
|
||||
enum mail_attach_result mail_setitem(struct map_session_data *sd, short idx, uint32 amount);
|
||||
bool mail_setattachment(struct map_session_data *sd, struct mail_message *msg);
|
||||
void mail_getattachment(struct map_session_data* sd, int zeny, struct item* item);
|
||||
void mail_getattachment(struct map_session_data* sd, struct mail_message* msg, int zeny, struct item* item);
|
||||
int mail_openmail(struct map_session_data *sd);
|
||||
void mail_deliveryfail(struct map_session_data *sd, struct mail_message *msg);
|
||||
bool mail_invalid_operation(struct map_session_data *sd);
|
||||
void mail_send(struct map_session_data *sd, const char *dest_name, const char *title, const char *body_msg, int body_len);
|
||||
void mail_refresh_remaining_amount( struct map_session_data* sd );
|
||||
|
||||
#endif /* _MAIL_H_ */
|
||||
|
@ -1449,7 +1449,7 @@ void pc_reg_received(struct map_session_data *sd)
|
||||
sd->vip.enabled = 0;
|
||||
chrif_req_login_operation(sd->status.account_id, sd->status.name, CHRIF_OP_LOGIN_VIP, 0, 1|8, 0); // request VIP information
|
||||
#endif
|
||||
intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
|
||||
intif_Mail_requestinbox(sd->status.char_id, 0, MAIL_INBOX_NORMAL); // MAIL SYSTEM - Request Mail Inbox
|
||||
intif_request_questlog(sd);
|
||||
|
||||
if (sd->state.connect_new == 0 && sd->fd) { //Character already loaded map! Gotta trigger LoadEndAck manually.
|
||||
|
@ -262,6 +262,7 @@ struct map_session_data {
|
||||
unsigned int workinprogress : 2; // See clif.h::e_workinprogress
|
||||
bool pc_loaded; // Ensure inventory data and status data is loaded before we calculate player stats
|
||||
bool keepshop; // Whether shop data should be removed when the player disconnects
|
||||
bool mail_writing; // Whether the player is currently writing a mail in RODEX or not
|
||||
} state;
|
||||
struct {
|
||||
unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
|
||||
@ -583,8 +584,11 @@ struct map_session_data {
|
||||
|
||||
// Mail System [Zephyrus]
|
||||
struct s_mail {
|
||||
unsigned short nameid;
|
||||
int index, amount, zeny;
|
||||
struct {
|
||||
unsigned short nameid;
|
||||
int index, amount;
|
||||
} item[MAIL_MAX_ITEM];
|
||||
int zeny;
|
||||
struct mail_data inbox;
|
||||
bool changed; // if true, should sync with charserver on next mailbox request
|
||||
} mail;
|
||||
|
@ -23081,6 +23081,25 @@ BUILDIN_FUNC(channel_delete) {
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
BUILDIN_FUNC(unloadnpc) {
|
||||
const char *name;
|
||||
struct npc_data* nd;
|
||||
|
||||
name = script_getstr(st, 2);
|
||||
nd = npc_name2id(name);
|
||||
|
||||
if( nd == NULL ){
|
||||
ShowError( "buildin_unloadnpc: npc '%s' was not found.\n", name );
|
||||
return SCRIPT_CMD_FAILURE;
|
||||
}
|
||||
|
||||
npc_unload_duplicates(nd);
|
||||
npc_unload(nd, true);
|
||||
npc_read_event_script();
|
||||
|
||||
return SCRIPT_CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#include "../custom/script.inc"
|
||||
|
||||
// declarations that were supposed to be exported from npc_chat.c
|
||||
@ -23682,6 +23701,7 @@ struct script_function buildin_func[] = {
|
||||
BUILDIN_DEF(needed_status_point,"ii?"),
|
||||
BUILDIN_DEF(jobcanentermap,"s?"),
|
||||
BUILDIN_DEF(openstorage2,"ii?"),
|
||||
BUILDIN_DEF(unloadnpc, "s"),
|
||||
|
||||
// WoE TE
|
||||
BUILDIN_DEF(agitstart3,""),
|
||||
|
@ -1466,6 +1466,7 @@
|
||||
export_constant(SC_ARMOR_ELEMENT_EARTH);
|
||||
export_constant(SC_ARMOR_ELEMENT_FIRE);
|
||||
export_constant(SC_ARMOR_ELEMENT_WIND);
|
||||
export_constant(SC_DAILYSENDMAILCNT);
|
||||
#ifdef RENEWAL
|
||||
export_constant(SC_EXTREMITYFIST2);
|
||||
#endif
|
||||
|
@ -1886,6 +1886,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
|
||||
case SC_SPRITEMABLE: case SC_BITESCAR:
|
||||
case SC_CLAN_INFO: case SC_SWORDCLAN: case SC_ARCWANDCLAN:
|
||||
case SC_GOLDENMACECLAN: case SC_CROSSBOWCLAN:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
continue;
|
||||
case SC_WHISTLE: case SC_ASSNCROS: case SC_POEMBRAGI:
|
||||
case SC_APPLEIDUN: case SC_HUMMING: case SC_DONTFORGETME:
|
||||
@ -7940,6 +7941,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
|
||||
case SC_GOLDENMACECLAN:
|
||||
case SC_CROSSBOWCLAN:
|
||||
case SC_JUMPINGCLAN:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
continue;
|
||||
case SC_WHISTLE:
|
||||
case SC_ASSNCROS:
|
||||
@ -9337,6 +9339,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
|
||||
case SC_QUEST_BUFF1: case SC_QUEST_BUFF2: case SC_QUEST_BUFF3:
|
||||
case SC_CLAN_INFO: case SC_SWORDCLAN: case SC_ARCWANDCLAN:
|
||||
case SC_GOLDENMACECLAN: case SC_CROSSBOWCLAN:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
continue;
|
||||
case SC_ASSUMPTIO:
|
||||
if( bl->type == BL_MOB )
|
||||
|
@ -1116,6 +1116,9 @@ void initChangeTables(void)
|
||||
StatusIconChangeTable[SC_GEFFEN_MAGIC2] = SI_GEFFEN_MAGIC2;
|
||||
StatusIconChangeTable[SC_GEFFEN_MAGIC3] = SI_GEFFEN_MAGIC3;
|
||||
|
||||
// RODEX
|
||||
StatusIconChangeTable[SC_DAILYSENDMAILCNT] = SI_DAILYSENDMAILCNT;
|
||||
|
||||
/* Other SC which are not necessarily associated to skills */
|
||||
StatusChangeFlagTable[SC_ASPDPOTION0] |= SCB_ASPD;
|
||||
StatusChangeFlagTable[SC_ASPDPOTION1] |= SCB_ASPD;
|
||||
@ -1260,6 +1263,9 @@ void initChangeTables(void)
|
||||
StatusChangeFlagTable[SC_CROSSBOWCLAN] |= SCB_DEX|SCB_AGI|SCB_MAXHP|SCB_MAXSP;
|
||||
StatusChangeFlagTable[SC_JUMPINGCLAN] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK;
|
||||
|
||||
// RODEX
|
||||
StatusChangeFlagTable[SC_DAILYSENDMAILCNT] |= SCB_NONE;
|
||||
|
||||
#ifdef RENEWAL
|
||||
// renewal EDP increases your weapon atk
|
||||
StatusChangeFlagTable[SC_EDP] |= SCB_WATK;
|
||||
@ -9158,6 +9164,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
|
||||
case SC_PUSH_CART:
|
||||
case SC_SPRITEMABLE:
|
||||
case SC_CLAN_INFO:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
tick = -1;
|
||||
break;
|
||||
|
||||
@ -10898,6 +10905,7 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
|
||||
case SC_UPHEAVAL_OPTION:
|
||||
case SC_CIRCLE_OF_FIRE_OPTION:
|
||||
case SC_CLAN_INFO:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
val_flag |= 1|2;
|
||||
break;
|
||||
// Start |1|2|4 val_flag setting
|
||||
@ -11512,6 +11520,7 @@ int status_change_clear(struct block_list* bl, int type)
|
||||
case SC_GOLDENMACECLAN:
|
||||
case SC_CROSSBOWCLAN:
|
||||
case SC_JUMPINGCLAN:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -11545,6 +11554,7 @@ int status_change_clear(struct block_list* bl, int type)
|
||||
case SC_GOLDENMACECLAN:
|
||||
case SC_CROSSBOWCLAN:
|
||||
case SC_JUMPINGCLAN:
|
||||
case SC_DAILYSENDMAILCNT:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -798,6 +798,8 @@ typedef enum sc_type {
|
||||
SC_ARMOR_ELEMENT_FIRE,
|
||||
SC_ARMOR_ELEMENT_WIND,
|
||||
|
||||
SC_DAILYSENDMAILCNT,
|
||||
|
||||
#ifdef RENEWAL
|
||||
SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user