From cd8d185f8ea5e88a7bd48dc70d42b49a10d5e0de Mon Sep 17 00:00:00 2001 From: lemongrass3110 Date: Tue, 12 Mar 2013 23:05:36 +0000 Subject: [PATCH] Another char-server related BETA release. If you happen to find any errors feel free to report them, I tested it for some hours now and it should be working as intended. "the packets used by this feature are still unknown to us", I guess not. :P Thanks again Yommy and enjoy it Ind. git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@17194 54d463be-8e91-2dee-dedb-b68131a5f0ec --- conf/char_athena.conf | 22 ++++- db/const.txt | 1 + db/re/item_db.txt | 2 + db/re/item_trade.txt | 1 + sql-files/item_db_re.sql | 2 + sql-files/upgrades/upgrade_svn17194.sql | 1 + src/char/char.c | 125 ++++++++++++++++++++++-- src/common/mmo.h | 3 + src/map/map.h | 1 + src/map/pc.c | 4 + 10 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 sql-files/upgrades/upgrade_svn17194.sql diff --git a/conf/char_athena.conf b/conf/char_athena.conf index 1a5ead6129..9f0ff311ef 100644 --- a/conf/char_athena.conf +++ b/conf/char_athena.conf @@ -159,9 +159,12 @@ char_del_delay: 86400 // What folder the DB files are in (item_db.txt, etc.) db_path: db +//=================================== // Pincode system -// A window is opened before you can select your character and you will have to enter a pincode by using only your mouse. +//=================================== // NOTE: Requires client 2011-03-09aragexeRE or newer. +// A window is opened before you can select your character and you will have to enter a pincode by using only your mouse. +// Default: yes pincode_enabled: yes // How often does a user have to change his pincode? @@ -177,4 +180,21 @@ pincode_maxtry: 3 // Default: yes pincode_force: yes +//=================================== +// Addon system +//=================================== +// Character moving +// NOTE: Requires client 2011-09-28aragexeRE or newer. +// Allows users to move their characters between slots. +// Default: yes +char_move_enabled: yes + +// Allow users to move a character to a used slot? +// If enabled the characters are exchanged. +// Default: yes +char_movetoused: no + +// Allow users to move characters as often as they like? +char_moves_unlimited: no + import: conf/import/char_conf.txt diff --git a/db/const.txt b/db/const.txt index eafdbf7b4a..759e237b22 100644 --- a/db/const.txt +++ b/db/const.txt @@ -417,6 +417,7 @@ BaseClass 120 1 killerrid 121 1 killedrid 122 1 Sitting 123 1 +CharMoves 124 1 bMaxHP 6 bMaxSP 8 diff --git a/db/re/item_db.txt b/db/re/item_db.txt index 2a0918e515..8bd99587a9 100644 --- a/db/re/item_db.txt +++ b/db/re/item_db.txt @@ -5825,6 +5825,8 @@ // 12775,Ancient_Spirit_Amulet,Ancient Spirit Amulet,2,20,,600,,,,,0xFFFFFFFF,7,2,,,,,,{},{},{} // +12786,Change_Slot_Card,Character Position Change Coupon,2,,,,,,,,0xFFFFFFFF,7,2,,,,,,{ set CharMoves, CharMoves + 1; },{},{} +// 12848,Falcon_Flute,Falcon Flute,11,0,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ if(getskilllv("HT_FALCON")) { if(checkoption(Option_Wug)||checkoption(Option_Wugrider)) end; if(checkfalcon()==1) { setfalcon 0; } else { setfalcon 1; } } },{},{} 12900,Battle_Manual_Box,Battle Manual Box,18,20,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12208,10; },{},{} 12901,Insurance_Package,Insurance Package,18,20,,10,,,,,0xFFFFFFFF,7,2,,,,,,{ getitem 12209,10; },{},{} diff --git a/db/re/item_trade.txt b/db/re/item_trade.txt index 25c4e855c9..baa5d0db4a 100644 --- a/db/re/item_trade.txt +++ b/db/re/item_trade.txt @@ -1919,6 +1919,7 @@ 2826,65,100 // Dark Knight Belt 2827,65,100 // Dark Knight Glove 2843,507,100 // Gold Trickle +12786,459,100 // Character Position Change Coupon 13049,65,100 // Lacma 13052,499,100 // Tourist Dagger 13111,507,100 // Sharpshooter Revolver diff --git a/sql-files/item_db_re.sql b/sql-files/item_db_re.sql index e8fa62d0ab..909e6bd4fd 100644 --- a/sql-files/item_db_re.sql +++ b/sql-files/item_db_re.sql @@ -5856,6 +5856,8 @@ REPLACE INTO `item_db_re` VALUES (12773,'Victory_Hat_Box2','Victory Hat Box2',2, # REPLACE INTO `item_db_re` VALUES (12775,'Ancient_Spirit_Amulet','Ancient Spirit Amulet',2,20,NULL,600,NULL,NULL,NULL,NULL,0xFFFFFFFF,7,2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); # +REPLACE INTO `item_db_re` VALUES (12786,'Change_Slot_Card','Character Position Change Coupon',2,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0xFFFFFFFF,7,2,NULL,NULL,NULL,NULL,NULL,'set CharMoves, CharMoves + 1;',NULL,NULL); +# REPLACE INTO `item_db_re` VALUES (12848,'Falcon_Flute','Falcon Flute',11,NULL,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,7,2,NULL,NULL,NULL,NULL,NULL,'if(getskilllv("HT_FALCON")) { if(checkoption(Option_Wug)||checkoption(Option_Wugrider)) end; if(checkfalcon()==1) { setfalcon 0; } else { setfalcon 1; } }',NULL,NULL); REPLACE INTO `item_db_re` VALUES (12900,'Battle_Manual_Box','Battle Manual Box',18,20,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,7,2,NULL,NULL,NULL,NULL,NULL,'getitem 12208,10;',NULL,NULL); REPLACE INTO `item_db_re` VALUES (12901,'Insurance_Package','Insurance Package',18,20,NULL,10,NULL,NULL,NULL,NULL,0xFFFFFFFF,7,2,NULL,NULL,NULL,NULL,NULL,'getitem 12209,10;',NULL,NULL); diff --git a/sql-files/upgrades/upgrade_svn17194.sql b/sql-files/upgrades/upgrade_svn17194.sql new file mode 100644 index 0000000000..8afbb655ba --- /dev/null +++ b/sql-files/upgrades/upgrade_svn17194.sql @@ -0,0 +1 @@ +ALTER TABLE `char` ADD COLUMN `moves` int(11) unsigned NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/src/char/char.c b/src/char/char.c index 3349ca8195..e0359499b4 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -131,10 +131,13 @@ struct char_session_data { uint8 clienttype; char new_name[NAME_LENGTH]; char birthdate[10+1]; // YYYY-MM-DD + // Pincode system char pincode[4+1]; uint16 pincode_seed; time_t pincode_change; uint16 pincode_try; + // Addon system + unsigned int char_moves[MAX_CHARS]; // character moves left }; int max_connect_user = -1; @@ -168,6 +171,14 @@ void pincode_notifyLoginPinError( int account_id ); void pincode_decrypt( unsigned long userSeed, char* pin ); int pincode_compare( int fd, struct char_session_data* sd, char* pin ); +// Addon system +bool char_move_enabled = true; +bool char_movetoused = true; +bool char_moves_unlimited = false; + +void moveCharSlot( int fd, struct char_session_data* sd, unsigned short from, unsigned short to ); +void moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason ); + //Custom limits for the fame lists. [Skotlex] int fame_list_size_chemist = MAX_FAME_LIST; int fame_list_size_smith = MAX_FAME_LIST; @@ -502,7 +513,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) (p->pet_id != cp->pet_id) || (p->weapon != cp->weapon) || (p->hom_id != cp->hom_id) || (p->ele_id != cp->ele_id) || (p->shield != cp->shield) || (p->head_top != cp->head_top) || (p->head_mid != cp->head_mid) || (p->head_bottom != cp->head_bottom) || (p->delete_date != cp->delete_date) || - (p->rename != cp->rename) || (p->robe != cp->robe) + (p->rename != cp->rename) || (p->robe != cp->robe) || (p->character_moves != cp->character_moves) ) { //Save status if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `base_level`='%d', `job_level`='%d'," @@ -512,7 +523,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) "`option`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',`homun_id`='%d',`elemental_id`='%d'," "`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d'," "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d', `rename`='%d'," - "`delete_date`='%lu',`robe`='%d'" + "`delete_date`='%lu',`robe`='%d',`moves`='%d'" " WHERE `account_id`='%d' AND `char_id` = '%d'", char_db, p->base_level, p->job_level, p->base_exp, p->job_exp, p->zeny, @@ -523,7 +534,7 @@ int mmo_char_tosql(int char_id, struct mmo_charstatus* p) mapindex_id2name(p->last_point.map), p->last_point.x, p->last_point.y, mapindex_id2name(p->save_point.map), p->save_point.x, p->save_point.y, p->rename, (unsigned long)p->delete_date, // FIXME: platform-dependent size - p->robe, + p->robe,p->character_moves, p->account_id, p->char_id) ) { Sql_ShowDebug(sql_handle); @@ -1046,7 +1057,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`" + "`robe`,`moves`" " FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", char_db, sd->account_id, MAX_CHARS) || SQL_ERROR == SqlStmt_Execute(stmt) || SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &p.char_id, 0, NULL, NULL) @@ -1085,20 +1096,29 @@ int mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) || SQL_ERROR == SqlStmt_BindColumn(stmt, 33, SQLDT_SHORT, &p.rename, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 34, SQLDT_UINT32, &p.delete_date, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 35, SQLDT_SHORT, &p.robe, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 36, SQLDT_UINT, &p.character_moves, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); SqlStmt_Free(stmt); return 0; } + + for( i = 0; i < MAX_CHARS; i++ ){ + sd->found_char[i] = -1; + sd->char_moves[i] = 0; + } + for( i = 0; i < MAX_CHARS && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++ ) { p.last_point.map = mapindex_name2id(last_map); - sd->found_char[i] = p.char_id; + sd->found_char[p.slot] = p.char_id; j += mmo_char_tobuf(WBUFP(buf, j), &p); + + // Addon System + // store the required info into the session + sd->char_moves[p.slot] = p.character_moves; } - for( ; i < MAX_CHARS; i++ ) - sd->found_char[i] = -1; memset(sd->new_name,0,sizeof(sd->new_name)); @@ -1143,7 +1163,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything "`str`,`agi`,`vit`,`int`,`dex`,`luk`,`max_hp`,`hp`,`max_sp`,`sp`," "`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`" + "`save_map`,`save_x`,`save_y`,`partner_id`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,`robe`, `moves`" " FROM `%s` WHERE `char_id`=? LIMIT 1", char_db) || SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0) || SQL_ERROR == SqlStmt_Execute(stmt) @@ -1199,6 +1219,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything || SQL_ERROR == SqlStmt_BindColumn(stmt, 49, SQLDT_SHORT, &p->rename, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 50, SQLDT_UINT32, &p->delete_date, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 51, SQLDT_SHORT, &p->robe, 0, NULL, NULL) + || SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_UINT32, &p->character_moves, 0, NULL, NULL) ) { SqlStmt_ShowDebug(stmt); @@ -1866,7 +1887,13 @@ 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) = 0; // change slot feature (0 = disabled, otherwise enabled) + // change slot feature (0 = disabled, otherwise enabled) + if( !char_move_enabled ) + WBUFL(buf,132) = 0; + else if( char_moves_unlimited ) + WBUFL(buf,132) = 1; + else + WBUFL(buf,132) = max( 0, (int)p->character_moves ); offset += 4; #endif #if PACKETVER >= 20111025 @@ -3401,7 +3428,6 @@ int parse_frommap(int fd) } break; - default: { // inter server - packet @@ -4302,6 +4328,16 @@ int parse_char(int fd) RFIFOSKIP(fd,10); break; + // character movement request + case 0x8d4: + if( RFIFOREST(fd) < 8 ) + return 0; + + moveCharSlot( fd, sd, RFIFOW(fd, 2), RFIFOW(fd, 4) ); + + RFIFOSKIP(fd,8); + break; + // unknown packet received default: ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL)); @@ -4596,6 +4632,69 @@ void pincode_decrypt( unsigned long userSeed, char* pin ){ sprintf(pin, "%d%d%d%d", pin[0], pin[1], pin[2], pin[3]); } +//------------------------------------------------ +//Add On system +//------------------------------------------------ +void moveCharSlot( int fd, struct char_session_data* sd, unsigned short from, unsigned short to ){ + // Have we changed to often or is it disabled? + if( !char_move_enabled || ( !char_moves_unlimited && sd->char_moves[from] <= 0 ) ){ + moveCharSlotReply( fd, sd, from, 1 ); + return; + } + + // We dont even have a character on the chosen slot? + if( sd->found_char[from] <= 0 ){ + moveCharSlotReply( fd, sd, from, 1 ); + return; + } + + if( sd->found_char[to] > 0 ){ + // We want to move to a used position + if( char_movetoused ){ // TODO: check if the target is in deletion process + // Admin is friendly and uses triangle exchange + if( SQL_ERROR == Sql_QueryStr(sql_handle, "START TRANSACTION") + || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'", char_db, to, sd->found_char[from] ) + || SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id` = '%d'", char_db, from, sd->found_char[to] ) + || SQL_ERROR == Sql_QueryStr(sql_handle, "COMMIT") + ){ + moveCharSlotReply( fd, sd, from, 1 ); + Sql_ShowDebug(sql_handle); + Sql_QueryStr(sql_handle,"ROLLBACK"); + return; + } + }else{ + // Admin doesnt allow us to + moveCharSlotReply( fd, sd, from, 1 ); + return; + } + }else if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `char_num`='%d' WHERE `char_id`='%d'", char_db, to, sd->found_char[from] ) ){ + Sql_ShowDebug(sql_handle); + moveCharSlotReply( fd, sd, from, 1 ); + return; + } + + if( !char_moves_unlimited ){ + sd->char_moves[from]--; + Sql_Query(sql_handle, "UPDATE `%s` SET `moves`='%d' WHERE `char_id`='%d'", char_db, sd->char_moves[from], sd->found_char[from] ); + } + + // We successfully moved the char - time to notify the client + moveCharSlotReply( fd, sd, from, 0 ); + mmo_char_send006b( fd, sd ); +} + +// reason +// 0: success +// 1: failed +void moveCharSlotReply( int fd, struct char_session_data* sd, unsigned short index, short reason ){ + WFIFOHEAD(fd,8); + WFIFOW(fd,0) = 0x8d5; + WFIFOW(fd,2) = 8; + WFIFOW(fd,4) = reason; + WFIFOW(fd,6) = sd->char_moves[index]; + WFIFOSET(fd,8); +} + //------------------------------------------------ //Invoked 15 seconds after mapif_disconnectplayer in case the map server doesn't //replies/disconnect the player we tried to kick. [Skotlex] @@ -4927,6 +5026,12 @@ int char_config_read(const char* cfgName) pincode_maxtry = atoi(w2); } else if (strcmpi(w1, "pincode_force") == 0) { pincode_force = config_switch(w2); + } else if (strcmpi(w1, "char_move_enabled") == 0) { + char_move_enabled = config_switch(w2); + } else if (strcmpi(w1, "char_movetoused") == 0) { + char_movetoused = config_switch(w2); + } else if (strcmpi(w1, "char_moves_unlimited") == 0) { + char_moves_unlimited = config_switch(w2); } else if (strcmpi(w1, "import") == 0) { char_config_read(w2); } diff --git a/src/common/mmo.h b/src/common/mmo.h index 271129484f..194f169a1f 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -382,6 +382,9 @@ struct mmo_charstatus { short rename; time_t delete_date; + + // Char server addon system + unsigned int character_moves; }; typedef enum mail_status { diff --git a/src/map/map.h b/src/map/map.h index c69c61ca79..25ca34c8fb 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -369,6 +369,7 @@ enum _sp { SP_KILLERRID=121, SP_KILLEDRID=122, SP_SITTING=123, + SP_CHARMOVE=124, // Mercenaries SP_MERCFLEE=165, SP_MERCKILLS=189, SP_MERCFAITH=190, diff --git a/src/map/pc.c b/src/map/pc.c index d211c84868..1bbc88d2e9 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -6916,6 +6916,7 @@ int pc_readparam(struct map_session_data* sd,int type) case SP_KILLERRID: val = sd->killerrid; break; case SP_KILLEDRID: val = sd->killedrid; break; case SP_SITTING: val = pc_issit(sd)?1:0; break; + case SP_CHARMOVE: val = sd->status.character_moves; break; case SP_CRITICAL: val = sd->battle_status.cri/10; break; case SP_ASPD: val = (2000-sd->battle_status.amotion)/10; break; case SP_BASE_ATK: val = sd->battle_status.batk; break; @@ -7156,6 +7157,9 @@ int pc_setparam(struct map_session_data *sd,int type,int val) case SP_KILLEDRID: sd->killedrid = val; return 1; + case SP_CHARMOVE: + sd->status.character_moves = val; + return 1; default: ShowError("pc_setparam: Attempted to set unknown parameter '%d'.\n", type); return 0;