From 3d9c6e7763c3d257593ba159a18dffc435b5ff06 Mon Sep 17 00:00:00 2001 From: aleos89 Date: Fri, 19 Jun 2015 14:07:34 -0400 Subject: [PATCH] 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. --- conf/battle/feature.conf | 5 + conf/inter_athena.conf | 1 + conf/log_athena.conf | 45 ++-- conf/msg_conf/map_msg.conf | 3 + db/const.txt | 3 + db/packet_db.txt | 79 +++++- db/re/item_db.txt | 6 +- doc/script_commands.txt | 14 + sql-files/main.sql | 66 +++++ sql-files/upgrades/upgrade_20150619.sql | 63 +++++ sql-files/upgrades/upgrade_20150619_log.sql | 1 + src/char/char.c | 75 +++++- src/char/char.h | 3 +- src/char/char_clif.c | 3 +- src/char/char_logif.c | 159 +++++++---- src/char/char_logif.h | 2 + src/char/char_mapif.c | 38 ++- src/common/utils.c | 6 +- src/map/atcommand.c | 35 ++- src/map/battle.c | 8 + src/map/battle.h | 1 + src/map/chrif.c | 15 +- src/map/chrif.h | 3 +- src/map/clif.c | 276 +++++++++++++++++++- src/map/clif.h | 46 +++- src/map/itemdb.c | 102 ++++++++ src/map/itemdb.h | 14 + src/map/log.c | 3 +- src/map/log.h | 43 +-- src/map/map.c | 5 +- src/map/map.h | 4 + src/map/pc.c | 23 +- src/map/pc.h | 14 + src/map/script.c | 41 ++- 34 files changed, 1054 insertions(+), 151 deletions(-) create mode 100644 sql-files/upgrades/upgrade_20150619.sql create mode 100644 sql-files/upgrades/upgrade_20150619_log.sql diff --git a/conf/battle/feature.conf b/conf/battle/feature.conf index 66908989b1..38f068ea60 100644 --- a/conf/battle/feature.conf +++ b/conf/battle/feature.conf @@ -57,3 +57,8 @@ feature.autotrade_sit: 1 // Delay in miliseconds to open vending/buyingsotre after player logged in. 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 diff --git a/conf/inter_athena.conf b/conf/inter_athena.conf index 41ae45ca13..360f8f1c31 100644 --- a/conf/inter_athena.conf +++ b/conf/inter_athena.conf @@ -139,6 +139,7 @@ mapreg_db: mapreg vending_db: vendings vending_items_db: vending_items 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_db: no diff --git a/conf/log_athena.conf b/conf/log_athena.conf index 6667c606af..c9476aa977 100644 --- a/conf/log_athena.conf +++ b/conf/log_athena.conf @@ -8,31 +8,32 @@ //-------------------------------------------------------------- // Enable Logs? (Note 3) -// 0x00000 - Don't log at all -// 0x00001 - (T) Log trades -// 0x00002 - (V) Log vending transactions -// 0x00004 - (P) Log items drop/picked by players -// 0x00008 - (L) Log items drop/looted by monsters -// 0x00010 - (S) Log NPC transactions (buy/sell) -// 0x00020 - (N) Log Script transactions (items deleted/acquired through quests) -// 0x00040 - (D) Log items stolen from mobs (Steal/Gank) -// 0x00080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks) -// 0x00100 - (O) Log produced/ingredient items -// 0x00200 - (U) Log MVP prize items -// 0x00400 - (A) Log player created/deleted items (through @/# commands) -// 0x00800 - (R) Log items placed/retrieved from storage. -// 0x01000 - (G) Log items placed/retrieved from guild storage. -// 0x02000 - (E) Log mail system transactions. -// 0x04000 - (I) Log auction system transactions. -// 0x08000 - (B) Log buying store transactions -// 0x10000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/ +// 0x000000 - Don't log at all +// 0x000001 - (T) Log trades +// 0x000002 - (V) Log vending transactions +// 0x000004 - (P) Log items drop/picked by players +// 0x000008 - (L) Log items drop/looted by monsters +// 0x000010 - (S) Log NPC transactions (buy/sell) +// 0x000020 - (N) Log Script transactions (items deleted/acquired through quests) +// 0x000040 - (D) Log items stolen from mobs (Steal/Gank) +// 0x000080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks) +// 0x000100 - (O) Log produced/ingredient items +// 0x000200 - (U) Log MVP prize items +// 0x000400 - (A) Log player created/deleted items (through @/# commands) +// 0x000800 - (R) Log items placed/retrieved from storage. +// 0x001000 - (G) Log items placed/retrieved from guild storage. +// 0x002000 - (E) Log mail system transactions. +// 0x004000 - (I) Log auction system transactions. +// 0x008000 - (B) Log buying store transactions +// 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) -// 0x20000 - ($) Log cash transactions -// 0x40000 - (K) Log account bank transactions -// 0x80000 - (F) Removed bound items when guild/party is broken +// 0x020000 - ($) Log cash transactions +// 0x040000 - (K) Log account bank transactions +// 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 // 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) sql_logs: yes diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index 07501ee83d..48d2562fd0 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -1593,5 +1593,8 @@ 1495: You can't withdraw that much money 1496: Banking is disabled +// Roulette +1497: Roulette is disabled + //Custom translations //import: conf/msg_conf/import/map_msg_eng_conf.txt diff --git a/db/const.txt b/db/const.txt index a355d8595a..bbd3572132 100644 --- a/db/const.txt +++ b/db/const.txt @@ -432,6 +432,9 @@ CharMoves 124 1 CharRename 125 1 Font 126 1 BankVault 127 1 +RouletteBronze 128 1 +RouletteSilver 129 1 +RouletteGold 130 1 bMaxHP 6 bMaxSP 8 diff --git a/db/packet_db.txt b/db/packet_db.txt index 2d48a91b13..2f529d9e1f 100644 --- a/db/packet_db.txt +++ b/db/packet_db.txt @@ -2318,5 +2318,82 @@ packet_keys: 0x631C511C,0x111C111C,0x111C111C // [Shakto] 0x09D8,2,npcmarketclosed,0 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 -//packet_ver: 47 +//packet_ver: 52 diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 082400b737..61ae92a7fd 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -165,11 +165,11 @@ 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; },{},{} 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,,,,,,{},{},{} -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,,,,,,{},{},{} -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,,,,,,{},{},{} 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; },{},{} diff --git a/doc/script_commands.txt b/doc/script_commands.txt index eb87dbd8f4..6769ee6774 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -4169,6 +4169,20 @@ they will also have their skills reset upon 'changesex'. --------------------------------------- +*changecharsex({}); + +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 ,{,}; This command will give the invoking character a specified number of base and job diff --git a/sql-files/main.sql b/sql-files/main.sql index 47f224be17..605807432e 100644 --- a/sql-files/main.sql +++ b/sql-files/main.sql @@ -125,6 +125,7 @@ CREATE TABLE IF NOT EXISTS `char` ( `unban_time` int(11) unsigned NOT NULL default '0', `font` tinyint(3) 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`), UNIQUE KEY `name_key` (`name`), KEY `account_id` (`account_id`), @@ -637,6 +638,71 @@ CREATE TABLE IF NOT EXISTS `ragsrvinfo` ( `drop` int(11) unsigned NOT NULL default '0' ) 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` -- diff --git a/sql-files/upgrades/upgrade_20150619.sql b/sql-files/upgrades/upgrade_20150619.sql new file mode 100644 index 0000000000..52a84de2d7 --- /dev/null +++ b/sql-files/upgrades/upgrade_20150619.sql @@ -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 diff --git a/sql-files/upgrades/upgrade_20150619_log.sql b/sql-files/upgrades/upgrade_20150619_log.sql new file mode 100644 index 0000000000..3bd4d4dfee --- /dev/null +++ b/sql-files/upgrades/upgrade_20150619_log.sql @@ -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', diff --git a/src/char/char.c b/src/char/char.c index 86515a5efa..478d01cb51 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -837,6 +837,54 @@ int char_inventory_to_sql(const struct item items[], int max, int id) { 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); @@ -847,16 +895,16 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) { struct mmo_charstatus p; int j = 0, i; char last_map[MAP_NAME_LENGTH_EXT]; + char sex[2]; stmt = SqlStmt_Malloc(sql_handle); - if( stmt == NULL ) - { + if( stmt == NULL ) { SqlStmt_ShowDebug(stmt); return 0; } 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->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`," "`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`," - "`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) || SQL_ERROR == SqlStmt_Execute(stmt) || 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, 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, 40, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) ) { 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); sd->found_char[p.slot] = p.char_id; 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); // Addon System @@ -954,6 +1004,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev int hotkey_num; #endif StringBuf msg_buf; + char sex[2]; 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`," "`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`," - "`unban_time`,`font`,`uniqueitem_counter`" + "`unban_time`,`font`,`uniqueitem_counter`,`sex`" " 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_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, 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, 56, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL) ) { 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); return 0; } + p->sex = char_mmo_gender(NULL, p, sex[0]); p->last_point.map = mapindex_name2id(last_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,50) = DEFAULT_WALK_SPEED; // p->speed; 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; +#endif //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; @@ -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) offset += 4; #endif + #if PACKETVER >= 20141016 + WBUFB(buf,140) = p->sex;// sex - (0 = female, 1 = male, 99 = logindefined) + offset += 1; + #endif #endif 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_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){ ShowFatalError("char : A tables is missing in sql-server, please fix it, see (sql-files main.sql for structure) \n"); exit(EXIT_FAILURE); diff --git a/src/char/char.h b/src/char/char.h index e5deeddd3e..f49c6268d5 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -219,7 +219,7 @@ extern struct fame_list chemist_fame_list[MAX_FAME_LIST]; extern struct fame_list taekwon_fame_list[MAX_FAME_LIST]; #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_lan_subnetcheck(uint32 ip); @@ -233,6 +233,7 @@ void char_set_all_offline(int 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_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_tosql(uint32 char_id, struct mmo_charstatus* p); int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_everything); diff --git a/src/char/char_clif.c b/src/char/char_clif.c index 4d09da2de7..cd63dda63a 100644 --- a/src/char/char_clif.c +++ b/src/char/char_clif.c @@ -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] 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) { char esc_name[NAME_LENGTH*2+1]; diff --git a/src/char/char_logif.c b/src/char/char_logif.c index 9768ef0707..ac904c2166 100644 --- a/src/char/char_logif.c +++ b/src/char/char_logif.c @@ -340,76 +340,79 @@ int chlogif_parse_keepalive(int fd, struct char_session_data* sd){ 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) return 0; { unsigned char buf[7]; - int acc = RFIFOL(fd,2); int sex = RFIFOB(fd,6); RFIFOSKIP(fd,7); - if( acc > 0 ) - {// TODO: Is this even possible? - uint32 char_id[MAX_CHARS]; - int class_[MAX_CHARS]; - int guild_id[MAX_CHARS]; - unsigned char num, i; - char* data; - DBMap* auth_db = char_get_authdb(); - + if (acc > 0) { // TODO: Is this even possible? + unsigned char i; + int char_id = 0, class_ = 0, guild_id = 0; + DBMap* auth_db = char_get_authdb(); struct auth_node* node = (struct auth_node*)idb_get(auth_db, acc); - if( node != NULL ) + SqlStmt *stmt; + + if (node != NULL) node->sex = sex; // 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) ) - Sql_ShowDebug(sql_handle); - for( i = 0; i < MAX_CHARS && SQL_SUCCESS == Sql_NextRow(sql_handle); ++i ) - { - 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); + stmt = SqlStmt_Malloc(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)) { + SqlStmt_ShowDebug(stmt); + SqlStmt_Free(stmt); } - 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]) ) - Sql_ShowDebug(sql_handle); + SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &char_id, 0, NULL, NULL); + 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] - inter_guild_sex_changed(guild_id[i], acc, char_id[i], sex); + for (i = 0; i < MAX_CHARS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i) { + chlogif_parse_change_sex_sub(sex, acc, char_id, class_, guild_id); } - Sql_FreeResult(sql_handle); - - // disconnect player if online on char-server - char_disconnect_player(acc); + SqlStmt_Free(stmt); } // notify all mapservers about this change @@ -421,6 +424,54 @@ int chlogif_parse_ackchangesex(int fd, struct char_session_data* sd){ 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){ if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; diff --git a/src/char/char_logif.h b/src/char/char_logif.h index 252934d10b..360b6fa2a0 100644 --- a/src/char/char_logif.h +++ b/src/char/char_logif.h @@ -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_reqaccdata(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_ackchangecharsex(int char_id, int sex); 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_askkick(int fd, struct char_session_data* sd); diff --git a/src/char/char_mapif.c b/src/char/char_mapif.c index 5757b80f61..0c746c3358 100644 --- a/src/char/char_mapif.c +++ b/src/char/char_mapif.c @@ -717,7 +717,7 @@ int chmapif_parse_reqnewemail(int fd){ /** * 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 */ 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) 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 - int32 timediff = RFIFOL(fd,32); - int val1 = RFIFOL(fd,36); - //int val2 = RFIFOL(fd,40); // Since BankVault is moved out, this value is unused for now + int operation = RFIFOW(fd,30); // type of operation: 1-block, 2-ban, 3-unblock, 4-unban, 5-changesex, 6-vip, 7-changecharsex + int32 timediff = 0; + int val1 = 0, sex = SEX_MALE; + + if (operation == 2) { + timediff = RFIFOL(fd, 32); + val1 = RFIFOL(fd, 36); + } else if (operation == 7) + sex = RFIFOB(fd, 32); RFIFOSKIP(fd,44); 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); else if( Sql_NumRows(sql_handle) == 0 ) { result = 1; // 1-player not found @@ -746,10 +751,12 @@ int chmapif_parse_fwlog_changestatus(int fd){ Sql_ShowDebug(sql_handle); result = 1; } else { - int t_aid; //targit account id + int t_aid; // target account id + int t_cid; // target char id char* 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); if(!chlogif_isconnected()) @@ -794,16 +801,24 @@ int chmapif_parse_fwlog_changestatus(int fd){ WFIFOL(login_fd,2) = t_aid; WFIFOSET(login_fd,6); break; - case 6: + case 6: // vip 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); 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 } //login is connected } // 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); WFIFOW(fd, 0) = 0x2b0f; 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 * @param partner_id1: char id1 divorced * @param partner_id2: char id2 divorced @@ -1047,7 +1062,8 @@ int chmapif_parse_reqauth(int fd, int id){ node->ip == ip*/ ) {// auth ok uint16 mmo_charstatus_len = sizeof(struct mmo_charstatus) + 25; - cd->sex = sex; + if (cd->sex == 99) + cd->sex = sex; WFIFOHEAD(fd,mmo_charstatus_len); WFIFOW(fd,0) = 0x2afd; diff --git a/src/common/utils.c b/src/common/utils.c index df64cd8984..640015e435 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -378,7 +378,11 @@ uint32 date2version(int date) { else if(date < 20130717) return 43; else if(date < 20130807) return 44; 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 } diff --git a/src/map/atcommand.c b/src/map/atcommand.c index d7e318a1ed..5c3842abef 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -6769,17 +6769,43 @@ ACMD_FUNC(uptime) /*========================================== * @changesex - * => 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) { 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; iequip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3); - chrif_changesex(sd); + for (i = 0; i < EQI_MAX; i++) { + if (sd->equip_index[i] >= 0) + pc_unequipitem(sd, sd->equip_index[i], 3); + } + + chrif_changesex(sd, true); + return 0; +} + +/*========================================== + * @changecharsex + * => 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; } @@ -9886,6 +9912,7 @@ void atcommand_basecommands(void) { ACMD_DEF(clearweather), ACMD_DEF(uptime), ACMD_DEF(changesex), + ACMD_DEF(changecharsex), ACMD_DEF(mute), ACMD_DEF(refresh), ACMD_DEF(refreshall), diff --git a/src/map/battle.c b/src/map/battle.c index 45f0fedd23..be7c350369 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -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_reset", &battle_config.homunculus_evo_intimacy_reset, 1000, 0, INT_MAX, }, { "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 @@ -8396,6 +8397,13 @@ void battle_adjust_conf() } #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 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"); diff --git a/src/map/battle.h b/src/map/battle.h index 50caecb05c..e246079a18 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -590,6 +590,7 @@ extern struct Battle_Config int homunculus_evo_intimacy_need; int homunculus_evo_intimacy_reset; int monster_loot_search_type; + int feature_roulette; } battle_config; void do_init_battle(void); diff --git a/src/map/chrif.c b/src/map/chrif.c index 839a5a449f..83dd52eab0 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -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 //2b0b: Incoming, chrif_skillcooldown_load -> received the list of cooldown for char //2b0c: Outgoing, chrif_changeemail -> 'change mail address ...' -//2b0d: Incoming, chrif_changedsex -> 'Change sex of acc XY' +//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)' //2b0f: Incoming, chrif_ack_login_req -> 'answer of the 2b0e' //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 .l .24B .w L L 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 */ -int chrif_changesex(struct map_session_data *sd) { +int chrif_changesex(struct map_session_data *sd, bool change_account) { chrif_check(-1); WFIFOHEAD(char_fd,44); WFIFOW(char_fd,0) = 0x2b0e; WFIFOL(char_fd,2) = sd->status.account_id; 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); 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_UNBAN: 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 break; 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; } - 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 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; diff --git a/src/map/chrif.h b/src/map/chrif.h index fbbaddcb3b..1b05a54bb5 100644 --- a/src/map/chrif.h +++ b/src/map/chrif.h @@ -27,6 +27,7 @@ enum chrif_req_op { CHRIF_OP_LOGIN_UNBAN, CHRIF_OP_LOGIN_CHANGESEX, CHRIF_OP_LOGIN_VIP, + CHRIF_OP_LOGIN_CHANGECHARSEX, // Char-server operation 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 send_users_tochar(void); 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_divorce(int partner_id1, int partner_id2); diff --git a/src/map/clif.c b/src/map/clif.c index 7497be76dd..11c7d1e327 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -662,6 +662,9 @@ void clif_authok(struct map_session_data *sd) WFIFOB(fd,10) = 5; // ignored #if PACKETVER >= 20080102 WFIFOW(fd,11) = sd->status.font; +#endif +#if PACKETVER >= 20141016 + WFIFOB(fd,13) = sd->status.sex; #endif WFIFOSET(fd,packet_len(cmd)); } @@ -5751,6 +5754,11 @@ void clif_displaymessage(const int fd, const char* mes) if (fd == 0) ; 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; message = aStrdup(mes); @@ -5769,6 +5777,7 @@ void clif_displaymessage(const int fd, const char* mes) line = strtok(NULL, "\n"); } 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]); } 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 const int fd = sd->fd; int i; + int offset = 2; #if PACKETVER < 20090603 const int cmd = 0x2b9; -#else +#elif PACKETVER < 20141022 const int cmd = 0x7d9; +#else + const int cmd = 0xa00; + offset = 3; #endif if (!fd) return; - WFIFOHEAD(fd, 2+MAX_HOTKEYS*7); + WFIFOHEAD(fd, offset + MAX_HOTKEYS * 7); WFIFOW(fd, 0) = cmd; for(i = 0; i < MAX_HOTKEYS; i++) { - WFIFOB(fd, 2 + 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 - WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level + WFIFOB(fd, offset + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill + WFIFOL(fd, offset + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID + WFIFOW(fd, offset + 5 + i * 7) = sd->status.hotkeys[i].lv; // item qty or skill level } WFIFOSET(fd, packet_len(cmd)); #endif @@ -17729,6 +17750,229 @@ void DumpUnknown(int fd,TBL_PC *sd,int cmd,int packet_len) } #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 *------------------------------------------*/ @@ -18120,8 +18364,17 @@ void packetdb_readdb(bool reload) //#0x09C0 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 11, 0, 11, -1, 0, 3, 11, 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 { void (*func)(int, struct map_session_data *); @@ -18344,9 +18597,15 @@ void packetdb_readdb(bool reload) { clif_parse_client_version, "clientversion"}, { clif_parse_blocking_playcancel, "booking_playcancel"}, { clif_parse_ranklist, "ranklist"}, - /* Market NPC */ + // Market NPC { clif_parse_NPCMarketClosed, "npcmarketclosed" }, { 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} }; struct { @@ -18591,6 +18850,7 @@ void packetdb_readdb(bool reload) *------------------------------------------*/ void do_init_clif(void) { const char* colors[COLOR_MAX] = { + "0x00FF00", "0xFF0000", "0xFFFFFF", "0xFFFF00", diff --git a/src/map/clif.h b/src/map/clif.h index db9b91388e..7540be9739 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -33,9 +33,9 @@ struct party_booking_ad_info; #include enum { // packet DB - MIN_PACKET_DB = 0x0064, - MAX_PACKET_DB = 0xf00, - MAX_PACKET_VER = 46, + MIN_PACKET_DB = 0x064, + MAX_PACKET_DB = 0xAFF, + MAX_PACKET_VER = 51, MAX_PACKET_POS = 20, }; @@ -73,13 +73,42 @@ enum e_BANKING_DEPOSIT_ACK { BDA_NO_MONEY = 0x2, BDA_OVERFLOW = 0x3, }; - + enum e_BANKING_WITHDRAW_ACK { BWA_SUCCESS = 0x0, BWA_NO_MONEY = 0x1, 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 #define SERVER 0 #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); +/// 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 **/ @@ -892,6 +929,7 @@ void clif_monster_hp_bar( struct mob_data* md, int fd ); * Color Table **/ enum clif_colors { + COLOR_DEFAULT, COLOR_RED, COLOR_WHITE, COLOR_YELLOW, diff --git a/src/map/itemdb.c b/src/map/itemdb.c index b769c7362b..10360ede40 100644 --- a/src/map/itemdb.c +++ b/src/map/itemdb.c @@ -929,6 +929,7 @@ static int itemdb_combo_split_atoi (char *str, int *val) { return i; } + /** * ,<{ script }> **/ @@ -1056,7 +1057,101 @@ static void itemdb_read_combos(const char* basedir, bool silent) { 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] @@ -1074,6 +1169,7 @@ static char itemdb_gendercheck(struct item_data *id) return (battle_config.ignore_items_gender) ? 2 : id->sex; } + /** * [RRInd] * For backwards compatibility, in Renewal mode, MATK from weapons comes from the atk slot @@ -1579,6 +1675,9 @@ void itemdb_reload(void) { itemdb_read(); cashshop_reloaddb(); + if (!itemdb_parse_roulette_db()) + battle_config.feature_roulette = 0; + //Epoque's awesome @reloaditemdb fix - thanks! [Ind] //- Fixes the need of a @reloadmobdb after a @reloaditemdb to re-link monster drop data 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_create_dummy(); itemdb_read(); + + if (!itemdb_parse_roulette_db()) + battle_config.feature_roulette = 0; } diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 7dfec962a4..82a6d8df55 100644 --- a/src/map/itemdb.h +++ b/src/map/itemdb.h @@ -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_ROULETTE_LEVEL 7 /** client-defined value **/ +#define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/ + #define CARD0_FORGE 0x00FF #define CARD0_CREATE 0x00FE #define CARD0_PET 0x0100 @@ -39,6 +42,7 @@ enum item_itemid ITEMID_YELLOW_POTION = 503, ITEMID_WHITE_POTION = 504, ITEMID_BLUE_POTION = 505, + ITEMID_APPLE = 512, ITEMID_HOLY_WATER = 523, ITEMID_PUMPKIN = 535, 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 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 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); 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 do_final_itemdb(void); diff --git a/src/map/log.c b/src/map/log.c index debf48faed..40d0fb2b25 100644 --- a/src/map/log.c +++ b/src/map/log.c @@ -56,7 +56,7 @@ static char log_picktype2char(e_log_pick_type type) { case LOG_TYPE_TRADE: return 'T'; // (T)rade 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_NPC: return 'S'; // NPC (S)hop 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_CASH: return '$'; // Cash 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 diff --git a/src/map/log.h b/src/map/log.h index ebfc706274..c4a602349e 100644 --- a/src/map/log.h +++ b/src/map/log.h @@ -24,30 +24,31 @@ typedef enum e_log_chat_type typedef enum e_log_pick_type { LOG_TYPE_NONE = 0, - LOG_TYPE_TRADE = 0x00001, - LOG_TYPE_VENDING = 0x00002, - LOG_TYPE_PICKDROP_PLAYER = 0x00004, - LOG_TYPE_PICKDROP_MONSTER = 0x00008, - LOG_TYPE_NPC = 0x00010, - LOG_TYPE_SCRIPT = 0x00020, - LOG_TYPE_STEAL = 0x00040, - LOG_TYPE_CONSUME = 0x00080, - LOG_TYPE_PRODUCE = 0x00100, - LOG_TYPE_MVP = 0x00200, - LOG_TYPE_COMMAND = 0x00400, - LOG_TYPE_STORAGE = 0x00800, - LOG_TYPE_GSTORAGE = 0x01000, - LOG_TYPE_MAIL = 0x02000, - LOG_TYPE_AUCTION = 0x04000, - LOG_TYPE_BUYING_STORE = 0x08000, - LOG_TYPE_OTHER = 0x10000, - LOG_TYPE_CASH = 0x20000, - LOG_TYPE_BANK = 0x40000, - LOG_TYPE_BOUND_REMOVAL = 0x80000, + LOG_TYPE_TRADE = 0x000001, + LOG_TYPE_VENDING = 0x000002, + LOG_TYPE_PICKDROP_PLAYER = 0x000004, + LOG_TYPE_PICKDROP_MONSTER = 0x000008, + LOG_TYPE_NPC = 0x000010, + LOG_TYPE_SCRIPT = 0x000020, + LOG_TYPE_STEAL = 0x000040, + LOG_TYPE_CONSUME = 0x000080, + LOG_TYPE_PRODUCE = 0x000100, + LOG_TYPE_MVP = 0x000200, + LOG_TYPE_COMMAND = 0x000400, + LOG_TYPE_STORAGE = 0x000800, + LOG_TYPE_GSTORAGE = 0x001000, + LOG_TYPE_MAIL = 0x002000, + LOG_TYPE_AUCTION = 0x004000, + LOG_TYPE_BUYING_STORE = 0x008000, + LOG_TYPE_OTHER = 0x010000, + LOG_TYPE_CASH = 0x020000, + LOG_TYPE_BANK = 0x040000, + LOG_TYPE_BOUND_REMOVAL = 0x080000, + LOG_TYPE_ROULETTE = 0x100000, // combinations LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME, // all - LOG_TYPE_ALL = 0xFFFFF, + LOG_TYPE_ALL = 0xFFFFFF, } e_log_pick_type; typedef enum e_log_cash_type diff --git a/src/map/map.c b/src/map/map.c index d9c0b0bf32..88aa384828 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -70,6 +70,7 @@ char mob_skill_db2_db[32] = "mob_skill_db2"; char vendings_db[32] = "vendings"; char vending_items_db[32] = "vending_items"; char market_table[32] = "market"; +char db_roulette_table[32] = "db_roulette"; // log database 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 ) strcpy( vendings_db, w2 ); 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) strcpy(market_table, w2); else diff --git a/src/map/map.h b/src/map/map.h index 336c55e603..f2b5f0941f 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -459,6 +459,9 @@ enum _sp { SP_CHARRENAME=125, SP_CHARFONT=126, SP_BANK_VAULT = 127, + SP_ROULETTE_BRONZE = 128, + SP_ROULETTE_SILVER = 129, + SP_ROULETTE_GOLD = 130, // Mercenaries 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 vending_items_db[32]; extern char market_table[32]; +extern char db_roulette_table[32]; void do_shutdown(void); diff --git a/src/map/pc.c b/src/map/pc.c index bcebb33354..140f0251ba 100755 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -1344,6 +1344,12 @@ void pc_reg_received(struct map_session_data *sd) if (battle_config.feature_banking) 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] for(i=0;istatus.character_moves; break; case SP_CHARRENAME: val = sd->status.rename; 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_ASPD: val = (2000-sd->battle_status.amotion)/10; 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); pc_setreg2(sd, BANK_VAULT_VAR, sd->bank_vault); 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: ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); return false; diff --git a/src/map/pc.h b/src/map/pc.h index d69cc6979e..65f8590837 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -31,6 +31,9 @@ #define MAX_SPIRITCHARM 10 /// Max spirit charms #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 //Raised to 84 since Expanded Super Novice needs it. @@ -640,6 +643,17 @@ struct map_session_data { #ifdef PACKET_OBFUSCATION unsigned int cryptKey; ///< Packet obfuscation key to be used for the next received packet #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 diff --git a/src/map/script.c b/src/map/script.c index 8862c3585a..f6115e9338 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -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({}); - **/ + */ BUILDIN_FUNC(changesex) { int i; @@ -10994,12 +10994,42 @@ BUILDIN_FUNC(changesex) pc_resetskill(sd,4); // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for( i=0; iequip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3); - chrif_changesex(sd); + for(i = 0; i < EQI_MAX; i++) { + if (sd->equip_index[i] >= 0) + pc_unequipitem(sd, sd->equip_index[i], 3); + } + + chrif_changesex(sd, true); return SCRIPT_CMD_SUCCESS; } +/** + * Change character's sex and unequip all item and request for a changesex to char-serv + * changecharsex({}); + */ +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 *------------------------------------------*/ @@ -20486,6 +20516,7 @@ struct script_function buildin_func[] = { BUILDIN_DEF(skillpointcount,"?"), BUILDIN_DEF(changebase,"i?"), BUILDIN_DEF(changesex,"?"), + BUILDIN_DEF(changecharsex,"?"), BUILDIN_DEF(waitingroom,"si?????"), BUILDIN_DEF(delwaitingroom,"?"), BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),