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.
This commit is contained in:
Lemongrass3110 2018-07-06 22:39:58 +02:00 committed by GitHub
parent d612788ed7
commit fe7c0a78ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 216 additions and 174 deletions

View File

@ -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

View File

@ -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,,,{},{},{}

View File

@ -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.|
========================

View File

@ -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',

View File

@ -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';

View File

@ -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);
}
/**
*
**/
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];
/// 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 (packet_db[cmd].len == 0)
if (!battle_config.feature_roulette) {
clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //Roulette is disabled
return;
}
WBUFW(buf,0) = cmd;
WBUFB(buf,2) = type;
WBUFW(buf,3) = 0; //! TODO: Additional item
clif_send(buf, sizeof(buf), &sd->bl, SELF);
// 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
nullpo_retv(sd);
int fd = sd->fd;
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,118 +19406,13 @@ 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);
if (!battle_config.feature_roulette) {
clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //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;
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;
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;
pc_setreg2(sd, ROULETTE_GOLD_VAR, sd->roulette_point.gold);
}
}
sd->roulette.prizeStage = stage;
sd->roulette.prizeIdx = rnd()%rd.items[stage];
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;
sd->roulette.stage++;
}
}
clif_roulette_generate_ack(sd,result,stage,(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)
{
enum RECV_ROULETTE_ITEM_REQ type = RECV_ITEM_FAILED;
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;
}
if (sd->roulette.claimPrize && sd->roulette.prizeIdx != -1) {
switch (clif_roulette_getitem(sd)) {
case 0:
type = RECV_ITEM_SUCCESS;
break;
case 1:
case 4:
case 5:
type = RECV_ITEM_OVERCOUNT;
break;
case 2:
type = RECV_ITEM_OVERWEIGHT;
break;
case 7:
default:
type = RECV_ITEM_FAILED;
break;
}
}
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;
@ -19515,6 +19425,102 @@ void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char resul
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);
if (!battle_config.feature_roulette) {
clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //Roulette is disabled
return;
}
if( sd->roulette.tick && DIFF_TICK( sd->roulette.tick, gettick() ) > 0 ){
return;
}
if (sd->roulette.stage >= MAX_ROULETTE_LEVEL){
sd->roulette.stage = 0;
sd->roulette.claimPrize = false;
sd->roulette.prizeStage = 0;
sd->roulette.prizeIdx = -1;
}
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;
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;
sd->roulette.stage = 4;
pc_setreg2(sd, ROULETTE_GOLD_VAR, sd->roulette_point.gold);
}
}
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[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(sd,result,sd->roulette.prizeStage,(sd->roulette.prizeIdx == -1 ? 0 : sd->roulette.prizeIdx),0);
}
/// 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);
if (!battle_config.feature_roulette) {
clif_messagecolor(&sd->bl,color_table[COLOR_RED],msg_txt(sd,1497),false,SELF); //Roulette is disabled
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 ADDITEM_SUCCESS:
type = RECV_ITEM_SUCCESS;
break;
case ADDITEM_INVALID:
case ADDITEM_OVERITEM:
case ADDITEM_OVERAMOUNT:
type = RECV_ITEM_OVERCOUNT;
break;
case ADDITEM_OVERWEIGHT:
type = RECV_ITEM_OVERWEIGHT;
break;
case ADDITEM_STACKLIMIT:
default:
type = RECV_ITEM_FAILED;
break;
}
}
clif_roulette_recvitem_ack(sd,type);
}
/// MERGE ITEM
/**

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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},