· 6 years ago · Sep 19, 2019, 09:26 PM
1diff --git a/.gitignore b/.gitignore
2index 8c735b6..f78af28 100644
3--- a/.gitignore
4+++ b/.gitignore
5@@ -1,3 +1,7 @@
6+/autom4te.cache/output.1
7+/autom4te.cache/traces.1
8+/autom4te.cache/output.1
9+/autom4te.cache/traces.1
10 /autom4te.cache/output.0
11 /autom4te.cache/requests
12 /autom4te.cache/traces.0
13diff --git a/conf/battle/player.conf b/conf/battle/player.conf
14index b587839..ffb4444 100644
15--- a/conf/battle/player.conf
16+++ b/conf/battle/player.conf
17@@ -5,6 +5,8 @@
18 //--------------------------------------------------------------
19 // Note 1: Value is a config switch (on/off, yes/no or 1/0)
20 // Note 2: Value is in percents (100 means 100%)
21+// Note 3: Value is a bit field. If no description is given,
22+// assume unit types (1: Pc, 2: Mob, 4: Pet, 8: Homun)
23 //--------------------------------------------------------------
24
25 // Players' maximum HP rate? (Default is 100)
26@@ -141,4 +143,49 @@ idle_no_autoloot: 0
27
28 // Minimum distance a vending/chat room must be from a NPC in order to be placed.
29 // Default: 3 (0: disabled).
30-min_npc_vendchat_distance: 3
31\ No newline at end of file
32+min_npc_vendchat_distance: 3
33+
34+
35+// Storage slot increase. Setting to 0 will disable.
36+// Give more storage slots above the MIN_STORAGE limit.
37+// Note: MIN_STORAGE + storage_increase cannot exceed MAX_STORAGE.
38+// Default: 300
39+vip_storage_increase: 300
40+
41+// Base EXP rate increase. Setting to 0 will disable. (Note 2)
42+// Default: 50
43+vip_base_exp_increase: 50
44+
45+// Exp. penalty rate multiplier for Non-VIP accounts
46+// Multiplies the 'death_penalty_base' and 'death_penalty_job' settings in 'conf/battle/exp.conf'.
47+// Default: 3 (3*100 = 3% penalty)
48+vip_exp_penalty_base_normal: 3
49+vip_exp_penalty_job_normal: 3
50+
51+// Exp. penalty rate multiplier for VIP accounts
52+// Multiplies the 'death_penalty_base' and 'death_penalty_job' settings in 'conf/battle/exp.conf'.
53+// Default: 1 (1*100 = 1% penalty)
54+vip_exp_penalty_base: 1
55+vip_exp_penalty_job: 1
56+
57+// Job EXP rate increase. Setting to 0 will disable. (Note 2)
58+// Default: 50
59+vip_job_exp_increase: 50
60+
61+// Battle Manual EXP increase. Setting to 0 will disable.
62+// - Regular/Thick Battle Manual: 50+(50/X) = 75%
63+// - HE Battle Manual: 100+(100/X) = 150%
64+// - Battle Manual x3: 200+(200/X) = 300%
65+// Note: X is what the config is set to.
66+// Default: 2
67+vip_bm_increase: 2
68+
69+// Item drop increase. Setting to 0 will disable.
70+// Note: 50 = 0.5%
71+// Default: 50
72+vip_drop_increase: 50
73+
74+// GemStone requirement. Setting to false will disable.
75+// Can the VIP Group ignore GemStone requirement for skills?
76+// Default: true
77+vip_gemstone: true
78diff --git a/conf/char_athena.conf b/conf/char_athena.conf
79index c05d157..4dc80ca 100644
80--- a/conf/char_athena.conf
81+++ b/conf/char_athena.conf
82@@ -140,11 +140,6 @@ char_name_option: 1
83 // Note: Don't add spaces unless you mean to add 'space' to the list.
84 char_name_letters: abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
85
86-// How many Characters are allowed per Account ? (0 = disabled)
87-// You can not exceed the limit of MAX_CHARS slots, defined in mmo.h
88-// Doing that, chars_per_account will be default to MAX_CHARS.
89-chars_per_account: 0
90-
91 // Restrict character deletion by BaseLevel
92 // 0: no restriction (players can delete characters of any level)
93 // -X: you can't delete chars with BaseLevel <= X
94diff --git a/conf/groups.conf b/conf/groups.conf
95index 63ccdf8..1ff3853 100644
96--- a/conf/groups.conf
97+++ b/conf/groups.conf
98@@ -220,6 +220,19 @@ groups: (
99 }
100 },
101 {
102+ id: 5
103+ name: "VIP"
104+ inherit: ( "Player" ) /* can do everything Players can */
105+ level: 0
106+ commands: {
107+ rates: true
108+ who: true
109+ }
110+ permissions: {
111+ /* no permissions by default */
112+ }
113+},
114+{
115 id: 10
116 name: "Law Enforcement"
117 inherit: ( "Support" )
118diff --git a/conf/login_athena.conf b/conf/login_athena.conf
119index 6a9002b..cc0b0ef 100644
120--- a/conf/login_athena.conf
121+++ b/conf/login_athena.conf
122@@ -73,6 +73,23 @@ group_id_to_connect: -1
123 // 0 or more: group id
124 min_group_id_to_connect: -1
125
126+// Which group (ID) will be denoted as the VIP Group?
127+// Default: 5
128+vip_group: 5
129+
130+// How many Characters are allowed per Account ? (0 = disabled)
131+// You can not exceed the limit of MAX_CHARS slots, defined in mmo.h
132+// Doing that, chars_per_account will be default to MAX_CHARS.
133+// If setting to 0 MIN_CHARS value will be used
134+chars_per_account: 0
135+
136+// Max character limit increase. Setting to 0 will disable.
137+// Increase MAX_CHAR if you want to increase char_increase.
138+// Note: MAX_CHARS - chars_per_account = Amount of VIP Chars (char_increase value in login table).
139+// Note2: this setting need to be set after chars_per_account
140+// Default: 6
141+vip_char_increase: 6
142+
143 // Starting additional sec from now for the limited time at creation of account
144 // -1: new account are created with UNlimited time (default value)
145 // 0 or more: new accounts was created by addition of the value (in sec) to the actual time (to set first limited time)
146diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf
147index 976535f..f281e90 100644
148--- a/conf/msg_conf/map_msg.conf
149+++ b/conf/msg_conf/map_msg.conf
150@@ -693,6 +693,16 @@
151 694: Hanbok
152 695: Rebellion
153
154+// @vip
155+700: Usage: @vip <time> <character name>
156+701: Invalid time for VIP command.
157+702: Time parameter format is +/-<value> to alter. y/a = Year, m = Month, d/j = Day, h = Hour, n/mn = Minute, s = Second.
158+703: GM has removed your VIP time.
159+704: Player is no longer VIP.
160+705: %s is VIP for %d years, %d months, %d days, %d hours and %d minutes.
161+706: This player is now VIP for %d years, %d months, %d days, %d hours and %d minutes.
162+707: You are VIP until
163+//708-899 free
164
165 //------------------------------------
166 // More atcommands message
167diff --git a/sql-files/main.sql b/sql-files/main.sql
168index ffa5bbe..bbfe083 100644
169--- a/sql-files/main.sql
170+++ b/sql-files/main.sql
171@@ -460,6 +460,8 @@ CREATE TABLE IF NOT EXISTS `login` (
172 `pincode` varchar(4) NOT NULL DEFAULT '',
173 `pincode_change` int(11) unsigned NOT NULL DEFAULT '0',
174 `bank_vault` int(11) NOT NULL DEFAULT '0',
175+ `vip_time` int(11) unsigned NOT NULL default '0',
176+ `old_group` tinyint(3) NOT NULL default '0',
177 PRIMARY KEY (`account_id`),
178 KEY `name` (`userid`)
179 ) ENGINE=MyISAM AUTO_INCREMENT=2000000;
180diff --git a/src/char/char.c b/src/char/char.c
181index 052a8b2..c0edce3 100644
182--- a/src/char/char.c
183+++ b/src/char/char.c
184@@ -111,7 +111,6 @@ struct mmo_map_server {
185 #define TRIM_CHARS "\255\xA0\032\t\x0A\x0D " //The following characters are trimmed regardless because they cause confusion and problems on the servers. [Skotlex]
186 char char_name_letters[1024] = ""; // list of letters/symbols allowed (or not) in a character name. by [Yor]
187
188-int char_per_account = 0; //Maximum chars per account (default unlimited) [Sirius]
189 int char_del_level = 0; //From which level u can delete character [Lupus]
190 int char_del_delay = 86400;
191
192@@ -133,7 +132,9 @@ struct char_session_data {
193 char email[40]; // e-mail (default: a@a.com) by [Yor]
194 time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
195 int group_id; // permission
196- uint8 char_slots;
197+ uint8 char_slots; //total char that sd can create
198+ uint8 chars_vip;
199+ uint8 chars_billing;
200 uint32 version;
201 uint8 clienttype;
202 char new_name[NAME_LENGTH];
203@@ -146,6 +147,7 @@ struct char_session_data {
204 // Addon system
205 int bank_vault;
206 unsigned int char_moves[MAX_CHARS]; // character moves left
207+ uint8 isvip;
208 };
209
210 struct startitem {
211@@ -183,6 +185,11 @@ struct startitem {
212 void pincode_decrypt( uint32 userSeed, char* pin );
213 int pincode_compare( int fd, struct char_session_data* sd, char* pin );
214
215+int mapif_parse_vipactive(int fd);
216+int mapif_vipack(uint32 aid, uint32 vip_time, uint8 isvip, uint32 groupid);
217+int logif_reqviddata(uint32 aid, uint8 type, uint32 add_vip_time);
218+int logif_parse_vipack(int fd);
219+
220 // Addon system
221 bool char_move_enabled = true;
222 bool char_movetoused = true;
223@@ -1441,8 +1448,6 @@ int mmo_char_sql_init(void)
224 {
225 char_db_= idb_alloc(DB_OPT_RELEASE_DATA);
226
227- ShowStatus("Characters per Account: '%d'.\n", char_per_account);
228-
229 //the 'set offline' part is now in check_login_conn ...
230 //if the server connects to loginserver
231 //it will dc all off players
232@@ -1579,11 +1584,12 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag
233 if( flag < 0 )
234 return flag;
235
236+ ShowInfo("make_new_char_sql slot=%d, sd->char_slots=%d \n",slot,sd->char_slots);
237 //check other inputs
238 #if PACKETVER >= 20120307
239- if(slot >= sd->char_slots)
240+ if(slot < 0 || slot >= sd->char_slots)
241 #else
242- if((slot >= sd->char_slots) // slots
243+ if((slot < 0 || slot >= sd->char_slots) // slots
244 || (str + agi + vit + int_ + dex + luk != 6*5 ) // stats
245 || (str < 1 || str > 9 || agi < 1 || agi > 9 || vit < 1 || vit > 9 || int_ < 1 || int_ > 9 || dex < 1 || dex > 9 || luk < 1 || luk > 9) // individual stat values
246 || (str + int_ != 10 || agi + luk != 10 || vit + dex != 10) ) // pairs
247@@ -1596,12 +1602,10 @@ int make_new_char_sql(struct char_session_data* sd, char* name_, int str, int ag
248
249
250 // check the number of already existing chars in this account
251- if( char_per_account != 0 ) {
252- if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id) )
253- Sql_ShowDebug(sql_handle);
254- if( Sql_NumRows(sql_handle) >= char_per_account )
255- return -2; // character account limit exceeded
256- }
257+ if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d'", char_db, sd->account_id) )
258+ Sql_ShowDebug(sql_handle);
259+ if( Sql_NumRows(sql_handle) >= sd->char_slots )
260+ return -2; // character account limit exceeded
261
262 // check char slot
263 if( SQL_ERROR == Sql_Query(sql_handle, "SELECT 1 FROM `%s` WHERE `account_id` = '%d' AND `char_num` = '%d' LIMIT 1", char_db, sd->account_id, slot) )
264@@ -1979,7 +1983,11 @@ void char_parse_req_charlist(int fd, struct char_session_data* sd){
265 //----------------------------------------
266 int mmo_char_send006b(int fd, struct char_session_data* sd){
267 int j, offset = 0;
268- bool newvers = (sd->version >= (uint32)date2version(20100413));
269+ bool newvers = (sd->version >= date2version(20100413));
270+
271+ ShowInfo("mmo_char_send006b, sd.charslot=%d, nbmaxchars=%d, nbpremium=%d, nbbilling=%d, isvip=%d \n",
272+ sd->char_slots,MAX_CHARS,sd->chars_vip,sd->chars_billing,sd->isvip);
273+
274 if(newvers) //20100413
275 offset += 3;
276 if (save_log)
277@@ -1989,11 +1997,16 @@ int mmo_char_send006b(int fd, struct char_session_data* sd){
278 WFIFOHEAD(fd,j + MAX_CHARS*MAX_CHAR_BUF);
279 WFIFOW(fd,0) = 0x6b;
280 if(newvers){ //20100413
281- WFIFOB(fd,4) = MAX_CHARS; // Max slots.
282- WFIFOB(fd,5) = sd->char_slots; // Available slots. (PremiumStartSlot)
283- WFIFOB(fd,6) = MAX_CHARS; // Premium slots. (Any existent chars past sd->char_slots but within MAX_CHARS will show a 'Premium Service' in red)
284- }
285- memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes
286+ WFIFOB(fd,4) = MAX_CHARS; // Max slots. 15
287+ WFIFOB(fd,5) = MAX_CHARS-sd->chars_billing-sd->chars_vip; // PremiumStartSlot
288+ WFIFOB(fd,6) = MAX_CHARS-sd->chars_billing; // PremiumEndSlot // 9-15
289+ /* this+0x7 char dummy1_beginbilling */
290+ /* this+0x8 unsigned long code */
291+ /* this+0xc unsigned long time1 */
292+ /* this+0x10 unsigned long time2 */
293+ /* this+0x14 char dummy2_endbilling[7] */
294+ }
295+ memset(WFIFOP(fd,4 + offset), 0, 20); // unknown bytes 4-24 7-27
296 j+=mmo_chars_fromsql(sd, WFIFOP(fd,j));
297 WFIFOW(fd,2) = j; // packet len
298 WFIFOSET(fd,j);
299@@ -2007,21 +2020,25 @@ int mmo_char_send006b(int fd, struct char_session_data* sd){
300 void mmo_char_send082d(int fd, struct char_session_data* sd) {
301 if (save_log)
302 ShowInfo("Loading Char Data ("CL_BOLD"%d"CL_RESET")\n",sd->account_id);
303+
304+ ShowInfo("mmo_char_send082d, sd.charslot=%d, nbmaxchars=%d, nbpremium=%d, nbbilling=%d, isvip=%d\n",
305+ sd->char_slots,MAX_CHARS,sd->chars_vip,sd->chars_billing,sd->isvip);
306+
307 WFIFOHEAD(fd,29);
308 WFIFOW(fd,0) = 0x82d;
309 WFIFOW(fd,2) = 29;
310- WFIFOB(fd,4) = sd->char_slots;
311- WFIFOB(fd,5) = MAX_CHARS - sd->char_slots;
312- WFIFOB(fd,6) = MAX_CHARS - sd->char_slots;
313- WFIFOB(fd,7) = sd->char_slots;
314- WFIFOB(fd,8) = sd->char_slots;
315+ WFIFOB(fd,4) = sd->char_slots-sd->chars_vip; //NormalSlotNum
316+ WFIFOB(fd,5) = sd->chars_vip; //PremiumSlotNum
317+ WFIFOB(fd,6) = sd->chars_billing; //BillingSlotNum
318+ WFIFOB(fd,7) = sd->char_slots; //ProducibleSlotNum
319+ WFIFOB(fd,8) = sd->char_slots; //ValidSlotNum
320 memset(WFIFOP(fd,9), 0, 20); // unused bytes
321 WFIFOSET(fd,29);
322 }
323
324 void mmo_char_send(int fd, struct char_session_data* sd){
325- //ShowInfo("sd->version = %d\n",sd->version);
326- if(sd->version > (uint32)date2version(20130000) ){
327+ ShowInfo("sd->version = %d\n",sd->version);
328+ if(sd->version > date2version(20130000) ){
329 mmo_char_send082d(fd,sd);
330 char_charlist_notify(fd,sd);
331 char_block_character(fd,sd);
332@@ -2170,7 +2187,7 @@ int loginif_BankingReq(int32 account_id, int8 type, int32 data){
333 WFIFOB(login_fd,6) = type;
334 WFIFOL(login_fd,7) = data;
335 WFIFOSET(login_fd,11);
336- return 1;
337+ return 1;
338 }
339 return 0;
340 }
341@@ -2239,6 +2256,76 @@ int mapif_parse_ReqBankInfo(int fd){
342 return 1;
343 }
344
345+/*
346+ * ZH 0x2b2c
347+ * HA 0x2742
348+ * We received an request vip_info from map-server.
349+ * Transmit it to login-serv as it's the one knowing the info
350+ */
351+int mapif_parse_vipactive(int fd) {
352+#ifdef VIP_ENABLE
353+ uint32 aid = RFIFOL(fd,2); //aid
354+ uint8 type = RFIFOB(fd,6); //type
355+ uint32 adddur = RFIFOL(fd,7); //req_inc_duration
356+ RFIFOSKIP(fd,11);
357+ logif_reqviddata(aid, type, adddur);
358+#endif
359+ return 0;
360+}
361+
362+/*
363+ * HZ 0x2b2b
364+ * Transmist vip data to mapserv
365+ */
366+int mapif_vipack(uint32 aid, uint32 vip_time, uint8 isvip, uint32 groupid){
367+#ifdef VIP_ENABLE
368+ uint8 buf[16];
369+ WBUFW(buf,0) = 0x2b2b;
370+ WBUFL(buf,2) = aid;
371+ WBUFL(buf,6) = vip_time;
372+ WBUFB(buf,10) = isvip;
373+ WBUFL(buf,11) = groupid;
374+ mapif_sendall(buf,15); // inform all map-servers attached.
375+#endif
376+ return 0;
377+}
378+
379+/*
380+ * HZ 0x2b2b
381+ * Request to ask vip data from loginserv
382+ */
383+int logif_reqviddata(uint32 aid, uint8 type, uint32 add_vip_time){
384+#ifdef VIP_ENABLE
385+ WFIFOHEAD(login_fd,11);
386+ WFIFOW(login_fd,0) = 0x2742;
387+ WFIFOL(login_fd,2) = aid; //aid
388+ WFIFOB(login_fd,6) = type; //type
389+ WFIFOL(login_fd,7) = add_vip_time; //req_inc_duration
390+ WFIFOSET(login_fd,11);
391+#endif
392+ return 0;
393+}
394+
395+/*
396+ * AH 0x2743
397+ * We received the info from login-serv, ask to transmit it to map
398+ */
399+int logif_parse_vipack(int fd){
400+#ifdef VIP_ENABLE
401+ if (RFIFOREST(fd) < 15)
402+ return 0;
403+ else {
404+ uint32 aid = RFIFOL(fd,2); //aid
405+ uint32 vip_time = RFIFOL(fd,6); //vip_time
406+ uint8 isvip = RFIFOB(fd,10); //isvip
407+ uint32 groupid = RFIFOL(fd,11); //new group id
408+ RFIFOSKIP(fd,15);
409+ mapif_vipack(aid,vip_time,isvip,groupid);
410+ }
411+#endif
412+ return 1;
413+}
414+
415
416 /// Resets all the data.
417 void loginif_reset(void)
418@@ -2288,31 +2375,30 @@ void loginif_on_ready(void)
419
420 int logif_parse_reqpincode(int fd, struct char_session_data *sd){
421 #if PACKETVER >= 20110309
422- if( pincode_enabled ){
423- // PIN code system enabled
424- if( strlen( sd->pincode ) <= 0 ){
425- // No PIN code has been set yet
426- if( pincode_force ) pincode_sendstate( fd, sd, PINCODE_NEW );
427- else pincode_sendstate( fd, sd, PINCODE_PASSED );
428- } else {
429- if( !pincode_changetime || ( sd->pincode_change + pincode_changetime ) > time(NULL) ){
430- struct online_char_data* node = (struct online_char_data*)idb_get( online_char_db, sd->account_id );
431-
432- if( node != NULL && node->pincode_success ){ // User has already passed the check
433- pincode_sendstate( fd, sd, PINCODE_PASSED );
434- }else{
435- // Ask user for his PIN code
436- pincode_sendstate( fd, sd, PINCODE_ASK );
437- }
438- }else{ // User hasnt changed his PIN code too long
439- pincode_sendstate( fd, sd, PINCODE_EXPIRED );
440- }
441- }
442- } else { // PIN code system disabled
443- pincode_sendstate( fd, sd, PINCODE_OK );
444- }
445+ if( pincode_enabled ){
446+ // PIN code system enabled
447+ if( strlen( sd->pincode ) <= 0 ){
448+ // No PIN code has been set yet
449+ if( pincode_force ) pincode_sendstate( fd, sd, PINCODE_NEW );
450+ else pincode_sendstate( fd, sd, PINCODE_PASSED );
451+ } else {
452+ if( !pincode_changetime || ( sd->pincode_change + pincode_changetime ) > time(NULL) ){
453+ struct online_char_data* node = (struct online_char_data*)idb_get( online_char_db, sd->account_id );
454+ if( node != NULL && node->pincode_success ){ // User has already passed the check
455+ pincode_sendstate( fd, sd, PINCODE_PASSED );
456+ }else{
457+ // Ask user for his PIN code
458+ pincode_sendstate( fd, sd, PINCODE_ASK );
459+ }
460+ }else{ // User hasnt changed his PIN code too long
461+ pincode_sendstate( fd, sd, PINCODE_EXPIRED );
462+ }
463+ }
464+ } else { // PIN code system disabled
465+ pincode_sendstate( fd, sd, PINCODE_OK );
466+ }
467 #endif
468- return 0;
469+ return 0;
470 }
471
472 int parse_fromlogin(int fd) {
473@@ -2352,6 +2438,7 @@ int parse_fromlogin(int fd) {
474 switch( command )
475 {
476 case 0x2741: loginif_parse_BankingAck(fd); break;
477+ case 0x2743: logif_parse_vipack(fd); break;
478
479 // acknowledgement of connect-to-loginserver request
480 case 0x2711:
481@@ -2415,7 +2502,7 @@ int parse_fromlogin(int fd) {
482 break;
483
484 case 0x2717: // account data
485- if (RFIFOREST(fd) < 76)
486+ if (RFIFOREST(fd) < 79)
487 return 0;
488
489 // find the authenticated session with this account id
490@@ -2431,11 +2518,15 @@ int parse_fromlogin(int fd) {
491 ShowError("Account '%d' `character_slots` column is higher than supported MAX_CHARS (%d), update MAX_CHARS in mmo.h! capping to MAX_CHARS...\n",sd->account_id,sd->char_slots);
492 sd->char_slots = MAX_CHARS;/* cap to maximum */
493 } else if ( !sd->char_slots )/* no value aka 0 in sql */
494- sd->char_slots = MAX_CHARS;/* cap to maximum */
495+ sd->char_slots = MIN_CHARS;/* cap to minimum */
496 safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate));
497 safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode));
498 sd->pincode_change = (time_t)RFIFOL(fd,68);
499 sd->bank_vault = RFIFOL(fd,72);
500+ sd->isvip = RFIFOB(fd,76);
501+ sd->chars_vip = RFIFOB(fd,77);
502+ sd->chars_billing = RFIFOB(fd,78);
503+
504 ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] );
505 // continued from char_auth_ok...
506 if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359
507@@ -2452,7 +2543,7 @@ int parse_fromlogin(int fd) {
508 logif_parse_reqpincode(i, sd);
509 }
510 }
511- RFIFOSKIP(fd,76);
512+ RFIFOSKIP(fd,79);
513 break;
514
515 // login-server alive packet
516@@ -2641,7 +2732,7 @@ int parse_fromlogin(int fd) {
517 RFIFOSKIP(fd,2);
518 }
519 break;
520-
521+
522 default:
523 ShowError("Unknown packet 0x%04x received from login-server, disconnecting.\n", command);
524 set_eof(fd);
525@@ -3680,7 +3771,7 @@ int parse_frommap(int fd)
526
527 case 0x2b28: mapif_parse_UpdBankInfo(fd); break;
528 case 0x2b2a: mapif_parse_ReqBankInfo(fd); break;
529-
530+ case 0x2b2c: mapif_parse_vipactive(fd); break;
531 case 0x2b2d: //Load data
532 if (RFIFOREST(fd) < 6)
533 return 0;
534@@ -5382,13 +5473,6 @@ int char_config_read(const char* cfgName)
535 char_name_option = atoi(w2);
536 } else if (strcmpi(w1, "char_name_letters") == 0) {
537 safestrncpy(char_name_letters, w2, sizeof(char_name_letters));
538- } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
539- char_per_account = atoi(w2);
540- if( char_per_account == 0 || char_per_account > MAX_CHARS ) {
541- if( char_per_account > MAX_CHARS )
542- ShowWarning("Max chars per account '%d' exceeded limit. Defaulting to '%d'.\n", char_per_account, MAX_CHARS);
543- char_per_account = MAX_CHARS;
544- }
545 } else if (strcmpi(w1, "char_del_level") == 0) { //disable/enable char deletion by its level condition [Lupus]
546 char_del_level = atoi(w2);
547 } else if (strcmpi(w1, "char_del_delay") == 0) {
548diff --git a/src/common/mmo.h b/src/common/mmo.h
549index 8b184c5..efaa8ba 100644
550--- a/src/common/mmo.h
551+++ b/src/common/mmo.h
552@@ -48,6 +48,7 @@
553
554 #ifndef PACKETVER
555 #define PACKETVER 20130807
556+ //#define PACKETVER 20130320
557 //#define PACKETVER 20120410
558 #endif
559
560@@ -71,7 +72,8 @@
561 #define MAX_MAP_PER_SERVER 1500 // Increased to allow creation of Instance Maps
562 #define MAX_INVENTORY 100
563 //Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well.
564-#define MAX_CHARS 9
565+//max value tested was 265
566+#define MAX_CHARS 9
567 //Number of slots carded equipment can have. Never set to less than 4 as they are also used to keep the data of forged items/equipment. [Skotlex]
568 //Note: The client seems unable to receive data for more than 4 slots due to all related packets having a fixed size.
569 #define MAX_SLOTS 4
570@@ -90,7 +92,7 @@
571 #define DEFAULT_WALK_SPEED 150
572 #define MIN_WALK_SPEED 0
573 #define MAX_WALK_SPEED 1000
574-#define MAX_STORAGE 600
575+#define MAX_STORAGE 600 /// Max number of storage slots the client can support. Used as a cap for the VIP System.
576 #define MAX_GUILD_STORAGE 600
577 #define MAX_PARTY 12
578 #define MAX_GUILD 16+10*6 // increased max guild members +6 per 1 extension levels [Lupus]
579diff --git a/src/common/utils.c b/src/common/utils.c
580index 9729fcb..cec87f4 100644
581--- a/src/common/utils.c
582+++ b/src/common/utils.c
583@@ -259,7 +259,7 @@ uint32 MakeDWord(uint16 word0, uint16 word1)
584 ( (uint32)(word1 << 0x10) );
585 }
586
587-int date2version(int date){
588+uint32 date2version(int date){
589 if(date < 20040906) return 5;
590 else if(date < 20040920) return 10;
591 else if(date < 20041005) return 11;
592diff --git a/src/common/utils.h b/src/common/utils.h
593index 6ce1639..3fc5634 100644
594--- a/src/common/utils.h
595+++ b/src/common/utils.h
596@@ -29,6 +29,6 @@ extern uint16 GetWord(uint32 val, int idx);
597 extern uint16 MakeWord(uint8 byte0, uint8 byte1);
598 extern uint32 MakeDWord(uint16 word0, uint16 word1);
599
600-int date2version(int date);
601+uint32 date2version(int date);
602
603 #endif /* _UTILS_H_ */
604diff --git a/src/config/core.h b/src/config/core.h
605index 66b1ec6..93caad2 100644
606--- a/src/config/core.h
607+++ b/src/config/core.h
608@@ -72,6 +72,24 @@
609 /// Comment to disable the job HP/SP tables and use formulas instead
610 #define HP_SP_TABLES
611
612+/// Uncomment to disable vip system.
613+//#define VIP_ENABLE
614+#ifdef VIP_ENABLE
615+ #define MIN_STORAGE 300 // Default number of storage slots.
616+ #define MIN_CHARS 3 // Default number of characters per account.
617+ #define MAX_CHAR_VIP 6 //this must be below MAX_CHARS
618+ #define MAX_CHAR_BILLING 0 //this must be below MAX_CHARS
619+#else
620+ #define MIN_STORAGE 600 //if sys disable the min = max for storage slot
621+ #define MIN_CHARS 9 // Default number of characters per account.
622+ #define MAX_CHAR_BILLING 0
623+ #define MAX_CHAR_VIP 0
624+#endif
625+#if (MIN_CHARS+MAX_CHAR_VIP+MAX_CHAR_BILLING)>MAX_CHARS
626+ #error "Config of MAX_CHARS is invalid"
627+#endif
628+#define VIP_SCRIPT 0 //enable or disable scripts (require vip_enable)
629+
630 /**
631 * No settings past this point
632 **/
633diff --git a/src/login/account.h b/src/login/account.h
634index 3d7acef..9121c58 100644
635--- a/src/login/account.h
636+++ b/src/login/account.h
637@@ -34,8 +34,7 @@ AccountDB* ACCOUNTDB_CONSTRUCTOR(ACCOUNTDB_ENGINE_4)(void);
638 #endif
639
640
641-struct mmo_account
642-{
643+struct mmo_account {
644 int account_id;
645 char userid[NAME_LENGTH];
646 char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords
647@@ -54,6 +53,10 @@ struct mmo_account
648 time_t pincode_change; // (timestamp): last time of pincode change
649 int account_reg2_num;
650 int bank_vault;
651+#ifdef VIP_ENABLE
652+ int old_group;
653+ int vip_time;
654+#endif
655 struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server)
656 };
657
658diff --git a/src/login/account_sql.c b/src/login/account_sql.c
659index 3478870..bc28b05 100644
660--- a/src/login/account_sql.c
661+++ b/src/login/account_sql.c
662@@ -7,6 +7,7 @@
663 #include "../common/sql.h"
664 #include "../common/strlib.h"
665 #include "../common/timer.h"
666+#include "../config/core.h"
667 #include "account.h"
668 #include <stdlib.h>
669 #include <string.h>
670@@ -522,7 +523,11 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
671
672 // retrieve login entry for the specified account
673 if( SQL_ERROR == Sql_Query(sql_handle,
674- "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",
675+#ifdef VIP_ENABLE //FIXME query is only for mysql
676+ "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`, `vip_time`, `old_group` FROM `%s` WHERE `account_id` = %d",
677+#else
678+ "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",
679+#endif
680 db->account_db, account_id )
681 ) {
682 Sql_ShowDebug(sql_handle);
683@@ -552,10 +557,14 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc
684 Sql_GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode));
685 Sql_GetData(sql_handle, 15, &data, NULL); acc->pincode_change = atol(data);
686 Sql_GetData(sql_handle, 16, &data, NULL); acc->bank_vault = atoi(data);
687-
688+#ifdef VIP_ENABLE
689+ Sql_GetData(sql_handle, 17, &data, NULL); acc->vip_time = atol(data);
690+ Sql_GetData(sql_handle, 18, &data, NULL); acc->old_group = atoi(data);
691+ ShowInfo("mmo_auth_fromsql vip_time=%d, old_ground=%d\n",acc->vip_time,acc->old_group);
692+#endif
693 Sql_FreeResult(sql_handle);
694-
695-
696+
697+
698 // retrieve account regs for the specified user
699 if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `str`,`value` FROM `%s` WHERE `type`='1' AND `account_id`='%d'", db->accreg_db, acc->account_id) )
700 {
701@@ -597,10 +606,17 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
702 break;
703 }
704
705+#ifdef VIP_ENABLE
706+ ShowInfo("mmo_auth_tosql vip_time=%d, old_ground=%d\n",acc->vip_time,acc->old_group);
707+#endif
708 if( is_new )
709 {// insert into account table
710 if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
711+#ifdef VIP_ENABLE
712+ "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`, `vip_time`, `old_group` ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
713+#else
714 "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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
715+#endif
716 db->account_db)
717 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id))
718 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
719@@ -619,6 +635,10 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
720 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
721 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
722 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_INT, (void*)&acc->bank_vault, sizeof(acc->bank_vault))
723+#ifdef VIP_ENABLE
724+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 17, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
725+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 18, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group))
726+#endif
727 || SQL_SUCCESS != SqlStmt_Execute(stmt)
728 ) {
729 SqlStmt_ShowDebug(stmt);
730@@ -627,7 +647,13 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
731 }
732 else
733 {// update account table
734- 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)
735+ if( SQL_SUCCESS != SqlStmt_Prepare(stmt,
736+#ifdef VIP_ENABLE
737+ "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`=?, `vip_time`=?, `old_group`=? WHERE `account_id` = '%d'",
738+#else
739+ "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'",
740+#endif
741+ db->account_db, acc->account_id)
742 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid))
743 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass))
744 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex))
745@@ -644,6 +670,10 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo
746 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode))
747 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change))
748 || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_INT, (void*)&acc->bank_vault, sizeof(acc->bank_vault))
749+#ifdef VIP_ENABLE
750+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 16, SQLDT_LONG, (void*)&acc->vip_time, sizeof(acc->vip_time))
751+ || SQL_SUCCESS != SqlStmt_BindParam(stmt, 17, SQLDT_INT, (void*)&acc->old_group, sizeof(acc->old_group))
752+#endif
753 || SQL_SUCCESS != SqlStmt_Execute(stmt)
754 ) {
755 SqlStmt_ShowDebug(stmt);
756diff --git a/src/login/login.c b/src/login/login.c
757index daa8dca..06232f1 100644
758--- a/src/login/login.c
759+++ b/src/login/login.c
760@@ -13,6 +13,9 @@
761 #include "../common/msg_conf.h"
762 #include "../common/cli.h"
763 #include "../common/ers.h"
764+#include "../common/utils.h"
765+#include "../common/mmo.h"
766+#include "../config/core.h"
767 #include "account.h"
768 #include "ipban.h"
769 #include "login.h"
770@@ -294,11 +297,16 @@ bool check_password(const char* md5key, int passwdenc, const char* passwd, const
771 }
772
773
774-//-----------------------------------------------------
775-// custom timestamp formatting (from eApp)
776-//-----------------------------------------------------
777-const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format)
778-{
779+/**
780+ * Converting a timestamp is a srintf according to format
781+ * safefr then strftime as it ensure \0 at end of string
782+ * @param str, pointer to the destination string
783+ * @param size, max length of the string
784+ * @param timestamp, see unix epoch
785+ * @param format, format to convert timestamp on, see strftime format
786+ * @return the string of timestamp
787+ */
788+const char* timestamp2string(char* str, size_t size, time_t timestamp, const char* format){
789 size_t len = strftime(str, size, format, localtime(×tamp));
790 memset(str + len, '\0', size - len);
791 return str;
792@@ -428,6 +436,139 @@ int parse_console(const char* buf){
793 return 0;
794 }
795
796+int chrif_send_accdata(int fd, uint32 aid){
797+ struct mmo_account acc;
798+ time_t expiration_time = 0;
799+ char email[40] = "";
800+ int group_id = 0;
801+ char birthdate[10+1] = "";
802+ char pincode[PINCODE_LENGTH+1];
803+ int bank_vault = 0;
804+ char isvip=false;
805+ uint8 char_slots = MIN_CHARS, char_vip=0;
806+
807+ memset(pincode,0,PINCODE_LENGTH+1);
808+ if( !accounts->load_num(accounts, &acc, aid) )
809+ return -1;
810+ else{
811+ safestrncpy(email, acc.email, sizeof(email));
812+ expiration_time = acc.expiration_time;
813+ group_id = acc.group_id;
814+
815+ safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
816+ safestrncpy(pincode, acc.pincode, sizeof(pincode));
817+ bank_vault = acc.bank_vault;
818+#ifdef VIP_ENABLE
819+ ShowInfo("chrif_send_accdata now=%d, vip_time=%d, acc.vip_time > time = %d\n",
820+ time(NULL),acc.vip_time,acc.vip_time > time(NULL));
821+ if(acc.vip_time > time(NULL)){
822+ isvip=true;
823+ char_slots = login_config.char_per_account+login_config.vip_sys.char_increase;
824+ char_vip = login_config.vip_sys.char_increase;
825+ }
826+ else
827+ char_slots = login_config.char_per_account;
828+#endif
829+ }
830+
831+ WFIFOHEAD(fd,79);
832+ WFIFOW(fd,0) = 0x2717;
833+ WFIFOL(fd,2) = aid;
834+ safestrncpy((char*)WFIFOP(fd,6), email, 40);
835+ WFIFOL(fd,46) = (uint32)expiration_time;
836+ WFIFOB(fd,50) = (unsigned char)group_id;
837+ WFIFOB(fd,51) = char_slots;
838+ safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
839+ safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
840+ WFIFOL(fd,68) = (uint32)acc.pincode_change;
841+ WFIFOL(fd,72) = bank_vault;
842+ WFIFOB(fd,76) = isvip;
843+ WFIFOB(fd,77) = char_vip;
844+ WFIFOB(fd,78) = MAX_CHAR_BILLING; //TODO create a config for this
845+ WFIFOSET(fd,79);
846+ return 0;
847+}
848+
849+int chrif_parse_reqaccdata(int fd, int cid, char *ip){
850+ if( RFIFOREST(fd) < 6 )
851+ return 0;
852+ else{
853+ uint32 aid = RFIFOL(fd,2);
854+ RFIFOSKIP(fd,6);
855+ if(chrif_send_accdata(fd,aid) < 0)
856+ ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", server[cid].name, aid, ip);
857+ }
858+ return 0;
859+}
860+
861+
862+int chrif_sendvipdata(int fd, struct mmo_account acc, char isvip){
863+#ifdef VIP_ENABLE
864+ uint8 buf[16];
865+
866+ WBUFW(buf,0) = 0x2743;
867+ WBUFL(buf,2) = acc.account_id;
868+ WBUFL(buf,6) = acc.vip_time;
869+ WBUFB(buf,10) = isvip;
870+ WBUFL(buf,11) = acc.group_id; //new group id
871+ charif_sendallwos(-1,buf,15); //inform all char-servs of result
872+ ShowInfo("chrif_sendvipdata aid=%d, vip_time=%d, isvip=%d\n",
873+ acc.account_id,acc.vip_time,isvip);
874+
875+ chrif_send_accdata(fd,acc.account_id); //refresh char with new setting
876+#endif
877+ return 1;
878+}
879+
880+/**
881+ * Received a vip data reqest from char \n
882+ * type is the query to perform \n
883+ * &1 : Select info and update old_groupid \n
884+ * &2 : Update vip time
885+ * @param fd link to charserv
886+ * @return 0 missing data, 1 succeed
887+ */
888+int chrif_parse_reqvipdata(int fd){
889+#ifdef VIP_ENABLE
890+ if( RFIFOREST(fd) < 11 )
891+ return 0;
892+ else { //request vip info
893+ struct mmo_account acc;
894+ int aid = RFIFOL(fd,2);
895+ int8 type = RFIFOB(fd,6);
896+ uint32 req_duration = RFIFOL(fd,7);
897+ RFIFOSKIP(fd,11);
898+
899+ ShowInfo("chrif_parse_reqvipdata aid=%d, type=%d, req_duration=%d\n",
900+ aid,type,req_duration);
901+ if( accounts->load_num(accounts, &acc, aid ) ){
902+ time_t now = time(NULL);
903+ time_t vip_time = acc.vip_time;
904+ bool isvip = false;
905+
906+ if( type&2 ) vip_time = now+req_duration; // set new duration
907+ if(now < vip_time){ //isvip
908+ if(acc.group_id != login_config.vip_sys.group) //only upd this if we're not vip already
909+ acc.old_group = acc.group_id;
910+ acc.group_id = login_config.vip_sys.group;
911+ acc.char_slots = login_config.char_per_account+login_config.vip_sys.char_increase;
912+ isvip = true;
913+ } else { //expired
914+ vip_time = 0;
915+ acc.group_id = acc.old_group;
916+ acc.old_group = 0;
917+ acc.char_slots = login_config.char_per_account;
918+ }
919+ acc.vip_time = (int)vip_time;
920+ accounts->save(accounts,&acc);
921+
922+ chrif_sendvipdata(fd,acc,isvip);
923+ }
924+ }
925+#endif
926+ return 1;
927+}
928+
929
930 //--------------------------------
931 // Packet parsing for char-servers
932@@ -555,54 +696,10 @@ int parse_fromchar(int fd){
933 }
934 break;
935
936- case 0x2716: // request account data
937- if( RFIFOREST(fd) < 6 )
938- return 0;
939- else{
940- struct mmo_account acc;
941- time_t expiration_time = 0;
942- char email[40] = "";
943- uint8 char_slots = 0;
944- int group_id = 0;
945- char birthdate[10+1] = "";
946- char pincode[PINCODE_LENGTH+1];
947- int account_id = RFIFOL(fd,2);
948- int bank_vault = 0;
949-
950- memset(pincode,0,PINCODE_LENGTH+1);
951-
952- RFIFOSKIP(fd,6);
953-
954- if( !accounts->load_num(accounts, &acc, account_id) )
955- ShowNotice("Char-server '%s': account %d NOT found (ip: %s).\n", server[id].name, account_id, ip);
956- else{
957- safestrncpy(email, acc.email, sizeof(email));
958- expiration_time = acc.expiration_time;
959- group_id = acc.group_id;
960- char_slots = acc.char_slots;
961- safestrncpy(birthdate, acc.birthdate, sizeof(birthdate));
962- safestrncpy(pincode, acc.pincode, sizeof(pincode));
963- bank_vault = acc.bank_vault;
964- }
965-
966- WFIFOHEAD(fd,76);
967- WFIFOW(fd,0) = 0x2717;
968- WFIFOL(fd,2) = account_id;
969- safestrncpy((char*)WFIFOP(fd,6), email, 40);
970- WFIFOL(fd,46) = (uint32)expiration_time;
971- WFIFOB(fd,50) = (unsigned char)group_id;
972- WFIFOB(fd,51) = char_slots;
973- safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1);
974- safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 );
975- WFIFOL(fd,68) = (uint32)acc.pincode_change;
976- WFIFOL(fd,72) = bank_vault;
977- WFIFOSET(fd,76);
978- }
979- break;
980+ case 0x2716: chrif_parse_reqaccdata(fd,id,ip); break; // request account data
981
982 case 0x2719: // ping request from charserver
983 RFIFOSKIP(fd,2);
984-
985 WFIFOHEAD(fd,2);
986 WFIFOW(fd,0) = 0x2718;
987 WFIFOSET(fd,2);
988@@ -973,6 +1070,8 @@ int parse_fromchar(int fd){
989 }
990 break;
991
992+ case 0x2742: chrif_parse_reqvipdata(fd); break; //Vip sys
993+
994 default:
995 ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command);
996 set_eof(fd);
997@@ -1026,10 +1125,12 @@ int mmo_auth_new(const char* userid, const char* pass, const char sex, const cha
998 safestrncpy(acc.birthdate, "0000-00-00", sizeof(acc.birthdate));
999 safestrncpy(acc.pincode, "", sizeof(acc.pincode));
1000 acc.pincode_change = 0;
1001-
1002- acc.char_slots = 0;
1003+ acc.char_slots = MIN_CHARS;
1004 acc.bank_vault = 0;
1005-
1006+#ifdef VIP_ENABLE
1007+ acc.vip_time = 0;
1008+ acc.old_group = 0;
1009+#endif
1010 if( !accounts->create(accounts, &acc) )
1011 return 0;
1012
1013@@ -1638,8 +1739,7 @@ int parse_login(int fd)
1014 }
1015
1016
1017-void login_set_defaults()
1018-{
1019+void login_set_defaults(){
1020 login_config.login_ip = INADDR_ANY;
1021 login_config.login_port = 6900;
1022 login_config.ipban_cleanup_interval = 60;
1023@@ -1666,6 +1766,11 @@ void login_set_defaults()
1024
1025 login_config.client_hash_check = 0;
1026 login_config.client_hash_nodes = NULL;
1027+ login_config.char_per_account = MAX_CHARS-MAX_CHAR_VIP-MAX_CHAR_BILLING;
1028+#ifdef VIP_ENABLE
1029+ login_config.vip_sys.char_increase = MAX_CHAR_VIP;
1030+ login_config.vip_sys.group = 5;
1031+#endif
1032 }
1033
1034 //-----------------------------------
1035@@ -1767,14 +1872,31 @@ int login_config_read(const char* cfgName)
1036
1037 login_config.client_hash_nodes = nnode;
1038 }
1039+ } else if (strcmpi(w1, "chars_per_account") == 0) { //maxchars per account [Sirius]
1040+ login_config.char_per_account = atoi(w2);
1041+ if( login_config.char_per_account <= 0 || login_config.char_per_account > MAX_CHARS ) {
1042+ if( login_config.char_per_account > MAX_CHARS ){
1043+ ShowWarning("Max chars per account '%d' exceeded limit. Defaulting to '%d'.\n", login_config.char_per_account, MAX_CHARS);
1044+ login_config.char_per_account = MAX_CHARS;
1045+ }
1046+ login_config.char_per_account = MIN_CHARS;
1047+ }
1048 }
1049+#ifdef VIP_ENABLE
1050+ else if(strcmpi(w1,"vip_group")==0)
1051+ login_config.vip_sys.group = cap_value(atoi(w2),0,99);
1052+ else if(strcmpi(w1,"vip_char_increase")==0){
1053+ if(login_config.vip_sys.char_increase > (unsigned int) MAX_CHARS-login_config.char_per_account)
1054+ ShowWarning("vip_char_increase too high, can only go up to %d, according to your char_per_account config %d\n",
1055+ MAX_CHARS-login_config.char_per_account,login_config.char_per_account);
1056+ login_config.vip_sys.char_increase = cap_value(atoi(w2),0,MAX_CHARS-login_config.char_per_account);
1057+ }
1058+#endif
1059 else if(!strcmpi(w1, "import"))
1060 login_config_read(w2);
1061- else
1062- if(!strcmpi(w1, "account.engine"))
1063+ else if(!strcmpi(w1, "account.engine"))
1064 safestrncpy(login_config.account_engine, w2, sizeof(login_config.account_engine));
1065- else
1066- {// try the account engines
1067+ else {// try the account engines
1068 int i;
1069 for( i = 0; account_engines[i].constructor; ++i )
1070 {
1071diff --git a/src/login/login.h b/src/login/login.h
1072index 427ece6..d10282f 100644
1073--- a/src/login/login.h
1074+++ b/src/login/login.h
1075@@ -6,6 +6,7 @@
1076
1077 #include "../common/mmo.h" // NAME_LENGTH,SEX_*
1078 #include "../common/core.h" // CORE_ST_LAST
1079+#include "../config/core.h"
1080
1081 enum E_LOGINSERVER_ST
1082 {
1083@@ -58,7 +59,6 @@ struct client_hash_node {
1084 };
1085
1086 struct Login_Config {
1087-
1088 uint32 login_ip; // the address to bind to
1089 uint16 login_port; // the port to bind to
1090 unsigned int ipban_cleanup_interval; // interval (in seconds) to clean up expired IP bans
1091@@ -86,6 +86,13 @@ struct Login_Config {
1092
1093 int client_hash_check; // flags for checking client md5
1094 struct client_hash_node *client_hash_nodes; // linked list containg md5 hash for each gm group
1095+ int char_per_account; //number of char an account can have
1096+#ifdef VIP_ENABLE
1097+ struct {
1098+ unsigned int group;
1099+ unsigned int char_increase;
1100+ } vip_sys;
1101+#endif
1102 };
1103
1104 #define sex_num2str(num) ( (num == SEX_FEMALE ) ? 'F' : (num == SEX_MALE ) ? 'M' : 'S' )
1105@@ -100,7 +107,7 @@ const char* login_msg_txt(int msg_number);
1106 void login_do_final_msg(void);
1107
1108
1109-#define MAX_SERVERS 30
1110+#define MAX_SERVERS 30 //number of charserv loginserv can handle
1111 extern struct mmo_char_server server[MAX_SERVERS];
1112 extern struct Login_Config login_config;
1113
1114diff --git a/src/map/atcommand.c b/src/map/atcommand.c
1115index 4a78633..131aa67 100644
1116--- a/src/map/atcommand.c
1117+++ b/src/map/atcommand.c
1118@@ -7546,24 +7546,33 @@ static int atcommand_mutearea_sub(struct block_list *bl,va_list ap)
1119 }
1120
1121
1122-ACMD_FUNC(rates)
1123-{
1124+ACMD_FUNC(rates) {
1125 char buf[CHAT_SIZE_MAX];
1126+ int base_exp_rate = 0, job_exp_rate = 0, item_rate = 0;
1127
1128 nullpo_ret(sd);
1129 memset(buf, '\0', sizeof(buf));
1130
1131+#ifdef VIP_ENABLE
1132+ // Display EXP and item rate increase for VIP.
1133+ if( pc_isvip(sd) && ( battle_config.vip_base_exp_increase || battle_config.vip_job_exp_increase || battle_config.vip_drop_increase )) {
1134+ base_exp_rate += battle_config.vip_base_exp_increase;
1135+ job_exp_rate += battle_config.vip_job_exp_increase;
1136+ item_rate += battle_config.vip_drop_increase;
1137+ }
1138+#endif
1139+
1140 snprintf(buf, CHAT_SIZE_MAX, msg_txt(sd,1298), // Experience rates: Base %.2fx / Job %.2fx
1141- battle_config.base_exp_rate/100., battle_config.job_exp_rate/100.);
1142+ (battle_config.base_exp_rate+base_exp_rate)/100., (battle_config.job_exp_rate+job_exp_rate)/100.);
1143 clif_displaymessage(fd, buf);
1144 snprintf(buf, CHAT_SIZE_MAX, msg_txt(sd,1299), // Normal Drop Rates: Common %.2fx / Healing %.2fx / Usable %.2fx / Equipment %.2fx / Card %.2fx
1145- battle_config.item_rate_common/100., battle_config.item_rate_heal/100., battle_config.item_rate_use/100., battle_config.item_rate_equip/100., battle_config.item_rate_card/100.);
1146+ (battle_config.item_rate_common+item_rate)/100., (battle_config.item_rate_heal+item_rate)/100., (battle_config.item_rate_use+item_rate)/100., (battle_config.item_rate_equip+item_rate)/100., (battle_config.item_rate_card+item_rate)/100.);
1147 clif_displaymessage(fd, buf);
1148 snprintf(buf, CHAT_SIZE_MAX, msg_txt(sd,1300), // Boss Drop Rates: Common %.2fx / Healing %.2fx / Usable %.2fx / Equipment %.2fx / Card %.2fx
1149- battle_config.item_rate_common_boss/100., battle_config.item_rate_heal_boss/100., battle_config.item_rate_use_boss/100., battle_config.item_rate_equip_boss/100., battle_config.item_rate_card_boss/100.);
1150+ (battle_config.item_rate_common_boss+item_rate)/100., (battle_config.item_rate_heal_boss+item_rate)/100., (battle_config.item_rate_use_boss+item_rate)/100., (battle_config.item_rate_equip_boss+item_rate)/100., (battle_config.item_rate_card_boss+item_rate)/100.);
1151 clif_displaymessage(fd, buf);
1152 snprintf(buf, CHAT_SIZE_MAX, msg_txt(sd,1301), // Other Drop Rates: MvP %.2fx / Card-Based %.2fx / Treasure %.2fx
1153- battle_config.item_rate_mvp/100., battle_config.item_rate_adddrop/100., battle_config.item_rate_treasure/100.);
1154+ (battle_config.item_rate_mvp+item_rate)/100., (battle_config.item_rate_adddrop+item_rate)/100., (battle_config.item_rate_treasure+item_rate)/100.);
1155 clif_displaymessage(fd, buf);
1156
1157 return 0;
1158@@ -8295,7 +8304,7 @@ static int atcommand_mutearea_sub(struct block_list *bl,va_list ap)
1159 {
1160 location = "storage";
1161 items = sd->status.storage.items;
1162- size = MAX_STORAGE;
1163+ size = sd->storage_size;
1164 }
1165 else
1166 if( strcmp(command+1, "cartlist") == 0 )
1167@@ -9181,6 +9190,68 @@ static inline void atcmd_channel_help(struct map_session_data *sd, const char *c
1168 return -1;
1169 }
1170
1171+#ifdef VIP_ENABLE
1172+ACMD_FUNC(vip) {
1173+ struct map_session_data *pl_sd = NULL;
1174+ char * modif_p;
1175+ int viptime = 0;
1176+ nullpo_retr(-1, sd);
1177+
1178+ if (!message || !*message || sscanf(message, "%255s %23[^\n]",atcmd_output,atcmd_player_name) < 2) {
1179+ clif_displaymessage(fd, msg_txt(sd,700)); //Usage: @vip <time> <character name>
1180+ return -1;
1181+ }
1182+
1183+ atcmd_output[sizeof(atcmd_output)-1] = '\0';
1184+
1185+ modif_p = atcmd_output;
1186+ viptime = (int)solve_time(modif_p)/60; // Change to minutes
1187+ if(viptime==0) {
1188+ clif_displaymessage(fd, msg_txt(sd,701)); // Invalid time for vip command.
1189+ clif_displaymessage(fd, msg_txt(sd,702)); // Time parameter format is +/-<value> to alter. y/a = Year, m = Month, d/j = Day, h = Hour, n/mn = Minute, s = Second.
1190+ return -1;
1191+ }
1192+
1193+ if ((pl_sd = map_nick2sd(atcmd_player_name)) == NULL) {
1194+ clif_displaymessage(fd, msg_txt(sd,3)); // Character not found.
1195+ return -1;
1196+ }
1197+
1198+ if (pc_get_group_level(pl_sd) > pc_get_group_level(sd)) {
1199+ clif_displaymessage(fd, msg_txt(sd,81)); // Your GM level don't authorise you to do this action on this player.
1200+ return -1;
1201+ }
1202+
1203+ if(pc_isvip(pl_sd) && pl_sd->vip.time){ //Update the player's VIP time
1204+ pl_sd->vip.time += viptime;
1205+ if (pl_sd->vip.time <= 0) {
1206+ pl_sd->vip.time = 0;
1207+ pl_sd->vip.enabled = 0;
1208+ clif_displaymessage(pl_sd->fd, msg_txt(sd,703)); // GM has removed your VIP time.
1209+ clif_displaymessage(fd, msg_txt(sd,704)); // Player is no longer VIP.
1210+ chrif_req_vipActive(pl_sd, viptime, 3);
1211+ } else {
1212+ int year,month,day,hour,minute,second;
1213+ char timestr[128];
1214+ split_time(pl_sd->vip.time*60,&year,&month,&day,&hour,&minute,&second);
1215+ sprintf(atcmd_output,msg_txt(sd,704),msg_txt(sd,705),year,month,day,hour,minute); //%s is VIP for %d years, %d months, %d days, %d hours and %d minutes.
1216+ clif_displaymessage(pl_sd->fd, atcmd_output);
1217+ sprintf(atcmd_output,msg_txt(sd,705),msg_txt(sd,706),year,month,day,hour,minute); //This player is now VIP for %d years, %d months, %d days, %d hours and %d minutes.
1218+ clif_displaymessage(fd, atcmd_output);
1219+ time2str(timestr,"%Y-%m-%d %H:%M",pl_sd->vip.time*60);
1220+ sprintf(atcmd_output,"%s : %s",msg_txt(sd,707),timestr); //You are VIP until :
1221+ clif_displaymessage(pl_sd->fd, atcmd_output);
1222+ clif_displaymessage(fd, atcmd_output);
1223+ }
1224+ } else {
1225+ clif_displaymessage(fd, msg_txt(sd,704));
1226+ return -1;
1227+ }
1228+
1229+ return 0;
1230+}
1231+#endif
1232+
1233 #include "../custom/atcommand.inc"
1234
1235 /**
1236@@ -9461,6 +9532,9 @@ void atcommand_basecommands(void) {
1237 ACMD_DEF(channel),
1238 ACMD_DEF(fontcolor),
1239 ACMD_DEF(langtype),
1240+#ifdef VIP_ENABLE
1241+ ACMD_DEF(vip),
1242+#endif
1243 };
1244 AtCommandInfo* atcommand;
1245 int i;
1246diff --git a/src/map/battle.c b/src/map/battle.c
1247index 11ed206..3003551 100644
1248--- a/src/map/battle.c
1249+++ b/src/map/battle.c
1250@@ -7233,6 +7233,18 @@ bool battle_check_range(struct block_list *src, struct block_list *bl, int range
1251 { "bowling_bash_area", &battle_config.bowling_bash_area, 0, 0, 20, },
1252 { "drop_rateincrease", &battle_config.drop_rateincrease, 0, 0, 1, },
1253 { "feature.banking", &battle_config.feature_banking, 1, 0, 1, },
1254+#ifdef VIP_ENABLE
1255+ { "vip_storage_increase", &battle_config.vip_storage_increase, 0, 0, MAX_STORAGE-MIN_STORAGE, },
1256+ { "vip_base_exp_increase", &battle_config.vip_base_exp_increase, 0, 0, INT_MAX, },
1257+ { "vip_job_exp_increase", &battle_config.vip_job_exp_increase, 0, 0, INT_MAX, },
1258+ { "vip_exp_penalty_base_normal", &battle_config.vip_exp_penalty_base_normal, 0, 0, INT_MAX, },
1259+ { "vip_exp_penalty_job_normal", &battle_config.vip_exp_penalty_job_normal, 0, 0, INT_MAX, },
1260+ { "vip_exp_penalty_base", &battle_config.vip_exp_penalty_base, 0, 0, INT_MAX, },
1261+ { "vip_exp_penalty_job", &battle_config.vip_exp_penalty_job, 0, 0, INT_MAX, },
1262+ { "vip_bm_increase", &battle_config.vip_bm_increase, 0, 0, INT_MAX, },
1263+ { "vip_drop_increase", &battle_config.vip_drop_increase, 0, 0, INT_MAX, },
1264+ { "vip_gemstone", &battle_config.vip_gemstone, 0, 0, 1, },
1265+#endif
1266 { "mon_trans_disable_in_gvg", &battle_config.mon_trans_disable_in_gvg, 0, 0, 1, },
1267 { "homunculus_S_growth_level", &battle_config.hom_S_growth_level, 99, 0, MAX_LEVEL, },
1268 { "emblem_woe_change", &battle_config.emblem_woe_change, 0, 0, 1, },
1269diff --git a/src/map/battle.h b/src/map/battle.h
1270index a52e373..f0667f3 100644
1271--- a/src/map/battle.h
1272+++ b/src/map/battle.h
1273@@ -496,6 +496,18 @@ extern struct Battle_Config
1274 int bowling_bash_area;
1275 int drop_rateincrease;
1276 int feature_banking;
1277+#ifdef VIP_ENABLE
1278+ int vip_storage_increase;
1279+ int vip_base_exp_increase;
1280+ int vip_job_exp_increase;
1281+ int vip_bm_increase;
1282+ int vip_drop_increase;
1283+ int vip_gemstone;
1284+ int vip_exp_penalty_base_normal;
1285+ int vip_exp_penalty_base;
1286+ int vip_exp_penalty_job_normal;
1287+ int vip_exp_penalty_job;
1288+#endif
1289 int mon_trans_disable_in_gvg;
1290 int emblem_woe_change;
1291 int emblem_transparency_limit;
1292diff --git a/src/map/chrif.c b/src/map/chrif.c
1293index 933b5ce..57e4829 100644
1294--- a/src/map/chrif.c
1295+++ b/src/map/chrif.c
1296@@ -46,7 +46,7 @@
1297 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
1298 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
1299 -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
1300- 10,10, 6, 0, 0, 6, -1, -1, // 2b28-2b2f: U->2b28, U->2b29, U->2b2a, F->2b2b, F->2b2c, U->2b2d, U->2b2e, U->2b2f
1301+ 10,10, 6,15,11, 6,-1,-1, // 2b28-2b2f: U->2b28, U->2b29, U->2b2a, U->2b2b, U->2b2c, U->2b2d, U->2b2e, U->2b2f
1302 };
1303
1304 //Used Packets:
1305@@ -101,8 +101,8 @@
1306 //2b28: Outgoing, chrif_save_bankdata -> 'send bank data to be saved'
1307 //2b29: Incoming, chrif_load_bankdata -> 'received bank data for playeer to be loaded'
1308 //2b2a: Outgoing, chrif_bankdata_request -> 'request bank data for charid'
1309-//2b2b: FREE
1310-//2b2c: FREE
1311+//2b2b: Incoming, chrif_parse_ack_vipActive -> vip info result
1312+//2b2c: Outgoing, chrif_req_vipActive -> request vip info
1313 //2b2d: Outgoing, chrif_bsdata_request -> request bonus_script for pc_authok'ed char.
1314 //2b2e: Outgoing, chrif_save_bsdata -> Send bonus_script of player for saving.
1315 //2b2f: Incoming, chrif_load_bsdata -> received bonus_script of player for loading.
1316@@ -1513,6 +1513,49 @@ void chrif_keepalive(int fd) {
1317 void chrif_keepalive_ack(int fd) {
1318 session[fd]->flag.ping = 0;/* reset ping state, we received a packet */
1319 }
1320+
1321+void chrif_parse_ack_vipActive(int fd) {
1322+#ifdef VIP_ENABLE
1323+ int aid = RFIFOL(char_fd,2);
1324+ uint32 vip_time = RFIFOL(char_fd,6);
1325+ bool isvip = RFIFOB(char_fd,10);
1326+ uint32 groupid = RFIFOL(char_fd,11);
1327+ TBL_PC *sd = map_id2sd(aid);
1328+
1329+ if (sd && isvip) {
1330+ sd->vip.enabled = 1;
1331+ sd->vip.time = vip_time;
1332+ sd->group_id = groupid;
1333+ pc_group_pc_load(sd);
1334+
1335+ // Increase storage size for VIP.
1336+ sd->storage_size = battle_config.vip_storage_increase + MIN_STORAGE;
1337+ if( sd->storage_size > MAX_STORAGE ) {
1338+ ShowError("intif_parse_ack_vipActive: Storage size for player %s (%d:%d) is larger than MAX_STORAGE. Storage size has been set to MAX_STORAGE.\n", sd->status.name, sd->status.account_id, sd->status.char_id);
1339+ sd->storage_size = MAX_STORAGE;
1340+ }
1341+ // Magic Stone requirement avoidance for VIP.
1342+ if( battle_config.vip_gemstone && pc_isvip(sd) )
1343+ sd->special_state.no_gemstone = 2; //need to be done after status_calc_bl(bl,first);
1344+ }
1345+#endif
1346+}
1347+
1348+int chrif_req_vipActive(TBL_PC *sd, int8 req_duration, int8 type) {
1349+#ifdef VIP_ENABLE
1350+ if (CheckForCharServer() || sd == NULL)
1351+ return 0;
1352+
1353+ WFIFOHEAD(char_fd,11);
1354+ WFIFOW(char_fd,0) = 0x2b2c;
1355+ WFIFOL(char_fd,2) = sd->bl.id; // AID
1356+ WFIFOB(char_fd,6) = type; // type&1 - SQL SELECT, type&2 - SQL UPDATE
1357+ WFIFOL(char_fd,7) = req_duration;
1358+ WFIFOSET(char_fd,11);
1359+#endif
1360+ return 0;
1361+}
1362+
1363 /*==========================================
1364 *
1365 *------------------------------------------*/
1366@@ -1590,6 +1633,7 @@ int chrif_parse(int fd) {
1367 case 0x2b25: chrif_deadopt(RFIFOL(fd,2), RFIFOL(fd,6), RFIFOL(fd,10)); break;
1368 case 0x2b27: chrif_authfail(fd); break;
1369 case 0x2b29: chrif_load_bankdata(fd); break;
1370+ case 0x2b2b: chrif_parse_ack_vipActive(fd); break;
1371 case 0x2b2f: chrif_load_bsdata(fd); break;
1372 default:
1373 ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
1374diff --git a/src/map/chrif.h b/src/map/chrif.h
1375index 70a4a95..c3ec498 100644
1376--- a/src/map/chrif.h
1377+++ b/src/map/chrif.h
1378@@ -67,6 +67,9 @@ int chrif_divorce(int partner_id1, int partner_id2);
1379 int chrif_removefriend(int char_id, int friend_id);
1380 int chrif_send_report(char* buf, int len);
1381
1382+void chrif_parse_ack_vipActive(int fd);
1383+int chrif_req_vipActive(struct map_session_data *sd, int8 req_duration, int8 type);
1384+
1385 int chrif_bsdata_request(int char_id);
1386 int chrif_save_bsdata(struct map_session_data *sd);
1387 int chrif_load_bsdata(int fd);
1388diff --git a/src/map/clif.c b/src/map/clif.c
1389index 00f8658..05e7c97 100644
1390--- a/src/map/clif.c
1391+++ b/src/map/clif.c
1392@@ -9544,7 +9544,13 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
1393 clif_updatestatus(sd,SP_JOBEXP);
1394 clif_updatestatus(sd,SP_NEXTJOBEXP);
1395 clif_updatestatus(sd,SP_SKILLPOINT);
1396- clif_initialstatus(sd);
1397+ clif_initialstatus(sd);
1398+#ifdef VIP_ENABLE
1399+ if(pc_isvip(sd)) { // Display client information to VIP.
1400+ clif_vip_display_info(sd); //TODO wtf that should do
1401+ clif_vip_display_info2(sd);
1402+ }
1403+#endif
1404
1405 if (sd->sc.option&OPTION_FALCON)
1406 clif_status_load(&sd->bl, SI_FALCON, 1);
1407@@ -16933,6 +16939,49 @@ void clif_update_rankingpoint(struct map_session_data *sd, int rankingtype, int
1408 #endif
1409 }
1410
1411+/// 0x8cb <packet len>.W <exp>.W <death>.W <drop>.W (ZC_PERSONAL_INFOMATION)
1412+/// 0x97b <packet len>.W <exp>.L <death>.L <drop>.L (ZC_PERSONAL_INFOMATION2)
1413+void clif_vip_display_info(struct map_session_data *sd) {
1414+#ifdef VIP_ENABLE
1415+ nullpo_retv(sd);
1416+ sd->fd = (int)sd->fd;
1417+
1418+ WFIFOHEAD(sd->fd,17);
1419+
1420+ if(sd->packet_ver < date2version(20130320))
1421+ WFIFOW(sd->fd,0) = 0x8cb;
1422+ else
1423+ WFIFOW(sd->fd,0) = 0x97b;
1424+ WFIFOW(sd->fd,2) = 17;
1425+ WFIFOW(sd->fd,4) = battle_config.base_exp_rate;
1426+ WFIFOW(sd->fd,6) = battle_config.death_penalty_base;
1427+ WFIFOW(sd->fd,8) = battle_config.item_rate_common;
1428+ WFIFOB(sd->fd,10) = 0;
1429+ WFIFOW(sd->fd,11) = battle_config.vip_base_exp_increase;
1430+ WFIFOW(sd->fd,13) = battle_config.death_penalty_base*battle_config.vip_exp_penalty_base;
1431+ WFIFOW(sd->fd,15) = battle_config.vip_drop_increase;
1432+ WFIFOSET(sd->fd,17);
1433+#endif
1434+}
1435+
1436+/// 0981 <packet len>.W <exp>.W <death>.W <drop>.W <activity rate>.W (ZC_PERSONAL_INFOMATION_CHN)
1437+void clif_vip_display_info2(struct map_session_data* sd) {
1438+#ifdef VIP_ENABLE
1439+ nullpo_retv(sd);
1440+ sd->fd = (int)sd->fd;
1441+
1442+ WFIFOHEAD(sd->fd,12);
1443+ WFIFOW(sd->fd,0) = 0x981;
1444+ WFIFOW(sd->fd,2) = 12;
1445+ WFIFOW(sd->fd,4) = battle_config.vip_base_exp_increase;
1446+ WFIFOW(sd->fd,6) = battle_config.death_penalty_base*battle_config.vip_exp_penalty_base;
1447+ WFIFOW(sd->fd,8) = battle_config.vip_drop_increase;
1448+ WFIFOW(sd->fd,10) = 0; // ?
1449+ WFIFOSET(sd->fd,12);
1450+#endif
1451+}
1452+
1453+
1454 #ifdef DUMP_UNKNOWN_PACKET
1455 void DumpUnknow(int fd,TBL_PC *sd,int cmd,int packet_len){
1456 const char* packet_txt = "save/packet.txt";
1457diff --git a/src/map/clif.h b/src/map/clif.h
1458index 3deedb9..daf13f8 100644
1459--- a/src/map/clif.h
1460+++ b/src/map/clif.h
1461@@ -773,6 +773,9 @@ void clif_search_store_info_click_ack(struct map_session_data* sd, short x, shor
1462 void clif_cashshop_result( struct map_session_data* sd, uint16 item_id, uint16 result );
1463 void clif_cashshop_open( struct map_session_data* sd );
1464
1465+void clif_vip_display_info(struct map_session_data *sd);
1466+void clif_vip_display_info2(struct map_session_data *sd);
1467+
1468 /**
1469 * 3CeAM
1470 **/
1471diff --git a/src/map/mob.c b/src/map/mob.c
1472index 69ed2f8..477eb4d 100644
1473--- a/src/map/mob.c
1474+++ b/src/map/mob.c
1475@@ -2249,13 +2249,25 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
1476
1477 if (map[m].flag.nobaseexp || !md->db->base_exp)
1478 base_exp = 0;
1479- else
1480- base_exp = (unsigned int)cap_value(md->db->base_exp * per * bonus/100. * map[m].adjust.bexp/100., 1, UINT_MAX);
1481+ else {
1482+ int vip_bonus=0;
1483+#ifdef VIP_ENABLE // Increase base EXP rate for VIP.
1484+ if( battle_config.vip_base_exp_increase && ( sd && pc_isvip(sd) ) )
1485+ vip_bonus += battle_config.vip_base_exp_increase;
1486+#endif
1487+ base_exp = (unsigned int)cap_value(md->db->base_exp * per * (bonus+vip_bonus)/100. * map[m].adjust.bexp/100., 1, UINT_MAX);
1488+ }
1489
1490 if (map[m].flag.nojobexp || !md->db->job_exp || md->dmglog[i].flag == MDLF_HOMUN) //Homun earned job-exp is always lost.
1491 job_exp = 0;
1492- else
1493- job_exp = (unsigned int)cap_value(md->db->job_exp * per * bonus/100. * map[m].adjust.jexp/100., 1, UINT_MAX);
1494+ else {
1495+ int vip_bonus=0;
1496+#ifdef VIP_ENABLE // Increase job EXP rate for VIP.
1497+ if( battle_config.vip_job_exp_increase && ( sd && pc_isvip(sd) ) )
1498+ vip_bonus += battle_config.vip_job_exp_increase;
1499+#endif
1500+ job_exp = (unsigned int)cap_value(md->db->job_exp * per * (bonus+vip_bonus)/100. * map[m].adjust.jexp/100., 1, UINT_MAX);
1501+ }
1502
1503 if ( ( temp = tmpsd[i]->status.party_id)>0 ) {
1504 int j;
1505@@ -2371,6 +2383,11 @@ int mob_dead(struct mob_data *md, struct block_list *src, int type)
1506 // Increase drop rate if user has SC_ITEMBOOST
1507 if (sd && sd->sc.data[SC_ITEMBOOST]) // now rig the drop rate to never be over 90% unless it is originally >90%.
1508 drop_rate = max(drop_rate,cap_value((int)(0.5+drop_rate*(sd->sc.data[SC_ITEMBOOST]->val1)/100.),0,9000));
1509+#ifdef VIP_ENABLE // Increase item drop rate for VIP.
1510+ if( battle_config.vip_drop_increase && ( sd && pc_isvip(sd) ) )
1511+ drop_rate += (int)(0.5+(drop_rate*battle_config.vip_drop_increase)/10000.);
1512+ drop_rate = min(drop_rate,10000); //cap it to 100%
1513+#endif
1514 #ifdef RENEWAL_DROP
1515 if( drop_modifier != 100 ) {
1516 drop_rate = drop_rate * drop_modifier / 100;
1517diff --git a/src/map/pc.c b/src/map/pc.c
1518index b7f801c..567a867 100644
1519--- a/src/map/pc.c
1520+++ b/src/map/pc.c
1521@@ -1276,6 +1276,10 @@ int pc_reg_received(struct map_session_data *sd)
1522 chrif_skillcooldown_request(sd->status.account_id, sd->status.char_id);
1523 chrif_bankdata_request(sd->status.account_id, sd->status.char_id);
1524 chrif_bsdata_request(sd->status.char_id);
1525+ sd->storage_size = MIN_STORAGE; //default to min
1526+#ifdef VIP_ENABLE
1527+ chrif_req_vipActive(sd, 0, 1); // request VIP informations
1528+#endif
1529 intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
1530 intif_request_questlog(sd);
1531
1532@@ -2489,7 +2493,7 @@ int pc_bonus(struct map_session_data *sd,int type,int val)
1533 sd->special_state.no_misc_damage = cap_value(val,0,100);
1534 break;
1535 case SP_NO_GEMSTONE:
1536- if(sd->state.lr_flag != 2)
1537+ if(sd->state.lr_flag != 2 && sd->special_state.no_gemstone != 2)
1538 sd->special_state.no_gemstone = 1;
1539 break;
1540 case SP_INTRAVISION: // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
1541@@ -5877,8 +5881,13 @@ static void pc_calcexp(struct map_session_data *sd, unsigned int *base_exp, unsi
1542 (int)(status_get_lv(src) - sd->status.base_level) >= 20)
1543 bonus += 15; // pk_mode additional exp if monster >20 levels [Valaris]
1544
1545- if (sd->sc.data[SC_EXPBOOST])
1546- bonus += sd->sc.data[SC_EXPBOOST]->val1;
1547+ if (sd->sc.data[SC_EXPBOOST]) {
1548+ bonus += sd->sc.data[SC_EXPBOOST]->val1;
1549+#ifdef VIP_ENABLE
1550+ if( battle_config.vip_bm_increase && pc_isvip(sd) ) // Increase Battle Manual EXP rate for VIP.
1551+ bonus += ( sd->sc.data[SC_EXPBOOST]->val1 / battle_config.vip_bm_increase );
1552+#endif
1553+ }
1554
1555 *base_exp = (unsigned int) cap_value(*base_exp + (double)*base_exp * bonus/100., 1, UINT_MAX);
1556
1557@@ -6928,37 +6937,38 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
1558 && !map[sd->bl.m].flag.noexppenalty && !map_flag_gvg(sd->bl.m)
1559 && !sd->sc.data[SC_BABY] && !sd->sc.data[SC_LIFEINSURANCE])
1560 {
1561- unsigned int base_penalty =0;
1562- if (battle_config.death_penalty_base > 0) {
1563+ unsigned int base_penalty = battle_config.death_penalty_base, job_penalty = battle_config.death_penalty_job;
1564+#ifdef VIP_ENABLE
1565+ if(pc_isvip(sd)){
1566+ base_penalty = base_penalty*battle_config.vip_exp_penalty_base;
1567+ job_penalty = job_penalty*battle_config.vip_exp_penalty_job;
1568+ }
1569+ else {
1570+ base_penalty = base_penalty*battle_config.vip_exp_penalty_base_normal;
1571+ job_penalty = job_penalty*battle_config.vip_exp_penalty_job_normal;
1572+ }
1573+#endif
1574+ if (base_penalty > 0) {
1575 switch (battle_config.death_penalty_type) {
1576- case 1:
1577- base_penalty = (unsigned int) ((double)pc_nextbaseexp(sd) * (double)battle_config.death_penalty_base/10000);
1578- break;
1579- case 2:
1580- base_penalty = (unsigned int) ((double)sd->status.base_exp * (double)battle_config.death_penalty_base/10000);
1581- break;
1582+ case 1: base_penalty = (uint32) ((double)(pc_nextbaseexp(sd) * base_penalty)/10000); break;
1583+ case 2: base_penalty = (uint32) ((double)(sd->status.base_exp * base_penalty)/10000); break;
1584 }
1585- if(base_penalty) {
1586+ if (base_penalty > 0){ //recheck after altering to speedup
1587 if (battle_config.pk_mode && src && src->type==BL_PC)
1588 base_penalty*=2;
1589 sd->status.base_exp -= min(sd->status.base_exp, base_penalty);
1590 clif_updatestatus(sd,SP_BASEEXP);
1591 }
1592 }
1593- if(battle_config.death_penalty_job > 0) {
1594- base_penalty = 0;
1595+ if(job_penalty > 0) {
1596 switch (battle_config.death_penalty_type) {
1597- case 1:
1598- base_penalty = (unsigned int) ((double)pc_nextjobexp(sd) * (double)battle_config.death_penalty_job/10000);
1599- break;
1600- case 2:
1601- base_penalty = (unsigned int) ((double)sd->status.job_exp * (double)battle_config.death_penalty_job/10000);
1602- break;
1603+ case 1: job_penalty = (uint32) ((double)(pc_nextjobexp(sd) * job_penalty)/10000); break;
1604+ case 2: job_penalty = (uint32) ((double)(sd->status.job_exp * job_penalty)/10000); break;
1605 }
1606- if(base_penalty) {
1607+ if(job_penalty) {
1608 if (battle_config.pk_mode && src && src->type==BL_PC)
1609- base_penalty*=2;
1610- sd->status.job_exp -= min(sd->status.job_exp, base_penalty);
1611+ job_penalty*=2;
1612+ sd->status.job_exp -= min(sd->status.job_exp, job_penalty);
1613 clif_updatestatus(sd,SP_JOBEXP);
1614 }
1615 }
1616@@ -9087,7 +9097,7 @@ int pc_check_available_item(struct map_session_data *sd) {
1617 }
1618
1619 if( battle_config.item_check&4 ) { // Check for invalid(ated) items in storage.
1620- for( i = 0; i < MAX_STORAGE; i++ ) {
1621+ for( i = 0; i < sd->storage_size; i++ ) {
1622 it = sd->status.storage.items[i].nameid;
1623
1624 if( it && !itemdb_available(it) ) {
1625diff --git a/src/map/pc.h b/src/map/pc.h
1626index 6e2693e..b602a3d 100644
1627--- a/src/map/pc.h
1628+++ b/src/map/pc.h
1629@@ -201,7 +201,7 @@ struct map_session_data {
1630 unsigned int no_castcancel : 1;
1631 unsigned int no_castcancel2 : 1;
1632 unsigned int no_sizefix : 1;
1633- unsigned int no_gemstone : 1;
1634+ unsigned int no_gemstone : 2;
1635 unsigned int intravision : 1; // Maya Purple Card effect [DracoRPG]
1636 unsigned int perfect_hiding : 1; // [Valaris]
1637 unsigned int no_knockback : 1;
1638@@ -213,7 +213,7 @@ struct map_session_data {
1639 unsigned int permissions;/* group permissions */
1640
1641 int langtype;
1642- int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
1643+ uint32 packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
1644 struct mmo_charstatus status;
1645 struct registry save_reg;
1646
1647@@ -550,6 +550,15 @@ struct map_session_data {
1648 uint8 count; //Count of target for skill like RL_D_TAIL
1649 } c_marker;
1650 bool flicker;
1651+
1652+ // Holds player storage size (VIP system).
1653+ int storage_size;
1654+#ifdef VIP_ENABLE
1655+ struct {
1656+ unsigned int enabled;
1657+ unsigned int time;
1658+ } vip;
1659+#endif
1660
1661 //Timed bonus 'bonus_script' struct [Cydh]
1662 struct s_script {
1663@@ -562,6 +571,7 @@ struct map_session_data {
1664 } bonus_script[MAX_PC_BONUS_SCRIPT];
1665 };
1666
1667+
1668 enum weapon_type {
1669 W_FIST, //Bare hands
1670 W_DAGGER, //1
1671@@ -683,7 +693,9 @@ struct {
1672 #define pc_ishiding(sd) ( (sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) )
1673 #define pc_iscloaking(sd) ( !((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK) )
1674 #define pc_ischasewalk(sd) ( (sd)->sc.option&OPTION_CHASEWALK )
1675-
1676+#ifdef VIP_ENABLE
1677+ #define pc_isvip(sd) ( sd->vip.enabled ? 1 : 0 )
1678+#endif
1679 #ifdef NEW_CARTS
1680 #define pc_iscarton(sd) ( (sd)->sc.data[SC_PUSH_CART] )
1681 #else
1682diff --git a/src/map/script.c b/src/map/script.c
1683index f05dda5..5defc72 100644
1684--- a/src/map/script.c
1685+++ b/src/map/script.c
1686@@ -18010,6 +18010,7 @@ static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
1687 case 7: script_pushint(st,MAX_GUILDLEVEL); break;
1688 case 8: script_pushint(st,MAX_GUILD_STORAGE); break;
1689 case 9: script_pushint(st,MAX_BG_MEMBERS); break;
1690+ case 10: script_pushint(st,VIP_SCRIPT); break;
1691 default:
1692 ShowWarning("buildin_getserverdef: unknown type %d.\n", type);
1693 script_pushint(st,0);
1694@@ -18018,6 +18019,76 @@ static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
1695 return 0;
1696 }
1697
1698+#ifdef VIP_ENABLE
1699+/* Returns various information about a player's VIP status.
1700+ * vip_status <type>,{"<character name>"};
1701+ * Note: VIP System needs to be enabled.
1702+ */
1703+BUILDIN_FUNC(vip_status) {
1704+ TBL_PC *sd;
1705+ char *vip_str = (char *)aMalloc(24*sizeof(char));
1706+ time_t now = time(NULL);
1707+ int type = script_getnum(st, 2);
1708+
1709+ if( script_hasdata(st, 3) )
1710+ sd = map_nick2sd(script_getstr(st, 3));
1711+ else
1712+ sd = script_rid2sd(st);
1713+
1714+ if( sd == NULL )
1715+ return 0;
1716+
1717+ switch(type) {
1718+ case 0: // Get VIP status.
1719+ script_pushint(st, pc_isvip(sd));
1720+ break;
1721+ case 1: // Get VIP expire date.
1722+ if( pc_isvip(sd) ) {
1723+ time_t viptime= (time_t)sd->vip.time;
1724+ strftime(vip_str, 24, "%Y-%m-%d %H:%M", localtime(&viptime));
1725+ vip_str[24]='\0';
1726+ script_pushstr(st, vip_str);
1727+ } else
1728+ script_pushint(st, 0);
1729+ break;
1730+ case 2: // Get remaining time.
1731+ if( pc_isvip(sd) ) {
1732+ time_t viptime= (time_t)sd->vip.time;
1733+ strftime(vip_str, 24, "%Y-%m-%d %H:%M", localtime(&viptime - now));
1734+ vip_str[24]='\0';
1735+ script_pushstr(st, vip_str);
1736+ } else
1737+ script_pushint(st, 0);
1738+ break;
1739+ }
1740+ return 0;
1741+}
1742+
1743+
1744+/* Adds or removes VIP time in minutes.
1745+ * vip_time <time>,{"<character name>"};
1746+ * If time < 0 remove time, else add time.
1747+ * Note: VIP System needs to be enabled.
1748+ */
1749+BUILDIN_FUNC(vip_time) {
1750+ TBL_PC *sd;
1751+ int time = script_getnum(st, 2) * 60; // Convert since it's given in minutes.
1752+
1753+ if( script_hasdata(st, 3) )
1754+ sd = map_nick2sd(script_getstr(st, 3));
1755+ else
1756+ sd = script_rid2sd(st);
1757+
1758+ if( sd == NULL )
1759+ return 0;
1760+
1761+ if( pc_isvip(sd) )
1762+ chrif_req_vipActive(sd, time, 2);
1763+
1764+ return 0;
1765+}
1766+#endif
1767+
1768 /*==========================================
1769 * Turns a player into a monster and grants SC attribute effect. [malufett/Hercules]
1770 * montransform <monster name/ID>, <duration>, <sc type>, <val1>, <val2>, <val3>, <val4>;
1771@@ -18636,6 +18707,10 @@ struct script_function buildin_func[] = {
1772 BUILDIN_DEF(is_clientver,"ii?"),
1773 BUILDIN_DEF(getserverdef,"i"),
1774 BUILDIN_DEF2(montransform, "transform", "vii????"), // Monster Transform [malufett/Hercules]
1775+#ifdef VIP_ENABLE
1776+ BUILDIN_DEF(vip_status,"i?"),
1777+ BUILDIN_DEF(vip_time,"i?"),
1778+#endif
1779 BUILDIN_DEF(bonus_script,"si???"),
1780
1781 #include "../custom/script_def.inc"
1782diff --git a/src/map/skill.c b/src/map/skill.c
1783index f7c8ea0..4497292 100644
1784--- a/src/map/skill.c
1785+++ b/src/map/skill.c
1786@@ -13712,7 +13712,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
1787 * Warlock
1788 **/
1789 case WL_COMET:
1790- if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0
1791+ if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0 && require.itemid[0]
1792 && sd->special_state.no_gemstone == 0
1793 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) ) {
1794 //clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
1795@@ -14448,11 +14448,11 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16
1796 continue;
1797 break;
1798 case AB_ADORAMUS:
1799- if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2) )
1800+ if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && ( sd->special_state.no_gemstone==2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2)) )
1801 continue;
1802 break;
1803 case WL_COMET:
1804- if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0) )
1805+ if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && ( sd->special_state.no_gemstone==2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0)) )
1806 continue;
1807 break;
1808 case GN_FIRE_EXPANSION:
1809@@ -14475,21 +14475,25 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16
1810 req.itemid[i] = skill_db[idx].require.itemid[i];
1811 req.amount[i] = skill_db[idx].require.amount[i];
1812
1813- if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN )
1814- {
1815- if( sd->special_state.no_gemstone )
1816- { // All gem skills except Hocus Pocus and Ganbantein can cast for free with Mistress card -helvetica
1817- if( skill_id != SA_ABRACADABRA )
1818- req.itemid[i] = req.amount[i] = 0;
1819- else if( --req.amount[i] < 1 )
1820- req.amount[i] = 1; // Hocus Pocus always use at least 1 gem
1821- }
1822- if(sc && sc->data[SC_INTOABYSS])
1823- {
1824- if( skill_id != SA_ABRACADABRA )
1825- req.itemid[i] = req.amount[i] = 0;
1826- else if( --req.amount[i] < 1 )
1827- req.amount[i] = 1; // Hocus Pocus always use at least 1 gem
1828+ //check requirement for gemstone
1829+ if( itemid_isgemstone(req.itemid[i])){
1830+ if( sd->special_state.no_gemstone == 2 ) // Remove all Magic Stone required for all skills for VIP.
1831+ req.itemid[i] = req.amount[i] = 0;
1832+ else {
1833+ if( sd->special_state.no_gemstone )
1834+ { // All gem skills except Hocus Pocus and Ganbantein can cast for free with Mistress card -helvetica
1835+ if( skill_id != SA_ABRACADABRA )
1836+ req.itemid[i] = req.amount[i] = 0;
1837+ else if( --req.amount[i] < 1 )
1838+ req.amount[i] = 1; // Hocus Pocus always use at least 1 gem
1839+ }
1840+ if(sc && sc->data[SC_INTOABYSS])
1841+ {
1842+ if( skill_id != SA_ABRACADABRA )
1843+ req.itemid[i] = req.amount[i] = 0;
1844+ else if( --req.amount[i] < 1 )
1845+ req.amount[i] = 1; // Hocus Pocus always use at least 1 gem
1846+ }
1847 }
1848 }
1849 if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){
1850diff --git a/src/map/status.c b/src/map/status.c
1851index 48925e5..696729d 100644
1852--- a/src/map/status.c
1853+++ b/src/map/status.c
1854@@ -2636,10 +2636,10 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
1855 wa->atk2 = refine_info[wlv].bonus[r-1] / 100;
1856
1857 #ifdef RENEWAL
1858- wa->matk += sd->inventory_data[index]->matk;
1859- wa->wlv = wlv;
1860- if( r ) // Renewal magic attack refine bonus
1861- wa->matk += refine_info[wlv].bonus[r-1] / 100;
1862+ wa->matk += sd->inventory_data[index]->matk;
1863+ wa->wlv = wlv;
1864+ if( r ) // renewal magic attack refine bonus
1865+ wa->matk += refine_info[wlv].bonus[r-1] / 100;
1866 #endif
1867
1868 // Overrefine bonus.
1869@@ -4057,12 +4057,12 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
1870 unit_stop_walking(bl,1);
1871 }
1872
1873- /**
1874+ /*
1875 * No status changes alter these yet.
1876 * if(flag&SCB_SIZE)
1877 * if(flag&SCB_RACE)
1878 * if(flag&SCB_RANGE)
1879- **/
1880+ */
1881
1882 if(flag&SCB_MAXHP) {
1883 if( bl->type&BL_PC ) {
1884@@ -4105,10 +4105,10 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
1885 status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0);
1886 status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0);
1887 #else
1888- /**
1889+ /*
1890 * RE MATK Formula (from irowiki:http:// irowiki.org/wiki/MATK)
1891 * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers
1892- **/
1893+ */
1894 status->matk_min = status->matk_max = status_base_matk(status, status_get_lv(bl));
1895 if( bl->type&BL_PC ) {
1896 int wMatk = 0;
1897@@ -4984,7 +4984,7 @@ static unsigned short status_calc_ematk(struct block_list *bl, struct status_cha
1898 matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; // 70 lvl1, 100lvl2
1899 if(sc->data[SC_IZAYOI])
1900 matk += 50 * sc->data[SC_IZAYOI]->val1;
1901- return (unsigned short)cap_value(matk,0,USHRT_MAX);
1902+ return (unsigned short)cap_value(matk,0,USHRT_MAX);
1903 }
1904 #endif
1905 /**
1906diff --git a/src/map/storage.c b/src/map/storage.c
1907index 72a8b3f..cf4b65f 100644
1908--- a/src/map/storage.c
1909+++ b/src/map/storage.c
1910@@ -107,7 +107,7 @@ int storage_storageopen(struct map_session_data *sd)
1911 sd->state.storage_flag = 1;
1912 storage_sortitem(sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items));
1913 clif_storagelist(sd, sd->status.storage.items, ARRAYLENGTH(sd->status.storage.items));
1914- clif_updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE);
1915+ clif_updatestorageamount(sd, sd->status.storage.storage_amount, sd->storage_size);
1916 return 0;
1917 }
1918
1919@@ -162,7 +162,7 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data,
1920
1921 if( itemdb_isstackable2(data) )
1922 {//Stackable
1923- for( i = 0; i < MAX_STORAGE; i++ )
1924+ for( i = 0; i < sd->storage_size; i++ )
1925 {
1926 if( compare_item(&stor->items[i], item_data) )
1927 {// existing items found, stack them
1928@@ -176,8 +176,8 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data,
1929 }
1930
1931 // find free slot
1932- ARR_FIND( 0, MAX_STORAGE, i, stor->items[i].nameid == 0 );
1933- if( i >= MAX_STORAGE )
1934+ ARR_FIND( 0, sd->storage_size, i, stor->items[i].nameid == 0 );
1935+ if( i >= sd->storage_size )
1936 return 1;
1937
1938 // add item to slot
1939@@ -185,7 +185,7 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data,
1940 stor->storage_amount++;
1941 stor->items[i].amount = amount;
1942 clif_storageitemadded(sd,&stor->items[i],i,amount);
1943- clif_updatestorageamount(sd, stor->storage_amount, MAX_STORAGE);
1944+ clif_updatestorageamount(sd, stor->storage_amount, sd->storage_size);
1945
1946 return 0;
1947 }
1948@@ -203,7 +203,7 @@ int storage_delitem(struct map_session_data* sd, int n, int amount)
1949 {
1950 memset(&sd->status.storage.items[n],0,sizeof(sd->status.storage.items[0]));
1951 sd->status.storage.storage_amount--;
1952- if( sd->state.storage_flag == 1 ) clif_updatestorageamount(sd, sd->status.storage.storage_amount, MAX_STORAGE);
1953+ if( sd->state.storage_flag == 1 ) clif_updatestorageamount(sd, sd->status.storage.storage_amount, sd->storage_size);
1954 }
1955 if( sd->state.storage_flag == 1 ) clif_storageitemremoved(sd,n,amount);
1956 return 0;
1957@@ -220,7 +220,7 @@ int storage_storageadd(struct map_session_data* sd, int index, int amount)
1958 {
1959 nullpo_ret(sd);
1960
1961- if( sd->status.storage.storage_amount > MAX_STORAGE )
1962+ if( sd->status.storage.storage_amount > sd->storage_size )
1963 return 0; // storage full
1964
1965 if( index < 0 || index >= MAX_INVENTORY )
1966@@ -253,7 +253,7 @@ int storage_storageget(struct map_session_data* sd, int index, int amount)
1967 {
1968 int flag;
1969
1970- if( index < 0 || index >= MAX_STORAGE )
1971+ if( index < 0 || index >= sd->storage_size )
1972 return 0;
1973
1974 if( sd->status.storage.items[index].nameid <= 0 )
1975@@ -281,11 +281,11 @@ int storage_storageaddfromcart(struct map_session_data* sd, int index, int amoun
1976 {
1977 nullpo_ret(sd);
1978
1979- if( sd->status.storage.storage_amount > MAX_STORAGE )
1980- return 0; // storage full / storage closed
1981+ if( sd->status.storage.storage_amount > sd->storage_size )
1982+ return 0; // storage full / storage closed
1983
1984 if( index < 0 || index >= MAX_CART )
1985- return 0;
1986+ return 0;
1987
1988 if( sd->status.cart[index].nameid <= 0 )
1989 return 0; //No item there.
1990@@ -311,7 +311,7 @@ int storage_storagegettocart(struct map_session_data* sd, int index, int amount)
1991 short flag;
1992 nullpo_ret(sd);
1993
1994- if( index < 0 || index >= MAX_STORAGE )
1995+ if( index < 0 || index >= sd->storage_size )
1996 return 0;
1997
1998 if( sd->status.storage.items[index].nameid <= 0 )
1999diff --git a/src/map/unit.c b/src/map/unit.c
2000index bd17ad1..589ffc3 100644
2001--- a/src/map/unit.c
2002+++ b/src/map/unit.c
2003@@ -2191,7 +2191,7 @@ int unit_skillcastcancel(struct block_list *bl,int type)
2004 return 0;
2005
2006 if (sd && (sd->special_state.no_castcancel2 ||
2007- ((sd->sc.data[SC_UNLIMITEDHUMMINGVOICE] || sd->special_state.no_castcancel) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground))) // fixed flags being read the wrong way around [blackhole89]
2008+ ((sd->sc.data[SC_UNLIMITEDHUMMINGVOICE] || sd->special_state.no_castcancel) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground))) // fixed flags being read the wrong way around [blackhole89]
2009 return 0;
2010 }
2011
2012diff --git a/vcproj-10/char-server_sql.vcxproj b/vcproj-10/char-server_sql.vcxproj
2013index abed05b..36f94fc 100644
2014--- a/vcproj-10/char-server_sql.vcxproj
2015+++ b/vcproj-10/char-server_sql.vcxproj
2016@@ -138,6 +138,7 @@
2017 <ClInclude Include="..\3rdparty\libconfig\wincompat.h" />
2018 <ClInclude Include="..\3rdparty\mt19937ar\mt19937ar.h" />
2019 <ClInclude Include="..\src\common\cbasetypes.h" />
2020+ <ClInclude Include="..\src\common\conf.h" />
2021 <ClInclude Include="..\src\common\core.h" />
2022 <ClInclude Include="..\src\common\db.h" />
2023 <ClInclude Include="..\src\common\ers.h" />
2024@@ -179,6 +180,7 @@
2025 <ClCompile Include="..\3rdparty\libconfig\scanner.c" />
2026 <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
2027 <ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c" />
2028+ <ClCompile Include="..\src\common\conf.c" />
2029 <ClCompile Include="..\src\common\core.c" />
2030 <ClCompile Include="..\src\common\db.c" />
2031 <ClCompile Include="..\src\common\ers.c" />
2032@@ -214,4 +216,4 @@
2033 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
2034 <ImportGroup Label="ExtensionTargets">
2035 </ImportGroup>
2036-</Project>
2037\ No newline at end of file
2038+</Project>
2039diff --git a/vcproj-10/char-server_sql.vcxproj.filters b/vcproj-10/char-server_sql.vcxproj.filters
2040index 06f69e3..7dee36f 100644
2041--- a/vcproj-10/char-server_sql.vcxproj.filters
2042+++ b/vcproj-10/char-server_sql.vcxproj.filters
2043@@ -106,6 +106,11 @@
2044 <ClCompile Include="..\src\common\raconf.c">
2045 <Filter>common</Filter>
2046 </ClCompile>
2047+ <ClCompile Include="..\src\common\msg_conf.c" />
2048+ <ClCompile Include="..\src\common\cli.c" />
2049+ <ClCompile Include="..\src\common\conf.c">
2050+ <Filter>common</Filter>
2051+ </ClCompile>
2052 </ItemGroup>
2053 <ItemGroup>
2054 <ClInclude Include="..\src\common\cbasetypes.h">
2055@@ -228,6 +233,11 @@
2056 <ClInclude Include="..\src\common\raconf.h">
2057 <Filter>common</Filter>
2058 </ClInclude>
2059+ <ClInclude Include="..\src\common\msg_conf.h" />
2060+ <ClInclude Include="..\src\common\cli.h" />
2061+ <ClInclude Include="..\src\common\conf.h">
2062+ <Filter>common</Filter>
2063+ </ClInclude>
2064 </ItemGroup>
2065 <ItemGroup>
2066 <Filter Include="common">
2067@@ -246,4 +256,4 @@
2068 <UniqueIdentifier>{9e8badd7-548f-4eb4-9e87-613e87e772ff}</UniqueIdentifier>
2069 </Filter>
2070 </ItemGroup>
2071-</Project>
2072\ No newline at end of file
2073+</Project>
2074diff --git a/vcproj-10/login-server_sql.vcxproj b/vcproj-10/login-server_sql.vcxproj
2075index 0315ac3..e0d9f92 100644
2076--- a/vcproj-10/login-server_sql.vcxproj
2077+++ b/vcproj-10/login-server_sql.vcxproj
2078@@ -138,6 +138,7 @@
2079 <ClInclude Include="..\3rdparty\libconfig\scanner.h" />
2080 <ClInclude Include="..\3rdparty\libconfig\strbuf.h" />
2081 <ClInclude Include="..\3rdparty\libconfig\wincompat.h" />
2082+ <ClInclude Include="..\src\common\conf.h" />
2083 <ClInclude Include="..\src\common\mempool.h" />
2084 <ClInclude Include="..\src\common\mutex.h" />
2085 <ClInclude Include="..\src\common\raconf.h" />
2086@@ -172,6 +173,7 @@
2087 <ClCompile Include="..\3rdparty\libconfig\scanctx.c" />
2088 <ClCompile Include="..\3rdparty\libconfig\scanner.c" />
2089 <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
2090+ <ClCompile Include="..\src\common\conf.c" />
2091 <ClCompile Include="..\src\common\mempool.c" />
2092 <ClCompile Include="..\src\common\mutex.c" />
2093 <ClCompile Include="..\src\common\raconf.c" />
2094@@ -200,4 +202,4 @@
2095 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
2096 <ImportGroup Label="ExtensionTargets">
2097 </ImportGroup>
2098-</Project>
2099\ No newline at end of file
2100+</Project>
2101diff --git a/vcproj-10/login-server_sql.vcxproj.filters b/vcproj-10/login-server_sql.vcxproj.filters
2102index f4280a0..3b682ce 100644
2103--- a/vcproj-10/login-server_sql.vcxproj.filters
2104+++ b/vcproj-10/login-server_sql.vcxproj.filters
2105@@ -82,6 +82,11 @@
2106 <ClCompile Include="..\src\common\raconf.c">
2107 <Filter>common</Filter>
2108 </ClCompile>
2109+ <ClCompile Include="..\src\common\msg_conf.c" />
2110+ <ClCompile Include="..\src\common\cli.c" />
2111+ <ClCompile Include="..\src\common\conf.c">
2112+ <Filter>common</Filter>
2113+ </ClCompile>
2114 </ItemGroup>
2115 <ItemGroup>
2116 <ClInclude Include="..\src\login\account.h">
2117@@ -180,6 +185,11 @@
2118 <ClInclude Include="..\src\common\raconf.h">
2119 <Filter>common</Filter>
2120 </ClInclude>
2121+ <ClInclude Include="..\src\common\msg_conf.h" />
2122+ <ClInclude Include="..\src\common\cli.h" />
2123+ <ClInclude Include="..\src\common\conf.h">
2124+ <Filter>common</Filter>
2125+ </ClInclude>
2126 </ItemGroup>
2127 <ItemGroup>
2128 <Filter Include="common">
2129@@ -198,4 +208,4 @@
2130 <UniqueIdentifier>{779e8145-9bb2-4a88-9149-60586ab0bdd4}</UniqueIdentifier>
2131 </Filter>
2132 </ItemGroup>
2133-</Project>
2134\ No newline at end of file
2135+</Project>
2136diff --git a/vcproj-12/char-server_sql.vcxproj b/vcproj-12/char-server_sql.vcxproj
2137index 6cfd004..e76a799 100644
2138--- a/vcproj-12/char-server_sql.vcxproj
2139+++ b/vcproj-12/char-server_sql.vcxproj
2140@@ -142,6 +142,7 @@
2141 <ClInclude Include="..\3rdparty\mt19937ar\mt19937ar.h" />
2142 <ClInclude Include="..\src\common\cbasetypes.h" />
2143 <ClInclude Include="..\src\common\core.h" />
2144+ <ClInclude Include="..\src\common\conf.h" />
2145 <ClInclude Include="..\src\common\db.h" />
2146 <ClInclude Include="..\src\common\ers.h" />
2147 <ClInclude Include="..\src\common\malloc.h" />
2148@@ -183,6 +184,7 @@
2149 <ClCompile Include="..\3rdparty\libconfig\strbuf.c" />
2150 <ClCompile Include="..\3rdparty\mt19937ar\mt19937ar.c" />
2151 <ClCompile Include="..\src\common\core.c" />
2152+ <ClCompile Include="..\src\common\conf.c" />
2153 <ClCompile Include="..\src\common\db.c" />
2154 <ClCompile Include="..\src\common\ers.c" />
2155 <ClCompile Include="..\src\common\malloc.c" />
2156@@ -217,4 +219,4 @@
2157 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
2158 <ImportGroup Label="ExtensionTargets">
2159 </ImportGroup>
2160-</Project>
2161\ No newline at end of file
2162+</Project>
2163diff --git a/vcproj-12/char-server_sql.vcxproj.filters b/vcproj-12/char-server_sql.vcxproj.filters
2164index 06f69e3..ba4d82c 100644
2165--- a/vcproj-12/char-server_sql.vcxproj.filters
2166+++ b/vcproj-12/char-server_sql.vcxproj.filters
2167@@ -4,6 +4,9 @@
2168 <ClCompile Include="..\src\common\core.c">
2169 <Filter>common</Filter>
2170 </ClCompile>
2171+ <ClCompile Include="..\src\common\conf.c">
2172+ <Filter>common</Filter>
2173+ </ClCompile>
2174 <ClCompile Include="..\src\common\db.c">
2175 <Filter>common</Filter>
2176 </ClCompile>
2177@@ -114,6 +117,9 @@
2178 <ClInclude Include="..\src\common\core.h">
2179 <Filter>common</Filter>
2180 </ClInclude>
2181+ <ClInclude Include="..\src\common\conf.h">
2182+ <Filter>common</Filter>
2183+ </ClInclude>
2184 <ClInclude Include="..\src\common\db.h">
2185 <Filter>common</Filter>
2186 </ClInclude>
2187@@ -246,4 +252,4 @@
2188 <UniqueIdentifier>{9e8badd7-548f-4eb4-9e87-613e87e772ff}</UniqueIdentifier>
2189 </Filter>
2190 </ItemGroup>
2191-</Project>
2192\ No newline at end of file
2193+</Project>
2194diff --git a/vcproj-12/login-server_sql.vcxproj b/vcproj-12/login-server_sql.vcxproj
2195index 7a0a298..c4d9c89 100644
2196--- a/vcproj-12/login-server_sql.vcxproj
2197+++ b/vcproj-12/login-server_sql.vcxproj
2198@@ -153,6 +153,7 @@
2199 <ClInclude Include="..\src\login\loginlog.h" />
2200 <ClInclude Include="..\src\common\cbasetypes.h" />
2201 <ClInclude Include="..\src\common\core.h" />
2202+ <ClInclude Include="..\src\common\conf.h" />
2203 <ClInclude Include="..\src\common\db.h" />
2204 <ClInclude Include="..\src\common\ers.h" />
2205 <ClInclude Include="..\src\common\malloc.h" />
2206@@ -185,6 +186,7 @@
2207 <ClCompile Include="..\src\login\login.c" />
2208 <ClCompile Include="..\src\login\loginlog_sql.c" />
2209 <ClCompile Include="..\src\common\core.c" />
2210+ <ClCompile Include="..\src\common\conf.c" />
2211 <ClCompile Include="..\src\common\db.c" />
2212 <ClCompile Include="..\src\common\ers.c" />
2213 <ClCompile Include="..\src\common\malloc.c" />
2214@@ -204,4 +206,4 @@
2215 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
2216 <ImportGroup Label="ExtensionTargets">
2217 </ImportGroup>
2218-</Project>
2219\ No newline at end of file
2220+</Project>
2221diff --git a/vcproj-12/login-server_sql.vcxproj.filters b/vcproj-12/login-server_sql.vcxproj.filters
2222index f4280a0..d9ad6fc 100644
2223--- a/vcproj-12/login-server_sql.vcxproj.filters
2224+++ b/vcproj-12/login-server_sql.vcxproj.filters
2225@@ -16,6 +16,9 @@
2226 <ClCompile Include="..\src\common\core.c">
2227 <Filter>common</Filter>
2228 </ClCompile>
2229+ <ClCompile Include="..\src\common\conf.c">
2230+ <Filter>common</Filter>
2231+ </ClCompile>
2232 <ClCompile Include="..\src\common\db.c">
2233 <Filter>common</Filter>
2234 </ClCompile>
2235@@ -102,6 +105,9 @@
2236 <ClInclude Include="..\src\common\core.h">
2237 <Filter>common</Filter>
2238 </ClInclude>
2239+ <ClInclude Include="..\src\common\conf.h">
2240+ <Filter>common</Filter>
2241+ </ClInclude>
2242 <ClInclude Include="..\src\common\db.h">
2243 <Filter>common</Filter>
2244 </ClInclude>
2245@@ -198,4 +204,4 @@
2246 <UniqueIdentifier>{779e8145-9bb2-4a88-9149-60586ab0bdd4}</UniqueIdentifier>
2247 </Filter>
2248 </ItemGroup>
2249-</Project>
2250\ No newline at end of file
2251+</Project>
2252diff --git a/vcproj-9/login-server_sql.vcproj b/vcproj-9/login-server_sql.vcproj
2253index ed310a6..03d72ef 100644
2254--- a/vcproj-9/login-server_sql.vcproj
2255+++ b/vcproj-9/login-server_sql.vcproj
2256@@ -405,7 +405,7 @@
2257 <File
2258 RelativePath="..\src\common\cli.c"
2259 >
2260- </File>
2261+ </File>
2262 <File
2263 RelativePath="..\src\common\msg_conf.h"
2264 >
2265@@ -413,7 +413,7 @@
2266 <File
2267 RelativePath="..\src\common\msg_conf.c"
2268 >
2269- </File>
2270+ </File>
2271
2272 </Filter>
2273 <Filter