From 239d480487e24294975f35ed55f210837ad1088e Mon Sep 17 00:00:00 2001
From: shennetsind <ind@henn.et>
Date: Mon, 3 Nov 2014 08:10:26 -0200
Subject: Introducing 2014-10-22, Roulette and Per-Char Gender!

Details in http://hercules.ws/board/topic/7618-2014-10-22-roulette-and-per-char-gender/
Special Thanks to the all-mighty Yommy, Ziu and Haruna!

Signed-off-by: shennetsind <ind@henn.et>
---
 src/map/battle.c         |  10 +-
 src/map/battle.h         |   2 +
 src/map/clif.c           | 348 +++++++++++++++++++++++++++++++++++++++++++++--
 src/map/clif.h           |  46 +++++++
 src/map/itemdb.c         |   4 +
 src/map/itemdb.h         |   1 +
 src/map/packets.h        |  90 ++++++++++++
 src/map/packets_struct.h |  62 ++++++++-
 src/map/pc.h             |   7 +
 9 files changed, 555 insertions(+), 15 deletions(-)

(limited to 'src/map')

diff --git a/src/map/battle.c b/src/map/battle.c
index 1b8e44cb3..8aeb22fc2 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -6837,7 +6837,8 @@ static const struct battle_data {
 	{ "song_timer_reset",                   &battle_config.song_timer_reset,                0,      0,      1,              },
 	{ "snap_dodge",                         &battle_config.snap_dodge,                      0,      0,      1,              },
 	{ "monster_chase_refresh",              &battle_config.mob_chase_refresh,               1,      0,      30,             },
-	{ "icewall_walk_block",                 &battle_config.icewall_walk_block,              75,     0,      255,            }
+	{ "icewall_walk_block",                 &battle_config.icewall_walk_block,              75,     0,      255,            },
+	{ "feature.roulette",                   &battle_config.feature_roulette,                1,      0,      1,              }
 };
 #ifndef STATS_OPT_OUT
 /**
@@ -7088,6 +7089,13 @@ void battle_adjust_conf(void) {
 		battle_config.feature_banking = 0;
 	}
 #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
 
 #if PACKETVER > 20120000 && PACKETVER < 20130515 /* exact date (when it started) not known */
 	if( battle_config.feature_auction == 1 ) {
diff --git a/src/map/battle.h b/src/map/battle.h
index 6ac2df391..8164153da 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -479,6 +479,8 @@ struct Battle_Config {
 
 	int song_timer_reset; // [csnv]
 	int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
+	
+	int feature_roulette;
 };
 
 extern struct Battle_Config battle_config;
diff --git a/src/map/clif.c b/src/map/clif.c
index b9cd4cbaf..73fc387e3 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -619,7 +619,9 @@ void clif_authok(struct map_session_data *sd)
 #if PACKETVER >= 20080102
 	p.font = sd->status.font;
 #endif
-
+#if PACKETVER >= 20141016
+	p.sex = sd->status.sex;
+#endif
 	clif->send(&p,sizeof(p),&sd->bl,SELF);
 }
 
@@ -5580,6 +5582,7 @@ void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val
 	p.index = type;
 	p.AID = bl->id;
 	p.state = (unsigned char)flag;
+	
 #if PACKETVER >= 20120618
 	p.Total = tick; /* at this stage remain and total are the same value I believe */
 #endif
@@ -5600,6 +5603,11 @@ void clif_displaymessage(const int fd, const char* mes) {
 	if( map->cpsd_active && fd == 0 ) {
 		ShowInfo("HCP: %s\n",mes);
 	} else if ( fd > 0 ) {
+	#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_DEFAULT,mes);
+	#else
 		size_t len;
 
 		if ( ( len = strnlen(mes, 255) ) > 0 ) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
@@ -5609,6 +5617,7 @@ void clif_displaymessage(const int fd, const char* mes) {
 			safestrncpy((char *)WFIFOP(fd,4), mes, len + 1);
 			WFIFOSET(fd, 5 + len);
 		}
+	#endif
 	}
 }
 void clif_displaymessage2(const int fd, const char* mes) {
@@ -9651,18 +9660,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
@@ -15799,19 +15812,34 @@ void clif_parse_PartyTick(int fd, struct map_session_data* sd)
 /// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
 void clif_quest_send_list(struct map_session_data *sd) {
 	int fd = sd->fd;
-	int i;
-	int len = sd->avail_quests*5+8;
-
+ 	int i;
+#if PACKETVER >= 20141022
+	int info_len = 15;
+	int len = sd->avail_quests*info_len+8;
 	WFIFOHEAD(fd,len);
-	WFIFOW(fd, 0) = 0x2b1;
+	WFIFOW(fd, 0) = 0x97a;
+#else
+	int info_len = 5;
+	int len = sd->avail_quests*info_len+8;
+ 	WFIFOHEAD(fd,len);
+ 	WFIFOW(fd, 0) = 0x2b1;
+#endif
 	WFIFOW(fd, 2) = len;
 	WFIFOL(fd, 4) = sd->avail_quests;
 
 	for( i = 0; i < sd->avail_quests; i++ ) {
-		WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
-		WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
+	#if PACKETVER >= 20141022
+		struct quest_db *qi = quest->db(sd->quest_log[i].quest_id);
+	#endif
+		WFIFOL(fd, i*info_len+8) = sd->quest_log[i].quest_id;
+		WFIFOB(fd, i*info_len+12) = sd->quest_log[i].state;
+	#if PACKETVER >= 20141022
+		WFIFOL(fd, i*info_len+13) = sd->quest_log[i].time - qi->time;
+		WFIFOL(fd, i*info_len+17) = sd->quest_log[i].time;
+		WFIFOW(fd, i*info_len+21) = qi->num_objectives;
+	#endif
 	}
-
+	
 	WFIFOSET(fd, len);
 }
 
@@ -18270,7 +18298,285 @@ void clif_PartyLeaderChanged(struct map_session_data *sd, int prev_leader_aid, i
 	
 	clif->send(&p,sizeof(p),&sd->bl,PARTY);
 }
+
+/* Roulette System [Yommy/Hercules] */
+void clif_parse_RouletteOpen(int fd, struct map_session_data* sd) {
+	struct packet_roulette_open_ack p;
 	
+	if( !battle_config.feature_roulette ) {
+		clif->message(fd,"Roulette is disabled");
+		return;
+	}
+	
+	p.PacketType = 0xa1a;
+	p.Result = 0;
+	p.Serial = 0;
+	p.Step = sd->roulette.stage - 1;
+	p.Idx = sd->roulette.prizeIdx;
+	p.AdditionItemID = -1; /** TODO **/
+	p.BronzePoint = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze"));
+	p.GoldPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteGold"));
+	p.SilverPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver"));
+
+	clif->send(&p,sizeof(p), &sd->bl, SELF);
+}
+void clif_parse_RouletteInfo(int fd, struct map_session_data* sd) {
+	struct packet_roulette_info_ack p;
+	unsigned short i, j, count = 0;
+
+	if( !battle_config.feature_roulette ) {
+		clif->message(fd,"Roulette is disabled");
+		return;
+	}
+	
+	p.PacketType = rouletteinfoackType;
+	p.PacketLength = 8 + (42 * 8);
+	p.RouletteSerial = 1;
+	
+	for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+		for(j = 0; j < MAX_ROULETTE_COLUMNS-i; j++) {
+			p.ItemInfo[count].Row = i;
+			p.ItemInfo[count].Position = j;
+			p.ItemInfo[count].ItemId = clif->rd.nameid[i][j];
+			p.ItemInfo[count].Count = clif->rd.qty[i][j];
+			count++;
+		}
+	}
+	
+	clif->send(&p,sizeof(p), &sd->bl, SELF);
+	return;
+}
+void clif_parse_RouletteClose(int fd, struct map_session_data* sd) {
+	
+	if( !battle_config.feature_roulette ) {
+		clif->message(fd,"Roulette is disabled");
+		return;
+	}
+	
+		
+	/** What do we need this for? (other than state tracking), game client closes the window without our response. **/
+	
+	//ShowDebug("clif_parse_RouletteClose\n");
+	
+	return;
+}
+void clif_parse_RouletteGenerate(int fd, struct map_session_data* sd) {
+	unsigned char result = GENERATE_ROULETTE_SUCCESS;
+	short stage = sd->roulette.stage;
+	
+	if( !battle_config.feature_roulette ) {
+		clif->message(fd,"Roulette is disabled");
+		return;
+	}
+		
+	if( sd->roulette.stage >= MAX_ROULETTE_LEVEL )
+		stage = sd->roulette.stage = 0;
+	
+	if( stage <= 2 ) {
+		if( pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) <= 0 )
+			result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
+	} else if ( stage <= 4 ) {
+		if( pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) <= 0 )
+			result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
+	} else if ( stage <= 6 ) {
+		if( pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) <= 0 )
+			result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
+	}
+	
+	if( result == GENERATE_ROULETTE_SUCCESS ) {
+		
+		if( stage <= 2 ) {
+			pc_setglobalreg(sd, script->add_str("TmpRouletteBronze"), pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) - 1);
+		} else if ( stage <= 4 ) {
+			pc_setglobalreg(sd, script->add_str("TmpRouletteSilver"), pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) - 1);
+		} else if ( stage <= 6 ) {
+			pc_setglobalreg(sd, script->add_str("TmpRouletteGold"), pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) - 1);
+		}
+		
+		sd->roulette.prizeStage = stage;
+		sd->roulette.prizeIdx = rnd()%clif->rd.items[stage];
+		if( sd->roulette.prizeIdx == 0 ) {
+			struct item it;
+			memset(&it, 0, sizeof(it));
+
+			it.nameid = clif->rd.nameid[stage][0];
+			it.identify = 1;
+			
+			pc->additem(sd, &it, clif->rd.qty[stage][0], LOG_TYPE_OTHER);/** TODO maybe a new log type for roulette items? **/
+			
+			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!
+ **/
+void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd) {
+	struct packet_roulette_itemrecv_ack p;
+	
+	if( !battle_config.feature_roulette ) {
+		clif->message(fd,"Roulette is disabled");
+		return;
+	}
+	
+	p.PacketType = roulettercvitemackType;
+	p.AdditionItemID = 0;/** TODO **/
+	
+	if( sd->roulette.claimPrize ) {
+		struct item it;
+		memset(&it, 0, sizeof(it));
+		
+		it.nameid = clif->rd.nameid[sd->roulette.prizeStage][sd->roulette.prizeIdx];
+		it.identify = 1;
+
+		switch (pc->additem(sd, &it, clif->rd.qty[sd->roulette.prizeStage][sd->roulette.prizeIdx], LOG_TYPE_OTHER)) {
+			case 0:
+				p.Result = 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:
+				p.Result = RECV_ITEM_OVERCOUNT;
+				break;
+			case 2:
+				p.Result = RECV_ITEM_OVERWEIGHT;
+				break;
+			default:
+			case 7:
+				p.Result = RECV_ITEM_FAILED;
+				break;
+		}
+	} else
+		p.Result = RECV_ITEM_FAILED;
+	
+	clif->send(&p,sizeof(p), &sd->bl, SELF);
+	return;
+}
+
+bool clif_parse_roulette_db(void) {
+	config_t roulette_conf;
+	config_setting_t *roulette = NULL, *levels = NULL;
+	const char *config_filename = "db/roulette_db.conf"; // FIXME hardcoded name
+	int i, j, item_count_t = 0;
+	
+	for( i = 0; i < MAX_ROULETTE_LEVEL; i++ ) {
+		clif->rd.items[i] = 0;
+	}
+	
+	if (libconfig->read_file(&roulette_conf, config_filename)) {
+		ShowError("can't read %s\n", config_filename);
+		return false;
+	}
+	
+	roulette = libconfig->lookup(&roulette_conf, "roulette");
+	
+	if( roulette != NULL && (levels = libconfig->setting_get_elem(roulette, 0)) != NULL ) {
+		for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+			config_setting_t *level;
+			char entry_name[10];
+			
+			sprintf(entry_name,"level_%d",i+1);
+			
+			if( (level = libconfig->setting_get_member(levels, entry_name)) != NULL ) {
+				int k, item_count = libconfig->setting_length(level);
+				
+				for(k = 0; k < item_count; k++) {
+					config_setting_t *entry = libconfig->setting_get_elem(level,k);
+					const char *name = config_setting_name(entry);
+					int qty = libconfig->setting_get_int(entry);
+					struct item_data * data = NULL;
+					
+					if( qty < 1 ) {
+						ShowWarning("roulette_db: unsupported qty '%d' for entry named '%s' in category '%s'\n", qty, name, entry_name);
+						continue;
+					}
+					
+					if( name[0] == 'I' && name[1] == 'D' && strlen(name) <= 7 ) {
+						if( !( data = itemdb->exists(atoi(name+2))) ) {
+							ShowWarning("roulette_db: unknown item id '%s' in category '%s'\n", name+2, entry_name);
+							continue;
+						}
+					} else {
+						if( !( data = itemdb->search_name(name) ) ) {
+							ShowWarning("roulette_db: unknown item name '%s' in category '%s'\n", name, entry_name);
+							continue;
+						}
+					}
+					
+					j = clif->rd.items[i];
+					RECREATE(clif->rd.nameid[i],int,++clif->rd.items[i]);
+					RECREATE(clif->rd.qty[i],int,clif->rd.items[i]);
+					
+					clif->rd.nameid[i][j] = data->nameid;
+					clif->rd.qty[i][j] = qty;
+					
+					item_count_t++;
+				}
+			}
+		}
+		
+		libconfig->destroy(&roulette_conf);
+	}
+	
+	for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+		int limit = MAX_ROULETTE_COLUMNS-i;
+		if( clif->rd.items[i] == limit ) continue;
+		
+		if( clif->rd.items[i] > limit ) {
+			ShowWarning("roulette_db: level %d has %d items, only %d supported, capping...\n",i+1,clif->rd.items[i],limit);
+			clif->rd.items[i] = limit;
+			continue;
+		}
+		/** this scenario = clif->rd.items[i] < limit **/
+		ShowWarning("roulette_db: level %d has %d items, %d are required. filling with apples\n",i+1,clif->rd.items[i],limit);
+		
+		clif->rd.items[i] = limit;
+		RECREATE(clif->rd.nameid[i],int,clif->rd.items[i]);
+		RECREATE(clif->rd.qty[i],int,clif->rd.items[i]);
+
+		
+		for(j = 0; j < MAX_ROULETTE_COLUMNS-i; j++) {
+			if( clif->rd.qty[i][j] ) continue;
+			
+			clif->rd.nameid[i][j] = ITEMID_APPLE;
+			clif->rd.qty[i][j] = 1;
+		}
+	}
+
+	
+	ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", item_count_t, config_filename);
+	
+	return true;
+}
+
+/**
+ *
+ **/
+void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID) {
+	struct packet_roulette_generate_ack p;
+
+	p.PacketType = roulettgenerateackType;
+	p.Result = result;
+	p.Step = stage;
+	p.Idx = prizeIdx;
+	p.AdditionItemID = bonusItemID;
+	p.RemainBronze = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze"));
+	p.RemainGold = pc_readglobalreg(sd, script->add_str("TmpRouletteGold"));
+	p.RemainSilver = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver"));
+	
+	clif->send(&p,sizeof(p), &sd->bl, SELF);
+}
+
 /* */
 unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) {
 	if( sd ) {
@@ -18587,6 +18893,13 @@ void do_final_clif(void) {
 		}
 		aFree(clif->cs.data[i]);
 	}
+	
+	for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+		if( clif->rd.nameid[i] )
+			aFree(clif->rd.nameid[i]);
+		if( clif->rd.qty[i] )
+			aFree(clif->rd.qty[i]);
+	}
 
 }
 void clif_defaults(void) {
@@ -19088,6 +19401,9 @@ void clif_defaults(void) {
 	/* NPC Market */
 	clif->npc_market_open = clif_npc_market_open;
 	clif->npc_market_purchase_ack = clif_npc_market_purchase_ack;
+	/* */
+	clif->parse_roulette_db = clif_parse_roulette_db;
+	clif->roulette_generate_ack = clif_roulette_generate_ack;
 	/*------------------------
 	 *- Parse Incoming Packet
 	 *------------------------*/
@@ -19317,6 +19633,12 @@ void clif_defaults(void) {
 	clif->pBankCheck = clif_parse_BankCheck;
 	clif->pBankOpen = clif_parse_BankOpen;
 	clif->pBankClose = clif_parse_BankClose;
+	/* Roulette System [Yommy/Hercules] */
+	clif->pRouletteOpen = clif_parse_RouletteOpen;
+	clif->pRouletteInfo = clif_parse_RouletteInfo;
+	clif->pRouletteClose = clif_parse_RouletteClose;
+	clif->pRouletteGenerate = clif_parse_RouletteGenerate;
+	clif->pRouletteRecvItem = clif_parse_RouletteRecvItem;
 	/* */
 	clif->pNPCShopClosed = clif_parse_NPCShopClosed;
 	/* NPC Market */
diff --git a/src/map/clif.h b/src/map/clif.h
index 1013add85..17fda9a74 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -48,6 +48,8 @@ struct skill_cd;
 #define clif_disp_onlyself(sd,mes,len) clif->disp_message( &(sd)->bl, (mes), (len), SELF )
 #define clif_viewequip_fail( sd ) clif_msg( (sd), 0x54d );
 #define HCHSYS_NAME_LENGTH 20
+#define MAX_ROULETTE_LEVEL 7 /** client-defined value **/
+#define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/
 
 /**
  * Enumerations
@@ -476,6 +478,35 @@ enum e_trade_item_ok {
 	TIO_INDROCKS   = 0x9,
 };
 
+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,
+};
+
 /**
  * Structures
  **/
@@ -552,6 +583,12 @@ struct clif_interface {
 		struct hCSData **data[CASHSHOP_TAB_MAX];
 		unsigned int item_count[CASHSHOP_TAB_MAX];
 	} cs;
+	/* roulette data */
+	struct {
+		int *nameid[MAX_ROULETTE_LEVEL];//nameid
+		int *qty[MAX_ROULETTE_LEVEL];//qty of nameid
+		int items[MAX_ROULETTE_LEVEL];//number of items in the list for each 
+	} rd;
 	/* */
 	unsigned int cryptKey[3];
 	/* */
@@ -1051,6 +1088,9 @@ struct clif_interface {
 	/* NPC Market */
 	void (*npc_market_open) (struct map_session_data *sd, struct npc_data *nd);
 	void (*npc_market_purchase_ack) (struct map_session_data *sd, struct packet_npc_market_purchase *req, unsigned char response);
+	/* */
+	bool (*parse_roulette_db) (void);
+	void (*roulette_generate_ack) (struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID);
 	/*------------------------
 	 *- Parse Incoming Packet
 	 *------------------------*/
@@ -1278,6 +1318,12 @@ struct clif_interface {
 	void (*pBankCheck) (int fd, struct map_session_data *sd);
 	void (*pBankOpen) (int fd, struct map_session_data *sd);
 	void (*pBankClose) (int fd, struct map_session_data *sd);
+	/* Roulette System [Yommy/Hercules] */
+	void (*pRouletteOpen) (int fd, struct map_session_data *sd);
+	void (*pRouletteInfo) (int fd, struct map_session_data *sd);
+	void (*pRouletteClose) (int fd, struct map_session_data *sd);
+	void (*pRouletteGenerate) (int fd, struct map_session_data *sd);
+	void (*pRouletteRecvItem) (int fd, struct map_session_data *sd);
 	/* */
 	void (*pNPCShopClosed) (int fd, struct map_session_data *sd);
 	/* NPC Market (by Ind after an extensive debugging of the packet, only possible thanks to Yommy <3) */
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index e6210f871..41e0a5348 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -2248,6 +2248,10 @@ void do_init_itemdb(bool minimal) {
 		return;
 
 	clif->cashshop_load();
+	
+	/** it failed? we disable it **/
+	if( !clif->parse_roulette_db() )
+		battle_config.feature_roulette = 0;
 }
 void itemdb_defaults(void) {
 	itemdb = &itemdb_s;
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index 198d7a542..e8b8588e9 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -41,6 +41,7 @@ enum item_itemid {
 	ITEMID_YELLOW_POTION         = 503,
 	ITEMID_WHITE_POTION          = 504,
 	ITEMID_BLUE_POTION           = 505,
+	ITEMID_APPLE                 = 512,
 	ITEMID_HOLY_WATER            = 523,
 	ITEMID_RED_SLIM_POTION       = 545,
 	ITEMID_YELLOW_SLIM_POTION    = 546,
diff --git a/src/map/packets.h b/src/map/packets.h
index 699bb3fd2..3240a97a1 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -2800,6 +2800,88 @@ packet(0x020d,-1);
 	packet(0x0438,36,clif->pStoragePassword,0);
 #endif
 
+// 2014-10-16aRagexe - YomRawr
+#if PACKETVER >= 20141016
+	packet(0x0369,7,clif->pActionRequest,2,6);
+	packet(0x083C,10,clif->pUseSkillToId,2,4,6);
+	packet(0x0437,5,clif->pWalkToXY,2);
+	packet(0x035F,6,clif->pTickSend,2);
+	packet(0x0967,5,clif->pChangeDir,2,4);
+	packet(0x07E4,6,clif->pTakeItem,2);
+	packet(0x0362,6,clif->pDropItem,2,4);
+	packet(0x07EC,8,clif->pMoveToKafra,2,4);
+	packet(0x022D,8,clif->pMoveFromKafra,2,4);
+	packet(0x0438,10,clif->pUseSkillToPos,2,4,6,8);
+	packet(0x0366,90,clif->pUseSkillToPosMoreInfo,2,4,6,8,10);
+	packet(0x096A,6,clif->pGetCharNameRequest,2);
+	packet(0x0368,6,clif->pSolveCharName,2);
+	packet(0x0838,12,clif->pSearchStoreInfoListItemClick,2,6,10);
+	packet(0x0835,2,clif->pSearchStoreInfoNextPage,0);
+	packet(0x0819,-1,clif->pSearchStoreInfo,2,4,5,9,13,14,15);
+	packet(0x0811,-1,clif->pReqTradeBuyingStore,2,4,8,12);
+	packet(0x0360,6,clif->pReqClickBuyingStore,2);
+	packet(0x0817,2,clif->pReqCloseBuyingStore,0);
+	packet(0x0815,-1,clif->pReqOpenBuyingStore,2,4,8,9,89);
+	packet(0x0365,18,clif->pPartyBookingRegisterReq,2,4);
+	// packet(0x0363,8); // CZ_JOIN_BATTLE_FIELD
+	packet(0x0281,-1,clif->pItemListWindowSelected,2,4,8);
+	packet(0x086E,19,clif->pWantToConnection,2,6,10,14,18);
+	packet(0x0802,26,clif->pPartyInvite2,2);
+	// packet(0x0922,4); // CZ_GANGSI_RANK
+	packet(0x094B,26,clif->pFriendsListAdd,2);
+	packet(0x0364,5,clif->pHomMenu,2,4);
+	packet(0x0936,36,clif->pStoragePassword,0);
+	packet(0x09DF,7);
+	packet(0x0a00,269);
+#endif
+
+// 2014-10-22bRagexe - YomRawr
+#if PACKETVER >= 20141022
+	packet(0x0369,7,clif->pActionRequest,2,6);
+	packet(0x083C,10,clif->pUseSkillToId,2,4,6);
+	packet(0x0437,5,clif->pWalkToXY,2);
+	packet(0x035F,6,clif->pTickSend,2);
+	packet(0x08AD,5,clif->pChangeDir,2,4);
+	packet(0x094E,6,clif->pTakeItem,2);
+	packet(0x087D,6,clif->pDropItem,2,4);
+	packet(0x0878,8,clif->pMoveToKafra,2,4);
+	packet(0x08AA,8,clif->pMoveFromKafra,2,4);
+	packet(0x023B,10,clif->pUseSkillToPos,2,4,6,8);
+	packet(0x0366,90,clif->pUseSkillToPosMoreInfo,2,4,6,8,10);
+	packet(0x096A,6,clif->pGetCharNameRequest,2);
+	packet(0x0368,6,clif->pSolveCharName,2);
+	packet(0x0835,12,clif->pSearchStoreInfoListItemClick,2,6,10);
+	packet(0x0940,2,clif->pSearchStoreInfoNextPage,0);
+	packet(0x0819,-1,clif->pSearchStoreInfo,2,4,5,9,13,14,15);
+	packet(0x0811,-1,clif->pReqTradeBuyingStore,2,4,8,12);
+	packet(0x0360,6,clif->pReqClickBuyingStore,2);
+	packet(0x0817,2,clif->pReqCloseBuyingStore,0);
+	packet(0x0815,-1,clif->pReqOpenBuyingStore,2,4,8,9,89);
+	packet(0x0955,18,clif->pPartyBookingRegisterReq,2,4);
+	// packet(0x092B,8); // CZ_JOIN_BATTLE_FIELD
+	packet(0x0281,-1,clif->pItemListWindowSelected,2,4,8);
+	packet(0x093B,19,clif->pWantToConnection,2,6,10,14,18);
+	packet(0x0896,26,clif->pPartyInvite2,2);
+	// packet(0x08AB,4); // CZ_GANGSI_RANK
+	packet(0x091A,26,clif->pFriendsListAdd,2);
+	packet(0x0899,5,clif->pHomMenu,2,4);
+	packet(0x0438,36,clif->pStoragePassword,0);
+#endif
+
+/* Roulette System [Yommy/Hercules] */
+#if PACKETVER >= 20141016
+	packet(0x0A19,2,clif->pRouletteOpen,0);     // HEADER_CZ_REQ_OPEN_ROULETTE
+	packet(0x0A1A,23);                          // HEADER_ZC_ACK_OPEN_ROULETTE
+	packet(0x0A1B,2,clif->pRouletteInfo,0);     // HEADER_CZ_REQ_ROULETTE_INFO
+	packet(0x0A1C,-1);                          // HEADER_ZC_ACK_ROULEITTE_INFO
+	packet(0x0A1D,2,clif->pRouletteClose,0);    // HEADER_CZ_REQ_CLOSE_ROULETTE
+	packet(0x0A1E,3);                           // HEADER_ZC_ACK_CLOSE_ROULETTE
+	packet(0x0A1F,2,clif->pRouletteGenerate,0); // HEADER_CZ_REQ_GENERATE_ROULETTE
+	packet(0x0A20,21);                          // HEADER_ZC_ACK_GENERATE_ROULETTE
+	packet(0x0A21,3,clif->pRouletteRecvItem,2); // HEADER_CZ_RECV_ROULETTE_ITEM
+	packet(0x0A22,5);                           // HEADER_ZC_RECV_ROULETTE_ITEM
+#endif
+
 /* PacketKeys: http://hercules.ws/board/topic/1105-hercules-wpe-free-june-14th-patch/ */
 #if PACKETVER >= 20110817
 	packetKeys(0x053D5CED,0x3DED6DED,0x6DED6DED); /* Thanks to Shakto */
@@ -3035,6 +3117,14 @@ packet(0x020d,-1);
 	packetKeys(0x290551EA,0x2B952C75,0x2D67669B); /* YomRawr */
 #endif
 
+#if PACKETVER >= 20141016
+	packetKeys(0x2DFF467C,0x444B37EE,0x2C1B634F); /* YomRawr */
+#endif
+
+#if PACKETVER >= 20141022
+	packetKeys(0x290551EA,0x2B952C75,0x2D67669B); /* YomRawr */
+#endif
+
 #if defined(OBFUSCATIONKEY1) && defined(OBFUSCATIONKEY2) && defined(OBFUSCATIONKEY3)
 	packetKeys(OBFUSCATIONKEY1,OBFUSCATIONKEY2,OBFUSCATIONKEY3);
 #endif
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index 9f9349b88..1c6deab96 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -77,8 +77,10 @@ enum packet_headers {
 #endif
 #if PACKETVER < 20080102
 	authokType = 0x73,
-#else
+#elif PACKETVER < 20141022
 	authokType = 0x2eb,
+#else
+	authokType = 0xa18,
 #endif
 	script_clearType = 0x8d6,
 	package_item_announceType = 0x7fd,
@@ -210,6 +212,9 @@ enum packet_headers {
 	wisendType = 0x98,
 #endif
 	partyleaderchangedType = 0x7fc,
+	rouletteinfoackType = 0xa1c,
+	roulettgenerateackType = 0xA20,
+	roulettercvitemackType = 0xA22,
 };
 
 #if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
@@ -298,6 +303,9 @@ struct packet_authok {
 #if PACKETVER >= 20080102
 	short font;
 #endif
+#if PACKETVER >= 20141022
+	unsigned char sex;
+#endif
 } __attribute__((packed));
 
 struct packet_monster_hp {
@@ -762,6 +770,58 @@ struct packet_banking_withdraw_ack {
 	int Balance;
 } __attribute__((packed));
 
+/* Roulette System [Yommy/Hercules] */
+struct packet_roulette_open_ack {
+	short PacketType;
+	char Result;
+	int Serial;
+	char Step;
+	char Idx;
+	short AdditionItemID;
+	int GoldPoint;
+	int SilverPoint;
+	int BronzePoint;
+} __attribute__((packed));
+
+struct packet_roulette_info_ack {
+	short PacketType;
+	short PacketLength;
+	unsigned int RouletteSerial;
+	struct {
+		unsigned short Row;
+		unsigned short Position;
+		unsigned short ItemId;
+		unsigned short Count;
+	} ItemInfo[42];
+} __attribute__((packed));
+
+struct packet_roulette_close_ack {
+	short PacketType;
+	unsigned char Result;
+} __attribute__((packed));
+
+struct packet_roulette_generate_ack {
+	short PacketType;
+	unsigned char Result;
+	unsigned short Step;
+	unsigned short Idx;
+	unsigned short AdditionItemID;
+	int RemainGold;
+	int RemainSilver;
+	int RemainBronze;
+} __attribute__((packed));
+
+struct packet_roulette_itemrecv_req {
+	short PacketType;
+	unsigned char Condition;
+} __attribute__((packed));
+
+struct packet_roulette_itemrecv_ack {
+	short PacketType;
+	unsigned char Result;
+	unsigned short AdditionItemID;
+} __attribute__((packed));
+
 struct packet_itemlist_normal {
 	short PacketType;
 	short PacketLength;
diff --git a/src/map/pc.h b/src/map/pc.h
index 580908692..e613feec5 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -543,6 +543,13 @@ struct map_session_data {
 	bool vars_ok;
 	bool vars_dirty;
 	
+	struct {
+		short stage;
+		short prizeIdx;
+		short prizeStage;
+		bool claimPrize;
+	} roulette;
+	
 	// temporary debugging of bug #3504
 	const char* delunit_prevfile;
 	int delunit_prevline;
-- 
cgit v1.2.3-70-g09d2