From 239d480487e24294975f35ed55f210837ad1088e Mon Sep 17 00:00:00 2001 From: shennetsind 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 --- conf/battle/feature.conf | 5 + db/roulette_db.conf | 76 +++++++ sql-files/main.sql | 1 + sql-files/upgrades/2014-11-03--00-45.sql | 3 + src/char/char.c | 68 +++++- src/common/mmo.h | 2 +- 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 + 15 files changed, 699 insertions(+), 26 deletions(-) create mode 100644 db/roulette_db.conf create mode 100644 sql-files/upgrades/2014-11-03--00-45.sql 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 : +// 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 824c782bc..6744c708d 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -1011,6 +1011,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 ) { @@ -1030,7 +1031,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) @@ -1071,6 +1072,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); @@ -1082,6 +1084,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); } @@ -1112,6 +1126,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)); @@ -1131,7 +1146,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) @@ -1191,6 +1206,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); @@ -1204,6 +1220,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); @@ -1899,7 +1928,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; @@ -1934,9 +1963,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; @@ -1971,13 +2007,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; @@ -2044,8 +2084,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 @@ -3698,11 +3738,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; @@ -4338,7 +4379,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]; @@ -4795,6 +4837,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 .W .W .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.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 .W .L { .L .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