Skip to content

Commit

Permalink
Item Bind
Browse files Browse the repository at this point in the history
~ Original: by Xantara
~ Revised by Mhalicot
~ Topic: http://hercules.ws/board/topic/1198-please-implement-accound-bound-items/
Signed-off-by: Mhalicot <sevenzz23@yahoo.com>
  • Loading branch information
mhalicot committed Oct 18, 2013
1 parent 9883b81 commit e15015b
Show file tree
Hide file tree
Showing 23 changed files with 372 additions and 56 deletions.
1 change: 1 addition & 0 deletions conf/groups.conf
Expand Up @@ -260,6 +260,7 @@ groups: (
log_commands: true
permissions: {
can_trade: true
can_trade_bounded: false
can_party: true
all_skill: false
all_equipment: false
Expand Down
17 changes: 13 additions & 4 deletions conf/messages.conf
Expand Up @@ -455,6 +455,13 @@
// Messages of others (not for GM commands)
// ----------------------------------------

// Account-Bound Items
497: You cannot distribute this item - it is an account bounded item!

// @itembound / @itembound2
498: Cannot create bounded pet eggs or pet armors.
499: Cannot create bounded stackable items.

//500: FREE
501: Your account time limit is: %d-%m-%Y %H:%M:%S.
502: Day Mode is activated
Expand Down Expand Up @@ -711,11 +718,13 @@
981: Please enter color and message (usage: @kamic <color> <message>).
982: Invalid color.

// @item
983: Please enter an item name or ID (usage: @item <item name/ID> <quantity>).

// @item2
984: Please enter all parameters (usage: @item2 <item name/ID> <quantity>
// @item / @itembound
983: Please enter an item name or ID (usage: @%s <item name/ID> <quantity>).


// @item2 / @itembound2
984: Please enter all parameters (usage: @%s <item name/ID> <quantity>).
985: <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>).

// @baselevelup
Expand Down
15 changes: 15 additions & 0 deletions doc/atcommands.txt
Expand Up @@ -633,6 +633,21 @@ attribute: 0 = not broken, 1 = broken

---------------------------------------

@itembound <item name/ID> {<amount>}

Creates the specified item and bounds it to the account.
If an amount is given for @itembound, that number will be created.

---------------------------------------

@itembound2 <item name/ID> <quantity> <identify_flag> <refine> <attribute> <card1> <card2> <card3> <card4>

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

---------------------------------------

@produce <equip name/ID> <element> <# of Very's>

Creates a weapon with the given parameters.
Expand Down
1 change: 1 addition & 0 deletions doc/permissions.txt
Expand Up @@ -31,4 +31,5 @@ show_bossmobs : Ability to see boss mobs with @showmobs.
disable_pvm : Ability to disable Player vs. Monster.
disable_pvp : Ability to disable Player vs. Player.
disable_commands_when_dead : Ability to disable @command usage when dead.
can_trade_bounded : Ability to trade or otherwise distribute bounded items (drop, storage, vending etc...).
hchsys_admin : Hercules Chat System Admin (Ability to modify channel settings regardless of ownership and join password-protected channels without requiring a password.)
37 changes: 37 additions & 0 deletions doc/script_commands.txt
Expand Up @@ -2781,6 +2781,7 @@ recreate these items perfectly if they are destroyed. Here's what you get:
@inventorylist_expire[] - expire time (Unix time stamp). 0 means never
expires.
@inventorylist_count - the number of items in these lists.
@inventorylist_bound - whether it is an account bounded item or not.

This could be handy to save/restore a character's inventory, since no
other command returns such a complete set of data, and could also be the
Expand Down Expand Up @@ -4450,6 +4451,42 @@ mess will this really cause.

---------------------------------------

*getitembound <item id>,<amount>{,<account ID>};
*getitembound "<item name>",<amount>{,<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. They will not be able
to trade, sell, drop, nor auction the item.

---------------------------------------

*getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};
*getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<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. They will not be able
to trade, sell, drop, nor auction the item.

---------------------------------------

*equipisbounded <equipment slots>;

This command will check if the item in the specified equipment slot is bounded
to the player's account or not. It will return 1 if bounded, 0 otherwise.
For a list of equipment slots see 'getequipid'.

---------------------------------------

*getnameditem <item id>,<character name|character ID>;
*getnameditem "<item name>",<character name|character ID>;

Expand Down
6 changes: 5 additions & 1 deletion sql-files/main.sql
Expand Up @@ -43,6 +43,7 @@ CREATE TABLE IF NOT EXISTS `cart_inventory` (
`card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `char_id` (`char_id`)
Expand Down Expand Up @@ -341,6 +342,7 @@ CREATE TABLE IF NOT EXISTS `guild_storage` (
`card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `guild_id` (`guild_id`)
Expand Down Expand Up @@ -405,6 +407,7 @@ CREATE TABLE IF NOT EXISTS `inventory` (
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
`favorite` tinyint(3) unsigned NOT NULL default '0',
`bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `char_id` (`char_id`)
Expand Down Expand Up @@ -688,6 +691,7 @@ CREATE TABLE IF NOT EXISTS `storage` (
`card2` smallint(11) NOT NULL default '0',
`card3` smallint(11) NOT NULL default '0',
`expire_time` int(11) unsigned NOT NULL default '0',
`bound` tinyint(1) unsigned NOT NULL default '0',
`unique_id` bigint(20) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `account_id` (`account_id`)
Expand All @@ -713,5 +717,5 @@ CREATE TABLE IF NOT EXISTS `account_data` (
`account_id` int(11) unsigned NOT NULL default '0',
`bank_vault` int(11) unsigned NOT NULL default '0',
PRIMARY KEY (`account_id`)
) ENGINE=MyISAM;
) ENGINE=MyISAM;

3 changes: 3 additions & 0 deletions sql-files/upgrades/accountbound.sql
@@ -0,0 +1,3 @@
ALTER TABLE `inventory` ADD COLUMN `bound` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `favorite`;
ALTER TABLE `cart_inventory` ADD COLUMN `bound` tinyint(1) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
ALTER TABLE `storage` ADD COLUMN `bound` tinyint(1) UNSIGNED NOT NULL default '0' AFTER `expire_time`;
51 changes: 33 additions & 18 deletions src/char/char.c
Expand Up @@ -727,6 +727,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`");
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`", j);
if( tableswitch != TABLE_GUILD_STORAGE )
StrBuf->AppendStr(&buf, ", `bound`");
StrBuf->Printf(&buf, " FROM `%s` WHERE `%s`='%d'", tablename, selectoption, id);

stmt = SQL->StmtMalloc(sql_handle);
Expand All @@ -749,6 +751,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j )
SQL->StmtBindColumn(stmt, 8+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
if( tableswitch != TABLE_GUILD_STORAGE )
SQL->StmtBindColumn(stmt, 8+MAX_SLOTS, SQLDT_CHAR, &item.bound, 0, NULL, NULL);

// bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool));
Expand All @@ -775,7 +779,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
items[i].identify == item.identify &&
items[i].refine == item.refine &&
items[i].attribute == item.attribute &&
items[i].expire_time == item.expire_time )
items[i].expire_time == item.expire_time &&
(tableswitch != TABLE_GUILD_STORAGE && items[i].bound == item.bound) )
; //Do nothing.
else
{
Expand All @@ -784,7 +789,9 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u'",
tablename, items[i].amount, items[i].equip, items[i].identify, items[i].refine, items[i].attribute, items[i].expire_time);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
if( tableswitch != TABLE_GUILD_STORAGE )
StrBuf->Printf(&buf, ", `bound`=%d", items[i].bound);
StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);

if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) )
Expand Down Expand Up @@ -813,6 +820,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
StrBuf->Printf(&buf, "INSERT INTO `%s`(`%s`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`", tablename, selectoption);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`", j);
if( tableswitch != TABLE_GUILD_STORAGE )
StrBuf->AppendStr(&buf, ", `bound`");
StrBuf->AppendStr(&buf, ") VALUES ");

found = false;
Expand All @@ -832,6 +841,8 @@ int memitemdata_to_sql(const struct item items[], int max, int id, int tableswit
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);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
if( tableswitch != TABLE_GUILD_STORAGE )
StrBuf->Printf(&buf, ", '%d'", items[i].bound);
StrBuf->AppendStr(&buf, ")");

updateLastUid(items[i].unique_id); // Unique Non Stackable Item ID
Expand Down Expand Up @@ -868,7 +879,7 @@ int inventory_to_sql(const struct item items[], int max, int id) {
// it significantly reduces cpu load on the database server.

StrBuf->Init(&buf);
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`");
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`");
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`", j);
StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`='%d'", inventory_db, id);
Expand All @@ -892,8 +903,9 @@ int inventory_to_sql(const struct item items[], int max, int id) {
SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &item.attribute, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &item.expire_time, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &item.favorite, 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 9, SQLDT_CHAR, &item.bound, 0, NULL, NULL);
for( j = 0; j < MAX_SLOTS; ++j )
SQL->StmtBindColumn(stmt, 9+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);
SQL->StmtBindColumn(stmt, 10+j, SQLDT_SHORT, &item.card[j], 0, NULL, NULL);

// bit array indicating which inventory items have already been matched
flag = (bool*) aCalloc(max, sizeof(bool));
Expand All @@ -919,14 +931,15 @@ int inventory_to_sql(const struct item items[], int max, int id) {
items[i].refine == item.refine &&
items[i].attribute == item.attribute &&
items[i].expire_time == item.expire_time &&
items[i].favorite == item.favorite )
items[i].favorite == item.favorite &&
items[i].bound == item.bound )
; //Do nothing.
else {
// update all fields.
StrBuf->Clear(&buf);
StrBuf->Printf(&buf, "UPDATE `%s` SET `amount`='%d', `equip`='%d', `identify`='%d', `refine`='%d',`attribute`='%d', `expire_time`='%u', `favorite`='%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);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->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, items[i].bound);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`=%d", j, items[i].card[j]);
StrBuf->Printf(&buf, " WHERE `id`='%d' LIMIT 1", item.id);

Expand All @@ -950,7 +963,7 @@ int inventory_to_sql(const struct item items[], int max, int id) {
SQL->StmtFree(stmt);

StrBuf->Clear(&buf);
StrBuf->Printf(&buf, "INSERT INTO `%s` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`", inventory_db);
StrBuf->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 )
StrBuf->Printf(&buf, ", `card%d`", j);
StrBuf->AppendStr(&buf, ") VALUES ");
Expand All @@ -967,8 +980,8 @@ int inventory_to_sql(const struct item items[], int max, int id) {
else
found = true;

StrBuf->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].favorite, items[i].unique_id);
StrBuf->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].bound, items[i].unique_id);
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", '%d'", items[i].card[j]);
StrBuf->AppendStr(&buf, ")");
Expand Down Expand Up @@ -1230,7 +1243,7 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
//read inventory
//`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`)
StrBuf->Init(&buf);
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `unique_id`");
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `favorite`, `bound`, `unique_id`");
for( i = 0; i < MAX_SLOTS; ++i )
StrBuf->Printf(&buf, ", `card%d`", i);
StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", inventory_db, MAX_INVENTORY);
Expand All @@ -1247,10 +1260,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.favorite, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 10, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i )
if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
if( SQL_ERROR == SQL->StmtBindColumn(stmt, 11+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);

for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
Expand All @@ -1259,9 +1273,9 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
strcat(t_msg, " inventory");

//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`, `bound`, `unique_id`)
StrBuf->Clear(&buf);
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `unique_id`");
StrBuf->AppendStr(&buf, "SELECT `id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `expire_time`, `bound`, `unique_id`");
for( j = 0; j < MAX_SLOTS; ++j )
StrBuf->Printf(&buf, ", `card%d`", j);
StrBuf->Printf(&buf, " FROM `%s` WHERE `char_id`=? LIMIT %d", cart_db, MAX_CART);
Expand All @@ -1277,10 +1291,11 @@ int mmo_char_fromsql(int char_id, struct mmo_charstatus* p, bool load_everything
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 5, SQLDT_CHAR, &tmp_item.refine, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 6, SQLDT_CHAR, &tmp_item.attribute, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 7, SQLDT_UINT, &tmp_item.expire_time, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 8, SQLDT_CHAR, &tmp_item.bound, 0, NULL, NULL)
|| SQL_ERROR == SQL->StmtBindColumn(stmt, 9, SQLDT_ULONGLONG, &tmp_item.unique_id, 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);
for( i = 0; i < MAX_SLOTS; ++i )
if( SQL_ERROR == SQL->StmtBindColumn(stmt, 9+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
if( SQL_ERROR == SQL->StmtBindColumn(stmt, 10+i, SQLDT_SHORT, &tmp_item.card[i], 0, NULL, NULL) )
SqlStmt_ShowDebug(stmt);

for( i = 0; i < MAX_CART && SQL_SUCCESS == SQL->StmtNextRow(stmt); ++i )
Expand Down
2 changes: 2 additions & 0 deletions src/char/int_mail.c
Expand Up @@ -64,6 +64,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,15, &data, NULL); item->unique_id = strtoull(data, NULL, 10);
item->expire_time = 0;
item->bound = 0;

for (j = 0; j < MAX_SLOTS; j++)
{
Expand Down Expand Up @@ -184,6 +185,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,15, &data, NULL); msg->item.unique_id = strtoull(data, NULL, 10);
msg->item.expire_time = 0;
msg->item.bound = 0;

for( j = 0; j < MAX_SLOTS; j++ )
{
Expand Down

0 comments on commit e15015b

Please sign in to comment.