/* -*- LPC -*- */ /* * $Locker: presto $ * $Id: remove.c,v 1.17 2002/05/17 00:45:19 trilogy Exp presto $ * $Log: remove.c,v $ * Revision 1.17 2002/05/17 00:45:19 trilogy * Forcibly unlocked by presto * * Revision 1.15 2002/05/16 01:54:40 trilogy * Forcibly unlocked by presto * * Revision 1.13 2002/02/12 04:42:29 presto * Fixed so that an item never blocks itself (which is possible for items * of mixed type) * * Revision 1.12 2002/01/15 01:40:24 presto * So help me God, I hope it works now. * * Revision 1.11 2002/01/07 05:27:16 presto * Fixed mistake with not removing things that were *indirectly* blocked by * other things * * Revision 1.10 2002/01/06 05:37:27 presto * Rewrote. Seems to work better now, and I think it's more understandable. :) * Will keep an eye on it * * Revision 1.9 2001/12/07 15:12:11 taffyd * Fixed typo and grammar oddity. * * Revision 1.8 2001/11/08 02:22:07 pinkfish * Fox up some little errors. * * Revision 1.7 2001/11/07 23:09:56 pinkfish * Fix up the auto removal a little more. * * Revision 1.6 2001/11/07 21:46:34 pinkfish * Make it so it automatically takes and puts stuff on and off. * * Revision 1.5 1999/12/08 04:09:04 ceres * Modified to use standard can_wear_or_remove() function in /obj/handlers/clot * hing_handler * * Revision 1.4 1998/09/15 00:28:32 pinkfish * Fix up the output errors for remove. * * Revision 1.3 1998/08/25 10:18:41 pinkfish * Change remove so it will order the thnigs to remove... SO it takes * the toip off first. * * Revision 1.2 1998/03/27 20:49:01 pinkfish * To the new clothing handler. * * Revision 1.1 1998/01/06 05:28:43 ceres * Initial revision * */ #include <clothing.h> #include <tasks.h> #define DEBUG #define SKILL "covert.manipulation.sleight-of-hand" #ifdef DEBUG #define TELL_ME "presto" #endif inherit "cmds/base"; int cmd(object *things) { object *removed = ({ }); object *blocking = ({ }); object *blocked; object item; object *total_blocking = ({ }); object *succeeded; object *failed; object *failed_rewear; object ob; object blocker; mapping is_blocking = ([ ]); string tmp1; string tmp2; int max_index; int min_index1; int min_index2; int i; mapping hide_invis; int hiding, sneaking, difficulty, light, my_light; succeeded = this_player()->query_wearing(); failed = filter(things, (: member_array($1, $(succeeded)) == -1 :)); things -= failed; if (sizeof(things) == 0) { write("You are not wearing " + query_multiple_short(failed, "the") + ".\n"); return 1; } succeeded = copy(things); for (i = 0; i < sizeof(succeeded); i++) { ob = succeeded[i]; blocking = CLOTHING_HANDLER->query_items_blocking(ob, this_player()) - ({ ob }); if (sizeof(blocking)) { foreach (blocker in blocking) { if (undefinedp(is_blocking[blocker])) is_blocking[blocker] = ({ ob }); else is_blocking[blocker] |= ({ ob }); } total_blocking |= blocking; /* * We need to add the blocking things to the things we are checking, * because the blocking things may themselves be blocked by something * that was not blocking the thing that the original blocker was * blocking. Oh... you want an EXAMPLE. OK: * * Example: a silk shirt is blocked by a mail shirt, but NOT by a * scabbard. However, the mail shirt IS blocked by the scabbard, so * it has to be removed before we can remove the mail shirt; so that * we can remove the silk shirt. */ succeeded = ({ succeeded..., total_blocking... }); } } /* * OK, here is the important, complicated bit. At this point, the * is_blocking mapping is keeping track of everything that is blocking * something else, and what it is blocking. We need to build an array of * blocking objects in order from outermost layer to innermost. That way * there should be no failures when we remove the covering stuff to get * to the things that the player wants to remove. Here is how this works: * * 1. Loop over all items that are blocking something else * 2. For each item, find the last thing in the array (so far) that would * block the item. The item we are adding should go immediately after * that position. * 3. Find the first thing and last things in the array (so far) that * would be blocked *by* the new item. For the last thing, we only have * to check up to the index found in step 2. * 4. Insert the new item at the index found in step 2, and move all the * items between the indices found in step 3 to immediately after the * new item. */ total_blocking = ({ }); foreach (ob, blocked in is_blocking) { #ifdef DEBUG if (this_player() == find_player(TELL_ME)) tell_creator(TELL_ME, "ob == %s, blocked == %O\n", ob->short(), blocked->short()); #endif /* Find the last thing that is blocking 'ob' */ max_index = -1; for (i = sizeof(total_blocking) - 1; i >= 0; i--) { if (member_array(ob, is_blocking[total_blocking[i]]) > -1) { max_index = i; break; } } if (max_index == -1) { /* * Nothing is blocking 'ob', so we can safely put it at the front of * the list, because then, even if it is blocking something, it will * get removed first. */ total_blocking = ({ ob, total_blocking... }); #ifdef DEBUG if (this_player() == find_player(TELL_ME)) tell_creator(TELL_ME, "Nothing blocking ob, adding it to beginning\n%O\n", total_blocking->short()); #endif continue; } /* Find the first place where 'ob' would have blocked something */ min_index1 = sizeof(total_blocking); foreach (item in blocked) { i = member_array(item, total_blocking); if (i > -1 && i < min_index1) { min_index1 = i; } } if (min_index1 == sizeof(total_blocking)) { /* * 'ob' isn't blocking anything, so we can safely put it at the END * of the list, because then, even if it is blocked by something, the * other thing(s) will get removed first. */ total_blocking += ({ ob }); #ifdef DEBUG if (this_player() == find_player(TELL_ME)) tell_creator(TELL_ME, "ob not blocking anything, adding it to end\n%O\n", total_blocking->short()); #endif continue; } /* Find the last place where 'ob' would have blocked something */ min_index2 = min_index1; foreach (item in blocked) { i = member_array(item, total_blocking); if (i > min_index2 && i < max_index) min_index2 = i; } /* * Now we want to reorder the list so that 'ob' and anything that it * blocks goes at the end. */ if (min_index1 > max_index) total_blocking = ({ total_blocking[0 .. max_index]..., ob, total_blocking[max_index + 1 .. ]... }); else total_blocking = ({ total_blocking[0 .. min_index1 - 1]..., total_blocking[min_index2 + 1 .. max_index]..., ob, total_blocking[min_index1 .. min_index2]..., total_blocking[max_index + 1 .. ] ... }); #ifdef DEBUG if (this_player() == find_player(TELL_ME)) tell_creator(TELL_ME, "min_index1 == %d, min_index2 == %d, max_index == %d\n%O\n", min_index1, min_index2, max_index, total_blocking->short()); #endif } /* Remove stuff that's blocking the stuff we asked to remove */ foreach (blocker in total_blocking) { tmp1 = CLOTHING_HANDLER->can_wear_or_remove(blocker, this_player()); if (tmp1) { /* This *shouldn't* happen */ log_file("REMOVE_FAILURE", "things == %O\n", things); log_file("REMOVE_FAILURE", "reason == %s\n", tmp1); log_file("REMOVE_FAILURE", "blocker == %s (%s, %O)\n", blocker->short(), file_name(blocker), blocker); log_file("REMOVE_FAILURE", "total_blocking == \n"); for (i = 0; i < sizeof(total_blocking); i++) { log_file("REMOVE_FAILURE", " %O (%s)\n", total_blocking[i], total_blocking[i]->short()); } log_file("REMOVE_FAILURE", "is_blocking == \n"); foreach (ob, blocked in is_blocking) { log_file("REMOVE_FAILURE", " %O (%s): \n", ob, ob->short()); for (i = 0; i < sizeof(blocked); i++) { log_file("REMOVE_FAILURE", " %O (%s)\n", blocked[i], blocked[i]->short()); } } log_file("REMOVE_FAILURE", "removed == %O\n", removed); write("You cannot remove " + query_multiple_short(is_blocking[blocker], "the") + " " + tmp1 + ".\n"); things -= is_blocking[blocker]; break; } else if (this_player()->remove_armour(blocker)) { write("You cannot remove " + query_multiple_short(is_blocking[blocker], "the") + " because you cannot remove " + blocker->one_short() + ".\n"); things -= is_blocking[blocker]; break; } else removed += ({ blocker }); } /* Now remove the stuff we really wanted to */ /* * Start with things we already removed because they were blocking * something else */ succeeded = things & removed; failed = ({ }); /* Now try to remove the rest */ foreach (ob in things - removed) { if (this_player()->remove_armour(ob)) failed += ({ ob }); else succeeded += ({ ob }); } if (sizeof(succeeded) > 0) { removed -= things; tmp2 = query_multiple_short(succeeded, "the") ; if (sizeof(removed) > 0) { tmp1 = query_multiple_short(removed, "the"); write("You remove " + tmp1 + " so you can remove " + tmp2 + ".\n"); say(this_player()->the_short() + " removes " + tmp1 + " so " + this_player()->query_pronoun() + " can remove " + tmp2 + ".\n"); } else { hide_invis = ( mapping )this_player()->query_hide_invis(); hiding = hide_invis[ "hiding" ] ? 1 : 0; sneaking = this_player()->query_sneak_level() ? 1 : 0; if( hiding || sneaking ) { my_light = this_player()->query_light(); light = environment( this_player() )->query_light(); difficulty = light + ( 4 * my_light ) / ( light + 1 ); difficulty += succeeded[0]->query_complete_weight(); debug_printf( "Difficulty = %d.\n Skill = %s\n Bonus = %d\n", difficulty, SKILL, this_player()-> query_skill_bonus( SKILL ) ); switch( TASKER->perform_task( this_player(), SKILL, difficulty, TM_FREE ) ) { case AWARD : write( "%^YELLOW%^" + ({ "You discover something that lets your fingers move more " "nimbly.", "You find yourself capable of deceiving the eye with greater " "ease than before.", "You realise how to deceive the eye more effectively." })[ random(3) ] + "%^RESET%^\n" ); case SUCCEED : add_succeeded_mess( ({ "$N $V " + tmp2 + ", managing to stay " "unnoticed.\n", "" }) ); break; default : this_player()->add_succeeded_mess( this_object(), "$N " "unsuccessfully tr$y to " + query_verb() + " " + tmp2 + " while staying unnoticed.\n", ({ }) ); break; } } else { this_player()->add_succeeded_mess( this_object(), "$N $V " + tmp2 + ".\n", ({ }) ); } } } /* Put stuff back on that we took off */ succeeded = ({ }); failed_rewear = ({ }); foreach (ob in removed) { if (this_player()->wear_armour(ob)) failed_rewear += ({ ob }); else succeeded += ({ ob }); } if (sizeof(succeeded) > 0) { tmp1 = query_multiple_short(succeeded, "the"); write("You wear " + tmp1 + ".\n"); say(this_player()->the_short() + " wears " + tmp1 + ".\n"); } if (sizeof(failed_rewear) > 0) { write("You cannot put " + query_multiple_short(failed_rewear, "the") + " back on.\n"); } if (sizeof(failed) > 0) { write("You cannot remove " + query_multiple_short(failed, "the") + ".\n"); } return 1; } mixed *query_patterns() { return ({ "<indirect:object:me>", (: cmd($1) :) }); } /* query_patterns() */