/* Shapeshift function 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 #include #else #include #include #endif #include #include #include #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! */ 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; }