Introducing the Account/Guild/Party Bounded Items System - Suggestion tid:70372

- Thanks for Xantara for providing initial diff as well as Lighta for guild bound help
- Adds script commands 'getitembound', 'getitembound2', and 'countbound'
- Adds at commands @itembound and @itembound2
- Adds permission pc_can_traded_bounded
- Documentation updated

Don't forget to run the SQL upgrade file!

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@17351 54d463be-8e91-2dee-dedb-b68131a5f0ec
This commit is contained in:
akinari1087 2013-06-08 22:47:10 +00:00
parent e8dcdf9ecb
commit 4742adac12
26 changed files with 518 additions and 81 deletions

View File

@ -310,6 +310,13 @@
290: The player is no longer killable. 290: The player is no longer killable.
291: Weather effects will dispell on warp/refresh 291: Weather effects will dispell on warp/refresh
292: Killer state reset. 292: Killer state reset.
//Item Bound System
293: This bounded item cannot be traded to that character.
294: This bounded item cannot be stored there.
295: Please enter an item name or ID (usage: @item <item name/ID> <quantity> <bound_type>).
296: Please enter all parameters (usage: @item2 <item name/ID> <quantity>
297: <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4> <bound_type>).
298: Invalid bound type. Valid types are - 1:Account 2:Guild 3:Party
// Guild Castles Number // Guild Castles Number
// -------------------- // --------------------
//299: ?? Castles //299: ?? Castles

View File

@ -642,6 +642,22 @@ Creates an item with the given parameters (the 'cards' can be any item).
identify_flag: 0 = unidentified, 1 = identified identify_flag: 0 = unidentified, 1 = identified
attribute: 0 = not broken, 1 = broken attribute: 0 = not broken, 1 = broken
---------------------------------------
@itembound <item name/ID> <amount> <bound_type>
Creates the specified item and bounds it to the account.
bound_type: 1 = Account, 2 = Guild, 3 = Party
---------------------------------------
@itembound2 <item name/ID> <quantity> <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4> <bound_type>
Creates an item with the given parameters (the 'cards' can be any item) and bounds it to the account.
identify_flag: 0 = unidentified, 1 = identified
attribute: 0 = not broken, 1 = broken
bound_type: 1 = Account, 2 = Guild, 3 = Party
--------------------------------------- ---------------------------------------
@produce <equip name/ID> <element> <# of Very's> @produce <equip name/ID> <element> <# of Very's>

View File

@ -172,3 +172,9 @@ Allows player to use the client command /check (displays character status).
Allows player to use the client command /changemaptype. Allows player to use the client command /changemaptype.
--------------------------------------- ---------------------------------------
*can_trade_bounded
Allows player to do normal item actions (sell, trade, store, vend, auction, mail) with bounded items.
---------------------------------------

View File

@ -2687,6 +2687,7 @@ recreate these items perfectly if they are destroyed. Here's what you get:
@inventorylist_card4[] if the character owns an item made by a specific @inventorylist_card4[] if the character owns an item made by a specific
craftsman. craftsman.
@inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires. @inventorylist_expire[] - expire time (Unix time stamp). 0 means never expires.
@inventorylist_bound[] - whether it is bound to the character
@inventorylist_count - the number of items in these lists. @inventorylist_count - the number of items in these lists.
This could be handy to save/restore a character's inventory, since no other This could be handy to save/restore a character's inventory, since no other
@ -4248,6 +4249,46 @@ command, creating a pet which is the same, but simultaneously exists in two
eggs, and may hatch from either, although, I'm not sure what kind of a mess will eggs, and may hatch from either, although, I'm not sure what kind of a mess will
this really cause. this really cause.
---------------------------------------
*getitembound <item id>,<amount>,<bound_type>{,<account ID>};
*getitembound "<item name>",<amount>,<bound_type>{,<account ID>};
This command will give an amount of specified items to the invoking character.
If an optional account ID is specified, and the target character is currently
online, items will be created in their inventory instead. If they are not
online, nothing will happen.
It works essentially the same as 'getitem', except that items created using
this command will bound the item to the player's account. Depending on the bound type issued,
the character may not be able to trade or store these items, however all bounded types
cannot be sold, vended, auctioned, or mailed.
Bound Types:
1 - Account Bound
2 - Guild Bound
3 - Party Bound
---------------------------------------
*getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound_type>{,<account ID>};
*getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound_type>{,<account ID>};
This command will give an amount of specified items to the invoking character.
If an optional account ID is specified, and the target character is currently
online, items will be created in their inventory instead. If they are not
online, nothing will happen.
It works essentially the same as 'getitem2', except that items created using
this command will bound the item to the player's account. Depending on the bound type issued,
the character may not be able to trade or store these items, however all bounded types
cannot be sold, vended, auctioned, or mailed.
Bound Types:
1 - Account Bound
2 - Guild Bound
3 - Party Bound
--------------------------------------- ---------------------------------------
*getnameditem <item id>,<character name|character ID>; *getnameditem <item id>,<character name|character ID>;
@ -4386,6 +4427,30 @@ Expanded version of 'countitem' function, used for created/carded/forged items.
This function will return the number of items for the specified item ID and This function will return the number of items for the specified item ID and
other parameters that the invoking character has in the inventory. other parameters that the invoking character has in the inventory.
Check 'getitem2' to understand the arguments of the function. Check 'getitem2' to understand the arguments of the function.
---------------------------------------
*countbound({<bound_type>})
This function will return a number of bounded items on the character.
Optionally, you may count a specific type of bounded item on the character.
countbound will also build an array of items in the form of @bound_items.
Using countbound without a type counts all types of bounded items.
Available types are:
1 - Account Bound
2 - Guild Bound
3 - Party Bound
Example:
mes "[Bound Counter]";
mes "I see you currently have "+countbound+" bounded items.";
next;
mes "The list of bounded items include:";
for(.@i = 0; .@i<getarraysize(@bound_items); .@i++)
mes getitemname(@bound_items[.@i]);
close;
--------------------------------------- ---------------------------------------

View File

@ -43,6 +43,7 @@ CREATE TABLE IF NOT EXISTS `cart_inventory` (
`card2` smallint(11) NOT NULL default '0', `card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0', `card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0', `expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(3) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0', `unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `char_id` (`char_id`) KEY `char_id` (`char_id`)
@ -340,6 +341,7 @@ CREATE TABLE IF NOT EXISTS `guild_storage` (
`card2` smallint(11) NOT NULL default '0', `card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0', `card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0', `expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(3) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0', `unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `guild_id` (`guild_id`) KEY `guild_id` (`guild_id`)
@ -404,6 +406,7 @@ CREATE TABLE IF NOT EXISTS `inventory` (
`card3` smallint(11) NOT NULL default '0', `card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0', `expire_time` int(11) unsigned NOT NULL default '0',
`favorite` tinyint(3) unsigned NOT NULL default '0', `favorite` tinyint(3) unsigned NOT NULL default '0',
`bound` tinyint(3) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0', `unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `char_id` (`char_id`) KEY `char_id` (`char_id`)
@ -665,6 +668,7 @@ CREATE TABLE IF NOT EXISTS `storage` (
`card2` smallint(11) NOT NULL default '0', `card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0', `card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0', `expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(3) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0', `unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
KEY `account_id` (`account_id`) KEY `account_id` (`account_id`)

View File

@ -0,0 +1,4 @@
ALTER TABLE `inventory` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `favorite`;
ALTER TABLE `cart_inventory` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
ALTER TABLE `storage` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
ALTER TABLE `guild_storage` ADD COLUMN `bound` TINYINT(3) UNSIGNED NOT NULL default '0' AFTER `expire_time`;

View File

@ -775,7 +775,7 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
// it significantly reduces cpu load on the database server. // it significantly reduces cpu load on the database server.
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`"); StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id); StringBuf_Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);
@ -798,8 +798,9 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 8, SQLDT_UINT, &item.bound, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
SqlStmt_BindColumn(stmt, 8+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
// bit array indicating which inventory items have already been matched // bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool)); flag = (bool*) aCalloc(max, sizeof(bool));
@ -826,14 +827,15 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
items[i].identify == item.identify && items[i].identify == item.identify &&
items[i].refine == item.refine && items[i].refine == item.refine &&
items[i].attribute == item.attribute && items[i].attribute == item.attribute &&
items[i].expire_time == item.expire_time ) items[i].expire_time == item.expire_time &&
items[i].bound == item.bound )
; //Do nothing. ; //Do nothing.
else else
{ {
// update all fields. // update all fields.
StringBuf_Clear(&buf); StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u'", StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `bound`='%d'",
tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time); tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
@ -861,7 +863,7 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
SqlStmt_Free(stmt); SqlStmt_Free(stmt);
StringBuf_Clear(&buf); StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`", tablename, selectoption); StringBuf_Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`", tablename, selectoption);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_AppendStr(&buf, ") VALUES "); StringBuf_AppendStr(&buf, ") VALUES ");
@ -879,8 +881,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
else else
found = true; found = true;
StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%"PRIu64"'", StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'",
id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].unique_id); id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].bound, items[i].unique_id);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", '%d'", items[i].card[j]); StringBuf_Printf(&buf, ", '%d'", items[i].card[j]);
StringBuf_AppendStr(&buf, ")"); StringBuf_AppendStr(&buf, ")");
@ -919,7 +921,7 @@ int inventory_to_sql(const struct item items[], int max, int id) {
// it significantly reduces cpu load on the database server. // it significantly reduces cpu load on the database server.
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`"); StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`");
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id);
@ -943,8 +945,9 @@ int inventory_to_sql(const struct item items[], int max, int id) {
SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL); SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL); SqlStmt_BindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
// bit array indicating which inventory items have already been matched // bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool)); flag = (bool*) aCalloc(max, sizeof(bool));
@ -970,13 +973,14 @@ int inventory_to_sql(const struct item items[], int max, int id) {
items[i].refine == item.refine && items[i].refine == item.refine &&
items[i].attribute == item.attribute && items[i].attribute == item.attribute &&
items[i].expire_time == item.expire_time && items[i].expire_time == item.expire_time &&
items[i].favorite == item.favorite ) items[i].favorite == item.favorite &&
items[i].bound == item.bound )
; //Do nothing. ; //Do nothing.
else { else {
// update all fields. // update all fields.
StringBuf_Clear(&buf); StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d'", StringBuf_Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%d', `bound`='%d'",
inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite); inventory_db, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]); StringBuf_Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id); StringBuf_Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);
@ -1001,7 +1005,7 @@ int inventory_to_sql(const struct item items[], int max, int id) {
SqlStmt_Free(stmt); SqlStmt_Free(stmt);
StringBuf_Clear(&buf); StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db); StringBuf_Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`", inventory_db);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_AppendStr(&buf, ") VALUES "); StringBuf_AppendStr(&buf, ") VALUES ");
@ -1018,8 +1022,8 @@ int inventory_to_sql(const struct item items[], int max, int id) {
else else
found = true; found = true;
StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%"PRIu64"'", StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%u', '%d', '%d', '%"PRIu64"'",
id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].unique_id); id, items[i].nameid, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time, items[i].favorite, items[i].bound, items[i].unique_id);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", '%d'", items[i].card[j]); StringBuf_Printf(&buf, ", '%d'", items[i].card[j]);
StringBuf_AppendStr(&buf, ")"); StringBuf_AppendStr(&buf, ")");
@ -1282,7 +1286,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
//read inventory //read inventory
//`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`) //`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`)
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`"); StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`");
for( i = 0; i < MAX_SLOTS; ++i ) for( i = 0; i < MAX_SLOTS; ++i )
StringBuf_Printf(&buf, ", `card%d`", i); StringBuf_Printf(&buf, ", `card%d`", i);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
@ -1299,10 +1303,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) || SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt,10, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i ) for( i = 0; i < MAX_SLOTS; ++i )
if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) if( SQL_ERROR == SqlStmt_BindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
@ -1313,7 +1318,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
//read cart //read cart
//`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`) //`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`)
StringBuf_Clear(&buf); StringBuf_Clear(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`"); StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j); StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART); StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
@ -1329,10 +1334,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL) || SQL_ERROR == SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) ) || SQL_ERROR == SqlStmt_BindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
|| SQL_ERROR == SqlStmt_BindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i ) for( i = 0; i < MAX_SLOTS; ++i )
if( SQL_ERROR == SqlStmt_BindColumn(stmt, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) ) if( SQL_ERROR == SqlStmt_BindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt); SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i ) for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
@ -2962,7 +2968,7 @@ int parse_frommap(int fd)
break; break;
} }
//Check account only if this ain't final save. Final-save goes through because of the char-map reconnect //Check account only if this ain't final save. Final-save goes through because of the char-map reconnect
if (RFIFOB(fd,12) || ( if (RFIFOB(fd,12) || RFIFOB(fd,13) || (
(character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL && (character = (struct online_char_data*)idb_get(online_char_db, aid)) != NULL &&
character->char_id == cid)) character->char_id == cid))
{ {

View File

@ -63,6 +63,7 @@ static int mail_fromsql(int char_id, struct mail_data* md)
Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data); Sql_GetData(sql_handle,14, &data, NULL); item->identify = atoi(data);
Sql_GetData(sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10); Sql_GetData(sql_handle,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
item->expire_time = 0; item->expire_time = 0;
item->bound = 0;
for (j = 0; j < MAX_SLOTS; j++) for (j = 0; j < MAX_SLOTS; j++)
{ {
@ -183,6 +184,7 @@ static bool mail_loadmessage(int mail_id, struct mail_message* msg)
Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data); Sql_GetData(sql_handle,14, &data, NULL); msg->item.identify = atoi(data);
Sql_GetData(sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10); Sql_GetData(sql_handle,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10);
msg->item.expire_time = 0; msg->item.expire_time = 0;
msg->item.bound = 0;
for( j = 0; j < MAX_SLOTS; j++ ) for( j = 0; j < MAX_SLOTS; j++ )
{ {

View File

@ -38,7 +38,7 @@ int storage_fromsql(int account_id, struct storage_data* p)
// storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} // storage {`account_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`}
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`unique_id`"); StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`expire_time`,`bound`,`unique_id`");
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ",`card%d`", j); StringBuf_Printf(&buf, ",`card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id); StringBuf_Printf(&buf, " FROM `%s` WHERE `account_id`='%d' ORDER BY `nameid`", storage_db, account_id);
@ -59,10 +59,11 @@ int storage_fromsql(int account_id, struct storage_data* p)
Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data);
Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data); Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data); Sql_GetData(sql_handle, 7, &data, NULL); item->expire_time = (unsigned int)atoi(data);
Sql_GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10); Sql_GetData(sql_handle, 8, &data, NULL); item->bound = atoi(data);
Sql_GetData(sql_handle, 9, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
{ {
Sql_GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data); Sql_GetData(sql_handle, 10+j, &data, NULL); item->card[j] = atoi(data);
} }
} }
p->storage_amount = i; p->storage_amount = i;
@ -95,7 +96,7 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p)
// storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`} // storage {`guild_id`/`id`/`nameid`/`amount`/`equip`/`identify`/`refine`/`attribute`/`card0`/`card1`/`card2`/`card3`}
StringBuf_Init(&buf); StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`unique_id`"); StringBuf_AppendStr(&buf, "SELECT `id`,`nameid`,`amount`,`equip`,`identify`,`refine`,`attribute`,`bound`,`unique_id`");
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ",`card%d`", j); StringBuf_Printf(&buf, ",`card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id); StringBuf_Printf(&buf, " FROM `%s` WHERE `guild_id`='%d' ORDER BY `nameid`", guild_storage_db, guild_id);
@ -115,11 +116,12 @@ int guild_storage_fromsql(int guild_id, struct guild_storage* p)
Sql_GetData(sql_handle, 4, &data, NULL); item->identify = atoi(data); Sql_GetData(sql_handle, 4, &data, NULL); item->identify = atoi(data);
Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data); Sql_GetData(sql_handle, 5, &data, NULL); item->refine = atoi(data);
Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data); Sql_GetData(sql_handle, 6, &data, NULL); item->attribute = atoi(data);
Sql_GetData(sql_handle, 7, &data, NULL); item->unique_id = strtoull(data, NULL, 10); Sql_GetData(sql_handle, 7, &data, NULL); item->bound = atoi(data);
Sql_GetData(sql_handle, 8, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
item->expire_time = 0; item->expire_time = 0;
for( j = 0; j < MAX_SLOTS; ++j ) for( j = 0; j < MAX_SLOTS; ++j )
{ {
Sql_GetData(sql_handle, 8+j, &data, NULL); item->card[j] = atoi(data); Sql_GetData(sql_handle, 9+j, &data, NULL); item->card[j] = atoi(data);
} }
} }
p->storage_amount = i; p->storage_amount = i;
@ -158,18 +160,19 @@ int inter_guild_storage_delete(int guild_id)
//--------------------------------------------------------- //---------------------------------------------------------
// packet from map server // packet from map server
int mapif_load_guild_storage(int fd,int account_id,int guild_id) int mapif_load_guild_storage(int fd,int account_id,int guild_id, char flag)
{ {
if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) ) if( SQL_ERROR == Sql_Query(sql_handle, "SELECT `guild_id` FROM `%s` WHERE `guild_id`='%d'", guild_db, guild_id) )
Sql_ShowDebug(sql_handle); Sql_ShowDebug(sql_handle);
else if( Sql_NumRows(sql_handle) > 0 ) else if( Sql_NumRows(sql_handle) > 0 )
{// guild exists {// guild exists
WFIFOHEAD(fd, sizeof(struct guild_storage)+12); WFIFOHEAD(fd, sizeof(struct guild_storage)+13);
WFIFOW(fd,0) = 0x3818; WFIFOW(fd,0) = 0x3818;
WFIFOW(fd,2) = sizeof(struct guild_storage)+12; WFIFOW(fd,2) = sizeof(struct guild_storage)+13;
WFIFOL(fd,4) = account_id; WFIFOL(fd,4) = account_id;
WFIFOL(fd,8) = guild_id; WFIFOL(fd,8) = guild_id;
guild_storage_fromsql(guild_id, (struct guild_storage*)WFIFOP(fd,12)); WFIFOB(fd,12) = flag; //1 open storage, 0 don't open
guild_storage_fromsql(guild_id, (struct guild_storage*)WFIFOP(fd,13));
WFIFOSET(fd, WFIFOW(fd,2)); WFIFOSET(fd, WFIFOW(fd,2));
return 0; return 0;
} }
@ -200,7 +203,7 @@ int mapif_save_guild_storage_ack(int fd,int account_id,int guild_id,int fail)
int mapif_parse_LoadGuildStorage(int fd) int mapif_parse_LoadGuildStorage(int fd)
{ {
RFIFOHEAD(fd); RFIFOHEAD(fd);
mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6)); mapif_load_guild_storage(fd,RFIFOL(fd,2),RFIFOL(fd,6),1);
return 0; return 0;
} }
@ -234,6 +237,126 @@ int mapif_parse_SaveGuildStorage(int fd)
return 0; return 0;
} }
int mapif_itembound_ack(int fd, int aid, int guild_id)
{
WFIFOHEAD(fd,8);
WFIFOW(fd,0) = 0x3856;
WFIFOL(fd,2) = aid;
WFIFOW(fd,6) = guild_id;
WFIFOSET(fd,8);
return 0;
}
//------------------------------------------------
//Guild bound items pull for offline characters [Akinari]
//------------------------------------------------
int mapif_parse_itembound_retrieve(int fd)
{
StringBuf buf;
SqlStmt* stmt;
struct item item;
int j, i=0, s;
bool found=false;
struct item items[MAX_INVENTORY];
int char_id = RFIFOL(fd,2);
int aid = RFIFOL(fd,6);
int guild_id = RFIFOW(fd,10);
StringBuf_Init(&buf);
StringBuf_AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`");
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_Printf(&buf, " FROM `%s` WHERE `char_id`='%d'",inventory_db,char_id);
stmt = SqlStmt_Malloc(sql_handle);
if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
|| SQL_ERROR == SqlStmt_Execute(stmt) )
{
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
StringBuf_Destroy(&buf);
return 1;
}
SqlStmt_BindColumn(stmt, 0, SQLDT_INT, &item.id, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 1, SQLDT_SHORT, &item.nameid, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 2, SQLDT_SHORT, &item.amount, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 3, SQLDT_USHORT, &item.equip, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 4, SQLDT_CHAR, &item.identify, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 5, SQLDT_CHAR, &item.refine, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SqlStmt_BindColumn(stmt, 8, SQLDT_UINT, &item.bound, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j )
SqlStmt_BindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
while( SQL_SUCCESS == SqlStmt_NextRow(stmt) ) {
if(item.bound == 2) {
memcpy(&items[i],&item,sizeof(struct item));
i++;
}
}
if(!i) //No items found - No need to continue
return 0;
//First we delete the character's items
StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "DELETE FROM `%s` WHERE",inventory_db);
for(j=0; j<i; j++) {
if( found )
StringBuf_AppendStr(&buf, " OR");
else
found = true;
StringBuf_Printf(&buf, " `id`=%d",items[j].id);
}
stmt = SqlStmt_Malloc(sql_handle);
if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
|| SQL_ERROR == SqlStmt_Execute(stmt) )
{
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
StringBuf_Destroy(&buf);
return 1;
}
//Now let's update the guild storage with those deleted items
found = false;
StringBuf_Clear(&buf);
StringBuf_Printf(&buf, "INSERT INTO `%s` (`guild_id`, `nameid`, `amount`, `identify`, `refine`, `attribute`, `expire_time`, `bound`", guild_storage_db);
for( j = 0; j < MAX_SLOTS; ++j )
StringBuf_Printf(&buf, ", `card%d`", j);
StringBuf_AppendStr(&buf, ") VALUES ");
for( j = 0; j < i; ++j ) {
if( found )
StringBuf_AppendStr(&buf, ",");
else
found = true;
StringBuf_Printf(&buf, "('%d', '%d', '%d', '%d', '%d', '%d', '%d', '%d'",
guild_id, items[j].nameid, items[j].amount, items[j].identify, items[j].refine, items[j].attribute, items[j].expire_time, items[j].bound);
for( s = 0; s < MAX_SLOTS; ++s )
StringBuf_Printf(&buf, ", '%d'", items[j].card[s]);
StringBuf_AppendStr(&buf, ")");
}
stmt = SqlStmt_Malloc(sql_handle);
if( SQL_ERROR == SqlStmt_PrepareStr(stmt, StringBuf_Value(&buf))
|| SQL_ERROR == SqlStmt_Execute(stmt) )
{
SqlStmt_ShowDebug(stmt);
SqlStmt_Free(stmt);
StringBuf_Destroy(&buf);
return 1;
}
//Finally reload storage and tell map we're done
mapif_load_guild_storage(fd,aid,guild_id,0);
mapif_itembound_ack(fd,aid,guild_id);
return 0;
}
int inter_storage_parse_frommap(int fd) int inter_storage_parse_frommap(int fd)
{ {
@ -241,6 +364,7 @@ int inter_storage_parse_frommap(int fd)
switch(RFIFOW(fd,0)){ switch(RFIFOW(fd,0)){
case 0x3018: mapif_parse_LoadGuildStorage(fd); break; case 0x3018: mapif_parse_LoadGuildStorage(fd); break;
case 0x3019: mapif_parse_SaveGuildStorage(fd); break; case 0x3019: mapif_parse_SaveGuildStorage(fd); break;
case 0x3056: mapif_parse_itembound_retrieve(fd); break;
default: default:
return 0; return 0;
} }

View File

@ -51,7 +51,7 @@ int inter_recv_packet_length[] = {
-1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party
-1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030- -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
-1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040- -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
-1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus] -1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus]
6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish] 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
-1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil] -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil]
48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080- 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-

View File

@ -204,7 +204,7 @@ struct item {
char attribute; char attribute;
short card[MAX_SLOTS]; short card[MAX_SLOTS];
unsigned int expire_time; unsigned int expire_time;
char favorite; char favorite, bound;
uint64 unique_id; uint64 unique_id;
}; };

View File

@ -1112,11 +1112,12 @@ ACMD_FUNC(heal)
/*========================================== /*==========================================
* @item command (usage: @item <name/id_of_item> <quantity>) (modified by [Yor] for pet_egg) * @item command (usage: @item <name/id_of_item> <quantity>) (modified by [Yor] for pet_egg)
* @itembound command (usage: @itembound <name/id_of_item> <quantity> <bound_type>)
*------------------------------------------*/ *------------------------------------------*/
ACMD_FUNC(item) ACMD_FUNC(item)
{ {
char item_name[100]; char item_name[100];
int number = 0, item_id, flag = 0; int number = 0, item_id, flag = 0, bound = 0;
struct item item_tmp; struct item item_tmp;
struct item_data *item_data; struct item_data *item_data;
int get_count, i; int get_count, i;
@ -1124,9 +1125,15 @@ ACMD_FUNC(item)
memset(item_name, '\0', sizeof(item_name)); memset(item_name, '\0', sizeof(item_name));
if (!message || !*message || ( if (!strcmpi(command+1,"itembound") && (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 1 && sscanf(message, "\"%99[^\"]\" %d %d", item_name, &number, &bound) < 2 &&
sscanf(message, "%99s %d", item_name, &number) < 1 sscanf(message, "%99s %d %d", item_name, &number, &bound) < 2
))) {
clif_displaymessage(fd, msg_txt(sd,295)); // Please enter an item name or ID (usage: @item <item name/ID> <quantity> <bound_type>).
return -1;
} else if (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d", item_name, &number) < 1 &&
sscanf(message, "%99s %d", item_name, &number) < 1
)) { )) {
clif_displaymessage(fd, msg_txt(sd,983)); // Please enter an item name or ID (usage: @item <item name/ID> <quantity>). clif_displaymessage(fd, msg_txt(sd,983)); // Please enter an item name or ID (usage: @item <item name/ID> <quantity>).
return -1; return -1;
@ -1142,6 +1149,11 @@ ACMD_FUNC(item)
return -1; return -1;
} }
if( bound < 0 || bound > 3 ) {
clif_displaymessage(fd, msg_txt(sd,298)); // Invalid bound type
return -1;
}
item_id = item_data->nameid; item_id = item_data->nameid;
get_count = number; get_count = number;
//Check if it's stackable. //Check if it's stackable.
@ -1154,6 +1166,7 @@ ACMD_FUNC(item)
memset(&item_tmp, 0, sizeof(item_tmp)); memset(&item_tmp, 0, sizeof(item_tmp));
item_tmp.nameid = item_id; item_tmp.nameid = item_id;
item_tmp.identify = 1; item_tmp.identify = 1;
item_tmp.bound = bound;
if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND))) if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif_additem(sd, 0, 0, flag); clif_additem(sd, 0, 0, flag);
@ -1173,17 +1186,23 @@ ACMD_FUNC(item2)
struct item item_tmp; struct item item_tmp;
struct item_data *item_data; struct item_data *item_data;
char item_name[100]; char item_name[100];
int item_id, number = 0; int item_id, number = 0, bound = 0;
int identify = 0, refine = 0, attr = 0; int identify = 0, refine = 0, attr = 0;
int c1 = 0, c2 = 0, c3 = 0, c4 = 0; int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
nullpo_retr(-1, sd); nullpo_retr(-1, sd);
memset(item_name, '\0', sizeof(item_name)); memset(item_name, '\0', sizeof(item_name));
if (!message || !*message || ( if (!strcmpi(command+1,"itembound2") && (!message || !*message || (
sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 &&
sscanf(message, "%99s %d %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) {
clif_displaymessage(fd, msg_txt(sd,296)); // Please enter all parameters (usage: @item2 <item name/ID> <quantity>
clif_displaymessage(fd, msg_txt(sd,297)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4> <bound_type>).
return -1;
} else if ( !message || !*message || (
sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 && sscanf(message, "\"%99[^\"]\" %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 &&
sscanf(message, "%99s %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9 sscanf(message, "%99s %d %d %d %d %d %d %d %d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9
)) { )) {
clif_displaymessage(fd, msg_txt(sd,984)); // Please enter all parameters (usage: @item2 <item name/ID> <quantity> clif_displaymessage(fd, msg_txt(sd,984)); // Please enter all parameters (usage: @item2 <item name/ID> <quantity>
clif_displaymessage(fd, msg_txt(sd,985)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>). clif_displaymessage(fd, msg_txt(sd,985)); // <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>).
return -1; return -1;
@ -1192,6 +1211,11 @@ ACMD_FUNC(item2)
if (number <= 0) if (number <= 0)
number = 1; number = 1;
if( bound < 0 || bound > 3 ) {
clif_displaymessage(fd, msg_txt(sd,298)); // Invalid bound type
return -1;
}
item_id = 0; item_id = 0;
if ((item_data = itemdb_searchname(item_name)) != NULL || if ((item_data = itemdb_searchname(item_name)) != NULL ||
(item_data = itemdb_exists(atoi(item_name))) != NULL) (item_data = itemdb_exists(atoi(item_name))) != NULL)
@ -1228,6 +1252,7 @@ ACMD_FUNC(item2)
item_tmp.card[1] = c2; item_tmp.card[1] = c2;
item_tmp.card[2] = c3; item_tmp.card[2] = c3;
item_tmp.card[3] = c4; item_tmp.card[3] = c4;
item_tmp.bound = bound;
if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND))) if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif_additem(sd, 0, 0, flag); clif_additem(sd, 0, 0, flag);
} }
@ -9012,6 +9037,8 @@ void atcommand_basecommands(void) {
ACMD_DEF(heal), ACMD_DEF(heal),
ACMD_DEF(item), ACMD_DEF(item),
ACMD_DEF(item2), ACMD_DEF(item2),
ACMD_DEF2("itembound",item),
ACMD_DEF2("itembound2",item2),
ACMD_DEF(itemreset), ACMD_DEF(itemreset),
ACMD_DEF(clearstorage), ACMD_DEF(clearstorage),
ACMD_DEF(cleargstorage), ACMD_DEF(cleargstorage),

View File

@ -311,7 +311,7 @@ void buyingstore_trade(struct map_session_data* sd, int account_id, unsigned int
return; return;
} }
if( sd->status.inventory[index].expire_time || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) ) if( sd->status.inventory[index].expire_time || (sd->status.inventory[index].bound && !pc_can_give_bounded_items(sd)) || !itemdb_cantrade(&sd->status.inventory[index], pc_get_group_level(sd), pc_get_group_level(pl_sd)) || memcmp(sd->status.inventory[index].card, buyingstore_blankslots, sizeof(buyingstore_blankslots)) )
{// non-tradable item {// non-tradable item
clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid); clif_buyingstore_trade_failed_seller(sd, BUYINGSTORE_TRADE_SELLER_FAILED, nameid);
return; return;

View File

@ -1790,8 +1790,11 @@ void clif_selllist(struct map_session_data *sd)
if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) ) if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )
continue; continue;
if( sd->status.inventory[i].expire_time ) if( sd->status.inventory[i].expire_time || (sd->status.inventory[i].bound && !pc_can_give_bounded_items(sd)) )
continue; // Cannot Sell Rental Items continue; // Cannot Sell Rental Items or Account Bounded Items
if( sd->status.inventory[i].bound && !pc_can_give_bounded_items(sd))
continue; // Don't allow sale of bound items
val=sd->inventory_data[i]->value_sell; val=sd->inventory_data[i]->value_sell;
if( val < 0 ) if( val < 0 )
@ -2194,7 +2197,7 @@ void clif_additem(struct map_session_data *sd, int n, int amount, int fail)
WFIFOL(fd,offs+23)=sd->status.inventory[n].expire_time; WFIFOL(fd,offs+23)=sd->status.inventory[n].expire_time;
#endif #endif
#if PACKETVER >= 20071002 #if PACKETVER >= 20071002
WFIFOW(fd,offs+27)=0; // HireExpireDate WFIFOW(fd,offs+27)=sd->status.inventory[n].bound ? 2 : 0;
#endif #endif
} }
@ -2300,7 +2303,7 @@ void clif_item_sub(unsigned char *buf, int n, int idx, struct item *i, struct it
clif_addcards(WBUFP(buf, n+12), i); //8B clif_addcards(WBUFP(buf, n+12), i); //8B
#if PACKETVER >= 20071002 #if PACKETVER >= 20071002
WBUFL(buf,n+20)=i->expire_time; WBUFL(buf,n+20)=i->expire_time;
WBUFW(buf,n+24)=0; //Unknown WBUFW(buf,n+24)=i->bound ? 2 : 0;
#endif #endif
#if PACKETVER >= 20100629 #if PACKETVER >= 20100629
WBUFW(buf,n+26)= (id->equip&EQP_VISIBLE)?id->look:0; WBUFW(buf,n+26)= (id->equip&EQP_VISIBLE)?id->look:0;
@ -14199,6 +14202,11 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd)
} }
// Auction checks... // Auction checks...
if( sd->status.inventory[sd->auction.index].bound && !pc_can_give_bounded_items(sd) ) {
clif_displaymessage(sd->fd, msg_txt(sd,293));
clif_Auction_message(fd, 2); // The auction has been canceled
return;
}
if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) ) { if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) ) {
clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee. clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee.
return; return;

View File

@ -106,6 +106,7 @@ void guild_flag_remove(struct npc_data *nd);
void guild_flags_clear(void); void guild_flags_clear(void);
void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv); void guild_guildaura_refresh(struct map_session_data *sd, uint16 skill_id, uint16 skill_lv);
void guild_retrieveitembound(int char_id,int aid,int guild_id);
void do_final_guild(void); void do_final_guild(void);

View File

@ -39,7 +39,7 @@ static const int packet_len_table[]={
39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820 39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820
10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830 10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
-1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840 -1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
-1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] -1,-1, 7, 7, 7,11, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari]
-1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish] -1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish]
-1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil] -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil]
11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880 11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
@ -1002,15 +1002,19 @@ int intif_parse_LoadGuildStorage(int fd)
{ {
struct guild_storage *gstor; struct guild_storage *gstor;
struct map_session_data *sd; struct map_session_data *sd;
int guild_id; int guild_id, flag;
guild_id = RFIFOL(fd,8); guild_id = RFIFOL(fd,8);
flag = RFIFOL(fd,12);
if(guild_id <= 0) if(guild_id <= 0)
return 1; return 1;
sd=map_id2sd( RFIFOL(fd,4) ); sd=map_id2sd( RFIFOL(fd,4) );
if(sd==NULL){ if( flag ){ //If flag != 0, we attach a player and open the storage
ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4)); if(sd==NULL){
return 1; ShowError("intif_parse_LoadGuildStorage: user not found %d\n",RFIFOL(fd,4));
return 1;
}
} }
gstor=guild2storage(guild_id); gstor=guild2storage(guild_id);
if(!gstor) { if(!gstor) {
@ -1018,21 +1022,23 @@ int intif_parse_LoadGuildStorage(int fd)
return 1; return 1;
} }
if (gstor->storage_status == 1) { // Already open.. lets ignore this update if (gstor->storage_status == 1) { // Already open.. lets ignore this update
ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", sd->status.account_id, sd->status.char_id); ShowWarning("intif_parse_LoadGuildStorage: storage received for a client already open (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1);
return 1; return 1;
} }
if (gstor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex] if (gstor->dirty) { // Already have storage, and it has been modified and not saved yet! Exploit! [Skotlex]
ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", sd->status.account_id, sd->status.char_id); ShowWarning("intif_parse_LoadGuildStorage: received storage for an already modified non-saved storage! (User %d:%d)\n", flag?sd->status.account_id:1, flag?sd->status.char_id:1);
return 1; return 1;
} }
if( RFIFOW(fd,2)-12 != sizeof(struct guild_storage) ){ if( RFIFOW(fd,2)-13 != sizeof(struct guild_storage) ){
ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-12 , sizeof(struct guild_storage)); ShowError("intif_parse_LoadGuildStorage: data size error %d %d\n",RFIFOW(fd,2)-13 , sizeof(struct guild_storage));
gstor->storage_status = 0; gstor->storage_status = 0;
return 1; return 1;
} }
memcpy(gstor,RFIFOP(fd,12),sizeof(struct guild_storage)); memcpy(gstor,RFIFOP(fd,13),sizeof(struct guild_storage));
storage_guild_storageopen(sd); if( flag )
storage_guild_storageopen(sd);
return 0; return 0;
} }
@ -2152,6 +2158,31 @@ void intif_parse_MessageToFD(int fd) {
return; return;
} }
/*==========================================
* Item Bound System
*------------------------------------------*/
void intif_itembound_req(int char_id,int aid,int guild_id) {
struct guild_storage *gstor = guild2storage2(guild_id);
WFIFOHEAD(inter_fd,12);
WFIFOW(inter_fd,0) = 0x3056;
WFIFOL(inter_fd,2) = char_id;
WFIFOL(inter_fd,6) = aid;
WFIFOW(inter_fd,10) = guild_id;
WFIFOSET(inter_fd,12);
if(gstor)
gstor->lock = 1; //Lock for retrieval process
}
//3856
void intif_parse_itembound_ack(int fd) {
struct guild_storage *gstor;
int guild_id = RFIFOW(char_fd,6);
gstor = guild2storage2(guild_id);
if(gstor) gstor->lock = 0; //Unlock now that operation is completed
}
//----------------------------------------------------------------- //-----------------------------------------------------------------
// Communication from the inter server // Communication from the inter server
// Return a 0 (false) if there were any errors. // Return a 0 (false) if there were any errors.
@ -2235,6 +2266,9 @@ int intif_parse(int fd)
case 0x3854: intif_parse_Auction_message(fd); break; case 0x3854: intif_parse_Auction_message(fd); break;
case 0x3855: intif_parse_Auction_bid(fd); break; case 0x3855: intif_parse_Auction_bid(fd); break;
//Bound items
case 0x3856: intif_parse_itembound_ack(fd); break;
// Mercenary System // Mercenary System
case 0x3870: intif_parse_mercenary_received(fd); break; case 0x3870: intif_parse_mercenary_received(fd); break;
case 0x3871: intif_parse_mercenary_deleted(fd); break; case 0x3871: intif_parse_mercenary_deleted(fd); break;

View File

@ -60,6 +60,7 @@ int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
int intif_guild_emblem(int guild_id, int len, const char *data); int intif_guild_emblem(int guild_id, int len, const char *data);
int intif_guild_castle_dataload(int num, int *castle_ids); int intif_guild_castle_dataload(int num, int *castle_ids);
int intif_guild_castle_datasave(int castle_id, int index, int value); int intif_guild_castle_datasave(int castle_id, int index, int value);
void intif_itembound_req(int char_id, int aid, int guild_id);
int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id, int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name); short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);

View File

@ -78,8 +78,9 @@ unsigned char mail_setitem(struct map_session_data *sd, int idx, int amount) {
return 1; return 1;
if( amount < 0 || amount > sd->status.inventory[idx].amount ) if( amount < 0 || amount > sd->status.inventory[idx].amount )
return 1; return 1;
if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time || if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time
!itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd)) ) || !itemdb_canmail(&sd->status.inventory[idx],pc_get_group_level(sd))
|| (sd->status.inventory[idx].bound && !pc_can_give_bounded_items(sd)) )
return 1; return 1;
sd->mail.index = idx; sd->mail.index = idx;

View File

@ -552,12 +552,10 @@ int party_member_withdraw(int party_id, int account_id, int char_id)
struct map_session_data* sd = map_id2sd(account_id); struct map_session_data* sd = map_id2sd(account_id);
struct party_data* p = party_search(party_id); struct party_data* p = party_search(party_id);
if( p ) if( p ) {
{
int i; int i;
ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id ); ARR_FIND( 0, MAX_PARTY, i, p->party.member[i].account_id == account_id && p->party.member[i].char_id == char_id );
if( i < MAX_PARTY ) if( i < MAX_PARTY ) {
{
clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0); clif_party_withdraw(p,sd,account_id,p->party.member[i].name,0x0);
memset(&p->party.member[i], 0, sizeof(p->party.member[0])); memset(&p->party.member[i], 0, sizeof(p->party.member[0]));
memset(&p->data[i], 0, sizeof(p->data[0])); memset(&p->data[i], 0, sizeof(p->data[0]));
@ -566,8 +564,12 @@ int party_member_withdraw(int party_id, int account_id, int char_id)
} }
} }
if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) if( sd && sd->status.party_id == party_id && sd->status.char_id == char_id ) {
{ int idxlist[MAX_INVENTORY]; //or malloc to reduce consumtion
int j,i;
j = pc_bound_chk(sd,3,idxlist);
for(i=0;i<j;i++)
pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER);
sd->status.party_id = 0; sd->status.party_id = 0;
clif_charnameupdate(sd); //Update name display [Skotlex] clif_charnameupdate(sd); //Update name display [Skotlex]
//TODO: hp bars should be cleared too //TODO: hp bars should be cleared too

View File

@ -509,6 +509,14 @@ bool pc_can_give_items(struct map_session_data *sd)
return pc_has_permission(sd, PC_PERM_TRADE); return pc_has_permission(sd, PC_PERM_TRADE);
} }
/**
* Determines if player can give / drop / trade / vend bounded items
*/
bool pc_can_give_bounded_items(struct map_session_data *sd)
{
return pc_has_permission(sd, PC_PERM_TRADE_BOUNDED);
}
/*========================================== /*==========================================
* prepares character for saving. * prepares character for saving.
*------------------------------------------*/ *------------------------------------------*/
@ -914,7 +922,8 @@ int pc_isequip(struct map_session_data *sd,int n)
*------------------------------------------*/ *------------------------------------------*/
bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers) bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_time, int group_id, struct mmo_charstatus *st, bool changing_mapservers)
{ {
int i; int i, j;
int idxlist[MAX_INVENTORY];
unsigned long tick = gettick(); unsigned long tick = gettick();
uint32 ip = session[sd->fd]->client_addr; uint32 ip = session[sd->fd]->client_addr;
@ -1091,6 +1100,12 @@ bool pc_authok(struct map_session_data *sd, int login_id2, time_t expiration_tim
**/ **/
pc_itemcd_do(sd,true); pc_itemcd_do(sd,true);
// Party bound item check
if(sd->status.party_id == 0 && (j = pc_bound_chk(sd,3,idxlist))) { // Party was deleted while character offline
for(i=0;i<j;i++)
pc_delitem(sd,idxlist[i],sd->status.inventory[idxlist[i]].amount,0,1,LOG_TYPE_OTHER);
}
// Request all registries (auth is considered completed whence they arrive) // Request all registries (auth is considered completed whence they arrive)
intif_request_registry(sd,7); intif_request_registry(sd,7);
return true; return true;
@ -3878,7 +3893,7 @@ int pc_additem(struct map_session_data *sd,struct item *item_data,int amount,e_l
{ // Stackable | Non Rental { // Stackable | Non Rental
for( i = 0; i < MAX_INVENTORY; i++ ) for( i = 0; i < MAX_INVENTORY; i++ )
{ {
if( sd->status.inventory[i].nameid == item_data->nameid && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 ) if( sd->status.inventory[i].nameid == item_data->nameid && sd->status.inventory[i].bound == item_data->bound && memcmp(&sd->status.inventory[i].card, &item_data->card, sizeof(item_data->card)) == 0 )
{ {
if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) ) if( amount > MAX_AMOUNT - sd->status.inventory[i].amount || ( data->stack.inventory && amount > data->stack.amount - sd->status.inventory[i].amount ) )
return 5; return 5;
@ -4418,7 +4433,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun
return 1; return 1;
} }
if( !itemdb_cancartstore(item_data, pc_get_group_level(sd)) ) if( !itemdb_cancartstore(item_data, pc_get_group_level(sd)) || ((item_data->bound == 2 || item_data->bound == 3) && !pc_can_give_bounded_items(sd)))
{ // Check item trade restrictions [Skotlex] { // Check item trade restrictions [Skotlex]
clif_displaymessage (sd->fd, msg_txt(sd,264)); clif_displaymessage (sd->fd, msg_txt(sd,264));
return 1; return 1;
@ -4431,7 +4446,7 @@ int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amoun
if( itemdb_isstackable2(data) && !item_data->expire_time ) if( itemdb_isstackable2(data) && !item_data->expire_time )
{ {
ARR_FIND( 0, MAX_CART, i, ARR_FIND( 0, MAX_CART, i,
sd->status.cart[i].nameid == item_data->nameid && sd->status.cart[i].nameid == item_data->nameid && sd->status.cart[i].bound == item_data->bound &&
sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] && sd->status.cart[i].card[0] == item_data->card[0] && sd->status.cart[i].card[1] == item_data->card[1] &&
sd->status.cart[i].card[2] == item_data->card[2] && sd->status.cart[i].card[3] == item_data->card[3] ); sd->status.cart[i].card[2] == item_data->card[2] && sd->status.cart[i].card[3] == item_data->card[3] );
}; };
@ -4566,6 +4581,25 @@ int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount)
return 1; return 1;
} }
/*==========================================
* Bound Item Check
* Type:
* 1 Account Bound
* 2 Guild Bound
* 3 Party Bound
*------------------------------------------*/
int pc_bound_chk(TBL_PC *sd,int type,int *idxlist)
{
int i=0, j=0;
for(i=0;i<MAX_INVENTORY;i++){
if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0 && sd->status.inventory[i].bound == type) {
idxlist[j] = i;
j++;
}
}
return j;
}
/*========================================== /*==========================================
* Display item stolen msg to player sd * Display item stolen msg to player sd
*------------------------------------------*/ *------------------------------------------*/
@ -7880,7 +7914,7 @@ int pc_setmadogear(TBL_PC* sd, int flag)
*------------------------------------------*/ *------------------------------------------*/
int pc_candrop(struct map_session_data *sd, struct item *item) int pc_candrop(struct map_session_data *sd, struct item *item)
{ {
if( item && item->expire_time ) if( item && (item->expire_time || (item->bound && !pc_can_give_bounded_items(sd))) )
return 0; return 0;
if( !pc_can_give_items(sd) ) //check if this GM level can drop items if( !pc_can_give_items(sd) ) //check if this GM level can drop items
return 0; return 0;

View File

@ -705,6 +705,7 @@ int pc_get_group_level(struct map_session_data *sd);
int pc_get_group_id(struct map_session_data *sd); int pc_get_group_id(struct map_session_data *sd);
int pc_getrefinebonus(int lv,int type); int pc_getrefinebonus(int lv,int type);
bool pc_can_give_items(struct map_session_data *sd); bool pc_can_give_items(struct map_session_data *sd);
bool pc_can_give_bounded_items(struct map_session_data *sd);
bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type); bool pc_can_use_command(struct map_session_data *sd, const char *command, AtCommandType type);
#define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 ) #define pc_has_permission(sd, permission) ( ((sd)->permissions&permission) != 0 )
@ -748,6 +749,9 @@ int pc_additem(struct map_session_data*,struct item*,int,e_log_pick_type);
int pc_getzeny(struct map_session_data*,int, enum e_log_pick_type, struct map_session_data*); int pc_getzeny(struct map_session_data*,int, enum e_log_pick_type, struct map_session_data*);
int pc_delitem(struct map_session_data*,int,int,int,short,e_log_pick_type); int pc_delitem(struct map_session_data*,int,int,int,short,e_log_pick_type);
//Bound items
int pc_bound_chk(TBL_PC *sd,int type,int *idxlist);
// Special Shop System // Special Shop System
int pc_paycash( struct map_session_data *sd, int price, int points, e_log_pick_type type ); int pc_paycash( struct map_session_data *sd, int price, int points, e_log_pick_type type );
int pc_getcash( struct map_session_data *sd, int cash, int points, e_log_pick_type type ); int pc_getcash( struct map_session_data *sd, int cash, int points, e_log_pick_type type );

View File

@ -44,6 +44,7 @@ enum e_pc_permission {
PC_PERM_DISABLE_PVP = 0x080000, PC_PERM_DISABLE_PVP = 0x080000,
PC_PERM_DISABLE_CMD_DEAD = 0x100000, PC_PERM_DISABLE_CMD_DEAD = 0x100000,
PC_PERM_CHANNEL_ADMIN = 0x200000, PC_PERM_CHANNEL_ADMIN = 0x200000,
PC_PERM_TRADE_BOUNDED = 0x400000,
}; };
static const struct { static const struct {
@ -72,6 +73,7 @@ static const struct {
{ "disable_pvp", PC_PERM_DISABLE_PVP }, { "disable_pvp", PC_PERM_DISABLE_PVP },
{ "disable_commands_when_dead", PC_PERM_DISABLE_CMD_DEAD }, { "disable_commands_when_dead", PC_PERM_DISABLE_CMD_DEAD },
{ "channel_admin", PC_PERM_CHANNEL_ADMIN }, { "channel_admin", PC_PERM_CHANNEL_ADMIN },
{ "can_trade_bounded", PC_PERM_TRADE_BOUNDED },
}; };
#endif // _PC_GROUPS_H_ #endif // _PC_GROUPS_H_

View File

@ -6270,6 +6270,13 @@ BUILDIN_FUNC(checkweight2){
/*========================================== /*==========================================
* getitem <item id>,<amount>{,<account ID>}; * getitem <item id>,<amount>{,<account ID>};
* getitem "<item name>",<amount>{,<account ID>}; * getitem "<item name>",<amount>{,<account ID>};
*
* getitembound <item id>,<amount>,<type>{,<account ID>};
* getitembound "<item id>",<amount>,<type>{,<account ID>};
* Type:
* 1 - Account Bound
* 2 - Guild Bound
* 3 - Party Bound
*------------------------------------------*/ *------------------------------------------*/
BUILDIN_FUNC(getitem) BUILDIN_FUNC(getitem)
{ {
@ -6280,8 +6287,7 @@ BUILDIN_FUNC(getitem)
data=script_getdata(st,2); data=script_getdata(st,2);
get_val(st,data); get_val(st,data);
if( data_isstring(data) ) if( data_isstring(data) ) {// "<item name>"
{// "<item name>"
const char *name=conv_str(st,data); const char *name=conv_str(st,data);
struct item_data *item_data = itemdb_searchname(name); struct item_data *item_data = itemdb_searchname(name);
if( item_data == NULL ){ if( item_data == NULL ){
@ -6289,8 +6295,7 @@ BUILDIN_FUNC(getitem)
return 1; //No item created. return 1; //No item created.
} }
nameid=item_data->nameid; nameid=item_data->nameid;
} else if( data_isint(data) ) } else if( data_isint(data) ) {// <item id>
{// <item id>
nameid=conv_num(st,data); nameid=conv_num(st,data);
//Violet Box, Blue Box, etc - random item pick //Violet Box, Blue Box, etc - random item pick
if( nameid < 0 ) { if( nameid < 0 ) {
@ -6317,7 +6322,18 @@ BUILDIN_FUNC(getitem)
else else
it.identify=itemdb_isidentified(nameid); it.identify=itemdb_isidentified(nameid);
if( script_hasdata(st,4) ) if( !strcmp(script_getfuncname(st),"getitembound") ) {
char bound = script_getnum(st,4);
if( bound < 1 || bound > 3) { //Not a correct bound type
ShowError("script_getitembound: Not a correct bound type! Type=%d\n",bound);
return 1;
}
it.bound = bound;
if( script_hasdata(st,5) )
sd=map_id2sd(script_getnum(st,5));
else
sd=script_rid2sd(st); // Attached player
} else if( script_hasdata(st,4) )
sd=map_id2sd(script_getnum(st,4)); // <Account ID> sd=map_id2sd(script_getnum(st,4)); // <Account ID>
else else
sd=script_rid2sd(st); // Attached player sd=script_rid2sd(st); // Attached player
@ -6355,12 +6371,23 @@ BUILDIN_FUNC(getitem2)
{ {
int nameid,amount,get_count,i,flag = 0; int nameid,amount,get_count,i,flag = 0;
int iden,ref,attr,c1,c2,c3,c4; int iden,ref,attr,c1,c2,c3,c4;
char bound=0;
struct item_data *item_data; struct item_data *item_data;
struct item item_tmp; struct item item_tmp;
TBL_PC *sd; TBL_PC *sd;
struct script_data *data; struct script_data *data;
if( script_hasdata(st,11) ) if( !strcmp(script_getfuncname(st),"getitembound2") ) {
bound = script_getnum(st,11);
if( bound < 1 || bound > 3) { //Not a correct bound type
ShowError("script_getitembound2: Not a correct bound type! Type=%d\n",bound);
return 1;
}
if( script_hasdata(st,12) )
sd=map_id2sd(script_getnum(st,12));
else
sd=script_rid2sd(st); // Attached player
} else if( script_hasdata(st,11) )
sd=map_id2sd(script_getnum(st,11)); // <Account ID> sd=map_id2sd(script_getnum(st,11)); // <Account ID>
else else
sd=script_rid2sd(st); // Attached player sd=script_rid2sd(st); // Attached player
@ -6370,14 +6397,14 @@ BUILDIN_FUNC(getitem2)
data=script_getdata(st,2); data=script_getdata(st,2);
get_val(st,data); get_val(st,data);
if( data_isstring(data) ){ if( data_isstring(data) ) {
const char *name=conv_str(st,data); const char *name=conv_str(st,data);
struct item_data *item_data = itemdb_searchname(name); struct item_data *item_data = itemdb_searchname(name);
if( item_data ) if( item_data )
nameid=item_data->nameid; nameid=item_data->nameid;
else else
nameid=UNKNOWN_ITEM_ID; nameid=UNKNOWN_ITEM_ID;
}else } else
nameid=conv_num(st,data); nameid=conv_num(st,data);
amount=script_getnum(st,3); amount=script_getnum(st,3);
@ -6422,6 +6449,7 @@ BUILDIN_FUNC(getitem2)
item_tmp.card[1]=(short)c2; item_tmp.card[1]=(short)c2;
item_tmp.card[2]=(short)c3; item_tmp.card[2]=(short)c3;
item_tmp.card[3]=(short)c4; item_tmp.card[3]=(short)c4;
item_tmp.bound=bound;
//Check if it's stackable. //Check if it's stackable.
if (!itemdb_isstackable(nameid)) if (!itemdb_isstackable(nameid))
@ -6496,6 +6524,7 @@ BUILDIN_FUNC(rentitem)
it.nameid = nameid; it.nameid = nameid;
it.identify = 1; it.identify = 1;
it.expire_time = (unsigned int)(time(NULL) + seconds); it.expire_time = (unsigned int)(time(NULL) + seconds);
it.bound = 0;
if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) ) if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) )
{ {
@ -11371,6 +11400,7 @@ BUILDIN_FUNC(successremovecards) {
item_tmp.refine = sd->status.inventory[i].refine; item_tmp.refine = sd->status.inventory[i].refine;
item_tmp.attribute = sd->status.inventory[i].attribute; item_tmp.attribute = sd->status.inventory[i].attribute;
item_tmp.expire_time = sd->status.inventory[i].expire_time; item_tmp.expire_time = sd->status.inventory[i].expire_time;
item_tmp.bound = sd->status.inventory[i].bound;
for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
item_tmp.card[j]=sd->status.inventory[i].card[j]; item_tmp.card[j]=sd->status.inventory[i].card[j];
@ -11444,6 +11474,7 @@ BUILDIN_FUNC(failedremovecards) {
item_tmp.refine = sd->status.inventory[i].refine; item_tmp.refine = sd->status.inventory[i].refine;
item_tmp.attribute = sd->status.inventory[i].attribute; item_tmp.attribute = sd->status.inventory[i].attribute;
item_tmp.expire_time = sd->status.inventory[i].expire_time; item_tmp.expire_time = sd->status.inventory[i].expire_time;
item_tmp.bound = sd->status.inventory[i].bound;
for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++) for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
item_tmp.card[j]=sd->status.inventory[i].card[j]; item_tmp.card[j]=sd->status.inventory[i].card[j];
@ -12107,6 +12138,7 @@ BUILDIN_FUNC(getinventorylist)
pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]); pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]);
} }
pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time); pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time);
pc_setreg(sd,reference_uid(add_str("@inventorylist_bound"), j),sd->status.inventory[i].bound);
j++; j++;
} }
} }
@ -17660,6 +17692,40 @@ BUILDIN_FUNC(sit)
return 0; return 0;
} }
/*==========================================
* countbound {<type>};
* Creates an array of bounded item IDs
* Returns amount of items found
* Type:
* 1 - Account Bound
* 2 - Guild Bound
* 3 - Party Bound
*------------------------------------------*/
BUILDIN_FUNC(countbound)
{
int i, type, j=0, k=0;
TBL_PC *sd;
if( (sd = script_rid2sd(st)) == NULL )
return 0;
type = script_hasdata(st,2)?script_getnum(st,2):0;
for(i=0;i<MAX_INVENTORY;i++){
if(sd->status.inventory[i].nameid > 0 && (
(!type && sd->status.inventory[i].bound > 0) ||
(type && sd->status.inventory[i].bound == type)
)) {
pc_setreg(sd,reference_uid(add_str("@bound_items"), k),sd->status.inventory[i].nameid);
k++;
j += sd->status.inventory[i].amount;
}
}
script_pushint(st,j);
return 0;
}
// declarations that were supposed to be exported from npc_chat.c // declarations that were supposed to be exported from npc_chat.c
#ifdef PCRE_SUPPORT #ifdef PCRE_SUPPORT
BUILDIN_FUNC(defpattern); BUILDIN_FUNC(defpattern);
@ -18125,5 +18191,10 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(checkquest, "i?"), BUILDIN_DEF(checkquest, "i?"),
BUILDIN_DEF(changequest, "ii"), BUILDIN_DEF(changequest, "ii"),
BUILDIN_DEF(showevent, "ii"), BUILDIN_DEF(showevent, "ii"),
//Bound items [Xantara] & [Akinari]
BUILDIN_DEF2(getitem,"getitembound","vii?"),
BUILDIN_DEF2(getitem2,"getitembound2","viiiiiiiii?"),
BUILDIN_DEF(countbound, "?"),
{NULL,NULL,NULL}, {NULL,NULL,NULL},
}; };

View File

@ -120,7 +120,8 @@ int compare_item(struct item *a, struct item *b)
a->identify == b->identify && a->identify == b->identify &&
a->refine == b->refine && a->refine == b->refine &&
a->attribute == b->attribute && a->attribute == b->attribute &&
a->expire_time == b->expire_time ) a->expire_time == b->expire_time &&
a->bound == b->bound )
{ {
int i; int i;
for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++); for (i = 0; i < MAX_SLOTS && (a->card[i] == b->card[i]); i++);
@ -154,6 +155,11 @@ static int storage_additem(struct map_session_data* sd, struct item* item_data,
return 1; return 1;
} }
if( (item_data->bound == 2 || item_data->bound == 3) && !pc_can_give_bounded_items(sd) ) {
clif_displaymessage(sd->fd, msg_txt(sd,294));
return 1;
}
if( itemdb_isstackable2(data) ) if( itemdb_isstackable2(data) )
{//Stackable {//Stackable
for( i = 0; i < MAX_STORAGE; i++ ) for( i = 0; i < MAX_STORAGE; i++ )
@ -448,6 +454,11 @@ int guild_storage_additem(struct map_session_data* sd, struct guild_storage* sto
return 1; return 1;
} }
if( (item_data->bound == 1 || item_data->bound == 3) && !pc_can_give_bounded_items(sd) ) {
clif_displaymessage(sd->fd, msg_txt(sd,294));
return 1;
}
if(itemdb_isstackable2(data)){ //Stackable if(itemdb_isstackable2(data)){ //Stackable
for(i=0;i<MAX_GUILD_STORAGE;i++){ for(i=0;i<MAX_GUILD_STORAGE;i++){
if(compare_item(&stor->items[i], item_data)) { if(compare_item(&stor->items[i], item_data)) {

View File

@ -368,6 +368,12 @@ void trade_tradeadditem(struct map_session_data *sd, short index, short amount)
return; return;
} }
if( ((item->bound == 1 || item->bound == 3) || (item->bound == 2 && sd->status.guild_id != target_sd->status.guild_id)) && !pc_can_give_bounded_items(sd) ) { // Item Bound
clif_displaymessage(sd->fd, msg_txt(sd,293));
clif_tradeitemok(sd, index+2, 1);
return;
}
//Locate a trade position //Locate a trade position
ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 ); ARR_FIND( 0, 10, trade_i, sd->deal.item[trade_i].index == index || sd->deal.item[trade_i].amount == 0 );
if( trade_i == 10 ) //No space left if( trade_i == 10 ) //No space left

View File

@ -267,6 +267,7 @@ void vending_openvending(struct map_session_data* sd, const char* message, const
|| !sd->status.cart[index].identify // unidentified item || !sd->status.cart[index].identify // unidentified item
|| sd->status.cart[index].attribute == 1 // broken item || sd->status.cart[index].attribute == 1 // broken item
|| sd->status.cart[index].expire_time // It should not be in the cart but just in case || sd->status.cart[index].expire_time // It should not be in the cart but just in case
|| (sd->status.cart[index].bound && !pc_can_give_bounded_items(sd)) // can't trade account bound items and has no permission
|| !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item || !itemdb_cantrade(&sd->status.cart[index], pc_get_group_level(sd), pc_get_group_level(sd)) ) // untradeable item
continue; continue;