Implemented inventory expansion (#6485)

Fixes #5218
This commit is contained in:
Lemongrass3110 2022-01-12 09:12:47 +01:00 committed by GitHub
parent c8d1d1ea1e
commit 916860fef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 246 additions and 17 deletions

View File

@ -270,6 +270,7 @@ CREATE TABLE IF NOT EXISTS `char` (
`last_login` datetime DEFAULT NULL,
`title_id` INT(11) unsigned NOT NULL default '0',
`show_equip` tinyint(3) unsigned NOT NULL default '0',
`inventory_slots` smallint(6) NOT NULL default '100',
PRIMARY KEY (`char_id`),
UNIQUE KEY `name_key` (`name`),
KEY `account_id` (`account_id`),

View File

@ -0,0 +1,3 @@
ALTER TABLE `char`
ADD COLUMN `inventory_slots` smallint(6) NOT NULL default '100'
;

View File

@ -346,18 +346,18 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
(p->partner_id != cp->partner_id) || (p->father != cp->father) ||
(p->mother != cp->mother) || (p->child != cp->child) ||
(p->karma != cp->karma) || (p->manner != cp->manner) ||
(p->fame != cp->fame)
(p->fame != cp->fame) || (p->inventory_slots != cp->inventory_slots)
)
{
if( SQL_ERROR == Sql_Query(sql_handle, "UPDATE `%s` SET `class`='%d',"
"`hair`='%d', `hair_color`='%d', `clothes_color`='%d', `body`='%d',"
"`partner_id`='%u', `father`='%u', `mother`='%u', `child`='%u',"
"`karma`='%d',`manner`='%d', `fame`='%d'"
"`karma`='%d',`manner`='%d', `fame`='%d', `inventory_slots`='%hu'"
" WHERE `account_id`='%d' AND `char_id` = '%d'",
schema_config.char_db, p->class_,
p->hair, p->hair_color, p->clothes_color, p->body,
p->partner_id, p->father, p->mother, p->child,
p->karma, p->manner, p->fame,
p->karma, p->manner, p->fame, p->inventory_slots,
p->account_id, p->char_id) )
{
Sql_ShowDebug(sql_handle);
@ -933,7 +933,8 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun
"`clothes_color`,`body`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`,`last_map`,`rename`,`delete_date`,"
"`robe`,`moves`,`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`title_id`,`show_equip`,"
"`hotkey_rowshift2`,"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`,"
"`inventory_slots`"
" FROM `%s` WHERE `account_id`='%d' AND `char_num` < '%d'", schema_config.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)
@ -991,6 +992,7 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf, uint8* coun
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 52, SQLDT_SHORT, &p.spl, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 53, SQLDT_SHORT, &p.con, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 54, SQLDT_SHORT, &p.crt, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 55, SQLDT_UINT16, &p.inventory_slots, 0, NULL, NULL)
)
{
SqlStmt_ShowDebug(stmt);
@ -1059,7 +1061,8 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
"`hair_color`,`clothes_color`,`body`,`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`, `moves`,"
"`unban_time`,`font`,`uniqueitem_counter`,`sex`,`hotkey_rowshift`,`clan_id`,`title_id`,`show_equip`,`hotkey_rowshift2`,"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`,"
"`inventory_slots`"
" FROM `%s` WHERE `char_id`=? LIMIT 1", schema_config.char_db)
|| SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
|| SQL_ERROR == SqlStmt_Execute(stmt)
@ -1135,6 +1138,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 69, SQLDT_SHORT, &p->spl, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 70, SQLDT_SHORT, &p->con, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 71, SQLDT_SHORT, &p->crt, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 72, SQLDT_UINT16, &p->inventory_slots, 0, NULL, NULL)
)
{
SqlStmt_ShowDebug(stmt);
@ -2323,7 +2327,8 @@ bool char_checkdb(void){
"`save_x`,`save_y`,`partner_id`,`online`,`father`,`mother`,`child`,`fame`,`rename`,`delete_date`,"
"`moves`,`unban_time`,`font`,`sex`,`hotkey_rowshift`,`clan_id`,`last_login`,`title_id`,`show_equip`,"
"`hotkey_rowshift2`,"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`"
"`max_ap`,`ap`,`trait_point`,`pow`,`sta`,`wis`,`spl`,`con`,`crt`,"
"`inventory_slots`"
" FROM `%s` LIMIT 1;", schema_config.char_db) ){
Sql_ShowDebug(sql_handle);
return false;

View File

@ -40,7 +40,27 @@
#endif
#define MAX_MAP_PER_SERVER 1500 /// Maximum amount of maps available on a server
#define MAX_INVENTORY 100 ///Maximum items in player inventory
#ifndef INVENTORY_BASE_SIZE
#define INVENTORY_BASE_SIZE 100 // Amount of inventory slots each player has
#endif
#ifndef INVENTORY_EXPANSION_SIZE
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
#define INVENTORY_EXPANSION_SIZE 100 // Amount of additional inventory slots a player can have
#else
#define INVENTORY_EXPANSION_SIZE 0
#endif
#endif
#ifndef MAX_INVENTORY
#define MAX_INVENTORY ( INVENTORY_BASE_SIZE + INVENTORY_EXPANSION_SIZE ) // Maximum items in player inventory (in total)
#else
#if MAX_INVENTORY < ( INVENTORY_BASE_SIZE + INVENTORY_EXPANSION_SIZE )
#error Your custom MAX_INVENTORY define is too low
#endif
#endif
/** Max number of characters per account. Note that changing this setting alone is not enough if the client is not hexed to support more characters as well.
* Max value tested was 265 */
#ifndef MAX_CHARS
@ -573,6 +593,7 @@ struct mmo_charstatus {
unsigned char hotkey_rowshift;
unsigned char hotkey_rowshift2;
unsigned long title_id;
uint16 inventory_slots;
};
typedef enum mail_status {

View File

@ -22537,6 +22537,185 @@ void clif_parse_stylist_close( int fd, struct map_session_data* sd ){
#endif
}
void clif_inventory_expansion_info( struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
nullpo_retv( sd );
struct PACKET_ZC_INVENTORY_EXPANSION_INFO p = {};
p.packetType = HEADER_ZC_INVENTORY_EXPANSION_INFO;
p.expansionSize = sd->status.inventory_slots - INVENTORY_BASE_SIZE;
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
enum class e_inventory_expansion_response : uint8{
ASK_CONFIRMATION = 0,
FAILED,
BUSY,
MISSING_ITEM,
MAXIMUM_REACHED
};
void clif_inventory_expansion_response( struct map_session_data* sd, e_inventory_expansion_response response ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
nullpo_retv( sd );
struct PACKET_ZC_ACK_INVENTORY_EXPAND p = {};
p.packetType = HEADER_ZC_ACK_INVENTORY_EXPAND;
p.result = (uint8)response;
p.itemId = sd->state.inventory_expansion_confirmation;
clif_send( &p, sizeof( p ), &sd->bl, SELF );
#endif
}
void clif_parse_inventory_expansion_request( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
// Check if player is dead or busy with other stuff
if( pc_isdead( sd ) || pc_cant_act( sd ) ){
clif_inventory_expansion_response( sd, e_inventory_expansion_response::BUSY );
return;
}
// Check if the player already reached the maximum
if( sd->status.inventory_slots >= MAX_INVENTORY ){
clif_inventory_expansion_response( sd, e_inventory_expansion_response::MAXIMUM_REACHED );
return;
}
static std::map<t_itemid, uint16> items = {
// The order of entries in this list defines which will be used first
// This order and the usable items are hardcoded into the client
// The number of increased slots is "hardcoded" in the message of the client and cannot be sent per item
{ ITEMID_INVENTORY_EX_EVT, 10 },
{ ITEMID_INVENTORY_EX_DIS, 10 },
{ ITEMID_INVENTORY_EX, 10 },
};
int16 index = -1;
bool found_over_limit = false;
for( const auto& entry : items ){
// Check if the player has the required item
index = pc_search_inventory( sd, entry.first );
// Found an item
if( index >= 0 ){
// Check if the player would exceed the maximum
if( sd->status.inventory_slots + entry.second > MAX_INVENTORY ){
found_over_limit = true;
}else{
found_over_limit = false;
sd->state.inventory_expansion_confirmation = entry.first;
sd->state.inventory_expansion_amount = entry.second;
break;
}
}
}
// Check if an item was found
if( sd->state.inventory_expansion_confirmation == 0 ){
clif_inventory_expansion_response( sd, e_inventory_expansion_response::MISSING_ITEM );
return;
}
// Check if an item would have been found, but the player would exceed the maximum
if( found_over_limit ){
clif_inventory_expansion_response( sd, e_inventory_expansion_response::MAXIMUM_REACHED );
return;
}
// The player met all requirements => ask him for confirmation
clif_inventory_expansion_response( sd, e_inventory_expansion_response::ASK_CONFIRMATION );
#endif
}
enum class e_inventory_expansion_result : uint8{
SUCCESS = 0,
FAILED,
BUSY,
MISSING_ITEM,
MAXIMUM_REACHED
};
void clif_inventory_expansion_result( struct map_session_data* sd, e_inventory_expansion_result result ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
nullpo_retv( sd );
struct PACKET_ZC_ACK_INVENTORY_EXPAND_RESULT p = {};
p.packetType = HEADER_ZC_ACK_INVENTORY_EXPAND_RESULT;
p.result = (uint8)result;
clif_send( &p, sizeof( p ), &sd->bl, SELF );
// Reset the state tracking
sd->state.inventory_expansion_confirmation = 0;
sd->state.inventory_expansion_amount = 0;
#endif
}
void clif_parse_inventory_expansion_confirm( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
if( sd->state.inventory_expansion_confirmation == 0 ){
return;
}
// Check if player is dead
if( pc_isdead( sd ) ){
clif_inventory_expansion_result( sd, e_inventory_expansion_result::BUSY );
return;
}
// Check if the player already reached the maximum
if( sd->status.inventory_slots >= MAX_INVENTORY ){
clif_inventory_expansion_result( sd, e_inventory_expansion_result::MAXIMUM_REACHED );
return;
}
// Check if the player has the required item
int index = pc_search_inventory( sd, sd->state.inventory_expansion_confirmation );
// The player did not have the item anymore
if( index < 0 ){
clif_inventory_expansion_result( sd, e_inventory_expansion_result::MISSING_ITEM );
return;
}
// Check if the player would exceed the maximum
if( sd->status.inventory_slots + sd->state.inventory_expansion_amount > MAX_INVENTORY ){
clif_inventory_expansion_result( sd, e_inventory_expansion_result::MAXIMUM_REACHED );
return;
}
// Delete the required item
if( pc_delitem( sd, index, 1, 0, 0, LOG_TYPE_OTHER ) ){
clif_inventory_expansion_result( sd, e_inventory_expansion_result::FAILED );
return;
}
// Increase the slots
sd->status.inventory_slots += sd->state.inventory_expansion_amount;
// Save player data (slots) and inventory data (removed item)
chrif_save( sd, CSAVE_NORMAL | CSAVE_INVENTORY );
// Inform the player of success
clif_inventory_expansion_result( sd, e_inventory_expansion_result::SUCCESS );
clif_inventory_expansion_info( sd );
#endif
}
void clif_parse_inventory_expansion_reject( int fd, struct map_session_data* sd ){
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
sd->state.inventory_expansion_confirmation = 0;
sd->state.inventory_expansion_amount = 0;
#endif
}
/*==========================================
* Main client packet processing function
*------------------------------------------*/

View File

@ -1159,4 +1159,6 @@ void clif_pet_evolution_result( struct map_session_data* sd, e_pet_evolution_res
void clif_parse_skill_toid( struct map_session_data* sd, uint16 skill_id, uint16 skill_lv, int target_id );
void clif_inventory_expansion_info( struct map_session_data* sd );
#endif /* CLIF_HPP */

View File

@ -2409,9 +2409,9 @@
#endif
#if PACKETVER_MAIN_NUM >= 20181031 || PACKETVER_RE_NUM >= 20181031 || PACKETVER_ZERO_NUM >= 20181114
parseable_packet( 0x0B14, sizeof( struct PACKET_CZ_INVENTORY_EXPAND ), clif_parse_dull, 0 );
parseable_packet( 0x0B16, sizeof( struct PACKET_CZ_INVENTORY_EXPAND_CONFIRMED ), clif_parse_dull, 0 );
parseable_packet( 0x0B19, sizeof( struct PACKET_CZ_INVENTORY_EXPAND_REJECTED ), clif_parse_dull, 0 );
parseable_packet( HEADER_CZ_INVENTORY_EXPAND, sizeof( struct PACKET_CZ_INVENTORY_EXPAND ), clif_parse_inventory_expansion_request, 0 );
parseable_packet( HEADER_CZ_INVENTORY_EXPAND_CONFIRMED, sizeof( struct PACKET_CZ_INVENTORY_EXPAND_CONFIRMED ), clif_parse_inventory_expansion_confirm, 0 );
parseable_packet( HEADER_CZ_INVENTORY_EXPAND_REJECTED, sizeof( struct PACKET_CZ_INVENTORY_EXPAND_REJECTED ), clif_parse_inventory_expansion_reject, 0 );
#endif
#if PACKETVER_MAIN_NUM >= 20190227 || PACKETVER_RE_NUM >= 20190220 || PACKETVER_ZERO_NUM >= 20190220

View File

@ -120,6 +120,9 @@ enum item_itemid : t_itemid
ITEMID_WOB_RACHEL = 14584,
ITEMID_WOB_LOCAL = 14585,
ITEMID_SIEGE_TELEPORT_SCROLL = 14591,
ITEMID_INVENTORY_EX_EVT = 25791,
ITEMID_INVENTORY_EX_DIS = 25792,
ITEMID_INVENTORY_EX = 25793,
ITEMID_WL_MB_SG = 100065,
ITEMID_HOMUNCULUS_SUPPLEMENT = 100371,
};

View File

@ -1756,6 +1756,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
}
}
clif_inventory_expansion_info( sd );
clif_authok(sd);
//Prevent S. Novices from getting the no-death bonus just yet. [Skotlex]
@ -5088,6 +5089,10 @@ char pc_checkadditem(struct map_session_data *sd, t_itemid nameid, int amount)
if(sd->inventory.u.items_inventory[i].nameid == nameid){
if( amount > MAX_AMOUNT - sd->inventory.u.items_inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->inventory.u.items_inventory[i].amount ) )
return CHKADDITEM_OVERAMOUNT;
// If the item is in the inventory already, but the player is not allowed to use that many slots anymore
if( i >= sd->status.inventory_slots ){
return CHKADDITEM_OVERAMOUNT;
}
return CHKADDITEM_EXIST;
}
}
@ -5103,11 +5108,12 @@ char pc_checkadditem(struct map_session_data *sd, t_itemid nameid, int amount)
*------------------------------------------*/
uint8 pc_inventoryblank(struct map_session_data *sd)
{
uint8 i, b;
uint16 i;
uint8 b;
nullpo_ret(sd);
for(i = 0, b = 0; i < MAX_INVENTORY; i++){
for(i = 0, b = 0; i < sd->status.inventory_slots; i++){
if(sd->inventory.u.items_inventory[i].nameid == 0)
b++;
}
@ -5345,8 +5351,6 @@ enum e_additem_result pc_additem(struct map_session_data *sd,struct item *item,i
if(sd->weight + w > sd->max_weight)
return ADDITEM_OVERWEIGHT;
i = MAX_INVENTORY;
if (id->flag.guid && !item->unique_id)
item->unique_id = pc_generate_unique_id(sd);
@ -5360,17 +5364,26 @@ enum e_additem_result pc_additem(struct map_session_data *sd,struct item *item,i
memcmp(&sd->inventory.u.items_inventory[i].card, &item->card, sizeof(item->card)) == 0 ) {
if( amount > MAX_AMOUNT - sd->inventory.u.items_inventory[i].amount || ( id->stack.inventory && amount > id->stack.amount - sd->inventory.u.items_inventory[i].amount ) )
return ADDITEM_OVERAMOUNT;
// If the item is in the inventory already, but the player is not allowed to use that many slots anymore
if( i >= sd->status.inventory_slots ){
return ADDITEM_OVERAMOUNT;
}
sd->inventory.u.items_inventory[i].amount += amount;
clif_additem(sd,i,amount,0);
break;
}
}
}
}else{
i = MAX_INVENTORY;
}
if (i >= MAX_INVENTORY) {
i = pc_search_inventory(sd,0);
if( i < 0 )
return ADDITEM_OVERITEM;
if( i >= sd->status.inventory_slots ){
return ADDITEM_OVERITEM;
}
memcpy(&sd->inventory.u.items_inventory[i], item, sizeof(sd->inventory.u.items_inventory[0]));
// clear equip and favorite fields first, just in case

View File

@ -385,6 +385,8 @@ struct map_session_data {
bool stylist_open;
unsigned int block_action : 10;
bool refineui_open;
t_itemid inventory_expansion_confirmation;
uint16 inventory_expansion_amount;
} state;
struct {
unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
@ -1053,7 +1055,7 @@ extern JobDatabase job_db;
static bool pc_cant_act2( struct map_session_data* sd ){
return sd->state.vending || sd->state.buyingstore || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING)
|| sd->state.trading || sd->state.storage_flag || sd->state.prevend || sd->state.refineui_open
|| sd->state.stylist_open;
|| sd->state.stylist_open || sd->state.inventory_expansion_confirmation;
}
// equals pc_cant_act2 and additionally checks for chat rooms and npcs
static bool pc_cant_act( struct map_session_data* sd ){

View File

@ -401,7 +401,7 @@ void storage_storageaddfromcart(struct map_session_data *sd, struct s_storage *s
return;
}
result = storage_canAddItem(stor, index, sd->cart.u.items_inventory, amount, MAX_CART);
result = storage_canAddItem(stor, index, sd->cart.u.items_cart, amount, MAX_CART);
if (result == STORAGE_ADD_INVALID)
return;
else if (result == STORAGE_ADD_OK) {