CHARACTER BLEEDING v1.1
by: Seven

     Before I begin I'd like to give credit where credit is due.  This snippet
expands upon a bleeding snippet written by Grehyton at...

http://www.grehyton.inetsolve.com/snippets/bleeding.txt

for Merc and is similar in function.  As for licenses etc, I am hereby releasing
this code to the public domain.  I require nothing in return other than that you
keep all comments in place.  I would appreciate any feedback but it's not a
requirement.  Keep in mind that this won't get any better without fresh ideas
from all the people who look at it, nor will anything else.  If you're tired of
stagnant code that's been old since you STARTED mud coding, now's the time to
start interacting with the mud coding community and hopefully we all can push the
technology forward...  If you like something someone else has made and enjoy
getting it for free, why not take 10 seconds to send them any ideas or complaints
you may have. (You'll find my email address at the bottom of this text file)

Changes in this version (1.1)

1. The code to add bleeding counters has been moved to the armor damaging section
   of fight.c to solve many problems..
2. More number depth in bleeding counters (bleeding uses more counters to attain
   the same affects as before, which allows for minor bleeding now and possibly
   bleeding more often in the future without being a death sentance if not cured)
3. Bleeding causes less damage per counter, but there are more counters used so it
   evens out.  Damage also moved from my home-made jumble to the standard damage()
   function now that other bits of code have been changed appropriately to avoid
   loops and crashes.
4. More bleeding messages (just copies) span over a larger number of counters.
5. Bleeding slows faster in general, especially when a player is sitting, resting
   or sleeping. (Hmm.. slows faster... is that an oxymoron?)
6. PCs will take minor damage during a fight from blood loss.

Initial changes to Grehyton's code (1.0)

1. No character regeneration if they're bleeding.
2. Bleeding damage and messages are based upon severity of the bleeding condition.
3. Added some balancing chances to gain_condition so PC's don't bleed to death
   after every fight.
4. Added a pool of blood at the feet of a bleeding char on a tick.
5. Bleeding starts if a char is hit for 25% of their maxhit or more (bad bleeding),
   or by a small chance per damage taken (over 18).  1 in 10 chance if armor is
   being worn on the body area that was hit.  1 in 5 if no armor is covering that
   body area.  Chances seem small, but with the average number of hits landed in
   combat being more than one, it works out pretty well.
6. Mental state worsens while a char is bleeding and they'll begin to get tired.
7. Loss of blood causes thirst.
8. Other small semantic changes not worth mentioning.


Here we go...

--- ACT_WIZ.C ---
In function "do_mstat" find:

    if ( !IS_NPC( victim ) )
      pager_printf_color( ch, "&cThirst: &w%d   &cFull: &w%d   &cDrunk: &w%d\n\r",
	victim->pcdata->condition[COND_THIRST],
	victim->pcdata->condition[COND_FULL],
	victim->pcdata->condition[COND_DRUNK] );

and change it to read:

    if ( !IS_NPC( victim ) )
      pager_printf_color( ch, "&cThirst: &w%d   &cFull: &w%d   &cDrunk: &w%d   &cBl: %d\n\r",
	victim->pcdata->condition[COND_THIRST],
	victim->pcdata->condition[COND_FULL],
	victim->pcdata->condition[COND_DRUNK],
	victim->pcdata->condition[COND_BLEEDING] );


--- BUILD.C ---
In function "do_mset" find:

   if ( !str_cmp( arg2, "int" ) )
    {
	if ( !can_mmodify( ch, victim ) )
	  return;
	if ( value < minattr || value > maxattr )
	{
	    ch_printf( ch, "Intelligence range is %d to %d.\n\r", minattr, maxattr );
	    return;
	}
	victim->perm_int = value;
	if ( IS_NPC(victim) && xIS_SET(victim->act, ACT_PROTOTYPE) )
	  victim->pIndexData->perm_int = value;
	return;
    }

And immediately after, add:

    if ( !str_cmp( arg2, "bleeding" ) )
    {
        if ( IS_NPC(victim) )
        {
            send_to_char( "Not on NPC's.\n\r", ch );
            return;
        }
        if ( value < -1 || value > 100 )
        {
           send_to_char( "Bleeding range is -1 to 100.\n\r", ch );
            return;
        }
        victim->pcdata->condition[COND_BLEEDING] = value;
        return;
    }

Then find the section with the default mset message and somewhere near:

	send_to_char( "  attack defense numattacks\n\r",		ch );

Add a bleeding option such as:

	send_to_char( "  attack defense numattacks bleeding\n\r",		ch );


--- FIGHT.C ---
In function "violence_update" find:

	else
	if ( IS_AWAKE(ch) && ch->in_room == victim->in_room )
        retcode = multi_hit( ch, victim, TYPE_UNDEFINED );

And replace it with:

	else
	if ( IS_AWAKE(ch) && ch->in_room == victim->in_room )
	{
        retcode = multi_hit( ch, victim, TYPE_UNDEFINED );
		/*Random VERY minor bleeding during a fight*/
		if (!IS_NPC(ch) && ch->pcdata->condition[COND_BLEEDING] > 0 && number_range(1,4) == 1)
		{
			damage(ch, ch, ch->pcdata->condition[COND_BLEEDING], TYPE_UNDEFINED);
			act( AT_BLOOD, "You're losing blood...", ch, NULL, NULL, TO_CHAR);
			act( AT_BLOOD, "$N is losing blood...", ch, NULL, NULL, TO_ROOM);
		}
	}

In function "damage" find:

	if ( dam > victim->max_hit / 4 )
	{
	   act( AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR );

And immediately after, add:

	   /* Start bleeding unless level is 5 or less */
	   if ( !IS_NPC(victim) && victim->level > 5 )
	   gain_condition( victim, COND_BLEEDING, 3 );

Then find:

    /*
     * Code to handle equipment getting damaged, and also support  -Thoric
     * bonuses/penalties for having or not having equipment where hit
     */
    if (dam > 10 && dt != TYPE_UNDEFINED)
    {
	/* get a random body eq part */
	dameq  = number_range(WEAR_LIGHT, WEAR_EYES);
	damobj = get_eq_char(victim, dameq);
	if ( damobj )
	{
	    if ( dam > get_obj_resistance(damobj)
	    &&   number_bits(1) == 0 )
	    {
		set_cur_obj(damobj);
		damage_obj(damobj);
	    }
	    dam -= 5;  /* add a bonus for having something to block the blow */
	}
	else
	    dam += 5;  /* add penalty for bare skin! */
    }

And replace it with:

    /*
     * Code to handle equipment getting damaged, and also support  -Thoric
     * bonuses/penalties for having or not having equipment where hit.
     * Bleeding support added -Seven
     */
    if (dam > 10 && dt != TYPE_UNDEFINED)
    {
	/* get a random body eq part */
	dameq  = number_range(WEAR_LIGHT, WEAR_EYES);
	damobj = get_eq_char(victim, dameq);
	if ( damobj )
	{
	    if ( dam > get_obj_resistance(damobj)
	    &&   number_bits(1) == 0 )
	    {
		set_cur_obj(damobj);
		damage_obj(damobj);
	    }
	    dam -= 5;  /* add a bonus for having something to block the blow */
		/* 1 in 16 chance to begin or worsen bleeding */
		if (dam > 18)
		{
		if ( !IS_NPC(victim) && number_range( 1,10 ) == 1 )
			gain_condition( victim, COND_BLEEDING, 1 );
		}
	}
	else
	    dam += 5;  /* add penalty for bare skin! */
		/* 1 in 6 chance to begin or worsen bleeding */
	    if (dam > 18)
		{
		if ( !IS_NPC(victim) && number_range( 1,5 ) == 1 )
			gain_condition( victim, COND_BLEEDING, 1 );
		}
    }

In function "raw_kill" find:

    victim->damroll	= 0;
    victim->hitroll	= 0;
    victim->mental_state = -10;

And immediately after, add:

    victim->pcdata->condition[COND_BLEEDING] = 0;


--- MUD.H ---
Find:

/*
 * Conditions.
 */
typedef enum
{
  COND_DRUNK, COND_FULL, COND_THIRST, COND_BLOODTHIRST, MAX_CONDS
} conditions;

And change it to:

/*
 * Conditions.
 */
typedef enum
{
  COND_DRUNK, COND_FULL, COND_THIRST, COND_BLOODTHIRST, COND_BLEEDING,
  MAX_CONDS
} conditions;


--- MUD_COMM.C --
In function "simple_damage" find:

	if ( dam > victim->max_hit / 4 )
	{
	   act( AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR );

And immediately after, add:

	   /* Start bleeding unless level is 5 or less */
	   if ( !IS_NPC(victim) && victim->level > 5 )
	   gain_condition( victim, COND_BLEEDING, 3 );


--- SAVE.C ---
In function "load_char_obj" find:

    ch->pcdata->condition[COND_THIRST]	= 48;
    ch->pcdata->condition[COND_FULL]	= 48;
    ch->pcdata->condition[COND_BLOODTHIRST] = 10;

And immediately after add:

    ch->pcdata->condition[COND_BLEEDING] = 0;

In function "fwrite_char" find:

	fprintf( fp, "Condition    %d %d %d %d\n",
	    ch->pcdata->condition[0],
	    ch->pcdata->condition[1],
	    ch->pcdata->condition[2],
	    ch->pcdata->condition[3]);

And change it to read:

	fprintf( fp, "Condition    %d %d %d %d %d\n",
	    ch->pcdata->condition[0],
	    ch->pcdata->condition[1],
	    ch->pcdata->condition[2],
	    ch->pcdata->condition[3],
	    ch->pcdata->condition[4]);

In function "fread_char" find:

	    if ( !str_cmp( word, "Condition" ) )
	    {
		line = fread_line( fp );
		sscanf( line, "%d %d %d %d",
		      &x1, &x2, &x3, &x4 );
		ch->pcdata->condition[0] = x1;
		ch->pcdata->condition[1] = x2;
		ch->pcdata->condition[2] = x3;
		ch->pcdata->condition[3] = x4;
		fMatch = TRUE;

And change it to read:

	    if ( !str_cmp( word, "Condition" ) )
	    {
		line = fread_line( fp );
		sscanf( line, "%d %d %d %d %d",
		      &x1, &x2, &x3, &x4, &x5 );
		ch->pcdata->condition[0] = x1;
		ch->pcdata->condition[1] = x2;
		ch->pcdata->condition[2] = x3;
		ch->pcdata->condition[3] = x4;
		ch->pcdata->condition[4] = x5;
		fMatch = TRUE;


---UPDATE.C ---
In function "gain_condition" find:

if ( iCond == COND_BLOODTHIRST )
	ch->pcdata->condition[iCond]	= URANGE( 0, condition + value,
                                                10 + ch->level );

And immediately after, add:

	if ( iCond == COND_BLEEDING )
	ch->pcdata->condition[iCond]	= URANGE( 0, condition + value, 100 );

Then at the end of all the CASEs (case COND_DRUNK, case COND_FULL etc...)
add a CASE for COND_BLEEDING (there should be 4 places to add this case)

	case COND_BLEEDING:
		  retcode = rNONE;
                  break;

Then in function "char_update" find:

	if ( ch->position >= POS_STUNNED )
	{

	    if ( ch->hit  < ch->max_hit )
		ch->hit   += hit_gain(ch);

	    if ( ch->mana < ch->max_mana )
		ch->mana  += mana_gain(ch);

	    if ( ch->move < ch->max_move )
		ch->move  += move_gain(ch);
	}

And change it to read:

    if ( !IS_NPC(ch) && ch->pcdata->condition[COND_BLEEDING] == 0 )
	{
	if ( ch->position >= POS_STUNNED )
	{

	    if ( ch->hit  < ch->max_hit )
		ch->hit   += hit_gain(ch);

	    if ( ch->mana < ch->max_mana )
		ch->mana  += mana_gain(ch);

	    if ( ch->move < ch->max_move )
		ch->move  += move_gain(ch);
	}
	}
    else
	if ( IS_NPC(ch) )
	{
	if ( ch->position >= POS_STUNNED )
	{

	    if ( ch->hit  < ch->max_hit )
		ch->hit   += hit_gain(ch);

	    if ( ch->mana < ch->max_mana )
		ch->mana  += mana_gain(ch);

	    if ( ch->move < ch->max_move )
		ch->move  += move_gain(ch);
	}
	}

Then find the poison update code:

	    if ( IS_AFFECTED(ch, AFF_POISON) )
	    {
		act( AT_POISON, "$n shivers and suffers.", ch, NULL, NULL, TO_ROOM );
		act( AT_POISON, "You shiver and suffer.", ch, NULL, NULL, TO_CHAR );
		ch->mental_state = URANGE( 20, ch->mental_state
				 + (IS_NPC(ch) ? 2 : IS_PKILL(ch) ? 3 : 4), 100 );
		damage( ch, ch, 6, gsn_poison );
	    }

And immediately after, add:

		/* Bleeding update code -Seven */
		if ( !IS_NPC(ch))
        {
            if ( ch->pcdata->condition[COND_BLEEDING] > 0 )
            {
				switch( ch->pcdata->condition[COND_BLEEDING] )
				{
					default:
					{
					act( AT_BLOOD, "You are bleeding profusely!", ch, NULL, NULL, TO_CHAR );
					act( AT_BLOOD, "$n is bleeding profusely.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   1:
					{
					act( AT_BLOOD, "Blood drips slowly from your wounds...", ch, NULL, NULL, TO_CHAR );
	    				act( AT_BLOOD, "Blood drips slowly from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   2:
					{
					act( AT_BLOOD, "Blood drips slowly from your wounds...", ch, NULL, NULL, TO_CHAR );
	    				act( AT_BLOOD, "Blood drips slowly from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   3:
					{
					act( AT_BLOOD, "Blood is dripping quickly from your wounds...", ch, NULL, NULL, TO_CHAR );
    					act( AT_BLOOD, "Blood is dripping quickly from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   4:
					{
					act( AT_BLOOD, "Blood is dripping quickly from your wounds...", ch, NULL, NULL, TO_CHAR );
    					act( AT_BLOOD, "Blood is dripping quickly from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   5:
					{
					act( AT_BLOOD, "Blood is dripping quickly from your wounds...", ch, NULL, NULL, TO_CHAR );
    					act( AT_BLOOD, "Blood is dripping quickly from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   6:
					{
					act( AT_BLOOD, "Blood runs freely from your wounds...", ch, NULL, NULL, TO_CHAR );
					act( AT_BLOOD, "Blood runs freely from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
					case   7:
					{
					act( AT_BLOOD, "Blood runs freely from your wounds...", ch, NULL, NULL, TO_CHAR );
					act( AT_BLOOD, "Blood runs freely from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					case   8:
					{
					act( AT_BLOOD, "Blood runs freely from your wounds...", ch, NULL, NULL, TO_CHAR );
    					act( AT_BLOOD, "Blood runs freely from $n's wounds.", ch, NULL, NULL, TO_ROOM );
					}
					break;
				}
			/* 4 in 5 chance to slow bleeding on a tick
			 * A work in progress...
			 */
				if ( number_range( 1, 5 ) > 1 )
				{
					if ( ch->pcdata->condition[COND_BLEEDING] != 1
						&& (ch->position == POS_SITTING
						|| ch->position == POS_SLEEPING
						|| ch->position == POS_RESTING) )
					{
						gain_condition( ch, COND_BLEEDING,  -2 );
						if ( ch->pcdata->condition[COND_BLEEDING] == 0 )
							send_to_char("Your bleeding stops.\n\r",ch);
						else
							send_to_char("Your bleeding slows...\n\r",ch);
					}
					else
					{
						gain_condition( ch, COND_BLEEDING,  -1 );
						if ( ch->pcdata->condition[COND_BLEEDING] == 0 )
							send_to_char("Your bleeding stops.\n\r",ch);
						else
							send_to_char("Your bleeding slows a little...\n\r",ch);
					}
				}
				make_blood( ch );
				worsen_mental_state( ch, ch->pcdata->condition[COND_BLEEDING] );
				gain_condition( ch, COND_THIRST,  -1 );
		/* Damage moved to the end of the code to prevent characters who
		 * have bled to death from being addressed after the fact (crash bug fix)
		 */
				if ( ch->pcdata->condition[COND_BLEEDING] == 0 )
					damage(ch, ch, (ch->max_hit * 1/100), TYPE_UNDEFINED);
				else
					damage(ch, ch, (ch->pcdata->condition[COND_BLEEDING] * (ch->max_hit * 1/100)), TYPE_UNDEFINED);
			}
		}
		/* End of bleeding update code */


Make clean, recompile and you're done.  You can now use...

MSET <victim> bleeding <value>

... to manually set the bleeding counters on a player but watch out.  Anything
too high (20+) mark will cause SEVERE damage on a tick depending on the char's
maxhits.  Use with caution.

IDEAS FOR THE NEXT VERSION: (Some unfinished from the previous version)
1.  Bleeding chars in the same room cause increased BLOOD_THIRST in vampires
2.  Smaller pools of blood for lesser bleeding conditions
3.  Changes to healing spells to strip the bleeding condition
4.  A new spell called "aggrevate wounds" that worsens bleeding
5.  Scaled down bleeding for mobs (major project)
6.  Increased chance to track a bleeding character
7.  Slicing and piercing weapons cause more bleeding than blunt weapons.


If you've noticed, I'm a big fan of number_range ;)  Send any questions, comments,
flames, etc to seven@nycap.rr.com  I'll respond the same day.