/************************************************
7/30/2001

Explosives Version 3
Several changes have been made:
Nukes are diamond-shaped instead of box-shaped and do only double the
base damage as maximum.
Nukes now destroy rooms, objects in them, and resets. This actually
alters your area files, so you will want to backup frequently.
I also fixed the nuke message randomization problem.
------------------------------------------------
5/19/2001

Explosives Version 2 - updates
In the original version, there was a bug causing the mud to crash if somebody
was killed by a grenade exploding in their hands. This has been fixed.
--------------------------------------------------------------------

The snippet is for adding grenades, nukes, and mines to your mud.
It is by no means complete, but will give any IMP on a 20th century/
futuristic mud some extra fun additions. It is for any ROM2.4b6 mud
with OLC.

The grenades are meant to be used with a throwing skill. Players can
use the pull command found in this snippet to light the fuse.

The nukes explode and work properly, but there is no mechanism for
players/clans to use them yet. They are used with the 'nuclear' object.
The only thing a nuclear object does is blow up...it's meant to be created 
by something else. Have fun dropping  nukes while watching mobdeaths on 
wiznet! They blow up instantly causing a chain reaction based on it's
radius--pretty neat.

Mines in this snippet are basically modified grenades--they use the 'grenade'
object type also. The only difference is that they have the 'is-mine' flag
set. The chance of tripping any mine is fixed at 50%...you may want to change
this.

----------------------------------------------------
Credits
Snippet by: Thales from Islands of Exile mud
		funcity.org 9999
		email: gph2076@crosswinds.net

Original Grenade code by: Jeff Maxx from Lands of Altanos Mud
----------------------------------------------------
Please email me if you like this code...
it's not required but I would like to know if this code is put to
good use :)
****************************************************/

-- update.c --
Add to 'local functions' section:

void	explode		args( ( OBJ_DATA *obj ) );
void  grenade_update     args(( void ));
void  nuclear_update	 args(( void ));

Add anywhere in the file:

/*
* Update all grenades. -Jeff Maxx
*/
void grenade_update( void )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    CHAR_DATA *vch;
    char buf[MSL];

    for ( obj = object_list; obj != NULL; obj = obj_next )
    {
	obj_next = obj->next;

      if ( obj->item_type != ITEM_GRENADE )
       continue;

       if ( IS_SET(obj->value[3], GRENADE_IS_MINE ))
	continue;

       if ( IS_SET(obj->value[1], GRENADE_PULLED ) )    
       {
       	   if ( obj->value[0] > 0 )
           {
		obj->value[0]--;
		 if ( IS_SET( obj->value[3], GRENADE_QUIET_TICK ))
		    continue;

	       if (obj->carried_by == NULL)
	       {

		 if ( obj->in_obj != NULL)
		    return;

		 for (vch = obj->in_room->people; vch != NULL; vch =  vch->next_in_room)
                 {
		    sprintf( buf, "{Y%s{Y ticks quietly here.{x\n\r", capitalize(obj->short_descr));
		    send_to_char( buf, vch );
		 }
                 return;
	       }
	       else
	       {
		 for (vch = obj->carried_by->in_room->people; vch != NULL; vch =  vch->next_in_room)
                 {
		    sprintf( buf, "{Y%s{Y ticks quietly here.{x\n\r", capitalize(obj->short_descr));
		    send_to_char( buf, vch );
		 }
                 return;
               }
	   }
	   else
	   {
		explode( obj );
		extract_obj (obj);
		return;
	   }
      }

    }
    return;
}

//explode--by Thales
//Causes damage to characters in room, accepts only grenade & nuclear objects

void explode( OBJ_DATA *obj )
{
     CHAR_DATA *next_in_room;
    char buf[MSL];
    CHAR_DATA *vch;
    CHAR_DATA *hch;
    ROOM_INDEX_DATA *theroom;
	if (( obj->item_type != ITEM_GRENADE ) && ( obj->item_type != ITEM_NUCLEAR ))
	return;

      if ( obj->in_room == NULL )
      {
	   if ( obj->carried_by )
	   {
		hch = obj->carried_by;
		sprintf( buf, "{R%s{R explodes right in your hands!{x\n\r", capitalize(obj->short_descr));
		send_to_char( buf, hch );
		act( "{R$t{R explodes right in $n's hands!", hch, capitalize(obj->short_descr),
	 NULL, TO_ROOM );

		theroom = hch->in_room;
		switch ( obj->item_type )
		{
		    case ITEM_GRENADE:
			damage( hch, hch, obj->value[2] * 4, 0, DAM_NEGATIVE, FALSE);

		    case ITEM_NUCLEAR:
			damage( hch, hch, obj->value[0] * 50, 0, DAM_NEGATIVE, FALSE);
		}
		

		
		if ( hch )
		    obj_from_char(  obj );
		    obj_to_room( obj, theroom ); 
		if ( obj->in_obj )
		    obj->in_obj = NULL;
	   }
	   else
	   return;
      }
		
      //Do base damage to chars in room
      for (vch = obj->in_room->people; vch != NULL; vch = next_in_room)
           {
		next_in_room = vch->next_in_room;

		switch ( obj->item_type )
		{
		    case ITEM_GRENADE:
		    {
			sprintf( buf, "{R%s{R is consumed in flames as it EXPLODES!{x\n\r",capitalize(obj->short_descr));
			send_to_char( buf, vch );
			sprintf( buf, "{rYou are struck by the shrapnel!{x\n\r");
			send_to_char( buf, vch );
			damage(vch,vch,obj->value[2],0,DAM_NEGATIVE,FALSE);
			continue;
		    }
		    case ITEM_NUCLEAR:
           	    {
			int randnum = number_range( 0, 2 );
			switch ( randnum )
			{
			    case 0:       		
			    sprintf( buf, "{R%s{R extends to your vicinity and BURNS you!{x\n\r", capitalize(obj->short_descr));
                            break;
			    case 1:
			    sprintf( buf, "{R%s{R SEARS your flesh as it fills the area!{x\n\r", capitalize(obj->short_descr));
                            break;
			    case 2:
			    sprintf( buf, "{R%s{R's radiation singes your hair as it SCORCHES your body!{x\n\r", capitalize(obj->short_descr));
			}
		   	send_to_char( buf, vch );
		   	damage(vch,vch,obj->value[0],0,DAM_NEGATIVE,FALSE);
	   	    }
		}
	    }
}

void nuclear_update( void )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;

    for ( obj = object_list; obj != NULL; obj = obj_next )
    {
	obj_next = obj->next;

      if ( obj->item_type != ITEM_NUCLEAR )
       continue;

		
      //Blow up!
	explode ( obj );

	if ( obj == NULL ) continue;

	//Blow up lost objects
	if ( obj->in_room == NULL )
	{
	    extract_obj( obj );
	    continue;
	}

	//Cause cancer--*To be added*

	//Set rubble flag
	if (( obj->in_room->sector_type != SECT_WATER_SWIM ) &&
	    ( obj->in_room->sector_type != SECT_WATER_NOSWIM ))
	{
	SET_BIT( obj->in_room->area->area_flags, AREA_CHANGED );
	if ( !number_bits( 4 ) == 0 )
	    {
	        free_string( obj->in_room->description );
	        obj->in_room->description = str_dup( "All around you...everything has been razed to the ground!{/" );
	        free_string( obj->in_room->name );
	        obj->in_room->name = str_dup( "{wRubble{x" );
	        obj->in_room->sector_type = SECT_RUBBLE;
	    }
	    //Nuke resets
	    obj->in_room->reset_first = NULL;
	    obj->in_room->reset_last = NULL;
	}

	//Destroy other objects in room
	obj->next = NULL;
	obj->next_content = NULL;
	obj->in_room->contents = obj;


	//Spread the explosion
	if (( obj->value[1] > 0 ) && (obj->in_room->exit[DIR_NORTH] != NULL ))
	{
	    OBJ_DATA *nclone;
	    nclone = create_object( obj->pIndexData, 0 );
	    nclone->value[0] = obj->value[0];
	    nclone->value[1] = obj->value[1];
	    nclone->value[2] = obj->value[2];
	    nclone->value[3] = obj->value[3];
	    nclone->value[4] = obj->value[4];

	    nclone->value[1]--;
	    nclone->value[2] = 0;
            nclone->value[3]--;
            nclone->value[4]--;
	    obj_to_room( nclone, obj->in_room->exit[DIR_NORTH]->u1.to_room );
	}
	if (( obj->value[2] > 0 ) && (obj->in_room->exit[DIR_SOUTH] != NULL ))
	{
	    OBJ_DATA *sclone;
	    sclone = create_object( obj->pIndexData, 0 );
	    sclone->value[0] = obj->value[0];
	    sclone->value[1] = obj->value[1];
	    sclone->value[2] = obj->value[2];
	    sclone->value[3] = obj->value[3];
	    sclone->value[4] = obj->value[4];

	    sclone->value[2]--;
	    sclone->value[1] = 0;
            sclone->value[3]--;
            sclone->value[4]--;
	    obj_to_room( sclone, obj->in_room->exit[DIR_SOUTH]->u1.to_room );
	}
	if (( obj->value[3] > 0 ) && (obj->in_room->exit[DIR_EAST] != NULL ))
	{
	    OBJ_DATA *eclone;
	    eclone = create_object( obj->pIndexData, 0 );
	    eclone->value[0] = obj->value[0];
	    eclone->value[1] = obj->value[1];
	    eclone->value[2] = obj->value[2];
	    eclone->value[3] = obj->value[3];
	    eclone->value[4] = obj->value[4];

	    eclone->value[3]--;
	    eclone->value[4] = 0;
            eclone->value[1]--;
            eclone->value[2]--;
	    obj_to_room( eclone, obj->in_room->exit[DIR_EAST]->u1.to_room );
	}
	if (( obj->value[4] > 0 ) && (obj->in_room->exit[DIR_WEST] != NULL ))
	{
	    OBJ_DATA *wclone;
	    wclone = create_object( obj->pIndexData, 0 );
	    wclone->value[0] = obj->value[0];
	    wclone->value[1] = obj->value[1];
	    wclone->value[2] = obj->value[2];
	    wclone->value[3] = obj->value[3];
	    wclone->value[4] = obj->value[4];

	    wclone->value[4]--;
	    wclone->value[3] = 0;
            wclone->value[1]--;
            wclone->value[2]--;
	    obj_to_room( wclone, obj->in_room->exit[DIR_WEST]->u1.to_room );
	}
	//Destroy original explosion
	extract_obj( obj );
    }
}

Add this line to the beginning of the update_handler function:

    static  int     pulse_grenade;

Add this somewhere between the if clauses for the pulses:
    if ( --pulse_grenade <= 0 )
    {
      pulse_grenade     = PULSE_GRENADE;
      grenade_update    ( );
      nuclear_update	( );
    }


-- tables.c --
Add this line to the tabled called type_flags[]:

    {   "grenade",		ITEM_GRENADE,		TRUE	},
    {	"nuclear",		ITEM_NUCLEAR,		TRUE	},

Add this among the flags structs:

const struct flag_type grenade_flags[]=
{
    {	"is_mine",	  GRENADE_IS_MINE,	TRUE	},
    {	"quiet_tick",	  GRENADE_QUIET_TICK,	TRUE	},
    {	"lightable",	  GRENADE_LIGHTABLE,	TRUE	},
    {	"pullable",	  GRENADE_PULLABLE,	TRUE	},
    {	NULL,		  0,			0	}
};

At the end of the table that begins with:
const struct flag_type sector_flags[] =
but before the null entry, add this:
    {	"rubble",	SECT_RUBBLE,		TRUE	},


-- tables.h --
Add this anywhere:
extern  const   struct  flag_type	grenade_flags[];

-- olc_save.c --

Add this to the save_object function, with the other cases:

	case ITEM_GRENADE:
	    fprintf( fp, "%d %d %d %d %d\n",
		     pObjIndex->value[0],
                     pObjIndex->value[1],
                     pObjIndex->value[2],
                     pObjIndex->value[3],
                     pObjIndex->value[4]);
            break;
	case ITEM_NUCLEAR:
	    fprintf( fp, "%d %d %d %d %d\n",
		     pObjIndex->value[0],
                     pObjIndex->value[1],
                     pObjIndex->value[2],
                     pObjIndex->value[3],
                     pObjIndex->value[4]);
            break;

-- olc_act.c --

Add this to the structure beginning with this line:
const struct olc_help_type help_table[] =

    {	"grenade",	grenade_flags,	 "Grenade flags."		 },

Add this in the show_obj_values function, in the appropriate place:
          case ITEM_GRENADE: 
           sprintf( buf, 
            "[v0] Timer           [%d]\n\r" 
            "[v1] Unused          [?]\n\r" 
            "[v2] Damage          [%d]\n\r" 
            "[v3] Grenade Flags:  [%s]\n\r", 
		obj->value[0], 
		obj->value[2],
		flag_string( grenade_flags, obj->value[3])); 
	    send_to_char( buf, ch ); 
	    break; 
	case ITEM_NUCLEAR:
           sprintf( buf, 
            "[v0] Base Damage     [%d]\n\r" 
            "[v1] North Energy    [%d]\n\r" 
            "[v2] South Energy    [%d]\n\r"
	    "[v3] East Energy     [%d]\n\r"
	    "[v4] West Energy     [%d]\n\r", 
		obj->value[0],
		obj->value[1],
		obj->value[2],
		obj->value[3],
		obj->value[4]);
	    send_to_char( buf, ch ); 
	    break; 

Add this to set_obj_values:
        case ITEM_GRENADE: 
	    switch ( value_num ) 
	    { 
	        default: 
		    do_help( ch, "ITEM_GRENADE" ); 
	            return FALSE; 
              case 0: 
                  send_to_char( "TIMER SET.\n\r\n\r", ch); 
                  pObj->value[0] = atoi( argument ); 
                  break; 
	        case 2: 
	            send_to_char( "DAMAGE SET.\n\r\n\r", ch ); 
	            pObj->value[2] = atoi( argument ); 
	            break; 
		case 3:
		    send_to_char( "GRENADE FLAGS TOGGLED.\n\r\n\r", ch);
    		    ALT_FLAGVALUE_TOGGLE( pObj->value[3], grenade_flags, argument );
		    break;
	    } 
            break; 
	case ITEM_NUCLEAR:
	    switch ( value_num )
	    { 
	        default: 
		    do_help( ch, "ITEM_NUCLEAR" ); 
	            return FALSE; 
              case 0: 
                  send_to_char( "BASE DAMAGE SET.\n\r\n\r", ch); 
                  pObj->value[0] = atoi( argument ); 
                  break; 

		case 1:
		    send_to_char( "NORTH ENERGY SET.\n\r\n\r", ch );
	   	    pObj->value[1] = atoi( argument ); 
		    break;
		case 2:
		    send_to_char( "SOUTH ENERGY SET.\n\r\n\r", ch );
	   	    pObj->value[2] = atoi( argument ); 
		    break;
		case 3:
		    send_to_char( "EAST ENERGY SET.\n\r\n\r", ch );
	   	    pObj->value[3] = atoi( argument ); 
		    break;
		case 4:
		    send_to_char( "WEST ENERGY SET.\n\r\n\r", ch );
	   	    pObj->value[4] = atoi( argument ); 
		    break;

	    } 


-- merc.h --
Add this among the pulse definitions:

#define PULSE_GRENADE           ( 1 * PULSE_PER_SECOND)    /* Jeff Maxx */

Add this to the item types definition list:

#define ITEM_GRENADE		     35
#define ITEM_NUCLEAR                 36


NOTE: 35 and 36 may already be used by another snippet...
change them if needed

Add this among the flag definitions:

/* grenade flags */
#define GRENADE_IS_MINE		(A)
#define GRENADE_QUIET_TICK	(B)
#define GRENADE_LIGHTABLE	(C)
#define GRENADE_PULLABLE	(D)

Add this above the 'RT comm flags' section:

/* grenade states - Jeff Maxx */
#define GRENADE_NOTPULLED   0
#define GRENADE_PULLED      1

Add this among the Sector Types:
#define SECT_RUBBLE                  xx
where xx is the value for SECT_MAX currently,
then adjust SECT_MAX one higher.

-- interp.c --

Add this to the 'object manipulation commands' section:
    { "pull",		do_pull,	POS_RESTING,	 0,  LOG_NORMAL, 1},

-- interp.h --

Add this where it belongs (in alphabetical order):
DECLARE_DO_FUN( do_pull		);

-- db2.c --
Add to the load_objects function, before the ITEM_WAND case:

	case ITEM_GRENADE:
	    pObjIndex->value[0]		= fread_number(fp);
	    pObjIndex->value[1]		= fread_number(fp);
	    pObjIndex->value[2]		= fread_number(fp);
	    pObjIndex->value[3]		= fread_flag(fp);
            pObjIndex->value[4]         = fread_number(fp);
	    break;

	case ITEM_NUCLEAR:
	    pObjIndex->value[0]		= fread_number(fp);
	    pObjIndex->value[1]		= fread_number(fp);
	    pObjIndex->value[2]		= fread_number(fp);
	    pObjIndex->value[3]		= fread_number(fp);
	    pObjIndex->value[4]		= fread_number(fp);
	    break;

Add these cases to convert_object:
        case ITEM_GRENADE:
	case ITEM_NUCLEAR:

-- db.c --
Add the following cases to create_object:

    case ITEM_GRENADE:
    case ITEM_NUCLEAR:

-- const.c --
Add to the item_table structure:

    {	ITEM_GRENADE,	"grenade"	},
    {	ITEM_NUCLEAR,	"nuclear"	},

-- act_obj.c --
Add at the beginning:
void	explode		args( ( OBJ_DATA *obj ) );

Add after these lines in get_obj:
	send_to_char( "You can't take that.\n\r", ch );
	return;
    }

This:
        if ( obj->item_type == ITEM_GRENADE )
    {
       if ( IS_SET(obj->value[1], GRENADE_PULLED ) )
      {
       send_to_char("ARE YOU CRAZY OR SOMETHING???!?!?!?\n\r",ch);
       return;
      }
    }

Add this function to the end of the file:

void do_pull( CHAR_DATA *ch, char *argument )
{   
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    bool found;
         if ( !obj->item_type == ITEM_GRENADE )
    { 
    send_to_char( "That's not something that you can pull.\n\r", ch);
    return;
    }


   /* if ( !IS_SET( obj->value[3], GRENADE_PULLABLE ))
    {
	send_to_char( "That's not something that you can pull.\n\r", ch);
	return;
    }*/

    argument = one_argument( argument, arg );
    if ( arg[0] == '\0' )
    {  send_to_char( "Pull what grenade?\n\r", ch);
	return;
    }
             if ( ( obj = get_obj_carry( ch, arg, ch ) ) == NULL )
        {
            send_to_char( "You do not have that.\n\r", ch );
            return;
        }             
           if ( IS_SET(obj->value[1], GRENADE_PULLED) )
            { send_to_char( "It's already pulled.\n\r",      ch ); return;
	}
        SET_BIT(obj->value[1], GRENADE_PULLED);
        act("You pull open $p.",ch,obj,NULL,TO_CHAR);
        act( "$n pulls open $p.", ch, obj, NULL, TO_ROOM );
        return;             
}

IF your mud has the do_junk function, add this code to it,
just before the  obj_from_char( obj );

    if (( obj->item_type == ITEM_GRENADE ) && IS_SET( obj->value[1], GRENADE_PULLED))
    {
	send_to_char( "THROW IT BEFORE IT EXPLODES!!!!\n\r", ch );
	return;
    }

IF your mud has the do_lore function, add this code as the
last case:
      case ITEM_GRENADE:

-- act_move.c --
Add this in the 'local functions' section:

void	check_for_mines args( ( ROOM_INDEX_DATA *rm ) );

Add before the following in the move_char function:
    /* 
     * If someone is following the char, these triggers get activated

This:
    check_for_mines( ch->in_room );

Add this after the move_char function:
void check_for_mines( ROOM_INDEX_DATA *rm )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    for ( obj = rm->contents; obj != NULL; obj = obj_next )
    {
	obj_next = obj->next_content;
	if ((obj->item_type == ITEM_GRENADE) && IS_SET( obj->value[3], GRENADE_IS_MINE) &&
		number_range(0, 1))
	{
	    explode( obj );
	    extract_obj( obj );
	}
    }
}

