Pārlūkot izejas kodu

Adding banking support

lighta 11 gadi atpakaļ
vecāks
revīzija
1290826be2

+ 5 - 1
conf/battle/feature.conf

@@ -17,4 +17,8 @@ feature.search_stores: on
 
 // Atcommand suggestions (Note 1)
 // If one type incomplete atcommand, it will suggest the complete ones.
-feature.atcommand_suggestions: off
+feature.atcommand_suggestions: off
+
+// Banking (Note 1)
+// Requires: 2013-07-24aRagexe or later
+feature.banking: on

+ 1 - 0
conf/inter_athena.conf

@@ -103,6 +103,7 @@ mercenary_db: mercenary
 mercenary_owner_db: mercenary_owner
 elemental_db: elemental
 ragsrvinfo_db: ragsrvinfo
+skillcooldown_db: skillcooldown
 
 // Map Database Tables
 item_db_db: item_db

+ 1 - 0
conf/log_athena.conf

@@ -28,6 +28,7 @@
 // 0x10000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/
 //           rings deleted by divorce/pet egg (un)hatching/pet armor (un)equipping/Weapon Refine skill/Remove Trap skill)
 // 0x20000 - ($) Log cash transactions
+// 0x40000 - (K) Log account bank transactions
 // Example: Log trades+vending+script items+created items: 1+2+32+1024 = 1059
 // Please note that moving items from inventory to cart and back is not logged by design.
 enable_logs: 0xFFFFF

+ 2 - 1
conf/map_athena.conf

@@ -101,11 +101,12 @@ minsave_time: 100
 // 16: After successfully sending a mail with attachment
 // 32: After successfully submitting an item for auction
 // 64: After successfully get/delete/complete a quest
+// 128: After every bank transaction (deposit/withdraw)
 // NOTE: These settings decrease the chance of dupes/lost items when there's a
 // server crash at the expense of increasing the map/char server lag. If your 
 // server rarely crashes, but experiences interserver lag, you may want to set
 // these off.
-save_settings: 127
+save_settings: 255
 
 // Message of the day file, when a character logs on, this message is displayed.
 motd_txt: conf/motd.txt

+ 4 - 0
conf/msg_conf/map_msg.conf

@@ -1493,5 +1493,9 @@
 1490: Item types on your autoloottype list:
 1491: Your autoloottype list has been reset.
 
+//Banking
+1492: You can't withdraw that much money
+1493: Banking is disabled
+
 //Custom translations
 //import: conf/msg_conf/import/map_msg_eng_conf.txt

+ 42 - 0
db/packet_db.txt

@@ -2190,3 +2190,45 @@ packet_ver: 44
 0x0863,26,friendslistadd,2
 0x088A,5,hommenu,2:4
 0x095B,36,storagepassword,2:4:20
+0x09A6,12,ZC_BANKING_CHECK,2:10
+0x09A7,10,bankdeposit,2:6
+0x09A8,16,ZC_ACK_BANKING_DEPOSIT,2:4:12
+0x09A9,10,bankwithdrawal,2:6
+0x09AA,16,ZC_ACK_BANKING_WITHDRAW,2:4:12
+0x09AB,6,bankcheck,2
+0x09B6,6,bankopen,2
+0x09B7,4,ZC_ACK_OPEN_BANKING,2
+0x09B8,6,bankclose,2
+0x09B9,4,ZC_ACK_CLOSE_BANKING,2
+
+//2013-08-07Ragexe (Shakto)
+packet_ver: 45
+0x0369,7,actionrequest,2:6
+0x083C,10,useskilltoid,2:4:6
+0x0437,5,walktoxy,2
+0x035F,6,ticksend,2
+0x0202,5,changedir,2:4
+0x07E4,6,takeitem,2
+0x0362,6,dropitem,2:4
+0x07EC,8,movetokafra,2:4
+0x0364,8,movefromkafra,2:4
+0x0438,10,useskilltopos,2:4:6:8
+0x0366,90,useskilltoposinfo,2:4:6:8:10
+0x096A,6,getcharnamerequest,2
+0x0368,6,solvecharname,2
+0x0838,12,searchstoreinfolistitemclick,2:6:10
+0x0835,2,searchstoreinfonextpage,0
+0x0819,-1,searchstoreinfo,2:4:5:9:13:14:15
+0x0811,-1,reqtradebuyingstore,2:4:8:12
+0x0360,6,reqclickbuyingstore,2
+0x0817,2,reqclosebuyingstore,0
+0x0815,-1,reqopenbuyingstore,2:4:8:9:89
+0x0365,18,bookingregreq,2:4:6
+// 0x363,8 CZ_JOIN_BATTLE_FIELD
+0x0281,-1,itemlistwindowselected,2:4:8:12
+0x022D,19,wanttoconnection,2:6:10:14:18
+0x0802,26,partyinvite2,2
+// 0x436,4 CZ_GANGSI_RANK
+0x023B,26,friendslistadd,2
+0x0361,5,hommenu,2:4
+0x0887,36,storagepassword,2:4:20

+ 1 - 0
sql-files/main.sql

@@ -459,6 +459,7 @@ CREATE TABLE IF NOT EXISTS `login` (
   `character_slots` tinyint(3) unsigned NOT NULL default '0',
   `pincode` varchar(4) NOT NULL DEFAULT '',
   `pincode_change` int(11) unsigned NOT NULL DEFAULT '0',
+  `bank_vault` BIGINT(64) NOT NULL DEFAULT '0',
   PRIMARY KEY  (`account_id`),
   KEY `name` (`userid`)
 ) ENGINE=MyISAM AUTO_INCREMENT=2000000; 

+ 1 - 0
sql-files/upgrades/upgrade_svn17600.sql

@@ -0,0 +1 @@
+ALTER TABLE `login` ADD `bank_vault` BIGINT( 64 ) NOT NULL DEFAULT '0';

+ 93 - 4
src/char/char.c

@@ -143,6 +143,7 @@ struct char_session_data {
 	time_t pincode_change;
 	uint16 pincode_try;
 	// Addon system
+	int bank_vault;
 	unsigned int char_moves[MAX_CHARS]; // character moves left
 };
 
@@ -189,6 +190,12 @@ bool char_moves_unlimited = false;
 void moveCharSlot( int fd, struct char_session_data* sd, unsigned short from, unsigned short to );
 void moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason );
 
+int loginif_BankingReq(int32 account_id, int8 type, int32 data);
+int loginif_parse_BankingAck(int fd);
+int mapif_BankingAck(int32 account_id, int32 bank_vault);
+int mapif_parse_UpdBankInfo(int fd);
+int mapif_parse_ReqBankInfo(int fd);
+
 //Custom limits for the fame lists. [Skotlex]
 int fame_list_size_chemist = MAX_FAME_LIST;
 int fame_list_size_smith = MAX_FAME_LIST;
@@ -552,7 +559,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
 		} else
 			strcat(save_status, " status");
 	}
-
+	
 	//Values that will seldom change (to speed up saving)
 	if (
 		(p->hair != cp->hair) || (p->hair_color != cp->hair_color) || (p->clothes_color != cp->clothes_color) ||
@@ -591,7 +598,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p)
 		else
 			errors++;
 	}
-
+	
 	//memo points
 	if( memcmp(p->memo_point, cp->memo_point, sizeof(p->memo_point)) )
 	{
@@ -2146,6 +2153,83 @@ static void char_auth_ok(int fd, struct char_session_data *sd)
 int send_accounts_tologin(int tid, unsigned int tick, int id, intptr_t data);
 void mapif_server_reset(int id);
 
+/*
+ * HA 0x2740<aid>L <type>B <data>L
+ * type:
+ *  0 = select
+ *  1 = update
+ */
+int loginif_BankingReq(int32 account_id, int8 type, int32 data){
+	if (login_fd > 0 && session[login_fd] && !session[login_fd]->flag.eof){
+		WFIFOHEAD(login_fd,11);
+		WFIFOW(login_fd,0) = 0x2740;
+		WFIFOL(login_fd,2) = account_id;
+		WFIFOB(login_fd,6) = type;
+		WFIFOL(login_fd,7) = data;
+		WFIFOSET(login_fd,11);
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Received the banking data from login and transmit it to all map-serv
+ * AH 0x2741<aid>L <bank_vault>L <not_fw>B
+ * HZ 0x2b29 <aid>L <bank_vault>L 
+ */
+int loginif_parse_BankingAck(int fd){
+	if (RFIFOREST(fd) < 11)
+		return 0;
+	uint32 aid = RFIFOL(fd,2);
+	int32 bank_vault = RFIFOL(fd,6);
+	char not_fw = RFIFOB(fd,10);
+	RFIFOSKIP(fd,11);
+	
+	if(!not_fw) mapif_BankingAck(aid, bank_vault);
+	return 1;
+}
+
+//HZ 0x2b29 <aid>L <bank_vault>L
+int mapif_BankingAck(int32 account_id, int32 bank_vault){
+	unsigned char buf[14];
+	WBUFW(buf,0) = 0x2b29;
+	WBUFL(buf,2) = account_id;
+	WBUFL(buf,6) = bank_vault;
+	mapif_sendall(buf, 10); //inform all maps-attached
+	return 1;
+}
+
+/*
+ *  Receive a map request to save banking
+ * Fowarding it to login-serv
+ * ZH 0x2b28 <aid>L <money>L
+ * HA 0x2740<aid>L <type>B <money>L
+ */
+int mapif_parse_UpdBankInfo(int fd){
+	if( RFIFOREST(fd) < 10 )
+		return 0;
+	uint32 aid = RFIFOL(fd,2);
+	int money = RFIFOL(fd,6);
+	RFIFOSKIP(fd,10);
+	loginif_BankingReq(aid, 2, money);
+	return 1;
+}
+
+/*
+ *  Receive a map request to get banking info
+ * Fowarding it to login-serv
+ * ZH 0x2b2a <aid>L
+ * HA 0x2740<aid>L <type>B <money>L
+ */
+int mapif_parse_ReqBankInfo(int fd){
+	if( RFIFOREST(fd) < 6 )
+		return 0;
+	uint32 aid = RFIFOL(fd,2);
+	RFIFOSKIP(fd,6);
+	loginif_BankingReq(aid, 1, 0);
+	return 1;
+}
+
 
 /// Resets all the data.
 void loginif_reset(void)
@@ -2230,6 +2314,7 @@ int parse_fromlogin(int fd) {
 
 		switch( command )
 		{
+		case 0x2741: loginif_parse_BankingAck(fd); break;
 
 		// acknowledgement of connect-to-loginserver request
 		case 0x2711:
@@ -2289,7 +2374,7 @@ int parse_fromlogin(int fd) {
 		break;
 
 		case 0x2717: // account data
-			if (RFIFOREST(fd) < 72)
+			if (RFIFOREST(fd) < 76)
 				return 0;
 
 			// find the authenticated session with this account id
@@ -2309,6 +2394,7 @@ int parse_fromlogin(int fd) {
 				safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
 				safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
 				sd->pincode_change = (time_t)RFIFOL(fd,68);
+				sd->bank_vault = RFIFOL(fd,72);
 				ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] );
 				// continued from char_auth_ok...
 				if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359
@@ -2355,7 +2441,7 @@ int parse_fromlogin(int fd) {
 #endif
 				}
 			}
-			RFIFOSKIP(fd,72);
+			RFIFOSKIP(fd,76);
 		break;
 
 		// login-server alive packet
@@ -3579,6 +3665,9 @@ int parse_frommap(int fd)
 				RFIFOSKIP(fd, RFIFOW(fd,2) );/* skip this packet */
 		}
 		break;
+		
+		case 0x2b28: mapif_parse_UpdBankInfo(fd); break;
+		case 0x2b2a: mapif_parse_ReqBankInfo(fd); break; 
 
 		default:
 		{

+ 3 - 1
src/common/mmo.h

@@ -47,7 +47,7 @@
 // 20120307 - 2012-03-07aRagexeRE+ - 0x970
 
 #ifndef PACKETVER
-	#define PACKETVER 20120410
+	#define PACKETVER 20130724
 	//#define PACKETVER 20130320
 	//#define PACKETVER 20111116
 #endif
@@ -79,6 +79,7 @@
 //Max amount of a single stacked item
 #define MAX_AMOUNT 30000
 #define MAX_ZENY 1000000000
+#define MAX_BANK_ZENY SINT32_MAX
 #define MAX_FAME 1000000000
 #define MAX_CART 100
 #define MAX_SKILL 5020
@@ -351,6 +352,7 @@ struct mmo_charstatus {
 
 	unsigned int base_exp,job_exp;
 	int zeny;
+	int bank_vault;
 
 	short class_;
 	unsigned int status_point,skill_point;

+ 1 - 0
src/login/account.h

@@ -53,6 +53,7 @@ struct mmo_account
 	char pincode[PINCODE_LENGTH+1];		// pincode system
 	time_t pincode_change;	// (timestamp): last time of pincode change
 	int account_reg2_num;
+	int bank_vault;
 	struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server)
 };
 

+ 37 - 34
src/login/account_sql.c

@@ -522,7 +522,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 
 	// retrieve login entry for the specified account
 	if( SQL_ERROR == Sql_Query(sql_handle,
-	    "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`, `pincode_change` FROM `%s` WHERE `account_id` = %d",
+	    "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`, `pincode_change`, `bank_vault` FROM `%s` WHERE `account_id` = %d",
 		db->account_db, account_id )
 	) {
 		Sql_ShowDebug(sql_handle);
@@ -551,6 +551,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
 	Sql_GetData(sql_handle, 13, &data, NULL); acc->char_slots = atoi(data);
 	Sql_GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
 	Sql_GetData(sql_handle, 15, &data, NULL); acc->pincode_change = atol(data);
+	Sql_GetData(sql_handle, 16, &data, NULL); acc->bank_vault = atoi(data);
 
 	Sql_FreeResult(sql_handle);
 
@@ -599,24 +600,25 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
 	if( is_new )
 	{// insert into account table
 		if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
-			"INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+			"INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`, `bank_vault`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			db->account_db)
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  0, SQLDT_INT,    (void*)&acc->account_id,      sizeof(acc->account_id))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  1, SQLDT_STRING, (void*)acc->userid,           strlen(acc->userid))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  2, SQLDT_STRING, (void*)acc->pass,             strlen(acc->pass))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  3, SQLDT_ENUM,   (void*)&acc->sex,             sizeof(acc->sex))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  4, SQLDT_STRING, (void*)&acc->email,           strlen(acc->email))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  5, SQLDT_INT,    (void*)&acc->group_id,        sizeof(acc->group_id))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  6, SQLDT_UINT,   (void*)&acc->state,           sizeof(acc->state))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  7, SQLDT_LONG,   (void*)&acc->unban_time,      sizeof(acc->unban_time))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  8, SQLDT_INT,    (void*)&acc->expiration_time, sizeof(acc->expiration_time))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  9, SQLDT_UINT,   (void*)&acc->logincount,      sizeof(acc->logincount))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->lastlogin,       strlen(acc->lastlogin))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip,         strlen(acc->last_ip))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING, (void*)&acc->birthdate,       strlen(acc->birthdate))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR,  (void*)&acc->char_slots,      sizeof(acc->char_slots))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode,         strlen(acc->pincode))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG,   (void*)&acc->pincode_change,  sizeof(acc->pincode_change))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  0, SQLDT_INT,       (void*)&acc->account_id,      sizeof(acc->account_id))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  1, SQLDT_STRING,    (void*)acc->userid,           strlen(acc->userid))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  2, SQLDT_STRING,    (void*)acc->pass,             strlen(acc->pass))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  3, SQLDT_ENUM,      (void*)&acc->sex,             sizeof(acc->sex))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  4, SQLDT_STRING,    (void*)&acc->email,           strlen(acc->email))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  5, SQLDT_INT,       (void*)&acc->group_id,        sizeof(acc->group_id))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  6, SQLDT_UINT,      (void*)&acc->state,           sizeof(acc->state))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  7, SQLDT_LONG,      (void*)&acc->unban_time,      sizeof(acc->unban_time))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  8, SQLDT_INT,       (void*)&acc->expiration_time, sizeof(acc->expiration_time))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  9, SQLDT_UINT,      (void*)&acc->logincount,      sizeof(acc->logincount))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING,    (void*)&acc->lastlogin,       strlen(acc->lastlogin))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING,    (void*)&acc->last_ip,         strlen(acc->last_ip))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING,    (void*)&acc->birthdate,       strlen(acc->birthdate))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR,     (void*)&acc->char_slots,      sizeof(acc->char_slots))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING,    (void*)&acc->pincode,         strlen(acc->pincode))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG,      (void*)&acc->pincode_change,  sizeof(acc->pincode_change))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_INT,       (void*)&acc->bank_vault,      sizeof(acc->bank_vault))
 		||  SQL_SUCCESS != SqlStmt_Execute(stmt)
 		) {
 			SqlStmt_ShowDebug(stmt);
@@ -625,22 +627,23 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
 	}
 	else
 	{// update account table
-		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id)
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  0, SQLDT_STRING, (void*)acc->userid,           strlen(acc->userid))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  1, SQLDT_STRING, (void*)acc->pass,             strlen(acc->pass))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  2, SQLDT_ENUM,   (void*)&acc->sex,             sizeof(acc->sex))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  3, SQLDT_STRING, (void*)acc->email,            strlen(acc->email))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  4, SQLDT_INT,    (void*)&acc->group_id,        sizeof(acc->group_id))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  5, SQLDT_UINT,   (void*)&acc->state,           sizeof(acc->state))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  6, SQLDT_LONG,   (void*)&acc->unban_time,      sizeof(acc->unban_time))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  7, SQLDT_LONG,   (void*)&acc->expiration_time, sizeof(acc->expiration_time))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  8, SQLDT_UINT,   (void*)&acc->logincount,      sizeof(acc->logincount))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  9, SQLDT_STRING, (void*)&acc->lastlogin,       strlen(acc->lastlogin))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip,         strlen(acc->last_ip))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->birthdate,       strlen(acc->birthdate))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR,  (void*)&acc->char_slots,      sizeof(acc->char_slots))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode,         strlen(acc->pincode))
-		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG,   (void*)&acc->pincode_change,  sizeof(acc->pincode_change))
+		if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?, `pincode_change`=?, `bank_vault`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id)
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  0, SQLDT_STRING,    (void*)acc->userid,           strlen(acc->userid))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  1, SQLDT_STRING,    (void*)acc->pass,             strlen(acc->pass))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  2, SQLDT_ENUM,      (void*)&acc->sex,             sizeof(acc->sex))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  3, SQLDT_STRING,    (void*)acc->email,            strlen(acc->email))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  4, SQLDT_INT,       (void*)&acc->group_id,        sizeof(acc->group_id))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  5, SQLDT_UINT,      (void*)&acc->state,           sizeof(acc->state))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  6, SQLDT_LONG,      (void*)&acc->unban_time,      sizeof(acc->unban_time))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  7, SQLDT_LONG,      (void*)&acc->expiration_time, sizeof(acc->expiration_time))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  8, SQLDT_UINT,      (void*)&acc->logincount,      sizeof(acc->logincount))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt,  9, SQLDT_STRING,    (void*)&acc->lastlogin,       strlen(acc->lastlogin))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING,    (void*)&acc->last_ip,         strlen(acc->last_ip))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING,    (void*)&acc->birthdate,       strlen(acc->birthdate))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR,     (void*)&acc->char_slots,      sizeof(acc->char_slots))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING,    (void*)&acc->pincode,         strlen(acc->pincode))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG,      (void*)&acc->pincode_change,  sizeof(acc->pincode_change))
+		||  SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_INT,       (void*)&acc->bank_vault,      sizeof(acc->bank_vault))
 		||  SQL_SUCCESS != SqlStmt_Execute(stmt)
 		) {
 			SqlStmt_ShowDebug(stmt);

+ 35 - 4
src/login/login.c

@@ -567,6 +567,7 @@ int parse_fromchar(int fd){
 				char birthdate[10+1] = "";
 				char pincode[PINCODE_LENGTH+1];
 				int account_id = RFIFOL(fd,2);
+				int bank_vault = 0;
 
 				memset(pincode,0,PINCODE_LENGTH+1);
 
@@ -581,9 +582,10 @@ int parse_fromchar(int fd){
 					char_slots = acc.char_slots;
 					safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
 					safestrncpy(pincode, acc.pincode, sizeof(pincode));
+					bank_vault = acc.bank_vault;
 				}
 
-				WFIFOHEAD(fd,72);
+				WFIFOHEAD(fd,76);
 				WFIFOW(fd,0) = 0x2717;
 				WFIFOL(fd,2) = account_id;
 				safestrncpy((char*)WFIFOP(fd,6), email, 40);
@@ -593,7 +595,8 @@ int parse_fromchar(int fd){
 				safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
 				safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
 				WFIFOL(fd,68) = (uint32)acc.pincode_change;
-				WFIFOSET(fd,72);
+				WFIFOL(fd,72) = bank_vault;
+				WFIFOSET(fd,76);
 			}
 		break;
 
@@ -907,13 +910,11 @@ int parse_fromchar(int fd){
 				return 0;
 			else{
 				struct mmo_account acc;
-
 				if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ){
 					strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 );
 					acc.pincode_change = time( NULL );
 					accounts->save(accounts, &acc);
 				}
-
 				RFIFOSKIP(fd,11);
 			}
 		break;
@@ -941,6 +942,35 @@ int parse_fromchar(int fd){
 			}
 		break;
 
+		case 0x2740: // req upd bank_vault
+			if( RFIFOREST(fd) < 11 )
+				return 0;
+			else{
+				struct mmo_account acc;
+
+				int account_id = RFIFOL(fd,2);
+				char type = RFIFOB(fd,6);
+				int32 data = RFIFOL(fd,7);
+				RFIFOSKIP(fd,11);
+
+				if( !accounts->load_num(accounts, &acc, account_id) )
+					ShowNotice("Char-server '%s': Error on banking  (account: %d not found, ip: %s).\n", server[id].name, account_id, ip);
+				else{
+					unsigned char buf[11];
+					if(type==2){ // upd and Save
+						acc.bank_vault = data;
+						accounts->save(accounts, &acc);
+						WBUFB(buf,10) = 1;
+					}
+					// announce to other servers
+					WBUFW(buf,0) = 0x2741;
+					WBUFL(buf,2) = account_id;
+					WBUFL(buf,6) = acc.bank_vault;
+					charif_sendallwos(-1, buf, 11);
+				}
+			}
+		break;
+
 		default:
 			ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
 			set_eof(fd);
@@ -996,6 +1026,7 @@ int mmo_auth_new(const char* userid, const char* pass, const char sex, const cha
 	acc.pincode_change = 0;
 
 	acc.char_slots = 0;
+	acc.bank_vault = 0;
 
 	if( !accounts->create(accounts, &acc) )
 		return 0;

+ 8 - 0
src/map/battle.c

@@ -7138,6 +7138,7 @@ static const struct _battle_data {
 	{ "item_flooritem_check",               &battle_config.item_onfloor,                    1,      0,      1,              },
 	{ "bowling_bash_area",                  &battle_config.bowling_bash_area,               0,      0,      20,             },
 	{ "drop_rateincrease",                  &battle_config.drop_rateincrease,               0,      0,      1,              },
+	{ "feature.banking",                    &battle_config.feature_banking,                 1,      0,      1,              },
 };
 #ifndef STATS_OPT_OUT
 /**
@@ -7352,6 +7353,13 @@ void battle_adjust_conf()
 	}
 #endif
 
+#if PACKETVER < 20130724
+	if( battle_config.feature_banking ) {
+		ShowWarning("conf/battle/feature.conf banking is enabled but it requires PACKETVER 2013-07-24 or newer, disabling...\n");
+		battle_config.feature_banking = 0;
+	}
+#endif
+
 #ifndef CELL_NOSTACK
 	if (battle_config.cell_stack_limit != 1)
 		ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");

+ 1 - 0
src/map/battle.h

@@ -494,6 +494,7 @@ extern struct Battle_Config
 	int item_onfloor; // Whether to drop an undroppable item on the map or destroy it if inventory is full.
 	int bowling_bash_area;
 	int drop_rateincrease;
+	int feature_banking;
 
 } battle_config;
 

+ 1 - 0
src/map/channel.c

@@ -101,6 +101,7 @@ int channel_delete(struct Channel *channel) {
 	}
 	default:
 		strdb_remove(channel_db, channel->name);
+                aFree(channel);
 		break;
 	}
 	return 0;

+ 88 - 42
src/map/chrif.c

@@ -34,6 +34,7 @@
 #include <time.h>
 
 static int check_connect_char_server(int tid, unsigned int tick, int id, intptr_t data);
+int chrif_save_bankdata(struct map_session_data *sd);
 
 static struct eri *auth_db_ers; //For reutilizing player login structures.
 static DBMap* auth_db; // int id -> struct auth_node*
@@ -45,7 +46,8 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
 	11,10,10, 0,11, -1,266,10,	// 2b10-2b17: U->2b10, U->2b11, U->2b12, F->2b13, U->2b14, U->2b15, U->2b16, U->2b17
 	 2,10, 2,-1,-1,-1, 2, 7,	// 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
 	-1,10, 8, 2, 2,14,19,19,	// 2b20-2b27: U->2b20, U->2b21, U->2b22, U->2b23, U->2b24, U->2b25, U->2b26, U->2b27
-};
+	10,10, 6, 0, 0, 0, 0, 0,	// 2b28-2b2f: U->2b28, U->2b29, U->2b2a, F->2b2b, F->2b2c, F->2b2d, F->2b2e, F->2b2f
+ };
 
 //Used Packets:
 //2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
@@ -66,8 +68,8 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
 //2b07: Outgoing, chrif_removefriend -> 'Tell charserver to remove friend_id from char_id friend list'
 //2b08: Outgoing, chrif_searchcharid -> '...'
 //2b09: Incoming, map_addchariddb -> 'Adds a name to the nick db'
-//2b0a: FREE
-//2b0b: FREE
+//2b0a: Outgoing, chrif_skillcooldown_request -> requesting the list of skillcooldown for char
+//2b0b: Incoming, chrif_skillcooldown_load -> received the list of cooldown for char
 //2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
 //2b0d: Incoming, chrif_changedsex -> 'Change sex of acc XY'
 //2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
@@ -77,7 +79,7 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
 //2b12: Incoming, chrif_divorceack -> 'divorce chars
 //2b13: FREE
 //2b14: Incoming, chrif_accountban -> 'not sure: kick the player with message XY'
-//2b15: FREE
+//2b15: Outgoing, chrif_skillcooldown_save -> request to save skillcooldown
 //2b16: Outgoing, chrif_ragsrvinfo -> 'sends base / job / drop rates ....'
 //2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
 //2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
@@ -96,6 +98,9 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
 //2b25: Incoming, chrif_deadopt -> 'Removes baby from Father ID and Mother ID'
 //2b26: Outgoing, chrif_authreq -> 'client authentication request'
 //2b27: Incoming, chrif_authfail -> 'client authentication failed'
+//2b28: Outgoing, chrif_save_bankdata -> 'send bank data to be saved'
+//2b29: Incoming, chrif_load_bankdata -> 'received bank data for playeer to be loaded'
+//2b2a: Outgoing, chrif_bankdata_request -> 'request bank data for charid'
 
 int chrif_connected = 0;
 int char_fd = -1;
@@ -275,10 +280,11 @@ int chrif_save(struct map_session_data *sd, int flag) {
 
 	if (flag && sd->state.active) { //Store player data which is quitting
 		//FIXME: SC are lost if there's no connection at save-time because of the way its related data is cleared immediately after this function. [Skotlex]
-        if (chrif_isconnected()) {
-            chrif_save_scdata(sd);
-            chrif_skillcooldown_save(sd);
-        }
+	if (chrif_isconnected()) {
+		chrif_save_scdata(sd);
+		chrif_skillcooldown_save(sd);
+		chrif_save_bankdata(sd);
+	}
 		if ( !chrif_auth_logout(sd,flag == 1 ? ST_LOGOUT : ST_MAPCHANGE) )
 			ShowError("chrif_save: Failed to set up player %d:%d for proper quitting!\n", sd->status.account_id, sd->status.char_id);
 	}
@@ -577,20 +583,20 @@ int chrif_scdata_request(int account_id, int char_id) {
 	WFIFOL(char_fd,6) = char_id;
 	WFIFOSET(char_fd,10);
 #endif
-    return 0;
+	return 0;
 }
 
 /*==========================================
  * Request skillcooldown from charserver
  *------------------------------------------*/
 int chrif_skillcooldown_request(int account_id, int char_id) {
-    chrif_check(-1);
-    WFIFOHEAD(char_fd, 10);
-    WFIFOW(char_fd, 0) = 0x2b0a;
-    WFIFOL(char_fd, 2) = account_id;
-    WFIFOL(char_fd, 6) = char_id;
-    WFIFOSET(char_fd, 10);
-    return 0;
+	chrif_check(-1);
+	WFIFOHEAD(char_fd, 10);
+	WFIFOW(char_fd, 0) = 0x2b0a;
+	WFIFOL(char_fd, 2) = account_id;
+	WFIFOL(char_fd, 6) = char_id;
+	WFIFOSET(char_fd, 10);
+	return 0;
 }
 
 /*==========================================
@@ -1184,6 +1190,45 @@ int chrif_updatefamelist_ack(int fd) {
 	return 1;
 }
 
+int chrif_bankdata_request(int account_id, int char_id) {
+
+	chrif_check(-1);
+
+	WFIFOHEAD(char_fd,6);
+	WFIFOW(char_fd,0) = 0x2b2a;
+	WFIFOL(char_fd,2) = account_id;
+	WFIFOSET(char_fd,6);
+	return 0;
+}
+
+int chrif_load_bankdata(int fd){
+	struct map_session_data *sd;
+	int aid, bank_vault;
+
+	aid = RFIFOL(fd,2); //Player Account ID
+	bank_vault = RFIFOL(fd,6); //Player money in bank
+
+	sd = map_id2sd(aid);
+
+	if ( !sd ) {
+		ShowError("chrif_load_bankdata: Player of AID %d not found!\n", aid);
+		return -1;
+	}
+	sd->status.bank_vault = bank_vault;
+	return 1;
+}
+
+int chrif_save_bankdata(struct map_session_data *sd){
+	if( CheckForCharServer() )
+		return 0;
+	WFIFOHEAD(char_fd,10);
+	WFIFOW(char_fd,0) = 0x2b28;
+	WFIFOL(char_fd,2) = sd->status.account_id;
+	WFIFOL(char_fd,6) = sd->status.bank_vault;
+	WFIFOSET(char_fd,10);
+	return 1;
+}
+
 int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
 
 #ifdef ENABLE_SC_SAVING
@@ -1228,7 +1273,7 @@ int chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of the
 	WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
 	WFIFOSET(char_fd,WFIFOW(char_fd,2));
 #endif
-    return 0;
+	return 0;
 }
 
 int chrif_skillcooldown_save(struct map_session_data *sd) {
@@ -1300,35 +1345,35 @@ int chrif_load_scdata(int fd) {
 		status_change_start(NULL,&sd->bl, (sc_type)data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 1|2|4|8);
 	}
 #endif
-    return 0;
+	return 0;
 }
 
 //Retrieve and load skillcooldown for a player
 
 int chrif_skillcooldown_load(int fd) {
-    struct map_session_data *sd;
-    struct skill_cooldown_data *data;
-    int aid, cid, i, count;
-
-    aid = RFIFOL(fd, 4);
-    cid = RFIFOL(fd, 8);
-
-
-    sd = map_id2sd(aid);
-    if (!sd) {
-        ShowError("chrif_skillcooldown_load: Player of AID %d not found!\n", aid);
-        return -1;
-    }
-    if (sd->status.char_id != cid) {
-        ShowError("chrif_skillcooldown_load: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
-        return -1;
-    }
-    count = RFIFOW(fd, 12); //sc_count
-    for (i = 0; i < count; i++) {
-        data = (struct skill_cooldown_data*) RFIFOP(fd, 14 + i * sizeof (struct skill_cooldown_data));
-        skill_blockpc_start(sd, data->skill_id, data->tick);
-    }
-    return 0;
+	struct map_session_data *sd;
+	struct skill_cooldown_data *data;
+	int aid, cid, i, count;
+
+	aid = RFIFOL(fd, 4);
+	cid = RFIFOL(fd, 8);
+
+
+	sd = map_id2sd(aid);
+	if (!sd) {
+		ShowError("chrif_skillcooldown_load: Player of AID %d not found!\n", aid);
+		return -1;
+	}
+	if (sd->status.char_id != cid) {
+		ShowError("chrif_skillcooldown_load: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
+		return -1;
+	}
+	count = RFIFOW(fd, 12); //sc_count
+	for (i = 0; i < count; i++) {
+		data = (struct skill_cooldown_data*) RFIFOP(fd, 14 + i * sizeof (struct skill_cooldown_data));
+		skill_blockpc_start(sd, data->skill_id, data->tick);
+	}
+	return 0;
 }
 
 /*==========================================
@@ -1522,7 +1567,7 @@ int chrif_parse(int fd) {
 			case 0x2b04: chrif_recvmap(fd); break;
 			case 0x2b06: chrif_changemapserverack(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10), RFIFOL(fd,14), RFIFOW(fd,18), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28)); break;
 			case 0x2b09: map_addnickdb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
-            case 0x2b0b: chrif_skillcooldown_load(fd); break;
+			 case 0x2b0b: chrif_skillcooldown_load(fd); break;
 			case 0x2b0d: chrif_changedsex(fd); break;
 			case 0x2b0f: chrif_char_ask_name_answer(RFIFOL(fd,2), (char*)RFIFOP(fd,6), RFIFOW(fd,30), RFIFOW(fd,32)); break;
 			case 0x2b12: chrif_divorceack(RFIFOL(fd,2), RFIFOL(fd,6)); break;
@@ -1537,6 +1582,7 @@ int chrif_parse(int fd) {
 			case 0x2b24: chrif_keepalive_ack(fd); break;
 			case 0x2b25: chrif_deadopt(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
 			case 0x2b27: chrif_authfail(fd); break;
+			case 0x2b29: chrif_load_bankdata(fd); break;
 			default:
 				ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
 				set_eof(fd);

+ 3 - 0
src/map/chrif.h

@@ -41,6 +41,9 @@ int chrif_scdata_request(int account_id, int char_id);
 int chrif_skillcooldown_request(int account_id, int char_id);
 int chrif_skillcooldown_save(struct map_session_data *sd);
 int chrif_skillcooldown_load(int fd);
+int chrif_bankdata_request(int account_id, int char_id);
+int chrif_load_bankdata(int fd);
+int chrif_save_bankdata(struct map_session_data *sd);
 int chrif_save(struct map_session_data* sd, int flag);
 int chrif_charselectreq(struct map_session_data* sd, uint32 s_ip);
 int chrif_changemapserver(struct map_session_data* sd, uint32 ip, uint16 port);

+ 238 - 9
src/map/clif.c

@@ -55,8 +55,8 @@
 /* for clif_clearunit_delayed */
 static struct eri *delay_clearunit_ers;
 
-//#define DUMP_UNKNOWN_PACKET
-//#define DUMP_INVALID_PACKET
+#define DUMP_UNKNOWN_PACKET
+#define DUMP_INVALID_PACKET
 
 struct Clif_Config {
 	int packet_db_ver;	//Preferred packet version.
@@ -64,6 +64,7 @@ struct Clif_Config {
 } clif_config;
 
 struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1];
+int packet_db_ack[MAX_PACKET_VER + 1][MAX_ACK_FUNC + 1];
 
 //Converts item type in case of pet eggs.
 static inline int itemtype(int type) {
@@ -6116,8 +6117,14 @@ void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
 #endif
 }
 
-// [Ind/Hercules] - Data Thanks to Yommy
-void clif_cart_additem_ack(struct map_session_data *sd, int flag)
+// [Ind/Hercules] - Data Thanks to Yommy (ZC_ACK_ADDITEM_TO_CART)
+/* Acknowledge an item have been added to cart
+ * 012c <result>B
+ * result :
+ * 0 = ADDITEM_TO_CART_FAIL_WEIGHT
+ * 1 = ADDITEM_TO_CART_FAIL_COUNT
+ */
+void clif_cart_additem_ack(struct map_session_data *sd, uint8 flag)
 {
 	int fd;
 	unsigned char *buf;
@@ -6126,10 +6133,208 @@ void clif_cart_additem_ack(struct map_session_data *sd, int flag)
 	fd = sd->fd;
 	buf = WFIFOP(fd,0);
 	WBUFW(buf,0) = 0x12c;
-	WBUFL(buf,2) = flag;
+	WBUFB(buf,2) = flag;
 	clif_send(buf,packet_len(0x12c),&sd->bl,SELF);
 }
 
+// 09B7 <unknow data> (ZC_ACK_OPEN_BANKING) 
+void clif_bank_open(struct map_session_data *sd){
+	int fd = sd->fd;
+	
+	WFIFOHEAD(fd,4);
+	WFIFOW(fd,0) = 0x09b7;
+	WFIFOW(fd,2) = 0;
+	WFIFOSET(fd,4);
+        return; //TODO found out what going on here
+}
+
+/*
+ * Request to Open the banking system
+ * 09B6 <aid>L ??? (dunno just wild guess checkme)
+ */
+void clif_parse_BankOpen(int fd, struct map_session_data* sd) {
+	//TODO check if preventing trade or stuff like that
+	//also mark something in case char ain't available for saving, should we check now ?
+	if( !battle_config.feature_banking ) {
+		clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1483));
+		return;
+	}    
+	else {
+		struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+		int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
+                sd->state.banking = 1;
+	}
+        //request save ?
+//      chrif_bankdata_request(sd->status.account_id, sd->status.char_id); 
+        //on succes open bank ?
+	clif_bank_open(sd);
+        return;
+}
+
+// 09B9 <unknow data> (ZC_ACK_CLOSE_BANKING) 
+
+void clif_bank_close(struct map_session_data *sd){
+	int fd = sd->fd;
+	
+	WFIFOHEAD(fd,4);
+	WFIFOW(fd,0) = 0x09B9;
+	WFIFOW(fd,2) = 0;
+	WFIFOSET(fd,4);
+        return; //TODO found out what going on here
+}
+
+/*
+ * Request to close the banking system
+ * 09B8 <aid>L ??? (dunno just wild guess checkme)
+ */
+void clif_parse_BankClose(int fd, struct map_session_data* sd) {       
+        struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+        int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
+        if( !battle_config.feature_banking ) {
+		clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1483));
+                //still allow to go trough to not stuck player if we have disable it while they was in
+	}
+        sd->state.banking = 0;
+	clif_bank_close(sd);
+        return;
+}
+
+/*
+ * Display how much we got in bank (I suppose)
+  09A6 <Bank_Vault>Q <Reason>W (PACKET_ZC_BANKING_CHECK)
+ */
+void clif_Bank_Check(struct map_session_data* sd) {
+        unsigned char buf[13];
+        struct s_packet_db* info;
+        int16 len;
+        int cmd = 0;
+        
+	nullpo_retv(sd);
+        
+        cmd = packet_db_ack[sd->packet_ver][ZC_BANKING_CHECK];
+        if(!cmd) cmd = 0x09A6; //default
+        info = &packet_db[sd->packet_ver][cmd]; 
+        len = info->len;
+//        sd->state.banking = 1; //mark opening and closing
+
+        WBUFW(buf,0) = cmd;
+        WBUFQ(buf,info->pos[0]) = sd->status.bank_vault; //testig value
+        WBUFW(buf,info->pos[1]) = 0; //reason
+        clif_send(buf,len,&sd->bl,SELF);       
+}
+
+/*
+ * Requesting the data in bank
+ * 09AB <aid>L (PACKET_CZ_REQ_BANKING_CHECK)
+ */
+void clif_parse_BankCheck(int fd, struct map_session_data* sd) {  
+	nullpo_retv(sd);
+	
+	if( !battle_config.feature_banking ) {
+		clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1483));
+		return;
+	}
+	else {
+		struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+		int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
+                if(sd->status.account_id == aid) //since we have it let check it for extra security
+                        clif_Bank_Check(sd);
+	}
+}
+
+/*
+ * Acknowledge of deposit some money in bank
+  09A8 <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_DEPOSIT)
+ */
+void clif_bank_deposit(struct map_session_data *sd, enum e_BANKING_DEPOSIT_ACK reason) {
+	unsigned char buf[17];
+        struct s_packet_db* info;
+        int16 len;
+        int cmd =0;
+        
+	nullpo_retv(sd);
+        
+        cmd = packet_db_ack[sd->packet_ver][ZC_ACK_BANKING_DEPOSIT];
+        if(!cmd) cmd = 0x09A8;
+        info = &packet_db[sd->packet_ver][cmd]; 
+        len = info->len;
+        
+        WBUFW(buf,0) = cmd;
+        WBUFW(buf,info->pos[0]) = (short)reason;	
+	WBUFQ(buf,info->pos[1]) = sd->status.bank_vault;/* money in the bank */
+	WBUFL(buf,info->pos[2]) = sd->status.zeny;/* how much zeny char has after operation */
+	clif_send(buf,len,&sd->bl,SELF);
+}
+
+/*
+ * Request saving some money in bank
+ * @author : original [Yommy/Hercules]
+ * 09A7 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_DEPOSIT)
+ */
+void clif_parse_BankDeposit(int fd, struct map_session_data* sd) {
+	if( !battle_config.feature_banking ) {
+		clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1483));
+		return;
+	}
+	else {
+		struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+		int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
+		int money = RFIFOL(fd,info->pos[1]);
+                
+                if(sd->status.account_id == aid){
+                    money = max(0,money);
+                    enum e_BANKING_DEPOSIT_ACK reason = pc_bank_deposit(sd,money);
+                    clif_bank_deposit(sd,reason);
+                }
+	}
+}
+
+/*
+ * Acknowledge of withdrawing some money from bank
+  09AA <Reason>W <Money>Q <balance>L (PACKET_ZC_ACK_BANKING_WITHDRAW)
+ */
+void clif_bank_withdraw(struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK reason) {
+	unsigned char buf[17];
+        struct s_packet_db* info;
+        int16 len;
+        int cmd;
+        
+	nullpo_retv(sd);
+        
+        cmd = packet_db_ack[sd->packet_ver][ZC_ACK_BANKING_WITHDRAW];
+        if(!cmd) cmd = 0x09AA;
+        info = &packet_db[sd->packet_ver][cmd]; 
+        len = info->len;
+
+        WBUFW(buf,0) = cmd;
+        WBUFW(buf,info->pos[0]) = (short)reason;	
+	WBUFQ(buf,info->pos[1]) = sd->status.bank_vault;/* money in the bank */
+	WBUFL(buf,info->pos[2]) = sd->status.zeny;/* how much zeny char has after operation */
+
+	clif_send(buf,len,&sd->bl,SELF);
+}
+
+/*
+ * Request Withdrawing some money from bank
+ * 09A9 <AID>L <Money>L (PACKET_CZ_REQ_BANKING_WITHDRAW)
+ */
+void clif_parse_BankWithdraw(int fd, struct map_session_data* sd) {	
+	if( !battle_config.feature_banking ) {
+		clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1483));
+		return;
+	}
+	else {
+		struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+		int aid = RFIFOL(fd,info->pos[0]); //unused should we check vs fd ?
+		int money = RFIFOL(fd,info->pos[1]);
+                if(sd->status.account_id == aid){
+                    money = max(0,money);
+                    enum e_BANKING_WITHDRAW_ACK reason = pc_bank_withdraw(sd,money);
+                    clif_bank_withdraw(sd,reason);
+                }
+	}
+}
+
 /// Deletes an item from character's cart (ZC_DELETE_ITEM_FROM_CART).
 /// 0125 <index>.W <amount>.L
 void clif_cart_delitem(struct map_session_data *sd,int n,int amount)
@@ -17069,9 +17274,9 @@ void packetdb_readdb(void)
 		0,  0,  0,  0,  0,  0,  0, 14,  6, 50,  0,  0,  0,  0,  0,  0,
 	//#0x0980
 		0,  0,  0, 29,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-		31,  0,  0,  0,  0,  0,  0,  -1,  8,  11,  9,  8,  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, 14,  0,  0,  0,  0,  0,  0,  0,  0,
+		31, 0,  0,  0,  0,  0,  0, -1,  8, 11,  9,  8,  0,  0,  0,  0,
+		0,  0,  0,  0,  0,  0, 12, 10, 14, 10, 14,  6,  0,  0,  0,  0,
+		0,  0,  0,  0,  0,  0,  6,  4,  6,  4,  0,  0,  0,  0,  0,  0,
 
 	};
 	struct {
@@ -17261,6 +17466,12 @@ void packetdb_readdb(void)
 		{clif_parse_PartyBookingUpdateReq,"bookingupdatereq"},
 		{clif_parse_PartyBookingDeleteReq,"bookingdelreq"},
 #endif
+		{clif_parse_BankCheck,"bankcheck"},
+		{clif_parse_BankDeposit,"bankdeposit"},
+		{clif_parse_BankWithdraw,"bankwithdrawal"},
+		{clif_parse_BankOpen,"bankopen"},
+		{clif_parse_BankClose,"bankclose"},
+
 		{clif_parse_PVPInfo,"pvpinfo"},
 		{clif_parse_LessEffect,"lesseffect"},
 		// Buying Store
@@ -17288,6 +17499,15 @@ void packetdb_readdb(void)
 		{ clif_parse_ranklist, "ranklist"},
 		{NULL,NULL}
 	};
+        struct {
+		char *name; //function name
+                int funcidx; //
+	} clif_ack_func[]={ //hash
+            { "ZC_ACK_OPEN_BANKING", ZC_ACK_OPEN_BANKING},
+            { "ZC_ACK_BANKING_DEPOSIT", ZC_ACK_BANKING_DEPOSIT},
+            { "ZC_ACK_BANKING_WITHDRAW", ZC_ACK_BANKING_WITHDRAW},
+            { "ZC_BANKING_CHECK", ZC_BANKING_CHECK},
+        };
 
 	// initialize packet_db[SERVER] from hardcoded packet_len_table[] values
 	memset(packet_db,0,sizeof(packet_db));
@@ -17344,6 +17564,7 @@ void packetdb_readdb(void)
 				// copy from previous version into new version and continue
 				// - indicating all following packets should be read into the newer version
 				memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
+                                memcpy(&packet_db_ack[packet_ver], &packet_db_ack[prev_ver], sizeof(packet_db_ack[0]));
 				continue;
 			} else if(strcmpi(w1,"packet_db_ver")==0) {
 				if(strcmpi(w2,"default")==0) //This is the preferred version.
@@ -17390,7 +17611,15 @@ void packetdb_readdb(void)
 		ARR_FIND( 0, ARRAYLENGTH(clif_parse_func), j, clif_parse_func[j].name != NULL && strcmp(str[2],clif_parse_func[j].name)==0 );
 		if( j < ARRAYLENGTH(clif_parse_func) )
 			packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
-
+                else { //search if it's a mapped ack func
+                        ARR_FIND( 0, ARRAYLENGTH(clif_ack_func), j, clif_ack_func[j].name != NULL && strcmp(str[2],clif_ack_func[j].name)==0 );
+                        if( j < ARRAYLENGTH(clif_ack_func)) {
+                                int fidx = clif_ack_func[j].funcidx;
+                                packet_db_ack[packet_ver][fidx] = cmd;
+                                ShowInfo("Added %s, <=> %X i=%d for v=%d\n",clif_ack_func[j].name,cmd,fidx,packet_ver);
+                        }
+                }
+                
 		// set the identifying cmd for the packet_db version
 		if (strcmp(str[2],"wanttoconnection")==0)
 			clif_config.connect_cmd[packet_ver] = cmd;

+ 34 - 3
src/map/clif.h

@@ -35,20 +35,42 @@ struct party_booking_ad_info;
 enum
 {// packet DB
 	MAX_PACKET_DB  = 0xf00,
-	MAX_PACKET_VER = 44,
+	MAX_PACKET_VER = 45,
 	MAX_PACKET_POS = 20,
 };
 
+enum e_packet_ack {
+    ZC_ACK_OPEN_BANKING = 0,
+    ZC_ACK_BANKING_DEPOSIT,
+    ZC_ACK_BANKING_WITHDRAW,
+    ZC_BANKING_CHECK,
+    MAX_ACK_FUNC //auto upd len
+};
+
 struct s_packet_db {
 	short len;
 	void (*func)(int, struct map_session_data *);
 	short pos[MAX_PACKET_POS];
 };
 
+enum e_BANKING_DEPOSIT_ACK {
+	BDA_SUCCESS  = 0x0,
+	BDA_ERROR    = 0x1,
+	BDA_NO_MONEY = 0x2,
+	BDA_OVERFLOW = 0x3,
+};
+ 
+enum e_BANKING_WITHDRAW_ACK {
+	BWA_SUCCESS       = 0x0,
+	BWA_NO_MONEY      = 0x1,
+	BWA_UNKNOWN_ERROR = 0x2,
+};
+
 // packet_db[SERVER] is reserved for server use
 #define SERVER 0
 #define packet_len(cmd) packet_db[SERVER][cmd].len
 extern struct s_packet_db packet_db[MAX_PACKET_VER+1][MAX_PACKET_DB+1];
+extern int packet_db_ack[MAX_PACKET_VER + 1][MAX_ACK_FUNC + 1];
 
 // local define
 typedef enum send_target {
@@ -58,7 +80,7 @@ typedef enum send_target {
 	AREA_WOS,			// area, without self
 	AREA_WOC,			// area, without chatrooms
 	AREA_WOSC,			// area, without own chatroom
-	AREA_CHAT_WOC,		// hearable area, without chatrooms
+	AREA_CHAT_WOC,			// hearable area, without chatrooms
 	CHAT,				// current chatroom
 	CHAT_WOS,			// current chatroom, without self
 	PARTY,
@@ -488,7 +510,7 @@ void clif_inventorylist(struct map_session_data *sd);
 void clif_equiplist(struct map_session_data *sd);
 
 void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail);
-void clif_cart_additem_ack(struct map_session_data *sd, int flag);
+void clif_cart_additem_ack(struct map_session_data *sd, uint8 flag);
 void clif_cart_delitem(struct map_session_data *sd,int n,int amount);
 void clif_cartlist(struct map_session_data *sd);
 void clif_clearcart(int fd);
@@ -715,6 +737,15 @@ void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_boo
 void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index);
 void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad);
 
+/* Bank System [Yommy/Hercules] */
+void clif_bank_deposit (struct map_session_data *sd, enum e_BANKING_DEPOSIT_ACK reason);
+void clif_bank_withdraw (struct map_session_data *sd,enum e_BANKING_WITHDRAW_ACK reason);
+void clif_parse_BankDeposit (int fd, struct map_session_data *sd);
+void clif_parse_BankWithdraw (int fd, struct map_session_data *sd);
+void clif_parse_BankCheck (int fd, struct map_session_data *sd);
+void clif_parse_BankOpen (int fd, struct map_session_data *sd);
+void clif_parse_BankClose (int fd, struct map_session_data *sd);
+
 void clif_showdigit(struct map_session_data* sd, unsigned char type, int value);
 
 /// Buying Store System

+ 1 - 0
src/map/log.c

@@ -73,6 +73,7 @@ static char log_picktype2char(e_log_pick_type type)
 		case LOG_TYPE_AUCTION:          return 'I';  // Auct(I)on
 		case LOG_TYPE_BUYING_STORE:     return 'B';  // (B)uying Store
 		case LOG_TYPE_LOOT:             return 'L';  // (L)oot (consumed monster pick/drop)
+		case LOG_TYPE_BANK:             return 'K';  // Ban(K) Transactions
 		case LOG_TYPE_OTHER:			return 'X';  // Other
 		case LOG_TYPE_CASH:				return '$';  // Cash
 	}

+ 1 - 0
src/map/log.h

@@ -45,6 +45,7 @@ typedef enum e_log_pick_type
 	LOG_TYPE_BUYING_STORE     = 0x08000,
 	LOG_TYPE_OTHER            = 0x10000,
 	LOG_TYPE_CASH             = 0x20000,
+	LOG_TYPE_BANK             = 0x40000,
 	// combinations
 	LOG_TYPE_LOOT             = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME,
 	// all

+ 42 - 0
src/map/pc.c

@@ -1292,6 +1292,7 @@ int pc_reg_received(struct map_session_data *sd)
 	status_calc_pc(sd,1);
 	chrif_scdata_request(sd->status.account_id, sd->status.char_id);
 	chrif_skillcooldown_request(sd->status.account_id, sd->status.char_id);
+	chrif_bankdata_request(sd->status.account_id, sd->status.char_id);
 	intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
 	intif_request_questlog(sd);
 
@@ -4343,6 +4344,7 @@ int pc_isUseitem(struct map_session_data *sd,int n)
 		sd->sc.data[SC_TRICKDEAD] ||
 		sd->sc.data[SC_HIDING] ||
 		sd->sc.data[SC__SHADOWFORM] ||
+		sd->sc.data[SC__INVISIBILITY] ||
 		sd->sc.data[SC__MANHOLE] ||
 		sd->sc.data[SC_KAGEHUMI] ||
 		(sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM)
@@ -10248,6 +10250,46 @@ void pc_damage_log_clear(struct map_session_data *sd, int id)
 	}
 }
 
+enum e_BANKING_DEPOSIT_ACK pc_bank_deposit(struct map_session_data *sd, int money) {
+	unsigned int limit_check = money+sd->status.bank_vault;
+	
+	if( money <= 0 || limit_check > MAX_BANK_ZENY ) {
+		return BDA_OVERFLOW;
+	} else if ( money > sd->status.zeny ) {
+		return BDA_NO_MONEY;
+	}
+
+	if( pc_payzeny(sd,money, LOG_TYPE_BANK, NULL) )
+		return BDA_NO_MONEY;
+
+	sd->status.bank_vault += money;
+	if( save_settings&256 )
+		chrif_save(sd,0);
+	return BDA_SUCCESS;
+}
+
+enum e_BANKING_WITHDRAW_ACK pc_bank_withdraw(struct map_session_data *sd, int money) {
+	unsigned int limit_check = money+sd->status.zeny;
+	
+	if( money <= 0 ) {
+		return BWA_UNKNOWN_ERROR;
+	} else if ( money > sd->status.bank_vault ) {
+		return BWA_NO_MONEY;
+	} else if ( limit_check > MAX_ZENY ) {
+		/* no official response for this scenario exists. */
+		clif_colormes(sd,COLOR_RED,msg_txt(sd,1482));
+		return BWA_UNKNOWN_ERROR;
+	}
+	
+	if( pc_getzeny(sd,money, LOG_TYPE_BANK, NULL) )
+		return BWA_NO_MONEY;
+	
+	sd->status.bank_vault -= money;
+	if( save_settings&256 )
+		chrif_save(sd,0);
+	return BWA_SUCCESS;
+}
+
 /*==========================================
  * pc Init/Terminate
  *------------------------------------------*/

+ 4 - 0
src/map/pc.h

@@ -192,6 +192,7 @@ struct map_session_data {
 		unsigned int prevend : 1;//used to flag wheather you've spent 40sp to open the vending or not.
 		unsigned int warping : 1;//states whether you're in the middle of a warp processing
 		unsigned int permanent_speed : 1; // When 1, speed cannot be changed through status_calc_pc().
+		unsigned int banking : 1; //1 when we using the banking system 0 when closed
 	} state;
 	struct {
 		unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
@@ -1010,6 +1011,9 @@ void pc_baselevelchanged(struct map_session_data *sd);
 void pc_damage_log_add(struct map_session_data *sd, int id);
 void pc_damage_log_clear(struct map_session_data *sd, int id);
 
+enum e_BANKING_DEPOSIT_ACK pc_bank_deposit(struct map_session_data *sd, int money);
+enum e_BANKING_WITHDRAW_ACK pc_bank_withdraw(struct map_session_data *sd, int money);
+
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
 int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_race, uint32 mob_mode, int type);
 #endif

+ 0 - 1
src/map/status.c

@@ -1212,7 +1212,6 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
 			status_change_end(target, SC_CLOAKING, INVALID_TIMER);
 			status_change_end(target, SC_CHASEWALK, INVALID_TIMER);
 			status_change_end(target, SC_CAMOUFLAGE, INVALID_TIMER);
-			status_change_end(target, SC__INVISIBILITY, INVALID_TIMER);
 			status_change_end(target, SC_DEEPSLEEP, INVALID_TIMER);
 			if ((sce=sc->data[SC_ENDURE]) && !sce->val4) {
 				//Endure count is only reduced by non-players on non-gvg maps.