Checking if objects are in inventory

Posted by Robert Powell on Tue 25 Jan 2005 08:06 AM — 12 posts, 40,436 views.

Australia #0
Hi all, what im needing to do is check through a players inventory to see of they have X amount of an object in there,
The way it was done in the smippet i have is as follows,

for (x = 0; x <= mnum; x++)
        {
	 material = get_obj_carry (ch, "corpse" );
         if (material == NULL)
            {
              send_to_char( "You didn't realize it when you started, but you haven't enough material\n\r", ch);
              return;
            }
          extract_obj( material );
        }


The trouble with doing it this way is that once the first object is extracted it fails to find find the next corpse in inventory and always fails, could someone show me a better way to achieve the same outcome, or is there an existing function that i can use that will check how many of an object there are in a players inventory.

Thanks in advance.
#1
I dont understand why you have:

if (material == NULL)

that if you're trying to count for a specific number of materials. Shouldn't that be something like:

if (material < 3)

and so on?

IT's 4 am here, so maybe I just missed it?
#2
Alternatively you can check the SPELLCOMPONETS code, I believe that checks the player inventory for material, ie spell components.
Australia #3
I think that the material == null is checking what get_obj_carry would return if it didnt find that obect


code from get_obj_carry

for ( obj = ch->last_carrying; obj; obj = obj->prev_content )
	if ( obj->wear_loc == WEAR_NONE
	&&   can_see_obj( ch, obj )
	&&  (nifty_is_name( arg, obj->name ) || obj->pIndexData->vnum == vnum) )
	    if ( (count += obj->count) >= number )
		return obj;

    if ( vnum != -1 )
	return NULL;
USA #4
Right, a better way of doing it would be to iterate over the objects in the inventory, and count how many are correct. A for loop would do this quite nicely. But, you have to keep track of them as you go along, so that you can eventually remove them. C++ would make this task oh-so-easier with STL vectors or such, but in C you'd use a linked list or dynamic array or something. (Probably a dynamic array, which is simpler.)
Canada #5
If your looking for something like this, take a look at something like do_makecontainer in swskills.c in the SWR codebase. It can be downloaded from this website, and it has a system similar to what your trying to accomplish.
Australia #6
Thanks, not being much of a coder i find it much easier to work out how to do things from looking at code that does something simular to what i am trying to acheive, i dont know much about linked lists or dynamic arrays so i will have to look them up on the web and read about those, but in the mean time im downloading swr to take a look at the makcontainer code.
Australia #7
OK, have had a look at the makecontainer code and am assuming that this is the piece of code that i was looking for,

for ( obj = ch->last_carrying; obj; obj = obj->prev_content )     
    {
       if (obj->item_type == ITEM_THREAD)
          checksew = TRUE;
       if (obj->item_type == ITEM_FABRIC && checkfab == FALSE)
       {
          checkfab = TRUE;
          separate_obj( obj );
          obj_from_char( obj );
          material = obj;
       }
    }                           


and that, that will start at the last object you have and work its way to the beginning checking if you have some fabric and thread and when it finds fabric and you previously didnt have any, says you now have fabric, removes it from containers takes it from you, and will do that till it reaches the first object in your inv??


so i guess i could do something like this,

for ( obj = ch->last_carrying; obj; obj = obj->prev_content )
  {
    if (obj->item_type == ITEM_CORPSE)
      {
        checkcorpses == checkcorpses++
        obj_from_char( obj );
      }
    if checkcorpses = number_of_corpses_needed)
      {
        return; //bailout only take what i need
      }
    if checkcorpses != number_of_corpses_needed)
      {
        //better work out a way to give back the objects
      }
    
  }


i havent tryed this yet, tho im about to, hope im on the right track. Please tell me what you think and thanks again.
Australia #8
Ok, i have got it to work now, well sort of, it took some tinkering around on my part to work out exactly waht the hell was going on there for a while, Alas that call to seperate_obj(obj) was the most important part of all this code, trouble is that if all the objects are the same, ie, the corpse of robert (100), it still fails to seperate them and then the code fails, i have also config -combine, but that makes no difference either, here is the code as it stands

for ( obj = ch->last_carrying; obj; obj = obj->prev_content )
        {
          separate_obj( obj );
          if (obj->item_type == ITEM_CORPSE_NPC)
            {
              checkcorpses++;
            }
          if (checkcorpses == mnum)
            {
              obj_from_char( obj ); //take the last one;
              break; //Bust out now, dont take more than is needed.
            }
          separate_obj( obj );
          obj_from_char( obj );
        }

      if (checkcorpses != mnum)
        {
          send_to_char("You dont have enough corpses.\n\r", ch);
          return;//better work out a way to give back the objects
        }

If anyone has any ideas how i can solve this stacking of objects i would love to hear them. Thanks
USA #9
You will have to allocate an array of the size of the number of materials. Every time you find a material, you put that into the array. Once the array is full, you are done. If you can't fill up the array, you inform the player that there aren't enough materials and you free the array memory. if there array is full, you remove all those objects from the character and free the array memory.

The SWR code doesn't seem to take into account multiple source materials, so I'm not sure it'll work.
Canada #10
The reason that your losing 100 instead of one is that your likely missing the separate_obj( obj ); call. This will ungroup the object if there is more than one. If you want to use more than 1, you could do something like this:
for ( obj = ch->last_carrying; obj; obj = obj->prev_content )     
    {
       if (obj->item_type == ITEM_THREAD)
          checksew = TRUE;
       if (obj->item_type == ITEM_FABRIC && checkfab == FALSE && count < 4)
       {
          checkfab = TRUE;
          separate_obj( obj );
          obj_from_char( obj );
          material = obj;
          count++;
       }
    }
With this, to prevent losing 3 if thats all you have, the upper portion of that skill has the same checks before the add_timer call, but does not remove anything, so you cuold do the counting there.

[EDIT] Hmm, actually, I don't know if seperate_obj puts the rest of the grouped objects before obj, after, at the end, etc, so it might skip them. I can't check as I am at work, but you could try a simple compile and see if it finds the rest in the inventory.
Amended on Tue 25 Jan 2005 06:40 PM by Greven
Australia #11
This dont work, i dont realy know why it fails but it does, I think its as greven says and that seperate_obj put the new object in the wrong place and it all gets lost. At some point it just plain stops finding more of the same type of object, i guess it has something to do with seperate_obj and what it does, i think at best it finds 3 then fails. Here is the code for split_obj as seperate_obj calls that asking it to seperate one object off the group.

if ( count <= num || num == 0 )
      return;

    rest = clone_object(obj);
    --obj->pIndexData->count;	/* since clone_object() ups this value */
    --numobjsloaded;
    rest->count = obj->count - num;
    obj->count = num;
if ( obj->carried_by )
    {
	LINK( rest, obj->carried_by->first_carrying,
		   obj->carried_by->last_carrying,
		   next_content, prev_content );
	rest->carried_by	 	= obj->carried_by;
	rest->in_room	 		= NULL;
	rest->in_obj	 		= NULL;
    }

I dont mind that the tailor corpse code doesnt use more that one corpse to make an object as its a cheap skill, tho i was going to cheat some in my mining, forge eq and weapons code and do it in a simular way to the do_tailor.

I have decided that the best thing i could do now is change do_mining so that it adds a weight value to an existing ore object in a players inv, might even make a new obect called ore_container and have all the ore's stored in there, then create a new ore if one doesnt exist or increment the weight of the ore if it does. Then i will only be dealing with one object and wont have the trouble of grouped objects.