summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog-Trunk.txt6
-rw-r--r--src/map/script.c310
2 files changed, 194 insertions, 122 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index ef4c2a7cf..0078a63e7 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -1,6 +1,10 @@
Date Added
2010/12/25
+ * Updated script commands delitem and delitem2. [Ai4rei]
+ - Items are now deleted only if there is enough of them (as per r14395, related r13368).
+ - Not equipped items are now actually prioritized over equipped ones (related r200).
+ - Fixed item id not getting checked for validity.
* Fixed a compile error in txt-converter-char caused by 'save_log' being declared by both SQL and TXT (bugreport:4671, since r14619). [Ai4rei]
* Made SQL char-server options 'chars_per_account' and 'char_del_level' work on TXT as well (follow up to r1063 and r8420). [Ai4rei]
2010/12/24
@@ -1070,7 +1074,7 @@ Date Added
2008/11/11
* Updated script_commands.txt [FlavioJS]
* Made scripts terminate with an error: (fixes bugreport:2429)
- - when getitem/getitem2 tries to delete more items than the player has
+ - when delitem/delitem2 tries to delete more items than the player has
- when trying to set Zeny to a negative number.
2008/11/09
* Added a check to make sure killed monster's level was greater than half of the mercenary owner's level before incrementing mercenary's killcount. (bugreport:2410) [Paradox924X]
diff --git a/src/map/script.c b/src/map/script.c
index 725ef0204..a05d79159 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -5827,6 +5827,156 @@ BUILDIN_FUNC(makeitem)
return 0;
}
+
+/// Counts / deletes the current item given by idx.
+/// Used by buildin_delitem_search
+/// Relies on all input data being already fully valid.
+static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items)
+{
+ int delamount;
+ struct item* inv = &sd->status.inventory[idx];
+
+ delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount;
+
+ if( delete_items )
+ {
+ if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET )
+ {// delete associated pet
+ intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2]));
+ }
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ if( log_config.enable_logs&0x40 )
+ {
+ log_pick_pc(sd, "N", inv->nameid, -delamount, inv);
+ }
+ //Logs
+
+ pc_delitem(sd, idx, delamount, 0, 0);
+ }
+
+ amount[0]-= delamount;
+}
+
+
+/// Searches for item(s) and checks, if there is enough of them.
+/// Used by delitem and delitem2
+/// Relies on all input data being already fully valid.
+/// @param exact_match will also match item attributes and cards, not just name id
+/// @return true when all items could be deleted, false when there were not enough items to delete
+static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match)
+{
+ bool delete_items = false;
+ int i, amount, important;
+ struct item* inv;
+
+ // prefer always non-equipped items
+ it->equip = 0;
+
+ // when searching for nameid only, prefer additionally
+ if( !exact_match )
+ {
+ // non-refined items
+ it->refine = 0;
+ // card-less items
+ memset(it->card, 0, sizeof(it->card));
+ }
+
+ for(;;)
+ {
+ amount = it->amount;
+ important = 0;
+
+ // 1st pass -- less important items / exact match
+ for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( inv->equip != it->equip || inv->refine != it->refine )
+ {// not matching attributes
+ important++;
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching exact attributes
+ continue;
+ }
+ }
+ else
+ {
+ if( sd->inventory_data[i]->type == IT_PETEGG )
+ {
+ if( inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+ }
+ else if( memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// named/carded item
+ important++;
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ // 2nd pass -- any matching item
+ if( amount == 0 || important == 0 )
+ {// either everything was already consumed or no items were skipped
+ ;
+ }
+ else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching attributes
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ if( amount )
+ {// not enough items
+ return false;
+ }
+ else if( delete_items )
+ {// we are done with the work
+ return true;
+ }
+ else
+ {// get rid of the items now
+ delete_items = true;
+ }
+ }
+}
+
+
/// Deletes items from the target/attached player.
/// Prioritizes ordinary items.
///
@@ -5834,8 +5984,8 @@ BUILDIN_FUNC(makeitem)
/// delitem "<item name>",<amount>{,<account id>}
BUILDIN_FUNC(delitem)
{
- int nameid=0,amount,i,important_item=0;
TBL_PC *sd;
+ struct item it;
struct script_data *data;
if( script_hasdata(st,4) )
@@ -5868,89 +6018,30 @@ BUILDIN_FUNC(delitem)
st->state = END;
return 1;
}
- nameid = id->nameid;// "<item name>"
+ it.nameid = id->nameid;// "<item name>"
}
else
- nameid = conv_num(st,data);// <item id>
-
- amount=script_getnum(st,3);
-
- if( amount <= 0 )
- return 0;// nothing to do
-
- //1st pass
- //here we won't delete items with CARDS, named items but we count them
- for(i=0;i<MAX_INVENTORY;i++){
- //we don't delete wrong item or equipped item
- if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
- sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid)
- continue;
- //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
- if(sd->inventory_data[i]->type==IT_PETEGG &&
- sd->status.inventory[i].card[0] == CARD0_PET)
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
{
- if (!intif_delete_petdata(MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])))
- continue; //pet couldn't be sent for deletion.
- } else
- //is this item important? does it have cards? or Player's name? or Refined/Upgraded
- if(itemdb_isspecial(sd->status.inventory[i].card[0]) ||
- sd->status.inventory[i].card[0] ||
- sd->status.inventory[i].refine) {
- //this is important item, count it (except for pet eggs)
- if(sd->status.inventory[i].card[0] != CARD0_PET)
- important_item++;
- continue;
- }
-
- if(sd->status.inventory[i].amount>=amount){
-
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40)
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
-
- pc_delitem(sd,i,amount,0,0);
- return 0; //we deleted exact amount of items. now exit
- } else {
- amount-=sd->status.inventory[i].amount;
-
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40) {
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
- }
- //Logs
-
- pc_delitem(sd,i,sd->status.inventory[i].amount,0,0);
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
}
}
- //2nd pass
- //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally
- if (important_item>0 && amount>0)
- for(i=0;i<MAX_INVENTORY;i++){
- //we don't delete wrong item
- if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
- sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid )
- continue;
- if(sd->status.inventory[i].amount>=amount){
+ it.amount=script_getnum(st,3);
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40)
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
-
- pc_delitem(sd,i,amount,0,0);
- return 0; //we deleted exact amount of items. now exit
- } else {
- amount-=sd->status.inventory[i].amount;
-
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40)
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ if( it.amount <= 0 )
+ return 0;// nothing to do
- pc_delitem(sd,i,sd->status.inventory[i].amount,0,0);
- }
- }
+ if( buildin_delitem_search(sd, &it, false) )
+ {// success
+ return 0;
+ }
- ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", amount, sd->status.account_id, nameid);
+ ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
st->state = END;
clif_scriptclose(sd, st->oid);
return 1;
@@ -5962,9 +6053,8 @@ BUILDIN_FUNC(delitem)
/// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
BUILDIN_FUNC(delitem2)
{
- int nameid=0,amount,i=0;
- int iden,ref,attr,c1,c2,c3,c4;
TBL_PC *sd;
+ struct item it;
struct script_data *data;
if( script_hasdata(st,11) )
@@ -5997,59 +6087,37 @@ BUILDIN_FUNC(delitem2)
st->state = END;
return 1;
}
- nameid = id->nameid;// "<item name>"
+ it.nameid = id->nameid;// "<item name>"
}
else
- nameid = conv_num(st,data);// <item id>
-
- amount=script_getnum(st,3);
- iden=script_getnum(st,4);
- ref=script_getnum(st,5);
- attr=script_getnum(st,6);
- c1=(short)script_getnum(st,7);
- c2=(short)script_getnum(st,8);
- c3=(short)script_getnum(st,9);
- c4=(short)script_getnum(st,10);
-
- if( amount <= 0 )
- return 0;// nothing to do
-
- for(i=0;i<MAX_INVENTORY;i++){
- //we don't delete wrong item or equipped item
- if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
- sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ||
- sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref ||
- sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 ||
- sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 ||
- sd->status.inventory[i].card[3]!=c4)
- continue;
- //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle
- if(sd->inventory_data[i]->type==IT_PETEGG && sd->status.inventory[i].card[0] == CARD0_PET)
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
{
- if (!intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2])))
- continue; //Failed to send delete the pet.
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
}
+ }
- if(sd->status.inventory[i].amount>=amount){
-
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40)
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]);
-
- pc_delitem(sd,i,amount,0,0);
- return 0; //we deleted exact amount of items. now exit
- } else {
- amount-=sd->status.inventory[i].amount;
+ it.amount=script_getnum(st,3);
+ it.identify=script_getnum(st,4);
+ it.refine=script_getnum(st,5);
+ it.attribute=script_getnum(st,6);
+ it.card[0]=(short)script_getnum(st,7);
+ it.card[1]=(short)script_getnum(st,8);
+ it.card[2]=(short)script_getnum(st,9);
+ it.card[3]=(short)script_getnum(st,10);
- //Logs items, got from (N)PC scripts [Lupus]
- if(log_config.enable_logs&0x40)
- log_pick_pc(sd, "N", sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]);
+ if( it.amount <= 0 )
+ return 0;// nothing to do
- pc_delitem(sd,i,sd->status.inventory[i].amount,0,0);
- }
+ if( buildin_delitem_search(sd, &it, true) )
+ {// success
+ return 0;
}
- ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", amount, sd->status.account_id, nameid);
+ ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
st->state = END;
clif_scriptclose(sd, st->oid);
return 1;