/* Shapeshift function <c> 1997 Nebseni of Clandestine MUD (mudnet.net 9476).
 * Permission granted to use this code or code derived from it, with
 * appropriate attribution.  Email me if you find this useful or have
 * suggestions for improvement. (nebseni@mudnet.net)
 */

/* NOTE to Implementors:
   Shapeshifting is essentially a mortal implementation of the immortal
"SWITCH" command, and should not be used arbitrarily.  Many additions have
been made to this function over the past several months in order to curb
abuses.  The function essentially "clones" the specified mob, causes the
player to switch into the mob, and transfer's the player's "real" body to
the specified room (defined as "1" in this snippet.)  When the player returns
from shapeshifting, their body is transported to the room they are in, they
return from the switch, and the cloned NPC is destroyed.  Potential abuses of
the function which must be handled elsewhere in the code on an individual basis:
	- Players can shapeshift into a NPC and then give/drop/donate/etc. the
	mob's gold, equipment, etc. to another player or to themselves. 
	Recommend modifying the above	functions to disallow switched (non-imm)
	players from using them.
	- An extension of the above, if your MUD has a "create object" spell that
	costs hp, mana, or mv points, a shapeshifted player can use the NPC's hp,
	man, or mv points to execute that function.
	- Players can use the mob to unfairly gain experience, either by
	weakening another	mob, or allowing themselves to be killed by another
	player (after weakening themselves).  Limiting the level of NPC a player
	may shapeshift into reduces the benefit of this.
	- If you run a PK mud, shapeshifted players can PK anonymously.  Ensure
	your is_safe function uses the same restrictions (if any) you place on
	your players for the NPC.
   Finally, there are many instances in the "stock" ROM code where NPC checks
are not conducted before accessing the ch->pcdata structure.  You should
carefully go through your code and handle these cases with an appropriate
IS_NPC(ch) check.  (Primary examples are score/worth/etc. functions that access
data in the "pcdata" structure).
*/

#if defined(macintosh)
#include <types.h>
#include <time.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "lookup.h"

/*
 * Functions needed for shapeshifting, which uses cloning
 */
bool obj_check args((CHAR_DATA *ch, OBJ_DATA *obj));
void recursive_clone args((CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *clone));
ROOM_INDEX_DATA *       find_location   args( ( CHAR_DATA *ch, char *arg ) );

/* Finally, the shapeshift function, heavily commented to allow you to
   modify/change it to your liking */

void do_shapeshift( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    CHAR_DATA *clone;
    OBJ_DATA  *obj;
    OBJ_DATA *new_obj;
    char buf[MAX_STRING_LENGTH];
    ROOM_INDEX_DATA *location;
    CHAR_DATA *keeper;
    SHOP_DATA *pShop;

#ifdef DEBUG
        Debug ("do_shapeshift");
#endif
	/* This statement is in because of the "Freeze Tag" game.  If you do not
	have Freeze Tag implemented on your MUD, remove these lines. Or get
	Freeze Tag! <g>*/
    if ( IS_SET(ch->tag_flags,TAG_PLAYING))
    {
	REMOVE_BIT(ch->tag_flags,TAG_PLAYING);
    }

	/* The normal use of shapeshift is to specify the NPC to switch into,
		i.e. "shapeshift homer" to turn yourself into Homer Simpson.
		Used without an argument will return to your normal body. */
    one_argument( argument, arg );
    
    if ( arg[0] == '\0' ) 
    {
	if ( ch->desc == NULL )
	  return;

	if ( ch->desc->original == NULL )
	{
	  send_to_char( "{RWho do you want to shapeshift into?\n\r{x", ch );
	  return;
	}

	send_to_char( 
	  "You return to your original form. Type replay to see any missed tells.\n\r", ch );
	act ( "$n shifts into the shape of $N.", ch, NULL, ch->desc->original, TO_ROOM );
	if (ch->prompt != NULL)
	{
	  free_string(ch->prompt);
	  ch->prompt = NULL;
	}

	/* following lines are for wiznet */
	sprintf(buf,"$N returns from %s.",ch->short_descr);
	wiznet(buf,ch->desc->original,0,WIZ_SWITCHES,WIZ_SECURE,get_trust(ch));

	/* return player to room which NPC is presently in, then execute
	   equivalent of do_return code and extract the NPC. */
	victim = ch->desc->character;
	char_from_room( ch->desc->original );
	char_to_room( ch->desc->original, victim->in_room );

	ch->desc->character       = ch->desc->original;
	ch->desc->original        = NULL;
	ch->desc->character->desc = ch->desc; 
	ch->desc                  = NULL;

	extract_char( victim, TRUE );

	return;
    }

    if ( ch->desc == NULL )
	return;

	/* if already shapeshifted, can't shift again */
    if ( ch->desc->original != NULL )
    {
	send_to_char( 
	"{RYou are not familiar enough with your shape to change it again.{x\n\r", ch );
	return;
    }
    
	/* this is a boolean check used to see if the player has the attribute
	   required to shapeshift.  You will have to customize this for your
	   mud dependent on which players you want to allow to shapeshift */
    if (!(TRUE))
    {
	send_to_char( "{RYou have not yet mastered shapeshifting.\n\r{x", ch );
	return;
    }

	/* can only shapeshift into a NPC that exists */
    if ( ( victim = get_char_world( ch, arg ) ) == NULL )
    {
	send_to_char( "You fail to recognize what to shape yourself into.\n\r", ch );
	return;
    }

	/* players can shapeshift into themselves, no action taken */
    if ( victim == ch )
    {
	send_to_char( "Ok.\n\r", ch );
	return;
    }

	/* otherwise, only NPCs can be used */
    if (!IS_NPC(victim))
    {
	send_to_char("{RYou can only shift into the shape of creatures.{x\n\r",ch);
	return;
    }

	/* this should prevent players from shapeshifting into shopkeepers */
    pShop = NULL;
    for ( keeper = victim->in_room->people; keeper; keeper = keeper->next_in_room )
    {
	if ( IS_NPC(keeper) && (pShop = keeper->pIndexData->pShop) != NULL)
	    break;
    }

    if ( pShop != NULL )
    {
       send_to_char("{GYOU WISH!!!!!!!!{x\n\r", ch );
       return;
    }

	/* this should prevent players from shapeshifting into guildmasters, healers,
	money changers, etc. */
    if (IS_SET(victim->act,ACT_TRAIN)
	||  IS_SET(victim->act,ACT_PRACTICE)
	||  IS_SET(victim->act,ACT_IS_HEALER)
	||  IS_SET(victim->act,ACT_IS_CHANGER))
    {
	send_to_char("Don't even think about it!\n\r",ch);
	return;	  
    }

	/* level-based restriction */
    if ( victim->level > ch->level )
    {
      act ("{R$N is far too complex for you to shift shapes.{x",
	  ch, NULL, victim, TO_CHAR);
      return;
    }

/* OK we've passed all checks for use of the function, let's execute the shift */

/* Step 1, create a clone of the mob the player is shifting into, including eq.*/
	clone = create_mobile(victim->pIndexData);
	clone_mobile(victim,clone);
	
	for (obj = victim->carrying; obj != NULL; obj = obj->next_content)
	{
	    if (obj_check(ch,obj))
	    {
		new_obj = create_object(obj->pIndexData,0);
		clone_object(obj,new_obj);
		recursive_clone(ch,obj,new_obj);
		obj_to_char(new_obj,clone);
		new_obj->wear_loc = obj->wear_loc;
	    }
	}
	char_to_room(clone,ch->in_room);

/* Step 2, switch the player into NPC (clone) body */
	act ( "{RYou shift into the shape of {B$N{x.", ch, NULL, clone, TO_CHAR );
 	act ( "{R$n shifts into the shape of {B$N{x.", ch, NULL, clone, TO_ROOM );
	sprintf(buf,"$N shapeshifts into %s",victim->short_descr);
	wiznet(buf,ch,NULL,WIZ_SWITCHES,WIZ_SECURE,get_trust(ch));

	ch->desc->character = clone;
	ch->desc->original  = ch;
	clone->desc         = ch->desc;
	ch->desc            = NULL;
	/* change communications to match */
 	if (ch->prompt != NULL)
        clone->prompt = str_dup(ch->prompt);
	clone->comm = ch->comm;
	clone->lines = ch->lines;
/* Step 2a, since switched mob can move around, it might accidentally attack
   newbies.  Let's make it non-aggro */
	REMOVE_BIT(clone->act,ACT_AGGRESSIVE);
/* Step 3, transfer player to holding area.  Substitute "1" with appropriate
   vnum for room you want player to go to (limbo).  If player drops link while
   shapeshifted they will return in that room, so make sure it's recallable. */
	location = find_location( ch, "1" );
	char_from_room(ch);
	char_to_room(ch,location);
	return;
}


