Pārlūkot izejas kodu

Refactored roulette system (#3271)

Fixes #2887 - thanks to @Everade
Fixes #3292 - thanks to @admkakaroto

Added a script command to open the roulette window from server side.
Added support for clients >= 2018-05-16
Fixed losing behavior for roulette
Enabled roulette system by default
Added a server side delay
Added missing sql log enum value 'Y'

Thanks to @Everade, @admkakaroto, @V0rr, @ecdarreola, @Haikenz and everyone else who contributed to this.
Lemongrass3110 6 gadi atpakaļ
vecāks
revīzija
fe7c0a78ec

+ 1 - 2
conf/battle/feature.conf

@@ -61,8 +61,7 @@ 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
+feature.roulette: on
 
 // Achievement (Note 1)
 // Requires: 2015-05-13aRagexe or later

+ 4 - 0
db/pre-re/item_db.txt

@@ -5142,6 +5142,7 @@
 12580,Vending_Search_Scroll,Universal Catalog Silver,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,0; },{},{}
 12581,Vending_Search_Scroll2,Universal Catalog Gold,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,1; },{},{}
 12591,Uni_Catalog_Bz,Universal Catalog Bronze,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ searchstores 10,1; },{},{}
+12609,Old_Ore_Box,Old Ore Box,2,20,,100,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12701,Old_Blue_Box_F,Old Blue Box,2,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
 12702,Old_Bleu_Box,Old Navy Box,2,0,,200,,,,,0xFFFFFFFF,7,2,,,,,,{ getrandgroupitem(IG_BleuBox),1; getrandgroupitem(IG_BleuBox),1; },{},{}
 12703,Holy_Egg_2,Holy Egg,11,0,,50,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
@@ -5221,6 +5222,7 @@
 12771,Passion_Hat_Box2,Passion Hat Box2,2,0,,0,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
 12772,Cool_Hat_Box2,Cool Hat Box2,2,0,,0,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
 12773,Victory_Hat_Box2,Victory Hat Box2,2,0,,0,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{}
+12831,Potion_Box,Potion Box,2,0,,50,,,,,0xFFFFFFFF,63,2,,,,,,{},{},{}
 12848,Falcon_Flute,Falcon Flute,11,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ if(getskilllv("HT_FALCON")) { if(!checkoption(Option_Wug) && !checkoption(Option_Wugrider)) setfalcon (!checkfalcon()); } },{},{}
 12900,Battle_Manual_Box,Battle Manual Box,18,20,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12208,10; },{},{}
 12901,Insurance_Package,Insurance Package,18,20,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12209,10; },{},{}
@@ -6579,3 +6581,5 @@
 19505,T_Cigarette,T Cigarette,4,0,,0,,0,,0,0xFFFFFFFF,7,2,4096,,0,0,54,{},{},{}
 19506,T_Valkyrie_Feather_Band,T Valkyrie Feather Band,4,0,,0,,0,,0,0xFFFFFFFF,7,2,1024,,0,1,300,{},{},{}
 19507,Fine_Sun,Clear Sun,4,0,,0,,0,,0,0xFFFFFFFF,7,2,1024,,1,0,654,{},{},{}
+
+22777,Gift_Buff_Set,Gift Buff Set,2,10,,100,,,,0,0xFFFFFFFF,63,2,,,1,,,{},{},{}

+ 7 - 0
doc/script_commands.txt

@@ -8664,6 +8664,13 @@ This command will open a book item at the specified page.
 
 ---------------------------------------
 
+*open_roulette( {char_id} )
+
+Opens the roulette window for the currently attached character or the character
+with the given character id.
+
+---------------------------------------
+
 ========================
 |7.- Instance commands.|
 ========================

+ 1 - 1
sql-files/logs.sql

@@ -170,7 +170,7 @@ CREATE TABLE IF NOT EXISTS `picklog` (
   `id` int(11) NOT NULL auto_increment,
   `time` datetime NOT NULL,
   `char_id` int(11) NOT NULL default '0',
-  `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Z','Q') NOT NULL default 'P',
+  `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q') NOT NULL default 'P',
   `nameid` smallint(5) unsigned NOT NULL default '0',
   `amount` int(11) NOT NULL default '1',
   `refine` tinyint(3) unsigned NOT NULL default '0',

+ 2 - 0
sql-files/upgrades/upgrade_20180705_logs.sql

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

+ 125 - 119
src/map/clif.cpp

@@ -19260,19 +19260,12 @@ void DumpUnknown(int fd,TBL_PC *sd,int cmd,int packet_len)
 /// Roulette System
 /// Author: Yommy
 
-/**
- * Opens Roulette window
- * @param fd
- * @param sd
- */
-void clif_parse_RouletteOpen(int fd, struct map_session_data* sd)
-{
+/// Opens the roulette window
+/// 0A1A <result>.B <serial>.L <stage>.B <price index>.B <additional item id>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_OPEN_ROULETTE)
+void clif_roulette_open( struct map_session_data* sd ){
 	nullpo_retv(sd);
 
-	if (!battle_config.feature_roulette) {
-		clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //Roulette is disabled
-		return;
-	}
+	int fd = sd->fd;
 
 	WFIFOHEAD(fd,packet_len(0xa1a));
 	WFIFOW(fd,0) = 0xa1a;
@@ -19287,16 +19280,9 @@ void clif_parse_RouletteOpen(int fd, struct map_session_data* sd)
 	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);
-
+/// Request to open the roulette window
+/// 0A19 (CZ_REQ_OPEN_ROULETTE)
+void clif_parse_roulette_open( int fd, struct map_session_data* sd ){
 	nullpo_retv(sd);
 
 	if (!battle_config.feature_roulette) {
@@ -19304,32 +19290,53 @@ void clif_parse_RouletteInfo(int fd, struct map_session_data* sd)
 		return;
 	}
 
+	clif_roulette_open(sd);
+}
+
+/// Sends the info about the available roulette rewards to the client
+/// 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.W <amount>.W } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO)
+/// 0A1C <length>.W <serial>.L { { <level>.W <column>.W <item>.L <amount>.L } * MAX_ROULETTE_COLUMNS } * MAX_ROULETTE_LEVEL (ZC_ACK_ROULEITTE_INFO) >= 20180516
+void clif_roulette_info( struct map_session_data* sd ){
+	nullpo_retv(sd);
+
+	int fd = sd->fd;
+	int len = 8; // Initialize to header size
+#if PACKETVER < 20180516
+	int size = 8;
+#else
+	int size = 12;
+#endif
+
+	for( int i = 0; i < MAX_ROULETTE_LEVEL; i++ ){
+		len += (MAX_ROULETTE_COLUMNS - i) * size;
+	}
+
 	WFIFOHEAD(fd,len);
 	WFIFOW(fd,0) = 0xa1c;
 	WFIFOW(fd,2) = len;
 	WFIFOL(fd,4) = 1; // serial
 
-	for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
-		for(j = 0; j < MAX_ROULETTE_COLUMNS - i; j++) {
-			WFIFOW(fd,8 * count + 8) = i;
-			WFIFOW(fd,8 * count + 10) = j;
-			WFIFOW(fd,8 * count + 12) = rd.nameid[i][j];
-			WFIFOW(fd,8 * count + 14) = rd.qty[i][j];
-			count++;
+	for(int i = 0, offset = 8; i < MAX_ROULETTE_LEVEL; i++) {
+		for(int j = 0; j < MAX_ROULETTE_COLUMNS - i; j++) {
+			WFIFOW(fd,offset + 0) = i;
+			WFIFOW(fd,offset + 2) = j;
+#if PACKETVER < 20180516
+			WFIFOW(fd,offset + 4) = rd.nameid[i][j];
+			WFIFOW(fd,offset + 6) = rd.qty[i][j];
+#else
+			WFIFOL(fd,offset + 4) = rd.nameid[i][j];
+			WFIFOL(fd,offset + 8) = rd.qty[i][j];
+#endif
+			offset += size;
 		}
 	}
 
 	WFIFOSET(fd,len);
-	return;
 }
 
-/**
- * Closes Roulette window
- * @param fd
- * @param sd
- */
-void clif_parse_RouletteClose(int fd, struct map_session_data* sd)
-{
+/// Request the roulette reward data
+/// 0A1B (CZ_REQ_ROULETTE_INFO)
+void clif_parse_roulette_info( int fd, struct map_session_data* sd ){
 	nullpo_retv(sd);
 
 	if (!battle_config.feature_roulette) {
@@ -19337,27 +19344,35 @@ void clif_parse_RouletteClose(int fd, struct map_session_data* sd)
 		return;
 	}
 
-	/** What do we need this for? (other than state tracking), game client closes the window without our response. **/
-	return;
+	clif_roulette_info(sd);
 }
 
-/**
- *
- **/
+/// Notification of the client that the roulette window was closed
+/// 0A1D (CZ_REQ_CLOSE_ROULETTE)
+void clif_parse_roulette_close( int fd, struct map_session_data* sd ){
+	nullpo_retv(sd);
+
+	if (!battle_config.feature_roulette) {
+		clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //Roulette is disabled
+		return;
+	}
+
+	// What do we need this for? (other than state tracking), game client closes the window without our response.
+}
+
+/// Response to a item reward request
+/// 0A22 <type>.B <bonus item>.W (ZC_RECV_ROULETTE_ITEM)
 static void clif_roulette_recvitem_ack(struct map_session_data *sd, enum RECV_ROULETTE_ITEM_REQ type) {
 #if PACKETVER >= 20141016
-	uint16 cmd = 0xa22;
-	unsigned char buf[5];
-
 	nullpo_retv(sd);
 
-	if (packet_db[cmd].len == 0)
-		return;
+	int fd = sd->fd;
 
-	WBUFW(buf,0) = cmd;
-	WBUFB(buf,2) = type;
-	WBUFW(buf,3) = 0; //! TODO: Additional item
-	clif_send(buf, sizeof(buf), &sd->bl, SELF);
+	WFIFOHEAD(fd,packet_len(0xa22));
+	WFIFOW(fd,0) = 0xa22;
+	WFIFOB(fd,2) = type;
+	WFIFOW(fd,3) = 0; //! TODO: Additional item
+	WFIFOSET(fd,packet_len(0xa22));
 #endif
 }
 
@@ -19391,15 +19406,29 @@ static uint8 clif_roulette_getitem(struct map_session_data *sd) {
 	return res;
 }
 
-/**
- * Process the stage and attempt to give a prize
- * @param fd
- * @param sd
- */
-void clif_parse_RouletteGenerate(int fd, struct map_session_data* sd)
-{
-	enum GENERATE_ROULETTE_ACK result = GENERATE_ROULETTE_SUCCESS;
-	short stage = sd->roulette.stage;
+/// Update Roulette window with current stats
+/// 0A20 <result>.B <stage>.W <price index>.W <bonus item>.W <gold>.L <silver>.L <bronze>.L (ZC_ACK_GENERATE_ROULETTE)
+void clif_roulette_generate( struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID ){
+	nullpo_retv(sd);
+
+	int fd = sd->fd;
+
+	WFIFOHEAD(fd,packet_len(0xa20));
+	WFIFOW(fd,0) = 0xa20;
+	WFIFOB(fd,2) = result;
+	WFIFOW(fd,3) = stage;
+	WFIFOW(fd,5) = prizeIdx;
+	WFIFOW(fd,7) = bonusItemID;
+	WFIFOL(fd,9) = sd->roulette_point.gold;
+	WFIFOL(fd,13) = sd->roulette_point.silver;
+	WFIFOL(fd,17) = sd->roulette_point.bronze;
+	WFIFOSET(fd,packet_len(0xa20));
+}
+
+/// Request to start the roulette
+/// 0A1F (CZ_REQ_GENERATE_ROULETTE)
+void clif_parse_roulette_generate( int fd, struct map_session_data* sd ){
+	enum GENERATE_ROULETTE_ACK result;
 
 	nullpo_retv(sd);
 
@@ -19408,55 +19437,55 @@ void clif_parse_RouletteGenerate(int fd, struct map_session_data* sd)
 		return;
 	}
 
-	if (sd->roulette.stage >= MAX_ROULETTE_LEVEL)
-		stage = sd->roulette.stage = 0;
+	if( sd->roulette.tick && DIFF_TICK( sd->roulette.tick, gettick() ) > 0 ){
+		return;
+	}
 
-	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 (sd->roulette.stage >= MAX_ROULETTE_LEVEL){
+		sd->roulette.stage = 0;
+		sd->roulette.claimPrize = false;
+		sd->roulette.prizeStage = 0;
+		sd->roulette.prizeIdx = -1;
 	}
 
-	if (result == GENERATE_ROULETTE_SUCCESS) {
-		if (!stage) {
+	if( !sd->roulette.stage && sd->roulette_point.bronze <= 0 && sd->roulette_point.silver < 10 && sd->roulette_point.gold < 10 ){
+		result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
+	}else{
+		if (!sd->roulette.stage) {
 			if (sd->roulette_point.bronze > 0) {
 				sd->roulette_point.bronze -= 1;
 				pc_setreg2(sd, ROULETTE_BRONZE_VAR, sd->roulette_point.bronze);
 			} else if (sd->roulette_point.silver > 9) {
 				sd->roulette_point.silver -= 10;
-				stage = sd->roulette.stage = 2;
+				sd->roulette.stage = 2;
 				pc_setreg2(sd, ROULETTE_SILVER_VAR, sd->roulette_point.silver);
 			} else if (sd->roulette_point.gold > 9) {
 				sd->roulette_point.gold -= 10;
-				stage = sd->roulette.stage = 4;
+				sd->roulette.stage = 4;
 				pc_setreg2(sd, ROULETTE_GOLD_VAR, sd->roulette_point.gold);
 			}
 		}
 
-		sd->roulette.prizeStage = stage;
-		sd->roulette.prizeIdx = rnd()%rd.items[stage];
+		sd->roulette.prizeStage = sd->roulette.stage;
+		sd->roulette.prizeIdx = rnd()%rd.items[sd->roulette.stage];
+		sd->roulette.claimPrize = true;
+		sd->roulette.tick = gettick() + max( 1, ( MAX_ROULETTE_COLUMNS - sd->roulette.prizeStage - 3 ) ) * 1000;
 
-		if (rd.flag[stage][sd->roulette.prizeIdx]&1) {
-			clif_roulette_generate_ack(sd,GENERATE_ROULETTE_LOSING,stage,sd->roulette.prizeIdx,0);
-			clif_roulette_getitem(sd);
-			clif_roulette_recvitem_ack(sd, RECV_ITEM_SUCCESS);
-			return;
-		}
-		else {
-			sd->roulette.claimPrize = true;
+		if( rd.flag[sd->roulette.stage][sd->roulette.prizeIdx]&1 ){
+			result = GENERATE_ROULETTE_LOSING;
+			sd->roulette.stage = 0;
+		}else{
+			result = GENERATE_ROULETTE_SUCCESS;
 			sd->roulette.stage++;
 		}
 	}
 
-	clif_roulette_generate_ack(sd,result,stage,(sd->roulette.prizeIdx == -1 ? 0 : sd->roulette.prizeIdx),0);
+	clif_roulette_generate(sd,result,sd->roulette.prizeStage,(sd->roulette.prizeIdx == -1 ? 0 : sd->roulette.prizeIdx),0);
 }
 
-/**
- * Request to cash in prize
- * @param fd
- * @param sd
- */
-void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd)
-{
+/// Request to claim a prize
+/// 0A21 (CZ_RECV_ROULETTE_ITEM)
+void clif_parse_roulette_item( int fd, struct map_session_data* sd ){
 	enum RECV_ROULETTE_ITEM_REQ type = RECV_ITEM_FAILED;
 	nullpo_retv(sd);
 
@@ -19465,20 +19494,24 @@ void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd)
 		return;
 	}
 
+	if( sd->roulette.tick && DIFF_TICK( sd->roulette.tick, gettick() ) > 0 ){
+		return;
+	}
+
 	if (sd->roulette.claimPrize && sd->roulette.prizeIdx != -1) {
 		switch (clif_roulette_getitem(sd)) {
-			case 0:
+			case ADDITEM_SUCCESS:
 				type = RECV_ITEM_SUCCESS;
 				break;
-			case 1:
-			case 4:
-			case 5:
+			case ADDITEM_INVALID:
+			case ADDITEM_OVERITEM:
+			case ADDITEM_OVERAMOUNT:
 				type = RECV_ITEM_OVERCOUNT;
 				break;
-			case 2:
+			case ADDITEM_OVERWEIGHT:
 				type = RECV_ITEM_OVERWEIGHT;
 				break;
-			case 7:
+			case ADDITEM_STACKLIMIT:
 			default:
 				type = RECV_ITEM_FAILED;
 				break;
@@ -19486,33 +19519,6 @@ void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd)
 	}
 
 	clif_roulette_recvitem_ack(sd,type);
-	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));
-	WFIFOW(fd,0) = 0xa20;
-	WFIFOB(fd,2) = result;
-	WFIFOW(fd,3) = stage;
-	WFIFOW(fd,5) = prizeIdx;
-	WFIFOW(fd,7) = bonusItemID;
-	WFIFOL(fd,9) = sd->roulette_point.gold;
-	WFIFOL(fd,13) = sd->roulette_point.silver;
-	WFIFOL(fd,17) = sd->roulette_point.bronze;
-	WFIFOSET(fd,packet_len(0xa20));
 }
 
 /// MERGE ITEM

+ 6 - 6
src/map/clif.hpp

@@ -993,12 +993,12 @@ 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);
+void clif_roulette_open(struct map_session_data* sd);
+void clif_parse_roulette_open(int fd, struct map_session_data *sd);
+void clif_parse_roulette_info(int fd, struct map_session_data *sd);
+void clif_parse_roulette_close(int fd, struct map_session_data *sd);
+void clif_parse_roulette_generate(int fd, struct map_session_data *sd);
+void clif_parse_roulette_item(int fd, struct map_session_data *sd);
 
 int clif_elementalconverter_list(struct map_session_data *sd);
 

+ 5 - 5
src/map/clif_packetdb.hpp

@@ -2199,15 +2199,15 @@
 	packet(0x09E5,18); // ZC_DELETEITEM_FROM_MCSTORE2
 	packet(0x09E6,22); // ZC_UPDATE_ITEM_FROM_BUYING_STORE2
 	// Roulette System [Yommy]
-	parseable_packet(0x0A19,2,clif_parse_RouletteOpen,0); // CZ_REQ_OPEN_ROULETTE
+	parseable_packet(0x0A19,2,clif_parse_roulette_open,0); // CZ_REQ_OPEN_ROULETTE
 	packet(0x0A1A,23); // ZC_ACK_OPEN_ROULETTE
-	parseable_packet(0x0A1B,2,clif_parse_RouletteInfo,0); // CZ_REQ_ROULETTE_INFO
+	parseable_packet(0x0A1B,2,clif_parse_roulette_info,0); // CZ_REQ_ROULETTE_INFO
 	packet(0x0A1C,-1); // ZC_ACK_ROULETTE_INFO
-	parseable_packet(0x0A1D,2,clif_parse_RouletteClose,0); // CZ_REQ_CLOSE_ROULETTE
+	parseable_packet(0x0A1D,2,clif_parse_roulette_close,0); // CZ_REQ_CLOSE_ROULETTE
 	packet(0x0A1E,3); // ZC_ACK_CLOSE_ROULETTE
-	parseable_packet(0x0A1F,2,clif_parse_RouletteGenerate,0); // CZ_REQ_GENERATE_ROULETTE
+	parseable_packet(0x0A1F,2,clif_parse_roulette_generate,0); // CZ_REQ_GENERATE_ROULETTE
 	packet(0x0A20,21); // ZC_ACK_GENERATE_ROULETTE
-	parseable_packet(0x0A21,3,clif_parse_RouletteRecvItem,2); // CZ_RECV_ROULETTE_ITEM
+	parseable_packet(0x0A21,3,clif_parse_roulette_item,2); // CZ_RECV_ROULETTE_ITEM
 	packet(0x0A22,5); // ZC_RECV_ROULETTE_ITEM
 #endif
 

+ 1 - 1
src/map/itemdb.cpp

@@ -1120,7 +1120,7 @@ bool itemdb_parse_roulette_db(void)
 				ShowWarning("itemdb_parse_roulette_db: Unknown item ID '%hu' in level '%d'\n", item_id, level);
 				continue;
 			}
-			if (amount < 1) {
+			if (amount < 1 || amount > MAX_AMOUNT){
 				ShowWarning("itemdb_parse_roulette_db: Unsupported amount '%hu' for item ID '%hu' in level '%d'\n", amount, item_id, level);
 				continue;
 			}

+ 1 - 0
src/map/pc.hpp

@@ -729,6 +729,7 @@ struct map_session_data {
 		int8 prizeIdx;
 		short prizeStage;
 		bool claimPrize;
+		unsigned int tick;
 	} roulette;
 
 	unsigned short instance_id;

+ 23 - 0
src/map/script.cpp

@@ -24010,6 +24010,28 @@ BUILDIN_FUNC(mail){
 	return SCRIPT_CMD_SUCCESS;
 }
 
+BUILDIN_FUNC(open_roulette){
+#if PACKETVER < 20141022
+	ShowError( "buildin_open_roulette: This command requires PACKETVER 2014-10-22 or newer.\n" );
+	return SCRIPT_CMD_FAILURE;
+#else
+	struct map_session_data* sd;
+
+	if( !battle_config.feature_roulette ){
+		ShowError( "buildin_open_roulette: Roulette system is disabled.\n" );
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	if( !script_charid2sd( 2, sd ) ){
+		return SCRIPT_CMD_FAILURE;
+	}
+
+	clif_roulette_open(sd);
+
+	return SCRIPT_CMD_SUCCESS;
+#endif
+}
+
 #include "../custom/script.inc"
 
 // declarations that were supposed to be exported from npc_chat.c
@@ -24662,6 +24684,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF2(round, "floor", "i"),
 	BUILDIN_DEF(getequiptradability, "i?"),
 	BUILDIN_DEF(mail, "isss*"),
+	BUILDIN_DEF(open_roulette,"?"),
 #include "../custom/script_def.inc"
 
 	{NULL,NULL,NULL},