/***************************************************************************
* File: fight.c , Combat module. Part of DIKUMUD *
* Usage: Combat system and messages. *
* Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
* *
* Copyright (C) 1992, 1993 Michael Chastain, Michael Quan, Mitchell Tse *
* Performance optimization and bug fixes by MERC Industries. *
* You can use our stuff in any way you like whatsoever so long as this *
* copyright notice remains intact. If you like it please drop a line *
* to mec@garnet.berkeley.edu. *
* *
* This is free software and you are benefitting. We hope that you *
* share your changes too. What goes around, comes around. *
***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "structs.h"
#include "mob.h"
#include "obj.h"
#include "utils.h"
#include "handler.h"
#include "interp.h"
#include "db.h"
#include "spells.h"
struct char_data *combat_list = NULL;
struct char_data *combat_next_dude = NULL;
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data *object_list;
void stop_follower(struct char_data *ch);
void hit(struct char_data *ch, struct char_data *victim, int type);
bool is_in_safe(struct char_data *ch, struct char_data *victim);
bool is_first_level(struct char_data *ch, struct char_data *victim);
void dam_message(int dam, struct char_data *ch, struct char_data *victim,
int w_type);
void group_gain( struct char_data *ch, struct char_data *victim );
bool check_parry( struct char_data *ch, struct char_data *victim );
bool check_dodge( struct char_data *ch, struct char_data *victim );
void disarm( struct char_data *ch, struct char_data *victim );
void trip( struct char_data *ch, struct char_data *victim );
/*
* Control the fights going on.
* Called periodically by the main loop.
*/
void perform_violence( void )
{
struct char_data *ch;
for (ch = combat_list; ch; ch = combat_next_dude )
{
combat_next_dude = ch->next_fighting;
assert( ch->specials.fighting );
if ( AWAKE(ch) && ch->in_room == ch->specials.fighting->in_room )
hit( ch, ch->specials.fighting, TYPE_UNDEFINED );
else
stop_fighting( ch );
}
}
/*
* Do one group of attacks.
*/
void hit( struct char_data *ch, struct char_data *victim, int type )
{
int chance;
/*
* First attack.
*/
one_hit( ch, victim, type );
/*
* Second attack.
*/
chance = IS_NPC(ch) ? 2 * GET_LEVEL(ch)
: ch->skills[SKILL_SECOND_ATTACK].learned * 2 / 5;
if ( number(1, 100) < chance )
one_hit( ch, victim, type );
/*
* Third attack.
*/
chance = IS_NPC(ch) ? GET_LEVEL(ch)
: ch->skills[SKILL_THIRD_ATTACK].learned / 5;
if ( number(1, 100) < chance )
one_hit( ch, victim, type );
/*
* Fourth attack.
*/
chance = IS_NPC(ch) ? GET_LEVEL(ch) * 2 / 3 : 0;
if ( number(1, 100) < chance )
one_hit( ch, victim, type );
}
/*
* Hit one guy once.
*/
void one_hit( struct char_data *ch, struct char_data *victim, int type )
{
struct obj_data *wielded;
int w_type;
int victim_ac, calc_thaco;
int dam;
byte diceroll;
extern int thaco[4][36];
extern byte backstab_mult[];
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];
/*
* Can't beat a dead char!
*/
if ( GET_POS(victim) == POSITION_DEAD )
return;
/*
* This happens when people flee et cetera during multi attacks.
*/
if ( ch->in_room != victim->in_room )
return;
/*
* Figure out the type of damage message.
*/
wielded = ch->equipment[WIELD];
w_type = TYPE_HIT;
if ( wielded && wielded->obj_flags.type_flag == ITEM_WEAPON )
{
switch ( wielded->obj_flags.value[3] )
{
case 0: case 1: case 2: w_type = TYPE_WHIP; break;
case 3: w_type = TYPE_SLASH; break;
case 4: case 5: case 6: w_type = TYPE_CRUSH; break;
case 7: w_type = TYPE_BLUDGEON; break;
case 8: case 9: case 10: case 11: w_type = TYPE_PIERCE; break;
}
}
if ( type == SKILL_BACKSTAB )
w_type = SKILL_BACKSTAB;
/*
* Calculate to-hit-armor-class-0 versus armor class.
* thaco for monsters is set in hitroll.
*/
if ( !IS_NPC(ch) ) {
calc_thaco = thaco[(int) CLASS_MAGE-1][(int) GET_MM(ch)];
calc_thaco += thaco[(int) CLASS_CLERIC-1][(int) GET_CC(ch)];
calc_thaco += thaco[(int) CLASS_THIEF-1][(int) GET_TT(ch)];
calc_thaco += thaco[(int) CLASS_WARRIOR-1][(int) GET_WW(ch)];
calc_thaco -= 80;
calc_thaco /= 2;
calc_thaco += 20;
calc_thaco = MAX(0,MIN(20,calc_thaco));
}
else
calc_thaco = 20;
calc_thaco -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;
calc_thaco -= GET_HITROLL(ch);
victim_ac = GET_AC(victim)/10;
if ( AWAKE(victim) )
victim_ac += dex_app[GET_DEX(victim)].defensive;
victim_ac = MAX( -10, victim_ac );
/*
* The moment of excitement!
*/
diceroll = number(1, 20);
if ( diceroll < 20 && AWAKE(victim)
&& (diceroll == 1 || diceroll < calc_thaco - victim_ac) )
{
/* Miss. */
damage( ch, victim, 0, w_type );
return;
}
/*
* Hit.
* Calc damage.
*/
if ( wielded )
dam = dice( wielded->obj_flags.value[1], wielded->obj_flags.value[2] );
else if ( IS_NPC(ch) )
dam = dice( ch->specials.damnodice, ch->specials.damsizedice );
else
dam = number( 0, 2 );
/*
* Bonuses.
*/
dam += str_app[STRENGTH_APPLY_INDEX(ch)].todam;
dam += GET_DAMROLL(ch);
/*
* Bonus for hitting weak opponents.
* Bonus for backstabbing.
*/
if ( GET_POS(victim) < POSITION_FIGHTING )
dam *= 1 + ( 2 * ( POSITION_FIGHTING - GET_POS(victim) ) ) / 3;
if ( type == SKILL_BACKSTAB )
{
dam *= backstab_mult[(int) GET_TT(ch)];
type = TYPE_UNDEFINED; /* Prevents multi backstabbing/combat */
/* -Kahn */
}
if ( dam < 1 )
dam = 1;
damage( ch, victim, dam, w_type );
return;
}
/*
* Inflict damage from a hit.
*/
void damage( struct char_data *ch, struct char_data *victim,
int dam, int attacktype )
{
struct message_type *messages;
int i, j, nr, max_hit;
int hit_limit(struct char_data *ch);
if ( GET_POS(victim) == POSITION_DEAD )
return;
/*
* Can't hurt god, but he likes to see the messages.
*/
if ( GET_LEVEL(victim) >= 32 && !IS_NPC(victim) )
dam = 0;
/*
* Certain attacks are forbidden.
*/
if ( victim != ch )
{
if ( is_in_safe( ch, victim ) )
return;
if ( is_first_level( ch, victim ) )
return;
check_killer( ch, victim );
}
/*
* An eye for an eye, a tooth for a tooth, a life for a life.
*/
if ( GET_POS(victim) > POSITION_STUNNED && ch != victim )
{
if ( !victim->specials.fighting )
set_fighting( victim, ch );
GET_POS(victim) = POSITION_FIGHTING;
}
if ( GET_POS(ch) > POSITION_STUNNED && ch != victim )
{
if ( !ch->specials.fighting )
set_fighting( ch, victim );
/*
* If victim is charmed, ch might attack victim's master.
*/
if ( IS_NPC(ch) && IS_NPC(victim) && IS_AFFECTED(victim, AFF_CHARM)
&& victim->master != NULL && victim->master->in_room == ch->in_room
&& number(0,9) == 0 )
{
if ( ch->specials.fighting )
stop_fighting(ch);
hit( ch, victim->master, TYPE_UNDEFINED );
return;
}
}
/*
* More charm stuff.
*/
if ( victim->master == ch )
stop_follower( victim );
if ( IS_AFFECTED( ch, AFF_CHARM ) )
stop_follower( ch );
/*
* Inviso attacks. Not.
*/
if ( IS_AFFECTED(ch, AFF_INVISIBLE) )
{
act( "$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM );
if ( affected_by_spell( ch, SPELL_INVISIBLE ) )
affect_from_char( ch, SPELL_INVISIBLE );
REMOVE_BIT( ch->specials.affected_by, AFF_INVISIBLE );
ch->specials.wizInvis = FALSE;
}
/*
* Damage modifiers.
*/
if ( IS_AFFECTED(victim, AFF_SANCTUARY) )
dam /= 2;
if ( IS_AFFECTED(victim, AFF_PROTECT_EVIL) && GET_ALIGNMENT(ch) < -350 )
dam /= 2;
if ( dam < 0 )
dam = 0;
/*
* Check for parry, mob disarm, and trip.
* Print a suitable damage message.
*/
if ( attacktype >= TYPE_HIT && attacktype < TYPE_SUFFERING )
{
if ( IS_NPC(ch) && number( 1, 100 ) < GET_LEVEL(ch) / 5 )
disarm( ch, victim );
if ( IS_NPC(ch) && number( 1, 100 ) < GET_LEVEL(ch) / 5 )
trip( ch, victim );
if ( check_parry( ch, victim ) )
return;
if ( check_dodge( ch, victim ) )
return;
if ( ch->equipment[WIELD] == NULL )
dam_message(dam, ch, victim, TYPE_HIT);
else
dam_message(dam, ch, victim, attacktype);
}
else
{
for ( i = 0; i < MAX_MESSAGES; i++ )
{
if ( fight_messages[i].a_type != attacktype )
continue;
nr = dice( 1, fight_messages[i].number_of_attacks );
j = 1;
for ( messages = fight_messages[i].msg; j < nr && messages; j++ )
messages = messages->next;
if ( !IS_NPC(victim) && GET_LEVEL(victim) > 31)
{
act( messages->god_msg.attacker_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
act( messages->god_msg.victim_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
act( messages->god_msg.room_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
}
else if ( dam == 0 )
{
act( messages->miss_msg.attacker_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
act( messages->miss_msg.victim_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
act( messages->miss_msg.room_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
}
else if ( GET_POS(victim) == POSITION_DEAD )
{
act( messages->die_msg.attacker_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
act( messages->die_msg.victim_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
act( messages->die_msg.room_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
}
else
{
act( messages->hit_msg.attacker_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR );
act( messages->hit_msg.victim_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_VICT );
act( messages->hit_msg.room_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT );
}
}
}
/*
* Hurt the victim.
* Reward the perpetrator.
* Life is hard.
*/
GET_HIT(victim) -= dam;
update_pos( victim );
if ( ch != victim )
gain_exp( ch, GET_LEVEL(victim) * dam );
/*
* Inform the victim of his new state.
* Use send_to_char, because act() doesn't send to DEAD people.
*/
switch( GET_POS(victim) )
{
case POSITION_MORTALLYW:
act( "$n is mortally wounded, and will die soon, if not aided.",
TRUE, victim, 0, 0, TO_ROOM );
send_to_char(
"You are mortally wounded, and will die soon, if not aided.\r\n",
victim );
break;
case POSITION_INCAP:
act( "$n is incapacitated and will slowly die, if not aided.",
TRUE, victim, 0, 0, TO_ROOM );
send_to_char(
"You are incapacitated and will slowly die, if not aided.\r\n",
victim );
break;
case POSITION_STUNNED:
act( "$n is stunned, but will probably recover.",
TRUE, victim, 0, 0, TO_ROOM);
send_to_char("You are stunned, but will probably recover.\r\n",
victim );
break;
case POSITION_DEAD:
act( "$n is DEAD!!", TRUE, victim, 0, 0, TO_ROOM );
send_to_char( "You have been KILLED!!\r\n\r\n", victim );
break;
default:
max_hit = hit_limit( victim );
if ( dam > max_hit / 5 )
send_to_char( "That really did HURT!\r\n", victim );
/*
* Wimp out?
*/
if ( GET_HIT(victim) < max_hit/5 )
{
send_to_char( "You wish you would stop BLEEDING so much!\r\n",
victim );
if ( IS_NPC(victim) )
{
if ( IS_SET(victim->specials.act, ACT_WIMPY) )
{
do_flee( victim, "", 0 );
return;
}
}
else
{
if ( IS_AFFECTED(victim, AFF_WIMPY) )
{
do_flee( victim, "", 0 );
return;
}
}
}
break;
}
/*
* Take care of link dead people.
*/
if ( !IS_NPC(victim) && victim->desc == NULL )
{
do_flee( victim, "", 0 );
if ( !victim->specials.fighting )
{
act( "$n is rescued by divine forces.",
FALSE, victim, 0, 0, TO_ROOM );
victim->specials.was_in_room = victim->in_room;
char_from_room(victim);
char_to_room(victim, 0);
}
return;
}
/*
* Sleep spells.
*/
if ( !AWAKE(victim) )
{
if ( victim->specials.fighting )
stop_fighting( victim );
}
/*
* Payoff for killing things.
*/
if ( GET_POS(victim) == POSITION_DEAD )
{
WAIT_STATE(ch,PULSE_VIOLENCE/6);
if ( IS_NPC(victim) || victim->desc != NULL )
group_gain( ch, victim );
if ( !IS_NPC(victim) )
{
sprintf( log_buf, "%s killed by %s at %d",
GET_NAME(victim),
(IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)),
world[victim->in_room].number );
log( log_buf );
GET_EXP(victim) -= exp_table[(int) GET_LEVEL(victim)] /2;
GET_EXP(victim) = MAX(0,GET_EXP(victim));
}
raw_kill( victim );
return;
}
return;
}
/*
* See if an attack justifies a KILLER flag.
*/
void check_killer( struct char_data *ch, struct char_data *victim )
{
/*
* No attack in safe room anyways.
*/
if (IS_SET( world[ch->in_room].room_flags, SAFE) )
return;
/*
* NPC's are fair game.
* So are killers and thieves.
*/
if ( IS_NPC(victim) )
return;
if ( IS_SET(victim->specials.act, AFF_KILLER) )
return;
if ( IS_SET(victim->specials.act, AFF_THIEF) )
return;
/*
* Charm-o-rama.
*/
if ( IS_SET(ch->specials.affected_by, AFF_CHARM) )
{
if ( ch->master == NULL )
{
sprintf( log_buf, "Check_killer: %s bad AFF_CHARM",
IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch) );
log( log_buf );
affect_from_char( ch, SPELL_CHARM_PERSON );
REMOVE_BIT( ch->specials.affected_by, AFF_CHARM );
return;
}
send_to_char( "*** You are now a KILLER!! ***\r\n", ch->master );
SET_BIT(ch->master->specials.act, AFF_KILLER);
stop_follower(ch);
return;
}
/*
* NPC's are cool of course (as long as not charmed).
* Hitting yourself is cool too (bleeding).
* And current killers stay as they are.
*/
if ( IS_NPC(ch) || ch == victim )
return;
if ( IS_SET(ch->specials.act, AFF_KILLER) )
return;
send_to_char( "*** You are now a KILLER!! ***\r\n", ch );
SET_BIT(ch->specials.act, AFF_KILLER);
return;
}
/*
* Check for parry.
*/
bool check_parry( struct char_data *ch, struct char_data *victim )
{
int percent;
int chance;
if ( victim->equipment[WIELD] == NULL )
return FALSE;
if ( ch->equipment[WIELD] == NULL && number ( 1, 101 ) >= 50 )
return FALSE;
if ( IS_NPC(victim) )
chance = MIN( 30, GET_LEVEL(ch) );
else
chance = victim->skills[SKILL_PARRY].learned/5 + GET_STR(ch)-15;
percent = number(1, 101) - 2*(GET_LEVEL(victim) - GET_LEVEL(ch));
if ( percent >= chance )
return FALSE;
act( "$n parries $N's attack.", FALSE, victim, NULL, ch, TO_NOTVICT );
act( "$n parries your attack.", FALSE, victim, NULL, ch, TO_VICT );
act( "You parry $N's attack.", FALSE, victim, NULL, ch, TO_CHAR );
return TRUE;
}
/*
* Check for dodge.
*/
bool check_dodge( struct char_data *ch, struct char_data *victim )
{
int percent;
int chance;
if ( IS_NPC(victim) )
chance = MIN( 30, GET_LEVEL(ch) );
else
chance = victim->skills[SKILL_DODGE].learned / 5+GET_DEX(victim)-15;
percent = number(1, 101) - (GET_LEVEL(victim) - GET_LEVEL(ch));
if ( percent >= chance )
return FALSE;
act( "$n dodges $N's attack.", FALSE, victim, NULL, ch, TO_NOTVICT );
act( "$n dodges your attack.", FALSE, victim, NULL, ch, TO_VICT );
act( "You dodge $N's attack.", FALSE, victim, NULL, ch, TO_CHAR );
return TRUE;
}
/*
* Load fighting messages into memory.
*/
void load_messages(void)
{
FILE *f1;
int i,type;
struct message_type *messages;
char chk[100];
if (!(f1 = fopen(MESS_FILE, "r"))){
perror("read messages");
exit(0);
}
for (i = 0; i < MAX_MESSAGES; i++)
{
fight_messages[i].a_type = 0;
fight_messages[i].number_of_attacks=0;
fight_messages[i].msg = 0;
}
fscanf(f1, " %s \n", chk);
while(*chk == 'M')
{
fscanf(f1," %d\n", &type);
for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type!=type) &&
(fight_messages[i].a_type); i++);
if(i>=MAX_MESSAGES){
log("Too many combat messages.");
exit(0);
}
CREATE(messages,struct message_type,1);
fight_messages[i].number_of_attacks++;
fight_messages[i].a_type=type;
messages->next=fight_messages[i].msg;
fight_messages[i].msg=messages;
messages->die_msg.attacker_msg = fread_string(f1);
messages->die_msg.victim_msg = fread_string(f1);
messages->die_msg.room_msg = fread_string(f1);
messages->miss_msg.attacker_msg = fread_string(f1);
messages->miss_msg.victim_msg = fread_string(f1);
messages->miss_msg.room_msg = fread_string(f1);
messages->hit_msg.attacker_msg = fread_string(f1);
messages->hit_msg.victim_msg = fread_string(f1);
messages->hit_msg.room_msg = fread_string(f1);
messages->god_msg.attacker_msg = fread_string(f1);
messages->god_msg.victim_msg = fread_string(f1);
messages->god_msg.room_msg = fread_string(f1);
fscanf(f1, " %s \n", chk);
}
fclose(f1);
}
/*
* Set position of a victim.
*/
void update_pos( struct char_data *victim )
{
if ( GET_HIT(victim) > 0 )
{
if ( GET_POS(victim) <= POSITION_STUNNED )
GET_POS(victim) = POSITION_STANDING;
return;
}
if ( IS_NPC(victim) || GET_HIT(victim) <= -11 )
{
GET_POS(victim) = POSITION_DEAD;
return;
}
if ( GET_HIT(victim) <= -6 ) GET_POS(victim) = POSITION_MORTALLYW;
else if ( GET_HIT(victim) <= -3 ) GET_POS(victim) = POSITION_INCAP;
else GET_POS(victim) = POSITION_STUNNED;
return;
}
/*
* Start fights.
*/
void set_fighting(struct char_data *ch, struct char_data *vict)
{
assert(!ch->specials.fighting);
ch->next_fighting = combat_list;
combat_list = ch;
if(IS_AFFECTED(ch,AFF_SLEEP))
affect_from_char(ch,SPELL_SLEEP);
ch->specials.fighting = vict;
GET_POS(ch) = POSITION_FIGHTING;
return;
}
/*
* Stop fights.
*/
void stop_fighting(struct char_data *ch)
{
struct char_data *tmp;
assert(ch->specials.fighting);
if (ch == combat_next_dude)
combat_next_dude = ch->next_fighting;
if (combat_list == ch)
combat_list = ch->next_fighting;
else
{
for (tmp = combat_list; tmp && (tmp->next_fighting != ch);
tmp = tmp->next_fighting)
;
if (!tmp)
{
log( "Stop_fighting: char not found" );
abort();
}
tmp->next_fighting = ch->next_fighting;
}
ch->next_fighting = 0;
ch->specials.fighting = 0;
GET_POS(ch) = POSITION_STANDING;
update_pos(ch);
return;
}
#define MAX_NPC_CORPSE_TIME 3
#define MAX_PC_CORPSE_TIME 120
void make_corpse(struct char_data *ch)
{
struct obj_data *corpse, *o;
struct obj_data *money;
char buf[MAX_STRING_LENGTH];
int i;
CREATE(corpse, struct obj_data, 1);
clear_object(corpse);
corpse->item_number = NOWHERE;
corpse->in_room = NOWHERE;
corpse->name = str_dup("corpse");
sprintf( buf, "Corpse of %s is lying here.",
(IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)));
corpse->description = str_dup(buf);
sprintf( buf, "Corpse of %s",
(IS_NPC(ch) ? ch->player.short_descr : GET_NAME(ch)));
corpse->short_description = str_dup(buf);
corpse->contains = ch->carrying;
if ( IS_NPC(ch) && GET_GOLD(ch) > 0 )
{
money = create_money(GET_GOLD(ch));
GET_GOLD(ch)=0;
obj_to_obj(money, corpse);
}
corpse->obj_flags.type_flag = ITEM_CONTAINER;
corpse->obj_flags.wear_flags = ITEM_TAKE;
corpse->obj_flags.value[0] = 0; /* You can't store stuff in a corpse */
corpse->obj_flags.value[3] = 1; /* corpse identifyer */
corpse->obj_flags.weight = GET_WEIGHT(ch)+IS_CARRYING_W(ch);
corpse->obj_flags.eq_level = 0;
if (IS_NPC(ch))
{
corpse->obj_flags.cost_per_day = 100000;
corpse->obj_flags.timer = MAX_NPC_CORPSE_TIME;
}
else
{
corpse->obj_flags.cost_per_day = 200000;
corpse->obj_flags.timer = MAX_PC_CORPSE_TIME;
}
for ( i = 0; i < MAX_WEAR; i++ )
{
if ( ch->equipment[i] )
obj_to_obj( unequip_char(ch, i), corpse );
}
ch->carrying = 0;
IS_CARRYING_N(ch) = 0;
IS_CARRYING_W(ch) = 0;
corpse->next = object_list;
object_list = corpse;
for ( o = corpse->contains; o; o->in_obj = corpse, o = o->next_content )
;
object_list_new_owner( corpse, 0 );
obj_to_room( corpse, ch->in_room );
return;
}
/* When ch kills victim */
void change_alignment(struct char_data *ch, struct char_data *victim)
{
int align;
align = GET_ALIGNMENT(ch) - GET_ALIGNMENT(victim);
if ( align > 650 )
GET_ALIGNMENT(ch) += (align - 650) / 4;
else if ( align < -650 )
GET_ALIGNMENT(ch) += (align + 650) / 4;
else
GET_ALIGNMENT(ch) /= 2;
GET_ALIGNMENT(ch) = MIN( 1000, MAX( -1000, GET_ALIGNMENT(ch) ) );
}
void death_cry(struct char_data *ch)
{
int door, was_in;
char *message;
act( "Your blood freezes as you hear $n's death cry.",
FALSE, ch, 0, 0, TO_ROOM );
if ( IS_NPC(ch) )
message = "You hear something's death cry.";
else
message = "You hear someone's death cry.";
was_in = ch->in_room;
for ( door = 0; door <= 5; door++ )
{
if (CAN_GO(ch, door))
{
ch->in_room = world[was_in].dir_option[door]->to_room;
if ( ch->in_room == was_in )
continue;
act( message, FALSE, ch, 0, 0, TO_ROOM );
ch->in_room = was_in;
}
}
}
void raw_kill( struct char_data *ch )
{
if ( ch->specials.fighting )
stop_fighting( ch );
death_cry( ch );
make_corpse( ch );
if ( IS_NPC(ch) )
{
extract_char( ch, TRUE );
return;
}
if (IS_SET(ch->specials.affected_by,AFF_KILLER)) {
GET_TT(ch)=MAX(0,GET_TT(ch)-1);
GET_WW(ch)=MAX(0,GET_WW(ch)-1);
GET_MM(ch)=MAX(0,GET_MM(ch)-1);
GET_CC(ch)=MAX(0,GET_CC(ch)-1);
GET_CL(ch)[(int)GET_CLASS(ch)]=MAX(1,GET_CL(ch)[(int)GET_CLASS(ch)]);
GET_LEVEL(ch)=MAX(GET_CC(ch),MAX(GET_MM(ch),MAX(GET_TT(ch),GET_WW(ch))));
send_to_char("The gods punish you for having been a killer!\r\n",ch);
}
extract_char( ch, FALSE );
ch->specials.affected_by = 0;
GET_POS(ch) = POSITION_RESTING;
while ( ch->affected )
affect_remove( ch, ch->affected );
if ( GET_HIT(ch) <= 0 )
GET_HIT(ch) = 1;
if ( GET_MOVE(ch) <= 0 )
GET_MOVE(ch) = 1;
if ( GET_MANA(ch) <= 0 )
GET_MANA(ch) = 1;
save_char_obj( ch );
}
void group_gain( struct char_data *ch, struct char_data *victim )
{
char buf[256];
int no_members, share, check;
int totallevels;
struct char_data *k;
struct follow_type *f;
/*
* Monsters don't get kill xp's.
* Dying of mortal wounds doesn't give xp to anyone!
*/
if ( IS_NPC(ch) || ch == victim )
return;
if ( ( k = ch->master ) == NULL )
k = ch;
no_members = 0;
totallevels = 0;
if ( IS_AFFECTED(k, AFF_GROUP) && k->in_room == ch->in_room )
{
no_members += 1;
totallevels += GET_LEVEL(k);
}
for ( f = k->followers; f; f = f->next )
{
if ( IS_AFFECTED(k, AFF_GROUP) && f->follower->in_room == ch->in_room )
{
no_members += 1;
totallevels += GET_LEVEL(f->follower);
}
}
if ( no_members == 0 )
{
no_members = 1;
totallevels = GET_LEVEL(ch);
}
share = GET_EXP(victim);
share += share * (no_members - 3) / 6;
check = share+1;
if (!IS_NPC(victim)) share /= 15;
/*
* Kludgy loop to get k in at end.
*/
for ( f = k->followers; ; f = f->next )
{
struct char_data * tmp_ch;
int tmp_share;
tmp_ch = (f == NULL) ? k : f->follower;
if ( tmp_ch->in_room != ch->in_room )
goto LContinue;
if ( !IS_AFFECTED(tmp_ch, AFF_GROUP) && tmp_ch != ch )
goto LContinue;
if ( GET_LEVEL(tmp_ch) - GET_LEVEL(k) >= 7 )
{
act( "You are too high for this group. You gain no experience.",
FALSE, tmp_ch, 0, 0, TO_CHAR );
goto LContinue;
}
if ( GET_LEVEL(tmp_ch) - GET_LEVEL(k) <= -7 )
{
act( "You are too low for this group. You gain no experience.",
FALSE, tmp_ch, 0, 0, TO_CHAR );
goto LContinue;
}
tmp_share = MIN( 250000, GET_LEVEL(tmp_ch) * share / totallevels );
if((check-=tmp_share)<0) {
sprintf(buf, "Possible exp violation: %s leader: %s [%d/%d=%d]\r\n",
GET_NAME(tmp_ch),GET_NAME(k),tmp_share,share,check);
log(buf);
goto LContinue;
}
sprintf( buf, "You receive %d exps of %d total.\r\n",
tmp_share, share );
send_to_char( buf, tmp_ch );
gain_exp( tmp_ch, tmp_share );
change_alignment( tmp_ch, victim );
LContinue:
if ( f == NULL )
break;
}
}
void dam_message( int dam, struct char_data *ch, struct char_data *victim,
int w_type )
{
static char *attack_table[] =
{
"hit", "pound", "pierce", "slash", "whip", "claw",
"bite", "sting", "crush"
};
char buf1[256], buf2[256], buf3[256];
char *vs, *vp;
char *attack;
char punct;
if ( dam == 0 ) { vs = "miss"; vp = "misses"; }
else if ( dam <= 4 ) { vs = "hit"; vp = "hits"; }
else if ( dam <= 6 ) { vs = "injure"; vp = "injures"; }
else if ( dam <= 8 ) { vs = "wound"; vp = "wounds"; }
else if ( dam <= 11 ) { vs = "decimate"; vp = "decimates"; }
else if ( dam <= 14 ) { vs = "devastate"; vp = "devastates"; }
else if ( dam <= 17 ) { vs = "maim"; vp = "maims"; }
else if ( dam <= 21 ) { vs = "MUTILATE"; vp = "MUTILATES"; }
else if ( dam <= 25 ) { vs = "DISMEMBER"; vp = "DISMEMBERS"; }
else if ( dam <= 29 ) { vs = "DISEMBOWEL"; vp = "DISEMBOWELS"; }
else if ( dam <= 33 ) { vs = "MASSACRE"; vp = "MASSACRES"; }
else { vs = "*** DEMOLISH ***";
vp = "*** DEMOLISHES ***"; }
w_type -= TYPE_HIT;
if ( w_type >= sizeof(attack_table)/sizeof(attack_table[0]) )
{
log( "Dam_message: bad w_type" );
w_type = 0;
}
punct = (dam <= 8) ? '.' : '!';
if ( w_type == 0 )
{
sprintf( buf1, "$n %s $N%c", vp, punct );
sprintf( buf2, "You %s $N%c", vs, punct );
sprintf( buf3, "$n %s you%c", vp, punct );
}
else
{
attack = attack_table[w_type];
sprintf( buf1, "$n's %s %s $N%c", attack, vp, punct );
sprintf( buf2, "Your %s %s $N%c", attack, vp, punct );
sprintf( buf3, "$n's %s %s you%c", attack, vp, punct );
}
act( buf1, FALSE, ch, NULL, victim, TO_NOTVICT );
act( buf2, FALSE, ch, NULL, victim, TO_CHAR );
act( buf3, FALSE, ch, NULL, victim, TO_VICT );
}
/*
* Disarm a creature.
* Caller must check for successful attack.
*/
void disarm( struct char_data *ch, struct char_data *victim )
{
struct obj_data *obj;
if ( victim->equipment[WIELD] == NULL )
return;
if ( ch->equipment[WIELD] == NULL && number ( 1, 101 ) >= 5 )
return;
act( "$n disarms you and sends your weapon flying!",
FALSE, ch, NULL, victim, TO_VICT );
act( "You disarm $N and send $S weapon flying!",
FALSE, ch, NULL, victim, TO_CHAR );
act( "$n disarms $N and sends $S weapon flying!",
FALSE, ch, NULL, victim, TO_NOTVICT );
obj = unequip_char( victim, WIELD );
obj_to_char( obj, victim );
return;
}
/*
* Trip a creature.
* Caller must check for successful attack.
*/
void trip( struct char_data *ch, struct char_data *victim )
{
act( "$n trips you and you go down!",
FALSE, ch, NULL, victim, TO_VICT );
act( "You trip $N and $N goes down!",
FALSE, ch, NULL, victim, TO_CHAR );
act( "$n trips $N and $N goes down!",
FALSE, ch, NULL, victim, TO_NOTVICT );
damage( ch, victim, 1, SKILL_TRIP );
WAIT_STATE( ch, PULSE_VIOLENCE*2 );
WAIT_STATE( victim, PULSE_VIOLENCE*3 );
GET_POS(victim) = POSITION_SITTING;
return;
}