2014 Client Support

* Implemented the Roulette Game feature.
* Implemented per-character sex change feature.
* More info: https://rathena.org/board/topic/102247-2014-client-support/
- Special thanks to HerculesWS/Hercules@239d480 for the base.
This commit is contained in:
aleos89 2015-06-19 14:07:34 -04:00
parent 5cb90cd1d0
commit 3d9c6e7763
34 changed files with 1054 additions and 151 deletions

View File

@ -57,3 +57,8 @@ feature.autotrade_sit: 1
// Delay in miliseconds to open vending/buyingsotre after player logged in. // Delay in miliseconds to open vending/buyingsotre after player logged in.
feature.autotrade_open_delay: 5000 feature.autotrade_open_delay: 5000
// Roulette (Note 1)
// Requires: 2014-10-22bRagexe or later
// Off by default while test version is out; enable at your own risk.
feature.roulette: off

View File

@ -139,6 +139,7 @@ mapreg_db: mapreg
vending_db: vendings vending_db: vendings
vending_items_db: vending_items vending_items_db: vending_items
market_table: market market_table: market
db_roulette_table: db_roulette
// Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no) // Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
use_sql_db: no use_sql_db: no

View File

@ -8,31 +8,32 @@
//-------------------------------------------------------------- //--------------------------------------------------------------
// Enable Logs? (Note 3) // Enable Logs? (Note 3)
// 0x00000 - Don't log at all // 0x000000 - Don't log at all
// 0x00001 - (T) Log trades // 0x000001 - (T) Log trades
// 0x00002 - (V) Log vending transactions // 0x000002 - (V) Log vending transactions
// 0x00004 - (P) Log items drop/picked by players // 0x000004 - (P) Log items drop/picked by players
// 0x00008 - (L) Log items drop/looted by monsters // 0x000008 - (L) Log items drop/looted by monsters
// 0x00010 - (S) Log NPC transactions (buy/sell) // 0x000010 - (S) Log NPC transactions (buy/sell)
// 0x00020 - (N) Log Script transactions (items deleted/acquired through quests) // 0x000020 - (N) Log Script transactions (items deleted/acquired through quests)
// 0x00040 - (D) Log items stolen from mobs (Steal/Gank) // 0x000040 - (D) Log items stolen from mobs (Steal/Gank)
// 0x00080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks) // 0x000080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks)
// 0x00100 - (O) Log produced/ingredient items // 0x000100 - (O) Log produced/ingredient items
// 0x00200 - (U) Log MVP prize items // 0x000200 - (U) Log MVP prize items
// 0x00400 - (A) Log player created/deleted items (through @/# commands) // 0x000400 - (A) Log player created/deleted items (through @/# commands)
// 0x00800 - (R) Log items placed/retrieved from storage. // 0x000800 - (R) Log items placed/retrieved from storage.
// 0x01000 - (G) Log items placed/retrieved from guild storage. // 0x001000 - (G) Log items placed/retrieved from guild storage.
// 0x02000 - (E) Log mail system transactions. // 0x002000 - (E) Log mail system transactions.
// 0x04000 - (I) Log auction system transactions. // 0x004000 - (I) Log auction system transactions.
// 0x08000 - (B) Log buying store transactions // 0x008000 - (B) Log buying store transactions
// 0x10000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/ // 0x010000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/
// rings deleted by divorce/pet egg (un)hatching/pet armor (un)equipping/Weapon Refine skill/Remove Trap skill) // rings deleted by divorce/pet egg (un)hatching/pet armor (un)equipping/Weapon Refine skill/Remove Trap skill)
// 0x20000 - ($) Log cash transactions // 0x020000 - ($) Log cash transactions
// 0x40000 - (K) Log account bank transactions // 0x040000 - (K) Log account bank transactions
// 0x80000 - (F) Removed bound items when guild/party is broken // 0x080000 - (F) Removed bound items when guild/party is broken
// 0x100000 - (Y) Roulette Lottery
// Example: Log trades+vending+script items+created items: 1+2+32+1024 = 1059 // Example: Log trades+vending+script items+created items: 1+2+32+1024 = 1059
// Please note that moving items from inventory to cart and back is not logged by design. // Please note that moving items from inventory to cart and back is not logged by design.
enable_logs: 0xFFFFF enable_logs: 0xFFFFFF
// Use MySQL Logs? [SQL Version Only] (Note 1) // Use MySQL Logs? [SQL Version Only] (Note 1)
sql_logs: yes sql_logs: yes

View File

@ -1593,5 +1593,8 @@
1495: You can't withdraw that much money 1495: You can't withdraw that much money
1496: Banking is disabled 1496: Banking is disabled
// Roulette
1497: Roulette is disabled
//Custom translations //Custom translations
//import: conf/msg_conf/import/map_msg_eng_conf.txt //import: conf/msg_conf/import/map_msg_eng_conf.txt

View File

@ -432,6 +432,9 @@ CharMoves 124 1
CharRename 125 1 CharRename 125 1
Font 126 1 Font 126 1
BankVault 127 1 BankVault 127 1
RouletteBronze 128 1
RouletteSilver 129 1
RouletteGold 130 1
bMaxHP 6 bMaxHP 6
bMaxSP 8 bMaxSP 8

View File

@ -2318,5 +2318,82 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto]
0x09D8,2,npcmarketclosed,0 0x09D8,2,npcmarketclosed,0
0x09DF,7 0x09DF,7
//2014-10-16Ragexe
packet_ver: 50
0x0369,7,actionrequest,2,6
0x083C,10,useskilltoid,2,4,6
0x0437,5,walktoxy,2
0x035F,6,ticksend,2
0x0967,5,changedir,2,4
0x07E4,6,takeitem,2
0x0362,6,dropitem,2,4
0x07EC,8,movetokafra,2,4
0x022D,8,movefromkafra,2,4
0x0438,10,useskilltopos,2,4,6,8
0x0366,90,useskilltoposinfo,2,4,6,8,10
0x096A,6,getcharnamerequest,2
0x0368,6,solvecharname,2
0x0838,12,searchstoreinfolistitemclick,2,6,10
0x0835,2,searchstoreinfonextpage,0
0x0819,-1,searchstoreinfo,2,4,5,9,13,14,15
0x0811,-1,reqtradebuyingstore,2,4,8,12
0x0360,6,reqclickbuyingstore,2
0x0817,2,reqclosebuyingstore,0
0x0815,-1,reqopenbuyingstore,2,4,8,9,89
0x0365,18,bookingregreq,2,4
// 0x0363,8 // CZ_JOIN_BATTLE_FIELD
0x0281,-1,itemlistwindowselected,2,4,8
0x086E,19,wanttoconnection,2,6,10,14,18
0x0802,26,partyinvite,2
// 0x0922,4 // CZ_GANGSI_RANK
0x094B,26,friendslistadd,2
0x0364,5,hommenu,2,4
0x0936,36,storagepassword,0
0x09DF,7
0x0a00,269
// Roulette System [Yommy]
0x0A19,2,rouletteopen,0 // HEADER_CZ_REQ_OPEN_ROULETTE
0x0A1A,23 // HEADER_ZC_ACK_OPEN_ROULETTE
0x0A1B,2,rouletteinfo,0 // HEADER_CZ_REQ_ROULETTE_INFO
0x0A1C,-1 // HEADER_ZC_ACK_ROULETTE_INFO
0x0A1D,2,rouletteclose,0 // HEADER_CZ_REQ_CLOSE_ROULETTE
0x0A1E,3 // HEADER_ZC_ACK_CLOSE_ROULETTE
0x0A1F,2,roulettegenerate,0 // HEADER_CZ_REQ_GENERATE_ROULETTE
0x0A20,21 // HEADER_ZC_ACK_GENERATE_ROULETTE
0x0A21,3,rouletterecvitem,2 // HEADER_CZ_RECV_ROULETTE_ITEM
0x0A22,5 // HEADER_ZC_RECV_ROULETTE_ITEM
//2014-10-22bRagexe
packet_ver: 51
0x0369,7,actionrequest,2,6
0x083C,10,useskilltoid,2,4,6
0x0437,5,walktoxy,2
0x035F,6,ticksend,2
0x08AD,5,changedir,2,4
0x094E,6,takeitem,2
0x087D,6,dropitem,2,4
0x0878,8,movetokafra,2,4
0x08AA,8,movefromkafra,2,4
0x023B,10,useskilltopos,2,4,6,8
0x0366,90,useskilltoposinfo,2,4,6,8,10
0x096A,6,getcharnamerequest,2
0x0368,6,solvecharname,2
0x0835,12,searchstoreinfolistitemclick,2,6,10
0x0940,2,searchstoreinfonextpage,0
0x0819,-1,searchstoreinfo,2,4,5,9,13,14,15
0x0811,-1,reqtradebuyingstore,2,4,8,12
0x0360,6,reqclickbuyingstore,2
0x0817,2,reqclosebuyingstore,0
0x0815,-1,reqopenbuyingstore,2,4,8,9,89
0x0955,18,bookingregreq,2,4
// 0x092B,8 // CZ_JOIN_BATTLE_FIELD
0x0281,-1,itemlistwindowselected,2,4,8
0x093B,19,wanttoconnection,2,6,10,14,18
0x0896,26,partyinvite,2
// 0x08AB,4 // CZ_GANGSI_RANK
0x091A,26,friendslistadd,2
0x0899,5,hommenu,2,4
0x0438,36,storagepassword,0
//Add new packets here //Add new packets here
//packet_ver: 47 //packet_ver: 52

View File

@ -165,11 +165,11 @@
668,Handsei,Red Envelope,2,0,,20,,,,,0xFFFFFFFF,63,2,,,,,,{ set Zeny,Zeny+rand(1000,10000); },{},{} 668,Handsei,Red Envelope,2,0,,20,,,,,0xFFFFFFFF,63,2,,,,,,{ set Zeny,Zeny+rand(1000,10000); },{},{}
669,Rice_Cake_Soup,Tempting Rice-Cake Soup,0,500,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ percentheal -100,-100; },{},{} 669,Rice_Cake_Soup,Tempting Rice-Cake Soup,0,500,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ percentheal -100,-100; },{},{}
670,Gold_Coin_Moneybag,Bag of Gold Coins,3,100000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 670,Gold_Coin_Moneybag,Bag of Gold Coins,3,100000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
671,Gold_Coin,Gold Coin,3,10000,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ /*goldpoint++; (For Roulette game)*/ },{},{} 671,Gold_Coin,Gold Coin,3,10000,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ RouletteGold++; },{},{}
672,Copper_Coin_Moneybag,Bag of Bronze Coins,3,1000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 672,Copper_Coin_Moneybag,Bag of Bronze Coins,3,1000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
673,Copper_Coin,Bronze Coin,3,100,,40,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 673,Copper_Coin,Bronze Coin,3,100,,40,,,,,0xFFFFFFFF,63,2,,,,,,{ RouletteBronze++; },{},{}
674,Mithril_Coin,Mithril Coin,3,5000,,40,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 674,Mithril_Coin,Mithril Coin,3,5000,,40,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
675,Silver_Coin,Silver Coin,3,5000,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ /*silverpoint++; (For Roulette game)*/ },{},{} 675,Silver_Coin,Silver Coin,3,5000,,0,,,,,0xFFFFFFFF,63,2,,,,,,{ RouletteSilver++; },{},{}
676,Silver_Coin_Moneybag,Bag of Silver Coins,3,50000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 676,Silver_Coin_Moneybag,Bag of Silver Coins,3,50000,,400,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
677,White_Gold_Coin,Platinum Coin,3,2000,,40,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{} 677,White_Gold_Coin,Platinum Coin,3,2000,,40,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
678,Poison_Bottle,Poison Bottle,2,5000,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if(Class == Job_Assassin_Cross || Class == Job_Guillotine_Cross || Class == Job_Guillotine_Cross_T) { sc_start SC_DPOISON,60000,0; sc_start SC_ASPDPOTION3,60000,9; } else percentheal -100,-100; },{},{} 678,Poison_Bottle,Poison Bottle,2,5000,,100,,,,,0xFFFFFFFF,63,2,,,,,,{ if(Class == Job_Assassin_Cross || Class == Job_Guillotine_Cross || Class == Job_Guillotine_Cross_T) { sc_start SC_DPOISON,60000,0; sc_start SC_ASPDPOTION3,60000,9; } else percentheal -100,-100; },{},{}

View File

@ -4169,6 +4169,20 @@ they will also have their skills reset upon 'changesex'.
--------------------------------------- ---------------------------------------
*changecharsex({<char_id>});
This command will change the gender of the attached character. If it
was male, it will become female, if it was female, it will become male. The
change will be written to the character server, the player will receive the
message: "Need disconnection to perform change-sex request..." and the player
will be immediately kicked to the login screen. When they log back in, they will
be the opposite sex.
If the character being changed is a Dancer/Gypsy or Bard/Clown class type,
the character will also have their skills reset upon 'changecharsex'.
---------------------------------------
*getexp <base xp>,<job xp>{,<char_id>}; *getexp <base xp>,<job xp>{,<char_id>};
This command will give the invoking character a specified number of base and job This command will give the invoking character a specified number of base and job

View File

@ -125,6 +125,7 @@ CREATE TABLE IF NOT EXISTS `char` (
`unban_time` int(11) unsigned NOT NULL default '0', `unban_time` int(11) unsigned NOT NULL default '0',
`font` tinyint(3) unsigned NOT NULL default '0', `font` tinyint(3) unsigned NOT NULL default '0',
`uniqueitem_counter` int(11) unsigned NOT NULL default '0', `uniqueitem_counter` int(11) unsigned NOT NULL default '0',
`sex` ENUM('M','F','U') NOT NULL default 'U',
PRIMARY KEY (`char_id`), PRIMARY KEY (`char_id`),
UNIQUE KEY `name_key` (`name`), UNIQUE KEY `name_key` (`name`),
KEY `account_id` (`account_id`), KEY `account_id` (`account_id`),
@ -637,6 +638,71 @@ CREATE TABLE IF NOT EXISTS `ragsrvinfo` (
`drop` int(11) unsigned NOT NULL default '0' `drop` int(11) unsigned NOT NULL default '0'
) ENGINE=MyISAM; ) ENGINE=MyISAM;
--
-- Table structure for `db_roulette`
--
CREATE TABLE `db_roulette` (
`level` smallint(5) unsigned NOT NULL,
`item_id` smallint(5) unsigned NOT NULL,
`amount` smallint(5) unsigned NOT NULL DEFAULT '1',
`flag` smallint(5) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`level`,`item_id`)
) ENGINE=MyISAM;
-- ----------------------------
-- Records of db_roulette
-- ----------------------------
-- Info: http://ro.gnjoy.com/news/update/View.asp?seq=157&curpage=1
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 678, 1, 0 ); -- Poison_Bottle
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 604, 1, 0 ); -- Branch_Of_Dead_Tree
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 522, 1, 0 ); -- Fruit_Of_Mastela
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 671, 1, 0 ); -- Old_Ore_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 12523, 1, 0 ); -- E_Inc_Agi_10_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 985, 1, 0 ); -- Elunium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 984, 1, 0 ); -- Oridecon
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 603, 1, 0 ); -- Old_Blue_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 608, 1, 0 ); -- Seed_Of_Yggdrasil
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 607, 1, 0 ); -- Yggdrasilberry
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 12522, 1, 0 ); -- E_Blessing_10_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 6223, 1, 0 ); -- Carnium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 6224, 1, 0 ); -- Bradium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 12108, 1, 0 ); -- Bundle_Of_Magic_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 617, 1, 0 ); -- Old_Violet_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 12514, 1, 0 ); -- E_Abrasive
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 7444, 1, 0 ); -- Treasure_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 969, 1, 0 ); -- Gold
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 616, 1, 0 ); -- Old_Card_Album
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 12516, 1, 0 ); -- E_Small_Life_Potion
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 22777, 1, 0 ); -- Gift_Buff_Set
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 6231, 1, 0 ); -- Guarantee_Weapon_6Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 12246, 1, 0 ); -- Magic_Card_Album
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 12263, 1, 0 ); -- Comp_Battle_Manual
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 671, 1, 0 ); -- Potion_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 6235, 1, 0 ); -- Guarantee_Armor_6Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 12766, 1, 0 ); -- Reward_Job_BM25
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 6234, 1, 0 ); -- Guarantee_Armor_7Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 6233, 1, 0 ); -- Guarantee_Armor_8Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 6233, 1, 0 ); -- Guarantee_Armor_8Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 6233, 1, 0 ); -- Guarantee_Armor_8Up // KRO lists this twice
-- --
-- Table structure for table `skill` -- Table structure for table `skill`
-- --

View File

@ -0,0 +1,63 @@
ALTER TABLE `char` ADD COLUMN `sex` ENUM('M','F','U') NOT NULL default 'U';
CREATE TABLE `db_roulette` (
`level` smallint(5) unsigned NOT NULL,
`item_id` smallint(5) unsigned NOT NULL,
`amount` smallint(5) unsigned NOT NULL DEFAULT '1',
`flag` smallint(5) unsigned NOT NULL DEFAULT '1',
PRIMARY KEY (`level`,`item_id`)
) ENGINE=MyISAM;
-- ----------------------------
-- Records of db_roulette
-- ----------------------------
-- Info: http://ro.gnjoy.com/news/update/View.asp?seq=157&curpage=1
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 678, 1, 0 ); -- Poison_Bottle
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 604, 1, 0 ); -- Branch_Of_Dead_Tree
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 522, 1, 0 ); -- Fruit_Of_Mastela
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 671, 1, 0 ); -- Old_Ore_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 12523, 1, 0 ); -- E_Inc_Agi_10_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 985, 1, 0 ); -- Elunium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 1, 984, 1, 0 ); -- Oridecon
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 603, 1, 0 ); -- Old_Blue_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 608, 1, 0 ); -- Seed_Of_Yggdrasil
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 607, 1, 0 ); -- Yggdrasilberry
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 12522, 1, 0 ); -- E_Blessing_10_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 6223, 1, 0 ); -- Carnium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 2, 6224, 1, 0 ); -- Bradium
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 12108, 1, 0 ); -- Bundle_Of_Magic_Scroll
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 617, 1, 0 ); -- Old_Violet_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 12514, 1, 0 ); -- E_Abrasive
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 7444, 1, 0 ); -- Treasure_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 3, 969, 1, 0 ); -- Gold
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 675, 1, 1 ); -- Silver_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 671, 1, 0 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 616, 1, 0 ); -- Old_Card_Album
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 12516, 1, 0 ); -- E_Small_Life_Potion
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 22777, 1, 0 ); -- Gift_Buff_Set
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 4, 6231, 1, 0 ); -- Guarantee_Weapon_6Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 12246, 1, 0 ); -- Magic_Card_Album
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 12263, 1, 0 ); -- Comp_Battle_Manual
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 671, 1, 0 ); -- Potion_Box
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 5, 6235, 1, 0 ); -- Guarantee_Armor_6Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 12766, 1, 0 ); -- Reward_Job_BM25
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 6234, 1, 0 ); -- Guarantee_Armor_7Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 6, 6233, 1, 0 ); -- Guarantee_Armor_8Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 671, 1, 1 ); -- Gold_Coin
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 6233, 1, 0 ); -- Guarantee_Armor_8Up
INSERT INTO `db_roulette`(`level`, `item_id`, `amount`, `flag` ) VALUES ( 7, 6233, 1, 0 ); -- Guarantee_Armor_8Up // KRO lists this twice

View File

@ -0,0 +1 @@
ALTER TABLE `picklog` MODIFY `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y') NOT NULL default 'P',

View File

@ -837,6 +837,54 @@ int char_inventory_to_sql(const struct item items[], int max, int id) {
return errors; return errors;
} }
/**
* Returns the correct gender ID for the given character and enum value.
*
* If the per-character sex is defined but not supported by the current packetver, the database entries are corrected.
*
* @param sd Character data, if available.
* @param p Character status.
* @param sex Character sex (database enum)
*
* @retval SEX_MALE if the per-character sex is male
* @retval SEX_FEMALE if the per-character sex is female
* @retval 99 if the per-character sex is not defined or the current PACKETVER doesn't support it.
*/
int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charstatus *p, char sex)
{
#if PACKETVER >= 20141016
(void)sd; (void)p; // Unused
switch (sex) {
case 'M':
return SEX_MALE;
case 'F':
return SEX_FEMALE;
case 'U':
default:
return 99;
}
#else
if (sex == 'M' || sex == 'F') {
if (!sd) {
// sd is not available, there isn't much we can do. Just return and print a warning.
ShowWarning("Character '%s' (CID: %d, AID: %d) has sex '%c', but PACKETVER does not support per-character sex. Defaulting to 'U'.\n",
p->name, p->char_id, p->account_id, sex);
return 99;
}
if ((sex == 'M' && sd->sex == SEX_FEMALE)
|| (sex == 'F' && sd->sex == SEX_MALE)) {
ShowWarning("Changing sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id);
chlogif_parse_ackchangecharsex(p->char_id, sd->sex);
} else {
ShowInfo("Resetting sex of character '%s' (CID: %d, AID: %d) to 'U' due to incompatible PACKETVER.\n", p->name, p->char_id, p->account_id);
}
if (SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `sex` = 'U' WHERE `char_id` = '%d'", schema_config.char_db, p->char_id)) {
Sql_ShowDebug(sql_handle);
}
}
return 99;
#endif
}
int char_mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p); int char_mmo_char_tobuf(uint8* buf, struct mmo_charstatus* p);
@ -847,16 +895,16 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
struct mmo_charstatus p; struct mmo_charstatus p;
int j = 0, i; int j = 0, i;
char last_map[MAP_NAME_LENGTH_EXT]; char last_map[MAP_NAME_LENGTH_EXT];
char sex[2];
stmt = SqlStmt_Malloc(sql_handle); stmt = SqlStmt_Malloc(sql_handle);
if( stmt == NULL ) if( stmt == NULL ) {
{
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
return 0; return 0;
} }
memset(&p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
for( i = 0; i < MAX_CHARS; i++ ){ for( i = 0; i < MAX_CHARS; i++ ) {
sd->found_char[i] = -1; sd->found_char[i] = -1;
sd->unban_time[i] = 0; sd->unban_time[i] = 0;
} }
@ -867,7 +915,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
"`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`,"
"`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`hair`,`hair_color`,"
"`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`," "`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`,"
"`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`" "`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`"
" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS) " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.char_db, sd->account_id, MAX_CHARS)
|| SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_Execute(stmt)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL)
@ -910,6 +958,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_LONG, &p.unban_time, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 37, SQLDT_LONG, &p.unban_time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_UCHAR, &p.font, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 38, SQLDT_UCHAR, &p.font, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_UINT, &p.uniqueitem_counter, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 39, SQLDT_UINT, &p.uniqueitem_counter, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 40, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
) )
{ {
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
@ -922,6 +971,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
p.last_point.map = mapindex_name2id(last_map); p.last_point.map = mapindex_name2id(last_map);
sd->found_char[p.slot] = p.char_id; sd->found_char[p.slot] = p.char_id;
sd->unban_time[p.slot] = p.unban_time; sd->unban_time[p.slot] = p.unban_time;
p.sex = char_mmo_gender(sd, &p, sex[0]);
j += char_mmo_char_tobuf(WBUFP(buf, j), &p); j += char_mmo_char_tobuf(WBUFP(buf, j), &p);
// Addon System // Addon System
@ -954,6 +1004,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
int hotkey_num; int hotkey_num;
#endif #endif
StringBuf msg_buf; StringBuf msg_buf;
char sex[2];
memset(p, 0, sizeof(struct mmo_charstatus)); memset(p, 0, sizeof(struct mmo_charstatus));
@ -973,7 +1024,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
"`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`," "`status_point`,`skill_point`,`option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`homun_id`,`elemental_id`,`hair`,"
"`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`," "`hair_color`,`clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`last_x`,`last_y`,"
"`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`," "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`,"
"`unban_time`,`font`,`uniqueitem_counter`" "`unban_time`,`font`,`uniqueitem_counter`,`sex`"
" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db) " FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
|| SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
|| SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_Execute(stmt)
@ -1033,6 +1084,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_LONG, &p->unban_time, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_LONG, &p->unban_time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_UCHAR, &p->font, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_UCHAR, &p->font, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 56, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
) )
{ {
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
@ -1045,6 +1097,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
SqlStmt_Free(stmt); SqlStmt_Free(stmt);
return 0; return 0;
} }
p->sex = char_mmo_gender(NULL, p, sex[0]);
p->last_point.map = mapindex_name2id(last_map); p->last_point.map = mapindex_name2id(last_map);
p->save_point.map = mapindex_name2id(save_map); p->save_point.map = mapindex_name2id(save_map);
@ -1669,7 +1722,13 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p)
WBUFW(buf,48) = min(p->max_sp, INT16_MAX); WBUFW(buf,48) = min(p->max_sp, INT16_MAX);
WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed; WBUFW(buf,50) = DEFAULT_WALK_SPEED; // p->speed;
WBUFW(buf,52) = p->class_; WBUFW(buf,52) = p->class_;
#if PACKETVER >= 20141022
WBUFL(buf,54) = p->hair;
offset+=2;
buf = WBUFP(buffer,offset);
#else
WBUFW(buf,54) = p->hair; WBUFW(buf,54) = p->hair;
#endif
//When the weapon is sent and your option is riding, the client crashes on login!? //When the weapon is sent and your option is riding, the client crashes on login!?
WBUFW(buf,56) = p->option&(0x20|0x80000|0x100000|0x200000|0x400000|0x800000|0x1000000|0x2000000|0x4000000|0x8000000) ? 0 : p->weapon; WBUFW(buf,56) = p->option&(0x20|0x80000|0x100000|0x200000|0x400000|0x800000|0x1000000|0x2000000|0x4000000|0x8000000) ? 0 : p->weapon;
@ -1723,6 +1782,10 @@ int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p)
WBUFL(buf,136) = ( p->rename > 0 ) ? 1 : 0; // (0 = disabled, otherwise displays "Add-Ons" sidebar) WBUFL(buf,136) = ( p->rename > 0 ) ? 1 : 0; // (0 = disabled, otherwise displays "Add-Ons" sidebar)
offset += 4; offset += 4;
#endif #endif
#if PACKETVER >= 20141016
WBUFB(buf,140) = p->sex;// sex - (0 = female, 1 = male, 99 = logindefined)
offset += 1;
#endif
#endif #endif
return 106+offset; return 106+offset;
@ -2890,7 +2953,7 @@ int do_init(int argc, char **argv)
add_timer_func_list(char_online_data_cleanup, "online_data_cleanup"); add_timer_func_list(char_online_data_cleanup, "online_data_cleanup");
add_timer_interval(gettick() + 1000, char_online_data_cleanup, 0, 0, 600 * 1000); add_timer_interval(gettick() + 1000, char_online_data_cleanup, 0, 0, 600 * 1000);
//chek db tables //check db tables
if(charserv_config.char_check_db && char_checkdb() == 0){ if(charserv_config.char_check_db && char_checkdb() == 0){
ShowFatalError("char : A tables is missing in sql-server, please fix it, see (sql-files main.sql for structure) \n"); ShowFatalError("char : A tables is missing in sql-server, please fix it, see (sql-files main.sql for structure) \n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

View File

@ -219,7 +219,7 @@ extern struct fame_list chemist_fame_list[MAX_FAME_LIST];
extern struct fame_list taekwon_fame_list[MAX_FAME_LIST]; extern struct fame_list taekwon_fame_list[MAX_FAME_LIST];
#define DEFAULT_AUTOSAVE_INTERVAL 300*1000 #define DEFAULT_AUTOSAVE_INTERVAL 300*1000
#define MAX_CHAR_BUF 144 //Max size (for WFIFOHEAD calls) #define MAX_CHAR_BUF 150 //Max size (for WFIFOHEAD calls)
int char_search_mapserver(unsigned short map, uint32 ip, uint16 port); int char_search_mapserver(unsigned short map, uint32 ip, uint16 port);
int char_lan_subnetcheck(uint32 ip); int char_lan_subnetcheck(uint32 ip);
@ -233,6 +233,7 @@ void char_set_all_offline(int id);
void char_disconnect_player(uint32 account_id); void char_disconnect_player(uint32 account_id);
int char_chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr_t data); int char_chardb_waiting_disconnect(int tid, unsigned int tick, int id, intptr_t data);
int char_mmo_gender(const struct char_session_data *sd, const struct mmo_charstatus *p, char sex);
int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p); int char_mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p);
int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p); int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p);
int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_everything); int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_everything);

View File

@ -711,7 +711,8 @@ int chclif_parse_charselect(int fd, struct char_session_data* sd,uint32 ipl){
//Have to switch over to the DB instance otherwise data won't propagate [Kevin] //Have to switch over to the DB instance otherwise data won't propagate [Kevin]
cd = (struct mmo_charstatus *)idb_get(char_db_, char_id); cd = (struct mmo_charstatus *)idb_get(char_db_, char_id);
cd->sex = sd->sex; if (cd->sex == 99)
cd->sex = sd->sex;
if (charserv_config.log_char) { if (charserv_config.log_char) {
char esc_name[NAME_LENGTH*2+1]; char esc_name[NAME_LENGTH*2+1];

View File

@ -340,76 +340,79 @@ int chlogif_parse_keepalive(int fd, struct char_session_data* sd){
return 1; return 1;
} }
int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd){ /**
* Performs the necessary operations when changing a character's sex, such as
* correcting the job class and unequipping items, and propagating the
* information to the guild data.
*
* @param sex The new sex (SEX_MALE or SEX_FEMALE).
* @param acc The character's account ID.
* @param char_id The character ID.
* @param class_ The character's current job class.
* @param guild_id The character's guild ID.
*/
void chlogif_parse_change_sex_sub(int sex, int acc, int char_id, int class_, int guild_id)
{
// job modification
if (class_ == JOB_BARD || class_ == JOB_DANCER)
class_ = (sex == SEX_MALE ? JOB_BARD : JOB_DANCER);
else if (class_ == JOB_CLOWN || class_ == JOB_GYPSY)
class_ = (sex == SEX_MALE ? JOB_CLOWN : JOB_GYPSY);
else if (class_ == JOB_BABY_BARD || class_ == JOB_BABY_DANCER)
class_ = (sex == SEX_MALE ? JOB_BABY_BARD : JOB_BABY_DANCER);
else if (class_ == JOB_MINSTREL || class_ == JOB_WANDERER)
class_ = (sex == SEX_MALE ? JOB_MINSTREL : JOB_WANDERER);
else if (class_ == JOB_MINSTREL_T || class_ == JOB_WANDERER_T)
class_ = (sex == SEX_MALE ? JOB_MINSTREL_T : JOB_WANDERER_T);
else if (class_ == JOB_BABY_MINSTREL || class_ == JOB_BABY_WANDERER)
class_ = (sex == SEX_MALE ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
else if (class_ == JOB_KAGEROU || class_ == JOB_OBORO)
class_ = (sex == SEX_MALE ? JOB_KAGEROU : JOB_OBORO);
if (SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `equip` = '0' WHERE `char_id` = '%d'", schema_config.inventory_db, char_id))
Sql_ShowDebug(sql_handle);
if (SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class` = '%d', `weapon` = '0', `shield` = '0', `head_top` = '0', `head_mid` = '0', `head_bottom` = '0' WHERE `char_id` = '%d'", schema_config.char_db, class_, char_id))
Sql_ShowDebug(sql_handle);
if (guild_id) // If there is a guild, update the guild_member data [Skotlex]
inter_guild_sex_changed(guild_id, acc, char_id, sex);
}
int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd)
{
if (RFIFOREST(fd) < 7) if (RFIFOREST(fd) < 7)
return 0; return 0;
{ {
unsigned char buf[7]; unsigned char buf[7];
int acc = RFIFOL(fd,2); int acc = RFIFOL(fd,2);
int sex = RFIFOB(fd,6); int sex = RFIFOB(fd,6);
RFIFOSKIP(fd,7); RFIFOSKIP(fd,7);
if( acc > 0 ) if (acc > 0) { // TODO: Is this even possible?
{// TODO: Is this even possible? unsigned char i;
uint32 char_id[MAX_CHARS]; int char_id = 0, class_ = 0, guild_id = 0;
int class_[MAX_CHARS]; DBMap* auth_db = char_get_authdb();
int guild_id[MAX_CHARS];
unsigned char num, i;
char* data;
DBMap* auth_db = char_get_authdb();
struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc); struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc);
if( node != NULL ) SqlStmt *stmt;
if (node != NULL)
node->sex = sex; node->sex = sex;
// get characters // get characters
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `char_id`,`class`,`guild_id` FROM `%s` WHERE `account_id` = '%d'", schema_config.char_db, acc) ) stmt = SqlStmt_Malloc(sql_handle);
Sql_ShowDebug(sql_handle); if (SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `char_id`, `class`, `guild_id` FROM `%s` WHERE `account_id` = '%d'", schema_config.char_db, acc) || SqlStmt_Execute(stmt)) {
for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) SqlStmt_ShowDebug(stmt);
{ SqlStmt_Free(stmt);
Sql_GetData(sql_handle, 0, &data, NULL); char_id[i] = atoi(data);
Sql_GetData(sql_handle, 1, &data, NULL); class_[i] = atoi(data);
Sql_GetData(sql_handle, 2, &data, NULL); guild_id[i] = atoi(data);
} }
num = i;
for( i = 0; i < num; ++i )
{
if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER ||
class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY ||
class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER ||
class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER ||
class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T ||
class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER ||
class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO )
{
// job modification
if( class_[i] == JOB_BARD || class_[i] == JOB_DANCER )
class_[i] = (sex ? JOB_BARD : JOB_DANCER);
else if( class_[i] == JOB_CLOWN || class_[i] == JOB_GYPSY )
class_[i] = (sex ? JOB_CLOWN : JOB_GYPSY);
else if( class_[i] == JOB_BABY_BARD || class_[i] == JOB_BABY_DANCER )
class_[i] = (sex ? JOB_BABY_BARD : JOB_BABY_DANCER);
else if( class_[i] == JOB_MINSTREL || class_[i] == JOB_WANDERER )
class_[i] = (sex ? JOB_MINSTREL : JOB_WANDERER);
else if( class_[i] == JOB_MINSTREL_T || class_[i] == JOB_WANDERER_T )
class_[i] = (sex ? JOB_MINSTREL_T : JOB_WANDERER_T);
else if( class_[i] == JOB_BABY_MINSTREL || class_[i] == JOB_BABY_WANDERER )
class_[i] = (sex ? JOB_BABY_MINSTREL : JOB_BABY_WANDERER);
else if( class_[i] == JOB_KAGEROU || class_[i] == JOB_OBORO )
class_[i] = (sex ? JOB_KAGEROU : JOB_OBORO);
}
if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d', `weapon`='0', `shield`='0', `head_top`='0', `head_mid`='0', `head_bottom`='0' WHERE `char_id`='%d'", schema_config.char_db, class_[i], char_id[i]) ) SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &char_id, 0, NULL, NULL);
Sql_ShowDebug(sql_handle); SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT, &class_, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 2, SQLDT_INT, &guild_id, 0, NULL, NULL);
if( guild_id[i] )// If there is a guild, update the guild_member data [Skotlex] for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i) {
inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex); chlogif_parse_change_sex_sub(sex, acc, char_id, class_, guild_id);
} }
Sql_FreeResult(sql_handle); SqlStmt_Free(stmt);
// disconnect player if online on char-server
char_disconnect_player(acc);
} }
// notify all mapservers about this change // notify all mapservers about this change
@ -421,6 +424,54 @@ int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd){
return 1; return 1;
} }
/**
* Changes a character's sex.
* The information is updated on database, and the character is kicked if it
* currently is online.
*
* @param char_id The character's ID.
* @param sex The new sex.
* @retval 0 in case of success.
* @retval 1 in case of failure.
*/
int chlogif_parse_ackchangecharsex(int char_id, int sex)
{
int class_ = 0, guild_id = 0, account_id = 0;
unsigned char buf[7];
char *data;
// get character data
if (SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`,`class`,`guild_id` FROM `%s` WHERE `char_id` = '%d'", schema_config.char_db, char_id)) {
Sql_ShowDebug(sql_handle);
return 1;
}
if (Sql_NumRows(sql_handle) != 1 || SQL_ERROR == Sql_NextRow(sql_handle)) {
Sql_FreeResult(sql_handle);
return 1;
}
Sql_GetData(sql_handle, 0, &data, NULL); account_id = atoi(data);
Sql_GetData(sql_handle, 1, &data, NULL); class_ = atoi(data);
Sql_GetData(sql_handle, 2, &data, NULL); guild_id = atoi(data);
Sql_FreeResult(sql_handle);
if (SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `sex` = '%c' WHERE `char_id` = '%d'", schema_config.char_db, sex == SEX_MALE ? 'M' : 'F', char_id)) {
Sql_ShowDebug(sql_handle);
return 1;
}
chlogif_parse_change_sex_sub(sex, account_id, char_id, class_, guild_id);
// disconnect player if online on char-server
char_disconnect_player(account_id);
// notify all mapservers about this change
WBUFW(buf,0) = 0x2b0d;
WBUFL(buf,2) = account_id;
WBUFB(buf,6) = sex;
chmapif_sendall(buf, 7);
return 0;
}
int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd){ int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd){
if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2))
return 0; return 0;

View File

@ -30,7 +30,9 @@ int chlogif_parse_ackconnect(int fd, struct char_session_data* sd);
int chlogif_parse_ackaccreq(int fd, struct char_session_data* sd); int chlogif_parse_ackaccreq(int fd, struct char_session_data* sd);
int chlogif_parse_reqaccdata(int fd, struct char_session_data* sd); int chlogif_parse_reqaccdata(int fd, struct char_session_data* sd);
int chlogif_parse_keepalive(int fd, struct char_session_data* sd); int chlogif_parse_keepalive(int fd, struct char_session_data* sd);
void chlogif_parse_change_sex_sub(int sex, int acc, int char_id, int class_, int guild_id);
int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd); int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd);
int chlogif_parse_ackchangecharsex(int char_id, int sex);
int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd); int chlogif_parse_ackacc2req(int fd, struct char_session_data* sd);
int chlogif_parse_accbannotification(int fd, struct char_session_data* sd); int chlogif_parse_accbannotification(int fd, struct char_session_data* sd);
int chlogif_parse_askkick(int fd, struct char_session_data* sd); int chlogif_parse_askkick(int fd, struct char_session_data* sd);

View File

@ -717,7 +717,7 @@ int chmapif_parse_reqnewemail(int fd){
/** /**
* Forward a change of status for account to login-serv * Forward a change of status for account to login-serv
* @param fd: wich fd to parse from * @param fd: which fd to parse from
* @return : 0 not enough data received, 1 success * @return : 0 not enough data received, 1 success
*/ */
int chmapif_parse_fwlog_changestatus(int fd){ int chmapif_parse_fwlog_changestatus(int fd){
@ -730,14 +730,19 @@ int chmapif_parse_fwlog_changestatus(int fd){
int aid = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request) int aid = RFIFOL(fd,2); // account_id of who ask (-1 if server itself made this request)
const char* name = (char*)RFIFOP(fd,6); // name of the target character const char* name = (char*)RFIFOP(fd,6); // name of the target character
int operation = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex, 6-vip int operation = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex, 6-vip, 7-changecharsex
int32 timediff = RFIFOL(fd,32); int32 timediff = 0;
int val1 = RFIFOL(fd,36); int val1 = 0, sex = SEX_MALE;
//int val2 = RFIFOL(fd,40); // Since BankVault is moved out, this value is unused for now
if (operation == 2) {
timediff = RFIFOL(fd, 32);
val1 = RFIFOL(fd, 36);
} else if (operation == 7)
sex = RFIFOB(fd, 32);
RFIFOSKIP(fd,44); RFIFOSKIP(fd,44);
Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH)); Sql_EscapeStringLen(sql_handle, esc_name, name, strnlen(name, NAME_LENGTH));
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) ) if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `account_id`, `char_id` FROM `%s` WHERE `name` = '%s'", schema_config.char_db, esc_name) )
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);
else if( Sql_NumRows(sql_handle) == 0 ) { else if( Sql_NumRows(sql_handle) == 0 ) {
result = 1; // 1-player not found result = 1; // 1-player not found
@ -746,10 +751,12 @@ int chmapif_parse_fwlog_changestatus(int fd){
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);
result = 1; result = 1;
} else { } else {
int t_aid; //targit account id int t_aid; // target account id
int t_cid; // target char id
char* data; char* data;
Sql_GetData(sql_handle, 0, &data, NULL); t_aid = atoi(data); Sql_GetData(sql_handle, 0, &data, NULL); t_aid = atoi(data);
Sql_GetData(sql_handle, 0, &data, NULL); t_cid = atoi(data);
Sql_FreeResult(sql_handle); Sql_FreeResult(sql_handle);
if(!chlogif_isconnected()) if(!chlogif_isconnected())
@ -794,16 +801,24 @@ int chmapif_parse_fwlog_changestatus(int fd){
WFIFOL(login_fd,2) = t_aid; WFIFOL(login_fd,2) = t_aid;
WFIFOSET(login_fd,6); WFIFOSET(login_fd,6);
break; break;
case 6: case 6: // vip
answer = (val1&4); // vip_req val1=type, &1 login send return, &2 update timestamp, &4 map send answer answer = (val1&4); // vip_req val1=type, &1 login send return, &2 update timestamp, &4 map send answer
chlogif_reqvipdata(t_aid, val1, timediff, fd); chlogif_reqvipdata(t_aid, val1, timediff, fd);
break; break;
case 7: // changecharsex
answer = false;
WFIFOHEAD(login_fd,6);
WFIFOW(login_fd,0) = 0x2727;
WFIFOL(login_fd,2) = t_aid;
WFIFOL(login_fd,6) = t_cid;
WFIFOSET(login_fd,10);
break;
} //end switch operation } //end switch operation
} //login is connected } //login is connected
} }
// send answer if a player asks, not if the server asks // send answer if a player asks, not if the server asks
if( aid != -1 && answer) { // Don't send answer for changesex if( aid != -1 && answer) { // Don't send answer for changesex/changecharsex
WFIFOHEAD(fd,34); WFIFOHEAD(fd,34);
WFIFOW(fd, 0) = 0x2b0f; WFIFOW(fd, 0) = 0x2b0f;
WFIFOL(fd, 2) = aid; WFIFOL(fd, 2) = aid;
@ -817,7 +832,7 @@ int chmapif_parse_fwlog_changestatus(int fd){
} }
/** /**
* Transmit the acknolegement of divorce of partner_id1 and partner_id2 * Transmit the acknowledgement of divorce of partner_id1 and partner_id2
* Update the list associated and transmit the new ranking * Update the list associated and transmit the new ranking
* @param partner_id1: char id1 divorced * @param partner_id1: char id1 divorced
* @param partner_id2: char id2 divorced * @param partner_id2: char id2 divorced
@ -1047,7 +1062,8 @@ int chmapif_parse_reqauth(int fd, int id){
node->ip == ip*/ ) node->ip == ip*/ )
{// auth ok {// auth ok
uint16 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25; uint16 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25;
cd->sex = sex; if (cd->sex == 99)
cd->sex = sex;
WFIFOHEAD(fd,mmo_charstatus_len); WFIFOHEAD(fd,mmo_charstatus_len);
WFIFOW(fd,0) = 0x2afd; WFIFOW(fd,0) = 0x2afd;

View File

@ -378,7 +378,11 @@ uint32 date2version(int date) {
else if(date < 20130717) return 43; else if(date < 20130717) return 43;
else if(date < 20130807) return 44; else if(date < 20130807) return 44;
else if(date < 20131223) return 45; else if(date < 20131223) return 45;
else if(date >= 20131223) return 46; else if(date < 20140212) return 46;
else if(date < 20140613) return 47;
else if(date < 20141016) return 48;
else if(date < 20141022) return 49;
else if(date >= 20141022) return 50;
else return 30; //default else return 30; //default
} }

View File

@ -6769,17 +6769,43 @@ ACMD_FUNC(uptime)
/*========================================== /*==========================================
* @changesex <sex> * @changesex <sex>
* => Changes one's sex. Argument sex can be 0 or 1, m or f, male or female. * => Changes one's account sex. Argument sex can be 0 or 1, m or f, male or female.
*------------------------------------------*/ *------------------------------------------*/
ACMD_FUNC(changesex) ACMD_FUNC(changesex)
{ {
int i; int i;
nullpo_retr(-1, sd); nullpo_retr(-1, sd);
pc_resetskill(sd,4); pc_resetskill(sd,4);
// to avoid any problem with equipment and invalid sex, equipment is unequiped. // to avoid any problem with equipment and invalid sex, equipment is unequiped.
for( i=0; i<EQI_MAX; i++ ) for (i = 0; i < EQI_MAX; i++) {
if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3); if (sd->equip_index[i] >= 0)
chrif_changesex(sd); pc_unequipitem(sd, sd->equip_index[i], 3);
}
chrif_changesex(sd, true);
return 0;
}
/*==========================================
* @changecharsex <sex>
* => Changes one's character sex. Argument sex can be 0 or 1, m or f, male or female.
*------------------------------------------*/
ACMD_FUNC(changecharsex)
{
int i;
nullpo_retr(-1, sd);
pc_resetskill(sd,4);
// to avoid any problem with equipment and invalid sex, equipment is unequiped.
for (i = 0; i < EQI_MAX; i++) {
if (sd->equip_index[i] >= 0)
pc_unequipitem(sd, sd->equip_index[i], 3);
}
chrif_changesex(sd, false);
return 0; return 0;
} }
@ -9886,6 +9912,7 @@ void atcommand_basecommands(void) {
ACMD_DEF(clearweather), ACMD_DEF(clearweather),
ACMD_DEF(uptime), ACMD_DEF(uptime),
ACMD_DEF(changesex), ACMD_DEF(changesex),
ACMD_DEF(changecharsex),
ACMD_DEF(mute), ACMD_DEF(mute),
ACMD_DEF(refresh), ACMD_DEF(refresh),
ACMD_DEF(refreshall), ACMD_DEF(refreshall),

View File

@ -8168,6 +8168,7 @@ static const struct _battle_data {
{ "homunculus_evo_intimacy_need", &battle_config.homunculus_evo_intimacy_need, 91100, 0, INT_MAX, }, { "homunculus_evo_intimacy_need", &battle_config.homunculus_evo_intimacy_need, 91100, 0, INT_MAX, },
{ "homunculus_evo_intimacy_reset", &battle_config.homunculus_evo_intimacy_reset, 1000, 0, INT_MAX, }, { "homunculus_evo_intimacy_reset", &battle_config.homunculus_evo_intimacy_reset, 1000, 0, INT_MAX, },
{ "monster_loot_search_type", &battle_config.monster_loot_search_type, 1, 0, 1, }, { "monster_loot_search_type", &battle_config.monster_loot_search_type, 1, 0, 1, },
{ "feature.roulette", &battle_config.feature_roulette, 1, 0, 1, },
}; };
#ifndef STATS_OPT_OUT #ifndef STATS_OPT_OUT
@ -8396,6 +8397,13 @@ void battle_adjust_conf()
} }
#endif #endif
#if PACKETVER < 20141022
if (battle_config.feature_roulette) {
ShowWarning("conf/battle/feature.conf roulette is enabled but it requires PACKETVER 2014-10-22 or newer, disabling...\n");
battle_config.feature_roulette = 0;
}
#endif
#ifndef CELL_NOSTACK #ifndef CELL_NOSTACK
if (battle_config.custom_cell_stack_limit != 1) if (battle_config.custom_cell_stack_limit != 1)
ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");

View File

@ -590,6 +590,7 @@ extern struct Battle_Config
int homunculus_evo_intimacy_need; int homunculus_evo_intimacy_need;
int homunculus_evo_intimacy_reset; int homunculus_evo_intimacy_reset;
int monster_loot_search_type; int monster_loot_search_type;
int feature_roulette;
} battle_config; } battle_config;
void do_init_battle(void); void do_init_battle(void);

View File

@ -63,7 +63,7 @@ static const int packet_len_table[0x3d] = { // U - used, F - free
//2b0a: Outgoing, chrif_skillcooldown_request -> requesting the list of skillcooldown for char //2b0a: Outgoing, chrif_skillcooldown_request -> requesting the list of skillcooldown for char
//2b0b: Incoming, chrif_skillcooldown_load -> received the list of cooldown for char //2b0b: Incoming, chrif_skillcooldown_load -> received the list of cooldown for char
//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...' //2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
//2b0d: Incoming, chrif_changedsex -> 'Change sex of acc XY' //2b0d: Incoming, chrif_changedsex -> 'Change sex of acc XY' (or char)
//2b0e: Outgoing, chrif_req_login_operation -> 'Do some operations (change sex, ban / unban etc)' //2b0e: Outgoing, chrif_req_login_operation -> 'Do some operations (change sex, ban / unban etc)'
//2b0f: Incoming, chrif_ack_login_req -> 'answer of the 2b0e' //2b0f: Incoming, chrif_ack_login_req -> 'answer of the 2b0e'
//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them' //2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them'
@ -864,17 +864,19 @@ int chrif_req_login_operation(int aid, const char* character_name, unsigned shor
/** /**
* S 2b0e <accid>.l <name>.24B <operation_type>.w <timediff>L <val1>L <val2>L * S 2b0e <accid>.l <name>.24B <operation_type>.w <timediff>L <val1>L <val2>L
* Send an account modification (changesex) request to the login server (via char server). * Send a sex change (for account or character) request to the login server (via char server).
* @sd : Player requesting operation * @sd : Player requesting operation
*/ */
int chrif_changesex(struct map_session_data *sd) { int chrif_changesex(struct map_session_data *sd, bool change_account) {
chrif_check(-1); chrif_check(-1);
WFIFOHEAD(char_fd,44); WFIFOHEAD(char_fd,44);
WFIFOW(char_fd,0) = 0x2b0e; WFIFOW(char_fd,0) = 0x2b0e;
WFIFOL(char_fd,2) = sd->status.account_id; WFIFOL(char_fd,2) = sd->status.account_id;
safestrncpy((char*)WFIFOP(char_fd,6), sd->status.name, NAME_LENGTH); safestrncpy((char*)WFIFOP(char_fd,6), sd->status.name, NAME_LENGTH);
WFIFOW(char_fd,30) = CHRIF_OP_LOGIN_CHANGESEX; WFIFOW(char_fd,30) = (change_account ? CHRIF_OP_LOGIN_CHANGESEX : CHRIF_OP_LOGIN_CHANGECHARSEX);
if (!change_account)
WFIFOB(char_fd,32) = sd->status.sex == SEX_MALE ? SEX_FEMALE : SEX_MALE;
WFIFOSET(char_fd,44); WFIFOSET(char_fd,44);
clif_displaymessage(sd->fd, msg_txt(sd,408)); //"Need disconnection to perform change-sex request..." clif_displaymessage(sd->fd, msg_txt(sd,408)); //"Need disconnection to perform change-sex request..."
@ -917,6 +919,9 @@ static void chrif_ack_login_req(int aid, const char* player_name, uint16 type, u
case CHRIF_OP_LOGIN_UNBLOCK: case CHRIF_OP_LOGIN_UNBLOCK:
case CHRIF_OP_LOGIN_UNBAN: case CHRIF_OP_LOGIN_UNBAN:
case CHRIF_OP_LOGIN_CHANGESEX: case CHRIF_OP_LOGIN_CHANGESEX:
case CHRIF_OP_LOGIN_CHANGECHARSEX:
if (type == CHRIF_OP_LOGIN_CHANGECHARSEX)
type--; // So we don't have to create a new msgstring.
snprintf(action,25,"%s",msg_txt(sd,427+type)); //block|ban|unblock|unban|change the sex of snprintf(action,25,"%s",msg_txt(sd,427+type)); //block|ban|unblock|unban|change the sex of
break; break;
case CHRIF_OP_LOGIN_VIP: case CHRIF_OP_LOGIN_VIP:
@ -929,7 +934,7 @@ static void chrif_ack_login_req(int aid, const char* player_name, uint16 type, u
break; break;
} }
switch( answer ) { switch (answer) {
case 0: sprintf(output, msg_txt(sd,424), action, NAME_LENGTH, player_name); break; //Login-serv has been asked to %s '%.*s'. case 0: sprintf(output, msg_txt(sd,424), action, NAME_LENGTH, player_name); break; //Login-serv has been asked to %s '%.*s'.
case 1: sprintf(output, msg_txt(sd,425), NAME_LENGTH, player_name); break; case 1: sprintf(output, msg_txt(sd,425), NAME_LENGTH, player_name); break;
case 2: sprintf(output, msg_txt(sd,426), action, NAME_LENGTH, player_name); break; case 2: sprintf(output, msg_txt(sd,426), action, NAME_LENGTH, player_name); break;

View File

@ -27,6 +27,7 @@ enum chrif_req_op {
CHRIF_OP_LOGIN_UNBAN, CHRIF_OP_LOGIN_UNBAN,
CHRIF_OP_LOGIN_CHANGESEX, CHRIF_OP_LOGIN_CHANGESEX,
CHRIF_OP_LOGIN_VIP, CHRIF_OP_LOGIN_VIP,
CHRIF_OP_LOGIN_CHANGECHARSEX,
// Char-server operation // Char-server operation
CHRIF_OP_BAN, CHRIF_OP_BAN,
@ -73,7 +74,7 @@ int chrif_char_offline_nsd(uint32 account_id, uint32 char_id);
int chrif_char_reset_offline(void); int chrif_char_reset_offline(void);
int send_users_tochar(void); int send_users_tochar(void);
int chrif_char_online(struct map_session_data *sd); int chrif_char_online(struct map_session_data *sd);
int chrif_changesex(struct map_session_data *sd); int chrif_changesex(struct map_session_data *sd, bool change_account);
int chrif_chardisconnect(struct map_session_data *sd); int chrif_chardisconnect(struct map_session_data *sd);
int chrif_divorce(int partner_id1, int partner_id2); int chrif_divorce(int partner_id1, int partner_id2);

View File

@ -662,6 +662,9 @@ void clif_authok(struct map_session_data *sd)
WFIFOB(fd,10) = 5; // ignored WFIFOB(fd,10) = 5; // ignored
#if PACKETVER >= 20080102 #if PACKETVER >= 20080102
WFIFOW(fd,11) = sd->status.font; WFIFOW(fd,11) = sd->status.font;
#endif
#if PACKETVER >= 20141016
WFIFOB(fd,13) = sd->status.sex;
#endif #endif
WFIFOSET(fd,packet_len(cmd)); WFIFOSET(fd,packet_len(cmd));
} }
@ -5751,6 +5754,11 @@ void clif_displaymessage(const int fd, const char* mes)
if (fd == 0) if (fd == 0)
; ;
else { else {
#if PACKETVER == 20141022
/** for some reason game client crashes depending on message pattern (only for this packet) **/
/** so we redirect to ZC_NPC_CHAT **/
clif_colormes(fd, color_table[COLOR_DEFAULT], mes);
#else
char *message, *line; char *message, *line;
message = aStrdup(mes); message = aStrdup(mes);
@ -5769,6 +5777,7 @@ void clif_displaymessage(const int fd, const char* mes)
line = strtok(NULL, "\n"); line = strtok(NULL, "\n");
} }
aFree(message); aFree(message);
#endif
} }
} }
@ -6833,6 +6842,14 @@ void clif_openvending(struct map_session_data* sd, int id, struct s_vending* ven
clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]); clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]);
} }
WFIFOSET(fd,WFIFOW(fd,2)); WFIFOSET(fd,WFIFOW(fd,2));
#if PACKETVER >= 20141022
// Should go elsewhere perhaps? It has to be bundled with this however.
WFIFOHEAD(fd, 3);
WFIFOW(fd, 0) = 0xa28;
WFIFOB(fd, 2) = 0; // 1 is failure. Our current responses to failure are working so not yet implemented.
WFIFOSET(fd, 3);
#endif
} }
@ -10114,18 +10131,22 @@ void clif_hotkeys_send(struct map_session_data *sd) {
#ifdef HOTKEY_SAVING #ifdef HOTKEY_SAVING
const int fd = sd->fd; const int fd = sd->fd;
int i; int i;
int offset = 2;
#if PACKETVER < 20090603 #if PACKETVER < 20090603
const int cmd = 0x2b9; const int cmd = 0x2b9;
#else #elif PACKETVER < 20141022
const int cmd = 0x7d9; const int cmd = 0x7d9;
#else
const int cmd = 0xa00;
offset = 3;
#endif #endif
if (!fd) return; if (!fd) return;
WFIFOHEAD(fd, 2+MAX_HOTKEYS*7); WFIFOHEAD(fd, offset + MAX_HOTKEYS * 7);
WFIFOW(fd, 0) = cmd; WFIFOW(fd, 0) = cmd;
for(i = 0; i < MAX_HOTKEYS; i++) { for(i = 0; i < MAX_HOTKEYS; i++) {
WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill WFIFOB(fd, offset + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID WFIFOL(fd, offset + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level WFIFOW(fd, offset + 5 + i * 7) = sd->status.hotkeys[i].lv; // item qty or skill level
} }
WFIFOSET(fd, packet_len(cmd)); WFIFOSET(fd, packet_len(cmd));
#endif #endif
@ -17729,6 +17750,229 @@ void DumpUnknown(int fd,TBL_PC *sd,int cmd,int packet_len)
} }
#endif #endif
/// Roulette System
/// Author: Yommy
/**
* Opens Roulette window
* @param fd
* @param sd
*/
void clif_parse_RouletteOpen(int fd, struct map_session_data* sd)
{
nullpo_retv(sd);
if (!battle_config.feature_roulette) {
clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1497)); //Roulette is disabled
return;
}
WFIFOHEAD(fd,packet_len(0xa1a));
WBUFW(fd,0) = 0xa1a;
WBUFW(fd,2) = 0; // result
WBUFL(fd,3) = 0; // serial
WBUFW(fd,7) = sd->roulette.stage - 1;
WBUFW(fd,8) = (char)sd->roulette.prizeIdx;
WBUFW(fd,9) = -1; // TODO
WBUFL(fd,11) = sd->roulette_point.gold;
WBUFL(fd,15) = sd->roulette_point.silver;
WBUFL(fd,19) = sd->roulette_point.bronze;
WFIFOSET(fd,packet_len(0xa1a));
}
/**
* Generates information to be displayed
* @param fd
* @param sd
*/
void clif_parse_RouletteInfo(int fd, struct map_session_data* sd)
{
unsigned short i, j, count = 0;
int len = 8 + (42 * 8);
nullpo_retv(sd);
if (!battle_config.feature_roulette) {
clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1497)); //Roulette is disabled
return;
}
WFIFOHEAD(fd,len);
WBUFW(fd,0) = 0xa1c;
WBUFW(fd,2) = len;
WBUFL(fd,6) = 1; // serial
for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
for(j = 0; j < MAX_ROULETTE_COLUMNS - i; j++) {
WBUFW(fd,8 + i * 8) = i;
WBUFW(fd,8 + i * 8 + 2) = j;
WBUFW(fd,8 + i * 8 + 4) = rd.nameid[i][j];
WBUFW(fd,8 + i * 8 + 6) = rd.qty[i][j];
count++;
}
}
WFIFOSET(fd,len);
return;
}
/**
* Closes Roulette window
* @param fd
* @param sd
*/
void clif_parse_RouletteClose(int fd, struct map_session_data* sd)
{
nullpo_retv(sd);
if (!battle_config.feature_roulette) {
clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1497)); //Roulette is disabled
return;
}
/** What do we need this for? (other than state tracking), game client closes the window without our response. **/
return;
}
/**
* Process the stage and attempt to give a prize
* @param fd
* @param sd
*/
void clif_parse_RouletteGenerate(int fd, struct map_session_data* sd)
{
unsigned char result = GENERATE_ROULETTE_SUCCESS;
short stage = sd->roulette.stage;
nullpo_retv(sd);
if (!battle_config.feature_roulette) {
clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1497)); //Roulette is disabled
return;
}
if (sd->roulette.stage >= MAX_ROULETTE_LEVEL)
stage = sd->roulette.stage = 0;
if (!stage) {
if (sd->roulette_point.bronze <= 0 && sd->roulette_point.silver < 10 && sd->roulette_point.gold < 10)
result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
}
if (result == GENERATE_ROULETTE_SUCCESS) {
if (!stage) {
if (sd->roulette_point.bronze > 0) {
sd->roulette_point.bronze -= 1;
} else if (sd->roulette_point.silver > 9) {
sd->roulette_point.silver -= 10;
stage = sd->roulette.stage = 2;
} else if (sd->roulette_point.gold > 9) {
sd->roulette_point.gold -= 10;
stage = sd->roulette.stage = 4;
}
}
sd->roulette.prizeStage = stage;
sd->roulette.prizeIdx = rnd()%rd.items[stage];
if (sd->roulette.prizeIdx == 0) {
struct item it;
memset(&it, 0, sizeof(it));
it.nameid = rd.nameid[stage][0];
it.identify = 1;
pc_additem(sd, &it, rd.qty[stage][0], LOG_TYPE_ROULETTE);
sd->roulette.stage = 0;
result = GENERATE_ROULETTE_LOSING;
} else
sd->roulette.claimPrize = true;
}
clif_roulette_generate_ack(sd,result,stage,sd->roulette.prizeIdx,0);
if (result == GENERATE_ROULETTE_SUCCESS)
sd->roulette.stage++;
}
/**
* Request to cash in prize
* @param fd
* @param sd
*/
void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd)
{
nullpo_retv(sd);
if (!battle_config.feature_roulette) {
clif_colormes(sd,color_table[COLOR_RED],msg_txt(sd,1497)); //Roulette is disabled
return;
}
WFIFOHEAD(fd,packet_len(0xa22));
WBUFW(fd,0) = 0xa22;
if (sd->roulette.claimPrize) {
struct item it;
memset(&it, 0, sizeof(it));
it.nameid = rd.nameid[sd->roulette.prizeStage][sd->roulette.prizeIdx];
it.identify = 1;
switch (pc_additem(sd, &it, rd.qty[sd->roulette.prizeStage][sd->roulette.prizeIdx], LOG_TYPE_OTHER)) {
case 0:
WBUFW(fd,2) = RECV_ITEM_SUCCESS;
sd->roulette.claimPrize = false;
sd->roulette.prizeStage = 0;
sd->roulette.prizeIdx = 0;
sd->roulette.stage = 0;
break;
case 1:
case 4:
case 5:
WBUFW(fd,2) = RECV_ITEM_OVERCOUNT;
break;
case 2:
WBUFW(fd,2) = RECV_ITEM_OVERWEIGHT;
break;
default:
case 7:
WBUFW(fd,2) = RECV_ITEM_FAILED;
break;
}
} else
WBUFW(fd,2) = RECV_ITEM_FAILED;
WBUFL(fd,3) = 0; // additional item TODO
WFIFOSET(fd,packet_len(0xa22));
return;
}
/**
* Update Roulette window with current stats
* @param sd
* @param result
* @param stage
* @param prizeIdx
* @param bonusItemID
*/
void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID)
{
int fd = sd->fd;
nullpo_retv(sd);
WFIFOHEAD(fd,packet_len(0xa20));
WBUFW(fd,0) = 0xa20;
WBUFW(fd,2) = result;
WBUFW(fd,3) = stage;
WBUFW(fd,5) = prizeIdx;
WBUFW(fd,7) = bonusItemID;
WBUFL(fd,9) = sd->roulette_point.gold;
WBUFL(fd,13) = sd->roulette_point.silver;
WBUFL(fd,17) = sd->roulette_point.bronze;
WFIFOSET(fd,packet_len(0xa20));
}
/*========================================== /*==========================================
* Main client packet processing function * Main client packet processing function
*------------------------------------------*/ *------------------------------------------*/
@ -18120,8 +18364,17 @@ void packetdb_readdb(bool reload)
//#0x09C0 //#0x09C0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0,102, 0,
0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 2, 0, -1, -1, 2, 0, 0, 0, 0, 0, 0, 7,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 11, 0, 11, -1, 0, 3, 11, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 12, 11, 0, 0, 0, 0, 0,143, 0, 0, 0, 0, 0, 0,
//#0x0a00
#if PACKETVER >= 20141022
269, 0, 0, 2, 6, 49, 6, 9, 26, 45, 47, 47, 56, -1, 0, 0,
#else
269, 0, 0, 2, 6, 48, 6, 9, 26, 45, 47, 47, 56, -1, 0, 0,
#endif
0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 5, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}; };
struct { struct {
void (*func)(int, struct map_session_data *); void (*func)(int, struct map_session_data *);
@ -18344,9 +18597,15 @@ void packetdb_readdb(bool reload)
{ clif_parse_client_version, "clientversion"}, { clif_parse_client_version, "clientversion"},
{ clif_parse_blocking_playcancel, "booking_playcancel"}, { clif_parse_blocking_playcancel, "booking_playcancel"},
{ clif_parse_ranklist, "ranklist"}, { clif_parse_ranklist, "ranklist"},
/* Market NPC */ // Market NPC
{ clif_parse_NPCMarketClosed, "npcmarketclosed" }, { clif_parse_NPCMarketClosed, "npcmarketclosed" },
{ clif_parse_NPCMarketPurchase, "npcmarketpurchase" }, { clif_parse_NPCMarketPurchase, "npcmarketpurchase" },
// Roulette
{ clif_parse_RouletteOpen, "rouletteopen" },
{ clif_parse_RouletteInfo, "rouletteinfo" },
{ clif_parse_RouletteClose, "rouletteclose" },
{ clif_parse_RouletteGenerate, "roulettegenerate" },
{ clif_parse_RouletteRecvItem, "rouletterecvitem" },
{NULL,NULL} {NULL,NULL}
}; };
struct { struct {
@ -18591,6 +18850,7 @@ void packetdb_readdb(bool reload)
*------------------------------------------*/ *------------------------------------------*/
void do_init_clif(void) { void do_init_clif(void) {
const char* colors[COLOR_MAX] = { const char* colors[COLOR_MAX] = {
"0x00FF00",
"0xFF0000", "0xFF0000",
"0xFFFFFF", "0xFFFFFF",
"0xFFFF00", "0xFFFF00",

View File

@ -33,9 +33,9 @@ struct party_booking_ad_info;
#include <stdarg.h> #include <stdarg.h>
enum { // packet DB enum { // packet DB
MIN_PACKET_DB = 0x0064, MIN_PACKET_DB = 0x064,
MAX_PACKET_DB = 0xf00, MAX_PACKET_DB = 0xAFF,
MAX_PACKET_VER = 46, MAX_PACKET_VER = 51,
MAX_PACKET_POS = 20, MAX_PACKET_POS = 20,
}; };
@ -73,13 +73,42 @@ enum e_BANKING_DEPOSIT_ACK {
BDA_NO_MONEY = 0x2, BDA_NO_MONEY = 0x2,
BDA_OVERFLOW = 0x3, BDA_OVERFLOW = 0x3,
}; };
enum e_BANKING_WITHDRAW_ACK { enum e_BANKING_WITHDRAW_ACK {
BWA_SUCCESS = 0x0, BWA_SUCCESS = 0x0,
BWA_NO_MONEY = 0x1, BWA_NO_MONEY = 0x1,
BWA_UNKNOWN_ERROR = 0x2, BWA_UNKNOWN_ERROR = 0x2,
}; };
enum RECV_ROULETTE_ITEM_REQ {
RECV_ITEM_SUCCESS = 0x0,
RECV_ITEM_FAILED = 0x1,
RECV_ITEM_OVERCOUNT = 0x2,
RECV_ITEM_OVERWEIGHT = 0x3,
};
enum RECV_ROULETTE_ITEM_ACK {
RECV_ITEM_NORMAL = 0x0,
RECV_ITEM_LOSING = 0x1,
};
enum GENERATE_ROULETTE_ACK {
GENERATE_ROULETTE_SUCCESS = 0x0,
GENERATE_ROULETTE_FAILED = 0x1,
GENERATE_ROULETTE_NO_ENOUGH_POINT = 0x2,
GENERATE_ROULETTE_LOSING = 0x3,
};
enum OPEN_ROULETTE_ACK {
OPEN_ROULETTE_SUCCESS = 0x0,
OPEN_ROULETTE_FAILED = 0x1,
};
enum CLOSE_ROULETTE_ACK {
CLOSE_ROULETTE_SUCCESS = 0x0,
CLOSE_ROULETTE_FAILED = 0x1,
};
// packet_db[SERVER] is reserved for server use // packet_db[SERVER] is reserved for server use
#define SERVER 0 #define SERVER 0
#define packet_len(cmd) packet_db[SERVER][cmd].len #define packet_len(cmd) packet_db[SERVER][cmd].len
@ -861,6 +890,14 @@ void clif_cashshop_open( struct map_session_data* sd );
void clif_display_pinfo(struct map_session_data *sd, int type); void clif_display_pinfo(struct map_session_data *sd, int type);
/// Roulette
void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID);
void clif_parse_RouletteOpen(int fd, struct map_session_data *sd);
void clif_parse_RouletteInfo(int fd, struct map_session_data *sd);
void clif_parse_RouletteClose(int fd, struct map_session_data *sd);
void clif_parse_RouletteGenerate(int fd, struct map_session_data *sd);
void clif_parse_RouletteRecvItem(int fd, struct map_session_data *sd);
/** /**
* 3CeAM * 3CeAM
**/ **/
@ -892,6 +929,7 @@ void clif_monster_hp_bar( struct mob_data* md, int fd );
* Color Table * Color Table
**/ **/
enum clif_colors { enum clif_colors {
COLOR_DEFAULT,
COLOR_RED, COLOR_RED,
COLOR_WHITE, COLOR_WHITE,
COLOR_YELLOW, COLOR_YELLOW,

View File

@ -929,6 +929,7 @@ static int itemdb_combo_split_atoi (char *str, int *val) {
return i; return i;
} }
/** /**
* <combo{:combo{:combo:{..}}}>,<{ script }> * <combo{:combo{:combo:{..}}}>,<{ script }>
**/ **/
@ -1056,7 +1057,101 @@ static void itemdb_read_combos(const char* basedir, bool silent) {
return; return;
} }
/**
* Process Roulette items
*/
bool itemdb_parse_roulette_db(void)
{
int i, j;
uint32 count = 0;
// retrieve all rows from the item database
if (SQL_ERROR == Sql_Query(mmysql_handle, "SELECT * FROM `%s`", db_roulette_table)) {
Sql_ShowDebug(mmysql_handle);
return false;
}
for (i = 0; i < MAX_ROULETTE_LEVEL; i++) {
rd.items[i] = 0;
}
// process rows one by one
while (SQL_SUCCESS == Sql_NextRow(mmysql_handle)) {
char* str[4];
char* dummy = "";
int i;
for (i = 0; i < MAX_ROULETTE_LEVEL; i++) {
struct item_data * data = NULL;
Sql_GetData(mmysql_handle, i, &str[i], NULL);
if (str[i] == NULL)
str[i] = dummy; // get rid of NULL columns
if (!(data = itemdb_exists(atoi(str[1])))) {
ShowWarning("itemdb_parse_roulette_db: Unknown item_id '%hu' in level '%d'\n", atoi(str[1]), atoi(str[0]));
continue;
}
if (atoi(str[2]) < 1) {
ShowWarning("itemdb_parse_roulette_db: Unsupported amount '%hu' for item_id '%hu' in level '%d'\n", atoi(str[2]), atoi(str[1]), atoi(str[0]));
continue;
}
if (atoi(str[3]) < 0 || atoi(str[3]) > 1) {
ShowWarning("itemdb_parse_roueltte_db: Unsupported flag '%d' for item_id '%hu' in level '%d'\n", atoi(str[3]), atoi(str[1]), atoi(str[0]));
continue;
}
j = rd.items[i];
RECREATE(rd.nameid[i], unsigned short, ++rd.items[i]);
RECREATE(rd.qty[i], unsigned short, rd.items[i]);
RECREATE(rd.flag[i], int, rd.items[i]);
rd.nameid[i][j] = data->nameid;
rd.qty[i][j] = atoi(str[2]);
rd.flag[i][j] = atoi(str[3]);
++count;
}
}
// free the query result
Sql_FreeResult(mmysql_handle);
for (i = 0; i < MAX_ROULETTE_LEVEL; i++) {
int limit = MAX_ROULETTE_COLUMNS - i;
if (rd.items[i] == limit)
continue;
if (rd.items[i] > limit) {
ShowWarning("itemdb_parse_roulette_db: level %d has %d items, only %d supported, capping...\n", i + 1, rd.items[i], limit);
rd.items[i] = limit;
continue;
}
/** this scenario = rd.items[i] < limit **/
ShowWarning("itemdb_parse_roulette_db: Level %d has %d items, %d are required. filling with apples\n", i + 1, rd.items[i], limit);
rd.items[i] = limit;
RECREATE(rd.nameid[i], unsigned short, rd.items[i]);
RECREATE(rd.qty[i], unsigned short, rd.items[i]);
RECREATE(rd.flag[i], int, rd.items[i]);
for (j = 0; j < MAX_ROULETTE_COLUMNS - i; j++) {
if (rd.qty[i][j])
continue;
rd.nameid[i][j] = ITEMID_APPLE;
rd.qty[i][j] = 1;
rd.flag[i][j] = 1;
}
}
ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", count, db_roulette_table);
return true;
}
/*====================================== /*======================================
* Applies gender restrictions according to settings. [Skotlex] * Applies gender restrictions according to settings. [Skotlex]
@ -1074,6 +1169,7 @@ static char itemdb_gendercheck(struct item_data *id)
return (battle_config.ignore_items_gender) ? 2 : id->sex; return (battle_config.ignore_items_gender) ? 2 : id->sex;
} }
/** /**
* [RRInd] * [RRInd]
* For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot * For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot
@ -1579,6 +1675,9 @@ void itemdb_reload(void) {
itemdb_read(); itemdb_read();
cashshop_reloaddb(); cashshop_reloaddb();
if (!itemdb_parse_roulette_db())
battle_config.feature_roulette = 0;
//Epoque's awesome @reloaditemdb fix - thanks! [Ind] //Epoque's awesome @reloaditemdb fix - thanks! [Ind]
//- Fixes the need of a @reloadmobdb after a @reloaditemdb to re-link monster drop data //- Fixes the need of a @reloadmobdb after a @reloaditemdb to re-link monster drop data
for( i = 0; i < MAX_MOB_DB; i++ ) { for( i = 0; i < MAX_MOB_DB; i++ ) {
@ -1649,4 +1748,7 @@ void do_init_itemdb(void) {
itemdb_group = uidb_alloc(DB_OPT_BASE); itemdb_group = uidb_alloc(DB_OPT_BASE);
itemdb_create_dummy(); itemdb_create_dummy();
itemdb_read(); itemdb_read();
if (!itemdb_parse_roulette_db())
battle_config.feature_roulette = 0;
} }

View File

@ -25,6 +25,9 @@
#define MAX_ITEMGROUP_RANDGROUP 4 ///Max group for random item (increase this when needed). TODO: Remove this limit and use dynamic size if needed #define MAX_ITEMGROUP_RANDGROUP 4 ///Max group for random item (increase this when needed). TODO: Remove this limit and use dynamic size if needed
#define MAX_ROULETTE_LEVEL 7 /** client-defined value **/
#define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/
#define CARD0_FORGE 0x00FF #define CARD0_FORGE 0x00FF
#define CARD0_CREATE 0x00FE #define CARD0_CREATE 0x00FE
#define CARD0_PET 0x0100 #define CARD0_PET 0x0100
@ -39,6 +42,7 @@ enum item_itemid
ITEMID_YELLOW_POTION = 503, ITEMID_YELLOW_POTION = 503,
ITEMID_WHITE_POTION = 504, ITEMID_WHITE_POTION = 504,
ITEMID_BLUE_POTION = 505, ITEMID_BLUE_POTION = 505,
ITEMID_APPLE = 512,
ITEMID_HOLY_WATER = 523, ITEMID_HOLY_WATER = 523,
ITEMID_PUMPKIN = 535, ITEMID_PUMPKIN = 535,
ITEMID_RED_SLIM_POTION = 545, ITEMID_RED_SLIM_POTION = 545,
@ -371,6 +375,14 @@ struct s_item_group_db
struct s_item_group_random random[MAX_ITEMGROUP_RANDGROUP]; //! TODO: Move this fixed array to dynamic size if needed. struct s_item_group_random random[MAX_ITEMGROUP_RANDGROUP]; //! TODO: Move this fixed array to dynamic size if needed.
}; };
/// Struct of Roulette db
struct {
unsigned short *nameid[MAX_ROULETTE_LEVEL], /// Item ID
*qty[MAX_ROULETTE_LEVEL]; /// Amount of Item ID
int *flag[MAX_ROULETTE_LEVEL]; /// Whether the item is for loss or win
int items[MAX_ROULETTE_LEVEL]; /// Number of items in the list for each
} rd;
///Main item data struct ///Main item data struct
struct item_data struct item_data
{ {
@ -513,6 +525,8 @@ struct s_item_group_db *itemdb_group_exists(unsigned short group_id);
char itemdb_pc_get_itemgroup(uint16 group_id, struct map_session_data *sd); char itemdb_pc_get_itemgroup(uint16 group_id, struct map_session_data *sd);
uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, unsigned short nameid); uint16 itemdb_get_randgroupitem_count(uint16 group_id, uint8 sub_group, unsigned short nameid);
bool itemdb_parse_roulette_db(void);
void itemdb_reload(void); void itemdb_reload(void);
void do_final_itemdb(void); void do_final_itemdb(void);

View File

@ -56,7 +56,7 @@ static char log_picktype2char(e_log_pick_type type)
{ {
case LOG_TYPE_TRADE: return 'T'; // (T)rade case LOG_TYPE_TRADE: return 'T'; // (T)rade
case LOG_TYPE_VENDING: return 'V'; // (V)ending case LOG_TYPE_VENDING: return 'V'; // (V)ending
case LOG_TYPE_PICKDROP_PLAYER: return 'P'; // (P)player case LOG_TYPE_PICKDROP_PLAYER: return 'P'; // (P)layer
case LOG_TYPE_PICKDROP_MONSTER: return 'M'; // (M)onster case LOG_TYPE_PICKDROP_MONSTER: return 'M'; // (M)onster
case LOG_TYPE_NPC: return 'S'; // NPC (S)hop case LOG_TYPE_NPC: return 'S'; // NPC (S)hop
case LOG_TYPE_SCRIPT: return 'N'; // (N)PC Script case LOG_TYPE_SCRIPT: return 'N'; // (N)PC Script
@ -75,6 +75,7 @@ static char log_picktype2char(e_log_pick_type type)
case LOG_TYPE_OTHER: return 'X'; // Other case LOG_TYPE_OTHER: return 'X'; // Other
case LOG_TYPE_CASH: return '$'; // Cash case LOG_TYPE_CASH: return '$'; // Cash
case LOG_TYPE_BOUND_REMOVAL: return 'F'; // Removed bound items when guild/party is broken case LOG_TYPE_BOUND_REMOVAL: return 'F'; // Removed bound items when guild/party is broken
case LOG_TYPE_ROULETTE: return 'Y'; // Roulette Lotter(Y)
} }
// should not get here, fallback // should not get here, fallback

View File

@ -24,30 +24,31 @@ typedef enum e_log_chat_type
typedef enum e_log_pick_type typedef enum e_log_pick_type
{ {
LOG_TYPE_NONE = 0, LOG_TYPE_NONE = 0,
LOG_TYPE_TRADE = 0x00001, LOG_TYPE_TRADE = 0x000001,
LOG_TYPE_VENDING = 0x00002, LOG_TYPE_VENDING = 0x000002,
LOG_TYPE_PICKDROP_PLAYER = 0x00004, LOG_TYPE_PICKDROP_PLAYER = 0x000004,
LOG_TYPE_PICKDROP_MONSTER = 0x00008, LOG_TYPE_PICKDROP_MONSTER = 0x000008,
LOG_TYPE_NPC = 0x00010, LOG_TYPE_NPC = 0x000010,
LOG_TYPE_SCRIPT = 0x00020, LOG_TYPE_SCRIPT = 0x000020,
LOG_TYPE_STEAL = 0x00040, LOG_TYPE_STEAL = 0x000040,
LOG_TYPE_CONSUME = 0x00080, LOG_TYPE_CONSUME = 0x000080,
LOG_TYPE_PRODUCE = 0x00100, LOG_TYPE_PRODUCE = 0x000100,
LOG_TYPE_MVP = 0x00200, LOG_TYPE_MVP = 0x000200,
LOG_TYPE_COMMAND = 0x00400, LOG_TYPE_COMMAND = 0x000400,
LOG_TYPE_STORAGE = 0x00800, LOG_TYPE_STORAGE = 0x000800,
LOG_TYPE_GSTORAGE = 0x01000, LOG_TYPE_GSTORAGE = 0x001000,
LOG_TYPE_MAIL = 0x02000, LOG_TYPE_MAIL = 0x002000,
LOG_TYPE_AUCTION = 0x04000, LOG_TYPE_AUCTION = 0x004000,
LOG_TYPE_BUYING_STORE = 0x08000, LOG_TYPE_BUYING_STORE = 0x008000,
LOG_TYPE_OTHER = 0x10000, LOG_TYPE_OTHER = 0x010000,
LOG_TYPE_CASH = 0x20000, LOG_TYPE_CASH = 0x020000,
LOG_TYPE_BANK = 0x40000, LOG_TYPE_BANK = 0x040000,
LOG_TYPE_BOUND_REMOVAL = 0x80000, LOG_TYPE_BOUND_REMOVAL = 0x080000,
LOG_TYPE_ROULETTE = 0x100000,
// combinations // combinations
LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME, LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME,
// all // all
LOG_TYPE_ALL = 0xFFFFF, LOG_TYPE_ALL = 0xFFFFFF,
} e_log_pick_type; } e_log_pick_type;
typedef enum e_log_cash_type typedef enum e_log_cash_type

View File

@ -70,6 +70,7 @@ char mob_skill_db2_db[32] = "mob_skill_db2";
char vendings_db[32] = "vendings"; char vendings_db[32] = "vendings";
char vending_items_db[32] = "vending_items"; char vending_items_db[32] = "vending_items";
char market_table[32] = "market"; char market_table[32] = "market";
char db_roulette_table[32] = "db_roulette";
// log database // log database
char log_db_ip[32] = "127.0.0.1"; char log_db_ip[32] = "127.0.0.1";
@ -3759,7 +3760,9 @@ int inter_config_read(char *cfgName)
else if( strcmpi( w1, "vending_db" ) == 0 ) else if( strcmpi( w1, "vending_db" ) == 0 )
strcpy( vendings_db, w2 ); strcpy( vendings_db, w2 );
else if( strcmpi( w1, "vending_items_db" ) == 0 ) else if( strcmpi( w1, "vending_items_db" ) == 0 )
strcpy( vending_items_db, w2 ); strcpy(vending_items_db, w2);
else if( strcmpi(w1, "db_roulette_table") == 0)
strcpy(db_roulette_table, w2);
else if (strcmpi(w1, "market_table") == 0) else if (strcmpi(w1, "market_table") == 0)
strcpy(market_table, w2); strcpy(market_table, w2);
else else

View File

@ -459,6 +459,9 @@ enum _sp {
SP_CHARRENAME=125, SP_CHARRENAME=125,
SP_CHARFONT=126, SP_CHARFONT=126,
SP_BANK_VAULT = 127, SP_BANK_VAULT = 127,
SP_ROULETTE_BRONZE = 128,
SP_ROULETTE_SILVER = 129,
SP_ROULETTE_GOLD = 130,
// Mercenaries // Mercenaries
SP_MERCFLEE=165, SP_MERCKILLS=189, SP_MERCFAITH=190, SP_MERCFLEE=165, SP_MERCKILLS=189, SP_MERCFAITH=190,
@ -1006,6 +1009,7 @@ extern char mob_skill_db2_db[32];
extern char vendings_db[32]; extern char vendings_db[32];
extern char vending_items_db[32]; extern char vending_items_db[32];
extern char market_table[32]; extern char market_table[32];
extern char db_roulette_table[32];
void do_shutdown(void); void do_shutdown(void);

View File

@ -1344,6 +1344,12 @@ void pc_reg_received(struct map_session_data *sd)
if (battle_config.feature_banking) if (battle_config.feature_banking)
sd->bank_vault = pc_readreg2(sd, BANK_VAULT_VAR); sd->bank_vault = pc_readreg2(sd, BANK_VAULT_VAR);
if (battle_config.feature_roulette) {
sd->roulette_point.bronze = pc_readreg2(sd, ROULETTE_BRONZE_VAR);
sd->roulette_point.silver = pc_readreg2(sd, ROULETTE_SILVER_VAR);
sd->roulette_point.gold = pc_readreg2(sd, ROULETTE_GOLD_VAR);
}
//SG map and mob read [Komurka] //SG map and mob read [Komurka]
for(i=0;i<MAX_PC_FEELHATE;i++) { //for now - someone need to make reading from txt/sql for(i=0;i<MAX_PC_FEELHATE;i++) { //for now - someone need to make reading from txt/sql
uint16 j; uint16 j;
@ -7602,7 +7608,10 @@ int pc_readparam(struct map_session_data* sd,int type)
case SP_CHARMOVE: val = sd->status.character_moves; break; case SP_CHARMOVE: val = sd->status.character_moves; break;
case SP_CHARRENAME: val = sd->status.rename; break; case SP_CHARRENAME: val = sd->status.rename; break;
case SP_CHARFONT: val = sd->status.font; break; case SP_CHARFONT: val = sd->status.font; break;
case SP_BANK_VAULT: val = sd->bank_vault; break; case SP_BANK_VAULT: val = sd->bank_vault; break;
case SP_ROULETTE_BRONZE: val = sd->roulette_point.bronze; break;
case SP_ROULETTE_SILVER: val = sd->roulette_point.silver; break;
case SP_ROULETTE_GOLD: val = sd->roulette_point.gold; break;
case SP_CRITICAL: val = sd->battle_status.cri/10; break; case SP_CRITICAL: val = sd->battle_status.cri/10; break;
case SP_ASPD: val = (2000-sd->battle_status.amotion)/10; break; case SP_ASPD: val = (2000-sd->battle_status.amotion)/10; break;
case SP_BASE_ATK: val = sd->battle_status.batk; break; case SP_BASE_ATK: val = sd->battle_status.batk; break;
@ -7861,6 +7870,18 @@ bool pc_setparam(struct map_session_data *sd,int type,int val)
sd->bank_vault = cap_value(val, 0, MAX_BANK_ZENY); sd->bank_vault = cap_value(val, 0, MAX_BANK_ZENY);
pc_setreg2(sd, BANK_VAULT_VAR, sd->bank_vault); pc_setreg2(sd, BANK_VAULT_VAR, sd->bank_vault);
return true; return true;
case SP_ROULETTE_BRONZE:
sd->roulette_point.bronze = val;
pc_setreg2(sd, ROULETTE_BRONZE_VAR, sd->roulette_point.bronze);
return true;
case SP_ROULETTE_SILVER:
sd->roulette_point.silver = val;
pc_setreg2(sd, ROULETTE_SILVER_VAR, sd->roulette_point.silver);
return true;
case SP_ROULETTE_GOLD:
sd->roulette_point.gold = val;
pc_setreg2(sd, ROULETTE_GOLD_VAR, sd->roulette_point.gold);
return true;
default: default:
ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type);
return false; return false;

View File

@ -31,6 +31,9 @@
#define MAX_SPIRITCHARM 10 /// Max spirit charms #define MAX_SPIRITCHARM 10 /// Max spirit charms
#define BANK_VAULT_VAR "#BANKVAULT" #define BANK_VAULT_VAR "#BANKVAULT"
#define ROULETTE_BRONZE_VAR "RouletteBronze"
#define ROULETTE_SILVER_VAR "RouletteSilver"
#define ROULETTE_GOLD_VAR "RouletteGold"
//Update this max as necessary. 55 is the value needed for Super Baby currently //Update this max as necessary. 55 is the value needed for Super Baby currently
//Raised to 84 since Expanded Super Novice needs it. //Raised to 84 since Expanded Super Novice needs it.
@ -640,6 +643,17 @@ struct map_session_data {
#ifdef PACKET_OBFUSCATION #ifdef PACKET_OBFUSCATION
unsigned int cryptKey; ///< Packet obfuscation key to be used for the next received packet unsigned int cryptKey; ///< Packet obfuscation key to be used for the next received packet
#endif #endif
struct {
int bronze, silver, gold; ///< Roulette Coin
} roulette_point;
struct {
short stage;
short prizeIdx;
short prizeStage;
bool claimPrize;
} roulette;
}; };
struct eri *pc_sc_display_ers; /// Player's SC display table struct eri *pc_sc_display_ers; /// Player's SC display table

View File

@ -10981,9 +10981,9 @@ BUILDIN_FUNC(changebase)
} }
/** /**
* Change sec and unequip all item and request for a changesex to char-serv * Change account sex and unequip all item and request for a changesex to char-serv
* changesex({<char_id>}); * changesex({<char_id>});
**/ */
BUILDIN_FUNC(changesex) BUILDIN_FUNC(changesex)
{ {
int i; int i;
@ -10994,12 +10994,42 @@ BUILDIN_FUNC(changesex)
pc_resetskill(sd,4); pc_resetskill(sd,4);
// to avoid any problem with equipment and invalid sex, equipment is unequiped. // to avoid any problem with equipment and invalid sex, equipment is unequiped.
for( i=0; i<EQI_MAX; i++ ) for(i = 0; i < EQI_MAX; i++) {
if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3); if (sd->equip_index[i] >= 0)
chrif_changesex(sd); pc_unequipitem(sd, sd->equip_index[i], 3);
}
chrif_changesex(sd, true);
return SCRIPT_CMD_SUCCESS; return SCRIPT_CMD_SUCCESS;
} }
/**
* Change character's sex and unequip all item and request for a changesex to char-serv
* changecharsex({<char_id>});
*/
BUILDIN_FUNC(changecharsex)
{
#if PACKETVER >= 20141016
int i;
TBL_PC *sd = NULL;
if (!script_charid2sd(2,sd))
return SCRIPT_CMD_FAILURE;
pc_resetskill(sd,4);
// to avoid any problem with equipment and invalid sex, equipment is unequiped.
for (i = 0; i < EQI_MAX; i++) {
if (sd->equip_index[i] >= 0)
pc_unequipitem(sd, sd->equip_index[i], 3);
}
chrif_changesex(sd, false);
return SCRIPT_CMD_SUCCESS;
#else
return SCRIPT_CMD_FAILURE;
#endif
}
/*========================================== /*==========================================
* Works like 'announce' but outputs in the common chat window * Works like 'announce' but outputs in the common chat window
*------------------------------------------*/ *------------------------------------------*/
@ -20486,6 +20516,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(skillpointcount,"?"), BUILDIN_DEF(skillpointcount,"?"),
BUILDIN_DEF(changebase,"i?"), BUILDIN_DEF(changebase,"i?"),
BUILDIN_DEF(changesex,"?"), BUILDIN_DEF(changesex,"?"),
BUILDIN_DEF(changecharsex,"?"),
BUILDIN_DEF(waitingroom,"si?????"), BUILDIN_DEF(waitingroom,"si?????"),
BUILDIN_DEF(delwaitingroom,"?"), BUILDIN_DEF(delwaitingroom,"?"),
BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"), BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),