/*
This is the third release of my take on traps. 
It is updated and a bit more thorough than the first couple attempts. 
If you have any questions or comments please direct them to drew.haley@gmail.com.
I know this will work on a basic QuickMUD / Rom 2.4 base, should also go pretty 
easily in envy, smaug and other close relatives. This release includes the code 
for additional spells/skills (Detect Traps (mage), Remove Trap (thief), 
Find Traps (a passive thief skill), and Lay Trap (thief)). 
*/

____________________________________________________________________________________

/* Merc.h */

//Ok First off I added a new item type for this, which is ITEM_THIEVES_TOOLS, which is required to
//lay/remove traps. In order to do this, add this line to merc.h:

#define ITEM_THIEVES_TOOLS	39  //used for lock picking, trap setting/disarming, etc.. 

//Of course it may not be item type 39 in your mud, keep that in mind. Also remember to edit tables.c/db.c
//with the new item type.

//In merc.h, under these lines:
#define ITEM_BURN_PROOF    (Y)
#define ITEM_NOUNCURSE     (Z)

//Add these:
#define ITEM_FIRE_TRAP		(aa)
#define ITEM_GAS_TRAP		(bb)
#define ITEM_POISON_TRAP	(cc)
#define ITEM_DART_TRAP		(dd)


//Increase MAX_SKILLS by four.


//Also, I use a macro for send_to_char, you might do the same:
#define SEND send_to_char

//Put these with the rest of your gsns:
extern sh_int gsn_find_trap;
extern sh_int gsn_detect_trap;

__________________________________________________________________________________

/* Const.c */
 
//You will need to add entries for detect trap, find trap, remove trap, and lay trap.
__________________________________________________________________________________

/* Act_info.c */

//In local functions, add this:
bool is_trapped args((OBJ_DATA *obj));

//Then, in format_obj_to_char, add these lines:
	if (get_skill(ch, gsn_find_trap) > 0  && !is_affected(ch, gsn_detect_traps))
	{
		if (number_percent() < get_skill(ch, gsn_find_trap))
		{
			if (is_trapped(obj))
			{
				strcat (buf, "({CTrapped{x) ");					
				check_improve (ch, gsn_find_trap, TRUE, 4);
			}
		}
	}
	if (is_affected(ch, gsn_detect_traps) && is_trapped(obj))
		strcat (buf, "({CTrapped{x) ");	
 
_____________________________________________________________________________________
 
 /* Act_obj.c */

 //Add these to your local functions:

void remove_trap args((OBJ_DATA *obj));
void spring_trap args((CHAR_DATA *ch, OBJ_DATA *obj));
bool is_trapped  args((OBJ_DATA *obj));
void lay_trap    args((OBJ_DATA *obj, int type));

//Then at the bottom of the file add this:

bool has_thieves_tools(CHAR_DATA *ch)
{
	OBJ_DATA *obj;	
	bool found = FALSE;
	
	for (obj = ch->carrying; obj != NULL; obj = obj->next_content)
    {
		if (obj->item_type == ITEM_THIEVES_TOOLS && can_see_obj (ch, obj))                    
        {
			found = TRUE;            
			break;
        }
    }
		
	return found;
}

//This just sets a dart trap by default but you can always expand it to do others.

void do_set_trap(CHAR_DATA *ch, char *argument)
{

	OBJ_DATA *obj;    
	int chance = 0;
	char arg1[MSL];
	int trap_type = -1;
	
	one_argument(argument, arg1);
	
	if (arg1[0] == '\0')
    {
        SEND("Lay trap in/on what?\r\n", ch);
        return;
    }
	
	if ((obj = get_obj_here (ch, arg1)) == NULL || !can_see_obj(ch,obj))
	{
		SEND("You don't see that object here.\r\n",ch);
		return;
	}
	else
	{		
		if(get_skill(ch,gsn_lay_trap) < 1)
		{
			SEND ("You don't know how to lay traps.\r\n",ch);
			return;
		}				
		
		if (!has_thieves_tools(ch))
		{
			SEND ("You need some sort of tool to do that!\r\n",ch);
			return;
		}

		if (obj->item_type != ITEM_CONTAINER)
		{
			SEND ("That's not a container.\r\n", ch);
			return;
		}
		
		if (is_trapped(obj))
		{
			SEND ("That item already has a trap on it.\r\n",ch);
			return;
		}
		
		trap_type = DART_TRAP;
		
		chance = number_percent();
		if (obj->level > ch->level)
			chance += (obj->level - ch->level) * 2;
		if (get_curr_stat(ch, STAT_DEX) > 15)
			chance -= 25 - get_curr_stat(ch, STAT_DEX);
		if (chance < get_skill(ch,gsn_lay_trap))
		{
			lay_trap(obj, trap_type);
			act( "$n successfully places a trap on $p.\r\n", ch, obj, NULL, TO_ROOM );
			act( "You successfully place a trap on $p.\r\n", ch, obj, NULL, TO_CHAR );
			check_improve(ch,gsn_lay_trap,TRUE,3);	
			return;								
		}
		else
		{				
			act( "$n failed to place a trap on $p.\r\n", ch, obj, NULL, TO_ROOM );
			act( "You failed to place a trap on $p.\r\n", ch, obj, NULL, TO_CHAR );				
			return;								
		}
	}
	return;
}
 
//Also, in wear_obj, after these lines:

	if (ch->level < obj->level)
    {
        sprintf (buf, "You must be level %d to use this object.\n\r",
                 obj->level);
        send_to_char (buf, ch);
        act ("$n tries to use $p, but is too inexperienced.",
             ch, obj, NULL, TO_ROOM);
        return;
    }
//Add this to prevent people seeing weirdness in their eq command:

//We obviously don't want people wearing trapped objects.
	if (is_trapped(obj))
		spring_trap(ch,obj);
_________________________________________________________________________________________________

/* Magic.c */

// Add this:

void spell_detect_traps (int sn, int level, CHAR_DATA *ch, void *vo, int target)
{
	CHAR_DATA *victim = (CHAR_DATA *) vo;
    AFFECT_DATA af;

	if (victim != ch)
	{
		SEND("You can only cast that on yourself.\n\r",ch);
		return;
	}
	
    if (is_affected(ch, gsn_detect_traps))
    {
        SEND("You already sense traps.\n\r",ch);        
		return;
    }

    af.where = TO_AFFECTS;
    af.type = sn;
    af.level = level;
    af.duration = level;
    af.modifier = 0;
    af.location = APPLY_NONE;
    af.bitvector = gsn_detect_traps;
    affect_to_char (victim, &af);
    send_to_char ("You feel an inner sense of traps.\r\n", victim);    
    return;
}

__________________________________________________________________________________________________

/* Magic.h */

DECLARE_SPELL_FUN(	spell_detect_traps	);

__________________________________________________________________________________________________

/* Act_move.c */

//At the top, add these:

//Trap functions
bool is_trapped args((OBJ_DATA *obj));
int get_trap_type args((OBJ_DATA *obj));
void remove_trap args((OBJ_DATA *obj));
void spring_trap args((CHAR_DATA *victim, OBJ_DATA *obj));
bool save_vs_trap args((CHAR_DATA *ch, OBJ_DATA *obj));

//Then under these lines:
REMOVE_BIT (obj->value[1], CONT_CLOSED);
act ("You open $p.", ch, obj, NULL, TO_CHAR);
act ("$n opens $p.", ch, obj, NULL, TO_ROOM);

//Add these lines:
//This is the code that fires the traps themselves. You can always add this to other places,
//but I only put it for containers (treasure boxes and whatnot).

		if (is_trapped(obj))
		{
			if (!save_vs_trap(ch, obj))
			{
				spring_trap(ch, obj);
				remove_trap(obj);	
				return;
			}
			else
			{
				SEND ("You got lucky... the trap failed to spring.\r\n", ch);
				remove_trap(obj);	
				return;
			}
		}
		
__________________________________________________________________________________________

/* Tables.c */

//Underneath these lines:
    {"burnproof", ITEM_BURN_PROOF, TRUE},
    {"nouncurse", ITEM_NOUNCURSE, TRUE},

//Add these lines:
	{"fire_trap", ITEM_FIRE_TRAP, TRUE},
    {"poison_trap", ITEM_POISON_TRAP, TRUE},
    {"gas_trap", ITEM_GAS_TRAP, TRUE},
    {"dart_trap", ITEM_DART_TRAP, TRUE},

__________________________________________________________________________________________

/* Traps.c */

//Then make a new file called traps.c, and add all of the following to it:

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik Strfeldt, Tom Madsen, and Katja Nyboe.    *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Thanks to abaddon for proof-reading our comm.c and pointing out bugs.  *
 *  Any remaining bugs are, of course, our work, not his.  :)              *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*    ROM 2.4 is copyright 1993-1998 Russ Taylor                             *
*    ROM has been brought to you by the ROM consortium                      *
*        Russ Taylor (rtaylor@hypercube.org)                                *
*        Gabrielle Taylor (gtaylor@hypercube.org)                           *
*        Brian Moore (zump@rom.org)                                         *
*    By using this code, you have agreed to follow the terms of the         *
*    ROM license, in the file Rom24/doc/rom.license                         *
****************************************************************************/

#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 "interp.h"

void raw_kill args ((CHAR_DATA * victim));

int get_trap_type(OBJ_DATA *obj)
{
	int type = 0;
	
	if (IS_SET(obj->extra_flags, ITEM_FIRE_TRAP))
		type = FIRE_TRAP;
	if (IS_SET(obj->extra_flags, ITEM_POISON_TRAP))
		type = POISON_TRAP;
	if (IS_SET(obj->extra_flags, ITEM_GAS_TRAP))
		type = GAS_TRAP;
	if (IS_SET(obj->extra_flags, ITEM_DART_TRAP))
		type = DART_TRAP;
	
	return type;
}

void lay_trap(OBJ_DATA *obj, int type)
{		
	switch (type)
	{
	default:
		return;
	case FIRE_TRAP:
		SET_BIT (obj->extra_flags, ITEM_FIRE_TRAP);
	case POISON_TRAP:
		SET_BIT (obj->extra_flags, ITEM_POISON_TRAP);
	case GAS_TRAP:
		SET_BIT (obj->extra_flags, ITEM_GAS_TRAP);
	case DART_TRAP:
		SET_BIT (obj->extra_flags, ITEM_DART_TRAP);
	}
	return;
}

void remove_trap(OBJ_DATA *obj)
{
	int type;
	
	type = get_trap_type(obj);
	switch (type)
	{
	default:
		return;
	case FIRE_TRAP:
		REMOVE_BIT (obj->extra_flags, ITEM_FIRE_TRAP);
	case POISON_TRAP:
		REMOVE_BIT (obj->extra_flags, ITEM_POISON_TRAP);
	case GAS_TRAP:
		REMOVE_BIT (obj->extra_flags, ITEM_GAS_TRAP);
	case DART_TRAP:
		REMOVE_BIT (obj->extra_flags, ITEM_DART_TRAP);
	}
	return;
}

bool save_vs_trap(CHAR_DATA *ch, OBJ_DATA *obj)
{
	int trap_type = 0;
	int reaction = 0, difficulty = 15;
	
	trap_type = get_trap_type(obj);
	
	switch (trap_type)	
	{	
	case FIRE_TRAP:
		difficulty += 5;
	case POISON_TRAP:
		difficulty += 3;
	case GAS_TRAP:
		difficulty += 8;
	case DART_TRAP:
		difficulty += 2;
	default:
		difficulty = 15;
	}
	
	difficulty += number_range(1,4); //add a little randomness to it.
	reaction = number_range(1,20);   //roll a reaction.
	
	
	if (trap_type == POISON_TRAP || trap_type == DART_TRAP)
	{
		if (get_curr_stat(ch, STAT_DEX) < 15)
			reaction -= ((get_curr_stat(ch, STAT_DEX) - 25) / 2); //max neg. of 12 w/ a Dex of 1.
		if (get_curr_stat(ch, STAT_DEX) > 15)
			reaction += ((get_curr_stat(ch, STAT_DEX)) / 5); //max bonus of 5 at Dex 25.
	}	
	
	if (trap_type == FIRE_TRAP)
	{
		if (get_curr_stat(ch, STAT_INT) > 15)
			reaction += ((get_curr_stat(ch, STAT_INT)) / 5); //max bonus of 5 at Int 25.
	}
	
	if (reaction >= difficulty)
		return TRUE;
	else
		return FALSE;
}


bool is_trapped(OBJ_DATA *obj)
{
	if (IS_SET(obj->extra_flags, ITEM_FIRE_TRAP) || IS_SET(obj->extra_flags, ITEM_POISON_TRAP) || IS_SET(obj->extra_flags, ITEM_GAS_TRAP) || IS_SET(obj->extra_flags, ITEM_DART_TRAP))
		return TRUE;
	else
		return FALSE;
}


void spring_trap(CHAR_DATA *victim, OBJ_DATA *obj)
{
	int trap_type = 0;
	int dam = 0;
	char buf[MAX_STRING_LENGTH];
	AFFECT_DATA af;
	
	trap_type = get_trap_type(obj);

	switch (trap_type)
    {
        default:
			SEND ("You got lucky... the trap failed to spring.\n\r", victim);
            return;
		case FIRE_TRAP:
			dam = dice(6,8) + 5;
			act ("A gout of flames shoots forth, searing $n!", victim, NULL, NULL, TO_ROOM);
			sprintf( buf, "A gout of flames shoots forth, searing you! {r[{x%d{r]{x\r\n", dam );			
			remove_trap(obj);
			SEND(buf, victim);
			victim->hit -= dam;			
			if (victim->hit <= 0)
				raw_kill(victim);						
			return;
		case POISON_TRAP:
			dam = dice(4,8) + 5;
			act ("A poison dart springs forth, striking $n!", victim, NULL, NULL, TO_ROOM);			
			sprintf( buf, "A poison dart springs forth, striking you! {r[{x%d{r]{x\r\n", dam );			
			remove_trap(obj);
			SEND(buf, victim);
			af.where = TO_AFFECTS;
			af.type = gsn_poison;
			af.level = number_range(25,40);
			af.duration = number_range(25,40);	
			af.location = APPLY_STR;
			af.modifier = -2;
			af.bitvector = AFF_POISON;
			affect_join (victim, &af);
			SEND ("You feel very sick.\n\r", victim);
			act ("$n looks very ill.", victim, NULL, NULL, TO_ROOM);
			victim->hit -= dam;
			if (victim->hit <= 0)
				raw_kill(victim);
			return;
		case GAS_TRAP:
			dam = dice(3,8) + 5;
			act ("A cloud of gas seeps out, choking $n!", victim, NULL, NULL, TO_ROOM);
			sprintf( buf, "A cloud of gas seeps out, choking you! {r[{x%d{r]{x\r\n", dam );		
			remove_trap(obj);			
			SEND(buf, victim);
			victim->hit -= dam;
			if (victim->hit <= 0)
				raw_kill(victim);
			return;
		case DART_TRAP:
			dam = dice(3,8) + 5;
			act ("A spring loaded dart leaps out, stabbing $n!", victim, NULL, NULL, TO_ROOM);
			sprintf( buf, "A spring loaded dart leaps out, stabbing you! {r[{x%d{r]{x\r\n", dam );	
			remove_trap(obj);
			SEND(buf, victim);
			victim->hit -= dam;
			if (victim->hit <= 0)
				raw_kill(victim);
			return;
	}
	return;
}


___________________________________________________________________________________________________

//Ok that's it! You should now be able to do a clean compile and start trapping things all over your mud!
//Things I might add in the future are a spell to trap an object (think explosive runes), and trap difficulty
//(for disarming them and such). Email me if you have any issues with this code and I'll try to help you finger
//it out. Thanks for checking it out!