Can someone look this function over for me...

Login to reply  Page: « < 1 of 1 > »
24 Jul 2010 - 02:162917
Can someone look this function over for me...
Can someone look this function over for me and let me know if they see any errors/issues?
Just looking for a second set of eyes here.

#define MAX_TRIES 10
/*
 * This function destroys equipment worn by the character, or possibly in the character's equipment.
 * @params: character, victim, how many pieces to destroy, destroy inventory pieces?, send victim a message?,
 * message text (which will be appended after the object's name)
 * Ulath
 */
void destroy_equip(struct char_data *ch, struct char_data *vict, ush_int pieces, bool inv, bool msg, char *text)
{
	ush_int num = 0, tries = 0;
	bool found;
	struct obj_data *obj = NULL;
	
	while (num < pieces && tries < (MAX_TRIES + pieces)) {
		found = FALSE;
		obj = GET_EQ(vict, rand_number(0, NUM_WEARS));
		if (obj && OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN) && msg) {
			send_to_char(vict, "Your %s protects itself from destruction!\r\n", GET_OBJ_NAME(obj));
			send_to_char(ch, "%s's %s protects itself from destruction!\r\n", GET_NAME(vict), GET_OBJ_NAME(obj));
		} else if (obj) {
			if (msg) {
				send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
				send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
			}
			found = TRUE;
			num++;
			extract_obj(obj);
		}
		else if (!found && inv) {
			for (obj = vict->carrying; obj; obj = obj->next_content) {
				if (obj && rand_number(1, 20) < 5 && !OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN)) {
					if (msg) {
						send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
						send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
					}
					num++;
					extract_obj(obj);
					break;
				}
			}
		}
		tries++;
	}
}


__________________
Owner/Coder/Head Admin
Caer Dubrin
24 Jul 2010 - 22:552920
I made a few alterations...

1) Added get_random_eq and get_random_inventory functions. This means that if the player has ANY eq (or inventory), it will return one random item. If the player has no worn eq/inventory, it returns NULL. This is less hit-and-miss than just randomly choosing an eq slot or cycling through a player's inventory with a 1-in-4 chance of picking each item.
2) Removed the msg boolean variable. Can just send NULL for the text, or an empty string, if you don't want to show a message.
3) Changed the while loop to a for loop. This is really personal preference - I just prefer for loops, which seem to get 'stuck' in an infinite loop less often. I know it's a cosmetic change.
4) Changed rand_number(1,20) < 5 to !rand_number(0,3) - still a 1-in-4 chance, just tidier.
5) Removed the for loop for the inventory, and removed the break; that was in there. I don't use break in a loop, simply because I was taught it was wrong (like using a goto, because the program can jump about unexpectedly). Not all coders agree that it's wrong, and I now believe that using break in a loop isn't 'wrong' if it's clear why it's used (usually with a comment).
6) Removed 'tries' because now, each time through the main loop, it WILL find an eq or inventory item, there are no 'misses'. The 1-in-4 chance of removing an inventory item remains, but if that misses, it still counts (tries wasn't updated here before anyway). Instead, loop ends if found == FALSE

Other considerations:
I did think about converting the send_to_char's to acts. This would involve changing this:
send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
to this
char buf[MAX_STRING_LENGTH];
sprintf(buf, "$n's $o %s\r\n", text);
act (buf, TRUE, ch, obj, vict, TO_NOTVICT);

which may seem like a lot more work, but it allows everyone in the room to see the destruction, not just the destroyer.

Anyways, here's all the changed code, note that all of this code below is untested:
struct obj_data *get_random_eq(struct char_data *ch)
{
  struct obj_data *eq_list[NUM_WEARS], *obj;
  int num_found=0, i;

  if (!ch) return NULL;

  /* Cycle through slots */
  for (i=0; i<NUM_WEARS; i++) {
    if (GET_EQ(ch, i)) {
      /* Found one, add it to the list */
      eq_list[num_found] = GET_EQ(ch, i);
      num_found++;
    }
  }
  if (num_found == 0) 
    return NULL;

  /* Pick one of the eq items at random */
  obj = eq_list[(rand_number(0, num_found))];

  return obj;
}

struct obj_data *get_random_inventory(struct char_data *ch)
{
  struct obj_data *eq_list[NUM_WEARS], *obj;
  int num_found=0, o_num, i;

  if (if !ch || ch->carrying == NULL) 
    return NULL;

  /* Count inventory items */
  for (obj = ch->carrying; obj; obj = obj->next_content) 
    num_found++;

  /* Pick one at random */
  o_num = rand_number(0, num_found);

  /* Jump to the correct item in the inventory */
  for (obj = ch->carrying, i=0; obj && i<o_num; obj = obj->next_content) i++;

  return obj;
}
/*
 * This function destroys equipment worn by the character, or possibly in the character's equipment.
 * @params: character, victim, how many pieces to destroy, destroy inventory pieces?, send victim a message?,
 * message text (which will be appended after the object's name)
 * Ulath
 */
void destroy_equip(struct char_data *ch, struct char_data *vict, ush_int pieces, bool inv, char *text)
{
  ush_int num = 0, tries = 0;
  bool found;
  struct obj_data *obj = NULL;
	
  for (num=0; num < pieces && found; num++) {
    found = FALSE;
    obj = get_random_eq(vict);
    if (obj && OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN) && text && *text) {
      send_to_char(vict, "Your %s protects itself from destruction!\r\n", GET_OBJ_NAME(obj));
      send_to_char(ch, "%s's %s protects itself from destruction!\r\n", GET_NAME(vict), GET_OBJ_NAME(obj));
    } else if (obj) {
      if (text && *text) {
        send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
        send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
      }
      found = TRUE;
      extract_obj(obj);
    }
    else if (!found && inv) {
      obj = get_random_inventory(vict);
      if (obj && !rand_number(0, 3) && !OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN)) {
        if (msg) {
          send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
          send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
        }
        extract_obj(obj);
        found = TRUE;
      }
    }
  }
}


__________________
24 Jul 2010 - 23:482921
I like most of the changes, however the missing tries was unintentional. It was supposed to count as a try. Still, the changes you made/suggested are good ones.

EDIT:

Oh, but I still prefer the rand 1-20 less than 5... in testing it fires more often than the 0 in 0-3. Logically it shouldn't, but we all know computer randomization isn't true random.


__________________
Owner/Coder/Head Admin
Caer Dubrin

Last edited by ralgith (24 Jul 2010 - 23:59)
24 Jul 2010 - 23:572922
I also thought that the get_random_eq and get_random_inventory functions could be used to create script funcs:
set obj %random.eq(%actor%)%
set obj %random.inv(%actor%)%


__________________
25 Jul 2010 - 00:002923
Quote Jamdog:
I also thought that the get_random_eq and get_random_inventory functions could be used to create script funcs:
set obj %random.eq(%actor%)%
set obj %random.inv(%actor%)%


Nice idea.


__________________
Owner/Coder/Head Admin
Caer Dubrin
25 Jul 2010 - 19:072924
Just realized your version is flawed Jam. If the person has equipment that gets ignored such as relics and quest items... then found continues to stay false. If they also have no inventory, it stays false and exists. Now what if they have non-relic equipment worn as well and the random just happened to grab a relic? This is why the "tries" variable needs to be brought back. Unless you can think of another solution to that conundrum. In point of fact, that same issue holds true for inventory items.


__________________
Owner/Coder/Head Admin
Caer Dubrin

Last edited by ralgith (25 Jul 2010 - 19:07)
25 Jul 2010 - 19:322925
Quote ralgith:
Just realized your version is flawed Jam. If the person has equipment that gets ignored such as relics and quest items... then found continues to stay false. If they also have no inventory, it stays false and exists. Now what if they have non-relic equipment worn as well and the random just happened to grab a relic? This is why the "tries" variable needs to be brought back. Unless you can think of another solution to that conundrum. In point of fact, that same issue holds true for inventory items.

Good catch, as I said, my code was untested...
You could replace the line:
obj = get_random_eq(vict);
with
if ((obj = get_random_eq(vict)) != NULL) found=TRUE;
(and the same for the inventory line)
This means that if an object isn't found at all, found remains false, but if an object is found in eq or inventory, found is set to true, regardless of whether the item is destroyed. Using the above methos means you could remove the found=TRUE; lines elsewhere.


__________________
25 Jul 2010 - 19:432926
Quote Jamdog:

Good catch, as I said, my code was untested...
You could replace the line:
obj = get_random_eq(vict);
with
if ((obj = get_random_eq(vict)) != NULL) found=TRUE;
(and the same for the inventory line)
This means that if an object isn't found at all, found remains false, but if an object is found in eq or inventory, found is set to true, regardless of whether the item is destroyed. Using the above methos means you could remove the found=TRUE; lines elsewhere.


Still not right though, since it will increment num even if the obj isn't destroyed. Its only supposed to increment num if something is actually destroyed. This is why I was using a while loop instead of for, though it could be done with for also. Either way requires bringing back the "tries" variable, I just don't see any other way. I'm thinking its easier just to remove the "found" check from the for loop and change the for to this:
for (num=0; num < pieces && tries < (MAX_TRIES + pieces); tries++)
And incrementing num whenever something is destroyed, as I did originally. What do you think?


__________________
Owner/Coder/Head Admin
Caer Dubrin

Last edited by ralgith (25 Jul 2010 - 19:43)
27 Jul 2010 - 01:352929
Current Working Version
Ok, this is what I have now. I have this in utils.c/.h (.h for the prototype).
I have changed the function to return the number of equipment destroyed.
Also, I went back to using tries for the loop, so that num is only incremented when something is destroyed.
Cleaned up Jamdog's random object functions... thanks for the original code JD.
Lastly I added protected message for objects in your inventory is well.

/*
 * Choose a random worn object and return it
 * Original concept Jamdog. Cleanup & Testing Ulath.
 */
struct obj_data *get_random_eq(struct char_data *ch)
{
	struct obj_data *eq_list[NUM_WEARS];
	int num_found=0, i;

	if (!ch) return NULL;

	/* Cycle through slots */
	for (i=0; i<NUM_WEARS; i++) {
		if (GET_EQ(ch, i)) {
			/* Found one, add it to the list */
			eq_list[num_found] = GET_EQ(ch, i);
			num_found++;
		}
	}
	if (num_found == 0) 
		return NULL;

	/* 
	 * Pick one of the eq items at random and return it.
	 * I removed the unnecessary obj variable and its assignment here
	 * and instead just return straight from the eq_list.
	 * - Ulath 26 July 2010
	 */
	return eq_list[(rand_number(0, num_found))];
}


/*
 * Choose a random inventory object and return it
 * Original concept Jamdog. Cleanup & Testing Ulath.
 */
struct obj_data *get_random_inventory(struct char_data *ch)
{
	/* I removed the eq_list variable, we don't use it in this one. - Ulath 26 July 2010 */
	struct obj_data *obj;
	int num_found=0, o_num, i;

	if (!ch || ch->carrying == NULL) 
		return NULL;

	/* Count inventory items */
	for (obj = ch->carrying; obj; obj = obj->next_content) 
		num_found++;

/*
 * Possibly eliminate allthe code below, and instead change the above loop to add
 * if (!rand_number(0, 4) || !obj->next_content)
 *   return obj;
 *
 * ? Hmm, I'm not sure I want to do that. Seems it might default to the last object in
 * the list too often for smaller lists. We'll keep it like it is for now.
 * - Ulath 26 July 2010
 */

	/* Pick one at random */
	o_num = rand_number(0, num_found);

	/* Jump to the correct item in the inventory */
	for (obj = ch->carrying, i=0; obj && i < o_num; obj = obj->next_content)
		i++;

	return obj;
}


#define MAX_TRIES 5
/*
 * This function destroys equipment worn by the character, or possibly in the character's equipment.
 * @params: character, victim, how many pieces to destroy, destroy inventory pieces?,
 * message text (which will be appended after the object's name)
 * Ulath
 */
int destroy_equip(struct char_data *ch, struct char_data *vict, ush_int pieces, bool inv, char *text)
{
	ush_int num, tries;
	bool found;
	struct obj_data *obj = NULL;
	
	for (tries = 0, num = 0; num < pieces && tries < (MAX_TRIES + pieces); tries++) {
		found = FALSE;
		obj = get_random_eq(vict);
		if (obj && OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN) && text && *text) {
			send_to_char(vict, "Your %s protects itself from destruction!\r\n", GET_OBJ_NAME(obj));
			send_to_char(ch, "%s's %s protects itself from destruction!\r\n", GET_NAME(vict), GET_OBJ_NAME(obj));
		} else if (obj) {
			if (text && *text) {
				send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
				send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
			}
			found = TRUE;
			num++;
			extract_obj(obj);
		}
		else if (!found && inv) {
			obj = get_random_inventory(vict);
			if (obj && OBJ_FLAGGED(obj, ITEM_RELIC | ITEM_QUEST | ITEM_RARE | ITEM_NODISIN) && text && *text) {
				send_to_char(vict, "Your %s protects itself from destruction!\r\n", GET_OBJ_NAME(obj));
				send_to_char(ch, "%s's %s protects itself from destruction!\r\n", GET_NAME(vict), GET_OBJ_NAME(obj));
			} else if (obj && !rand_number(0, 3)) {
				if (text && *text) {
					send_to_char(vict, "Your %s %s\r\n", GET_OBJ_NAME(obj), text);
					send_to_char(ch, "%s's %s %s\r\n", GET_NAME(vict), GET_OBJ_NAME(obj), text);
				}
				extract_obj(obj);
				num++;
			}
		}
	}

	return (num);
}


__________________
Owner/Coder/Head Admin
Caer Dubrin
27 Jul 2010 - 07:512930
For the items, what about just using IS_CARRYING_N? Or do we have more people (like me) who are a little uncomfortable with these accumulated 'optimisation' variables now that CPU horsepower is a bit less of a factor?

You could probably factor out the "count items in equipment" as well. At the very least, it's used in do_stat_obj. I'd add an optional int* to return the total weight as well, which might well be useful to count at the same time.


27 Jul 2010 - 14:192932
Quote Dio:
For the items, what about just using IS_CARRYING_N? Or do we have more people (like me) who are a little uncomfortable with these accumulated 'optimisation' variables now that CPU horsepower is a bit less of a factor?


Good call, instead of counting them ourselves.

Quote Dio:

You could probably factor out the "count items in equipment" as well. At the very least, it's used in do_stat_obj. I'd add an optional int* to return the total weight as well, which might well be useful to count at the same time.


However we still need the items in equipment, because it saves our equipment objects to an array as well.

---

Ok, so the change Dio suggested would make the rand inventory item this:
/*
 * Choose a random inventory object and return it
 * Original concept Jamdog. Cleanup & Testing Ulath.
 */
struct obj_data *get_random_inventory(struct char_data *ch)
{
	/* I removed the eq_list variable, we don't use it in this one. - Ulath 26 July 2010 */
	struct obj_data *obj;
	int num_found=0, o_num, i;

	if (!ch || ch->carrying == NULL) 
		return NULL;

	/* Pick one at random */
	/* Dio Suggested change to IS_CARRYING_N - Ulath 27 July 2010 */
	o_num = rand_number(0, IS_CARRYING_N(ch));

	/* Jump to the correct item in the inventory */
	for (obj = ch->carrying, i=0; obj && i < o_num; obj = obj->next_content)
		i++;

	return obj;
}


__________________
Owner/Coder/Head Admin
Caer Dubrin
Login to reply  Page: « < 1 of 1 > »