summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/battle/feature.conf5
-rw-r--r--db/roulette_db.conf76
-rw-r--r--sql-files/main.sql1
-rw-r--r--sql-files/upgrades/2014-11-03--00-45.sql3
-rw-r--r--src/char/char.c68
-rw-r--r--src/common/mmo.h2
-rw-r--r--src/map/battle.h2
-rw-r--r--src/map/clif.c348
-rw-r--r--src/map/clif.h46
-rw-r--r--src/map/itemdb.c6
-rw-r--r--src/map/itemdb.h1
-rw-r--r--src/map/packets.h90
-rw-r--r--src/map/packets_struct.h62
-rw-r--r--src/map/pc.h7
-rw-r--r--src/map/skill.c2
15 files changed, 692 insertions, 27 deletions
diff --git a/conf/battle/feature.conf b/conf/battle/feature.conf
index 161ea324d..000bc318b 100644
--- a/conf/battle/feature.conf
+++ b/conf/battle/feature.conf
@@ -27,3 +27,8 @@ feature.banking: on
// Feature became unstable on clients 2012 onwards (exact date not known),
// it has been fixed on clients 2013-05-15 onwards however.
feature.auction: off
+
+// Roulette (Note 1)
+// Requires: 2014-10-22bRagexe or later
+// Off by default while test version is out; enable at your own risk -- the mean dev.
+feature.roulette: off
diff --git a/db/roulette_db.conf b/db/roulette_db.conf
new file mode 100644
index 000000000..bcffe88f7
--- /dev/null
+++ b/db/roulette_db.conf
@@ -0,0 +1,76 @@
+//====================================================
+//= _ _ _
+//= | | | | | |
+//= | |_| | ___ _ __ ___ _ _| | ___ ___
+//= | _ |/ _ \ '__/ __| | | | |/ _ \/ __|
+//= | | | | __/ | | (__| |_| | | __/\__ \
+//= \_| |_/\___|_| \___|\__,_|_|\___||___/
+//=
+//= http://hercules.ws/board/
+//================= More Information =================
+//= http://hercules.ws/board/ ???
+//====================================================
+// This file handles the Roulette, the format is <item>:<amount>
+// The first entry is the one that loses the game.
+//====================================================
+
+roulette: (
+{
+ level_1: {
+ Silver_Coin:1 // Lose
+ Gold_Coin:1
+ Poison_Bottle:1
+ Branch_Of_Dead_Tree:1
+ Fruit_Of_Mastela:1
+ //ID12609:1 // 12609 not in db
+ E_Inc_Agi_10_Scroll:1
+ Elunium:1
+ Oridecon:1
+ }
+ level_2: {
+ Silver_Coin:1 // Lose
+ Gold_Coin:1
+ Old_Blue_Box:1
+ Seed_Of_Yggdrasil:1
+ Yggdrasilberry:1
+ E_Blessing_10_Scroll:1
+ Carnium:1
+ Bradium:1
+ }
+ level_3: {
+ Silver_Coin:1 // Lose
+ Gold_Coin:1
+ Bundle_Of_Magic_Scroll:1
+ Old_Violet_Box:1
+ E_Abrasive:1
+ Treasure_Box:1
+ Gold:1
+ }
+ level_4: {
+ Silver_Coin:1 // Lose
+ Gold_Coin:1
+ Old_Card_Album:1
+ E_Small_Life_Potion:1
+ //Gift_Buff_Set:1 // 22777 not in db
+ Guarantee_Weapon_6Up:1
+ }
+ level_5: {
+ Gold_Coin:1 // Lose
+ Magic_Card_Album:1
+ Comp_Battle_Manual:1
+ //ID12831:1 // 12831 not in db // potion box ?
+ Guarantee_Armor_6Up:1
+ }
+ level_6: {
+ Gold_Coin:1 // Lose
+ Reward_Job_BM25:1
+ Guarantee_Armor_7Up:1
+ Guarantee_Armor_8Up:1
+ }
+ level_7: {
+ Gold_Coin:1 // Lose
+ Guarantee_Armor_8Up:1
+ Guarantee_Armor_8Up:1
+ }
+}
+) \ No newline at end of file
diff --git a/sql-files/main.sql b/sql-files/main.sql
index 934b406a8..cca81c66d 100644
--- a/sql-files/main.sql
+++ b/sql-files/main.sql
@@ -175,6 +175,7 @@ CREATE TABLE IF NOT EXISTS `char` (
`font` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0',
`unban_time` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`uniqueitem_counter` BIGINT(20) 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`),
diff --git a/sql-files/upgrades/2014-11-03--00-45.sql b/sql-files/upgrades/2014-11-03--00-45.sql
new file mode 100644
index 000000000..19d0a8ff5
--- /dev/null
+++ b/sql-files/upgrades/2014-11-03--00-45.sql
@@ -0,0 +1,3 @@
+#1414975503
+ALTER TABLE `char` ADD COLUMN `sex` ENUM('M','F','U') NOT NULL DEFAULT 'U';
+INSERT INTO `sql_updates` (`timestamp`) VALUES (1414975503);
diff --git a/src/char/char.c b/src/char/char.c
index 3dcc48f2e..fca2ec2fe 100644
--- a/src/char/char.c
+++ b/src/char/char.c
@@ -1012,6 +1012,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
int j = 0, i;
char last_map[MAP_NAME_LENGTH_EXT];
time_t unban_time = 0;
+ char sex[2];
stmt = SQL->StmtMalloc(sql_handle);
if( stmt == NULL ) {
@@ -1031,7 +1032,7 @@ int 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`,`slotchange`,`unban_time`"
+ "`robe`,`slotchange`,`unban_time`,`sex`"
" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS)
|| SQL_ERROR == SQL->StmtExecute(stmt)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL)
@@ -1072,6 +1073,7 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 35, SQLDT_SHORT, &p.robe, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 36, SQLDT_USHORT, &p.slotchange, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 37, SQLDT_LONG, &unban_time, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 38, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
)
{
SqlStmt_ShowDebug(stmt);
@@ -1083,6 +1085,18 @@ int 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] = unban_time;
+ switch( sex[0] ) {
+ case 'M':
+ p.sex = 1;
+ break;
+ case 'F':
+ p.sex = 0;
+ break;
+ case 'U':
+ default:
+ p.sex = 99;
+ break;
+ }
j += mmo_char_tobuf(WBUFP(buf, j), &p);
}
@@ -1113,6 +1127,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
#endif
unsigned int opt;
int account_id;
+ char sex[2];
memset(p, 0, sizeof(struct mmo_charstatus));
@@ -1132,7 +1147,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
"`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`,`slotchange`,"
- "`char_opt`,`font`,`uniqueitem_counter`"
+ "`char_opt`,`font`,`uniqueitem_counter`,`sex`"
" FROM `%s` WHERE `char_id`=? LIMIT 1", char_db)
|| SQL_ERROR == SQL->StmtBindParam(stmt, 0, SQLDT_INT, &char_id, 0)
|| SQL_ERROR == SQL->StmtExecute(stmt)
@@ -1192,6 +1207,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 53, SQLDT_UINT, &opt, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 54, SQLDT_UCHAR, &p->font, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 55, SQLDT_UINT, &p->uniqueitem_counter, 0, NULL, NULL)
+ || SQL_ERROR == SQL->StmtBindColumn(stmt, 56, SQLDT_ENUM, &sex, sizeof(sex), NULL, NULL)
)
{
SqlStmt_ShowDebug(stmt);
@@ -1205,6 +1221,19 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
return 0;
}
+ switch( sex[0] ) {
+ case 'M':
+ p->sex = 1;
+ break;
+ case 'F':
+ p->sex = 0;
+ break;
+ case 'U':
+ default:
+ p->sex = 99;
+ break;
+ }
+
account_id = p->account_id;
p->last_point.map = mapindex->name2id(last_map);
@@ -1900,7 +1929,7 @@ int count_users(void)
// Writes char data to the buffer in the format used by the client.
// Used in packets 0x6b (chars info) and 0x6d (new char info)
// Returns the size
-#define MAX_CHAR_BUF 144 //Max size (for WFIFOHEAD calls)
+#define MAX_CHAR_BUF 150 //Max size (for WFIFOHEAD calls)
int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
unsigned short offset = 0;
uint8* buf;
@@ -1935,9 +1964,16 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
WBUFW(buf,52) = p->class_;
WBUFW(buf,54) = p->hair;
+#if PACKETVER > 20140000
+ //When the weapon is sent and your option is riding, the client crashes on login!?
+ WBUFL(buf,56) = p->option&(0x20|0x80000|0x100000|0x200000|0x400000|0x800000|0x1000000|0x2000000|0x4000000|0x8000000) ? 0 : p->weapon;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#else
//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;
-
+#endif
+
WBUFW(buf,58) = p->base_level;
WBUFW(buf,60) = min(p->skill_point, INT16_MAX);
WBUFW(buf,62) = p->head_bottom;
@@ -1972,13 +2008,17 @@ int mmo_char_tobuf(uint8* buffer, struct mmo_charstatus* p) {
#endif
#if PACKETVER != 20111116 //2011-11-16 wants 136, ask gravity.
#if PACKETVER >= 20110928
- WBUFL(buf,132) = ( p->slotchange > 0 ) ? 1 : 0; // change slot feature (0 = disabled, otherwise enabled)
+ WBUFL(buf,132) = ( p->slotchange > 0 ) ? 1 : 0; // change slot feature (0 = disabled, otherwise enabled)
offset += 4;
#endif
#if PACKETVER >= 20111025
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;
@@ -2045,8 +2085,8 @@ void mmo_char_send082d(int fd, struct char_session_data* sd) {
WFIFOB(fd,8) = sd->char_slots;
memset(WFIFOP(fd,9), 0, 20); // unused bytes
WFIFOSET(fd,29);
- mmo_char_send006b(fd,sd);
+ mmo_char_send006b(fd,sd);
}
//----------------------------------------
// Function to send characters to a player
@@ -3699,11 +3739,12 @@ int parse_frommap(int fd)
node != NULL &&
node->account_id == account_id &&
node->char_id == char_id &&
- node->login_id1 == login_id1 &&
- node->sex == sex /*&&
+ node->login_id1 == login_id1 /*&&
+ node->sex == sex &&
node->ip == ip*/ )
{// auth ok
- cd->sex = sex;
+ if( cd->sex == 99 )
+ cd->sex = sex;
WFIFOHEAD(fd,25 + sizeof(struct mmo_charstatus));
WFIFOW(fd,0) = 0x2afd;
@@ -4339,7 +4380,8 @@ int parse_char(int fd)
//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 (log_char) {
char esc_name[NAME_LENGTH*2+1];
@@ -4796,6 +4838,12 @@ int parse_char(int fd)
RFIFOSKIP(fd,10);
break;
+ case 0x9a1:
+ FIFOSD_CHECK(2);
+ mmo_char_send099d(fd, sd);
+ RFIFOSKIP(fd,2);
+ break;
+
/* 0x8d4 <from>.W <to>.W <unused>.W (2+2+2+2) */
case 0x8d4:
FIFOSD_CHECK(8);
diff --git a/src/common/mmo.h b/src/common/mmo.h
index ff7c1da28..597b25126 100644
--- a/src/common/mmo.h
+++ b/src/common/mmo.h
@@ -49,7 +49,7 @@
// 20120307 - 2012-03-07aRagexeRE+ - 0x970
#ifndef PACKETVER
- #define PACKETVER 20131223
+ #define PACKETVER 20141022
#endif // PACKETVER
//Uncomment the following line if your client is ragexeRE instead of ragexe (required because of conflicting packets in ragexe vs ragexeRE).
diff --git a/src/map/battle.h b/src/map/battle.h
index 1b6321cbf..7633691a8 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -480,6 +480,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 a93bf0802..37cd03119 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);
}
@@ -5581,6 +5583,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
@@ -5601,6 +5604,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.
@@ -5610,6 +5618,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) {
@@ -9652,18 +9661,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
@@ -15800,19 +15813,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);
}
@@ -18271,7 +18299,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 ) {
@@ -18588,6 +18894,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) {
@@ -19089,6 +19402,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
*------------------------*/
@@ -19318,6 +19634,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 0d3146191..41e0a5348 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -1713,7 +1713,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source)
if( libconfig->setting_lookup_int(it, "Type", &i32) )
id.type = i32;
else if( !inherit )
- id.type = IT_UNKNOWN;
+ id.type = IT_ETC;
if( libconfig->setting_lookup_int(it, "Buy", &i32) )
id.value_buy = i32;
@@ -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 72572bb97..8d8cfd7c2 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_PUMPKIN = 535,
ITEMID_RED_SLIM_POTION = 545,
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;
diff --git a/src/map/skill.c b/src/map/skill.c
index 1fca0dfc7..992b72857 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -15770,7 +15770,7 @@ int skill_delunitgroup(struct skill_unit_group *group, const char* file, int lin
return 0;
}
- if( !status->isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
+ if( src->type == BL_PC && !status->isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
switch( group->skill_id ) {
case BA_DISSONANCE:
case BA_POEMBRAGI: