/***************************************************************************
* Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming *
* License by Wizards of the Coast. All comments referring to D20, OGL, *
* and SRD refer to the System Reference Document for the Open Gaming *
* system. Any inclusion of these derivatives must include credit to the *
* Mud20 system, the full and complete Open Gaming LIcense, and credit to *
* the respective authors. See ../doc/srd.txt for more information. *
* *
* Emud 2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem. *
* *
* MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey *
* *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe. *
***************************************************************************/
/***************************************************************************
* Originally fight.c in merc *
***************************************************************************/
#include "mud.h"
#define DO_ABILITY(ability) bool ability( int sn, int level, CHAR_DATA *ch, void *vo, int target )
/*
Global functions.
*/
sh_int martial_arts_attack;
sh_int body_part_attack;
sh_int brawling_attack;
bool remove_obj args( ( CHAR_DATA *ch, int iWear, bool fReplace, bool fDisplay ) );
/*
* Kludgy globals for passing the dam_type and critical hit along
*/
int DAM_TYPE;
bool CRIT_HIT = FALSE;
bool confused = FALSE;
/*
Local functions.
*/
void spam_attack_list args( ( CHAR_DATA * ) );
void add_to_victory_list args( ( CHAR_DATA *, CHAR_DATA *) );
void arena_death args( ( CHAR_DATA *victim ) );
void newbie_death args( ( CHAR_DATA *victim ) );
void player_death args( ( CHAR_DATA *victim, int dt ) );
void dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, OBJ_DATA *wield));
char *get_dam_nounce args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt, OBJ_DATA *wield));
bool has_mirror args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt));
int xp_compute args( ( CHAR_DATA *gch,CHAR_DATA *victim ) );
void group_gain args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
bool whirl args( ( CHAR_DATA *ch ) );
/*
* Unarmed attack tables - random
* damage nouns for each attack.
*/
const struct attack_type martial_arts_table[] =
{
{
"combination punch", DAM_BASH
},
{
"palm punch", DAM_BASH
},
{
"thrust kick", DAM_BASH
},
{
"spinning back-hand", DAM_BASH
},
{
"jump kick", DAM_BASH
},
{
"round house", DAM_BASH
},
{
"flip kick", DAM_BASH
},
{
"thrusting spear hand", DAM_PIERCE
},
{
"spinning reverse kick", DAM_BASH
},
{
"spinning hook kick", DAM_BASH
},
{
"flying scissors kick", DAM_BASH
},
{
"triple round house kick",DAM_BASH
}
};
const struct attack_type brawling_table[] =
{
{
"combination punch", DAM_BASH
},
{
"uppercut", DAM_BASH
},
{
"kidney punch", DAM_BASH
},
{
"right hook", DAM_BASH
},
{
"left hook", DAM_BASH
},
{
"sucker punch", DAM_BASH
},
{
"knee", DAM_BASH
},
{
"head butt", DAM_BASH
},
{
"elbow slam", DAM_BASH
}
};
/*
* Fixed damage dice based on size and body part,
* taken from Pathfinder RPG's Universal Monster
* Rules for all creature attacks - Kregor
*/
const struct attack_part_type dam_no_dice [ATTK_MAX] =
{
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 4} }, /* BITE */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* CLAW */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* GORE */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* HOOF */
{ {0, 1, 1, 1, 1, 1, 1, 1, 1, 2} }, /* KICK */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 4} }, /* PINCER */
{ {0, 1, 1, 1, 1, 1, 1, 1, 1, 2} }, /* PUNCH */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* RAKE */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* SLAM */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* STING */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 4} }, /* TAIL */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* TALON */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* TENTACLE */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* WING */
{ {0, 1, 1, 1, 1, 1, 1, 1, 2, 2} }, /* OTHER */
{ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1} } /* TOUCH */
};
const struct attack_part_type dam_size_dice [ATTK_MAX] =
{
{ {0, 1, 2, 3, 4, 6, 8, 12, 8, 6} }, /* BITE */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* CLAW */
{ {0, 1, 2, 3, 3, 4, 6, 8, 6, 8} }, /* GORE */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* HOOF */
{ {0, 1, 1, 1, 2, 3, 4, 6, 8, 6} }, /* KICK */
{ {0, 1, 2, 3, 4, 6, 8, 12, 8, 6} }, /* PINCER */
{ {0, 1, 1, 1, 2, 3, 4, 6, 8, 6} }, /* PUNCH */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* RAKE */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* SLAM */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* STING */
{ {0, 1, 2, 3, 4, 6, 8, 12, 8, 6} }, /* TAIL */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* TALON */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* TENTACLE */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* WING */
{ {0, 1, 1, 2, 3, 4, 6, 8, 6, 8} }, /* OTHER */
{ {0, 1, 1, 1, 1, 1, 1, 1, 1, 1} } /* TOUCH */
};
const struct body_type attack_part_table [ATTK_MAX] =
{
{ "bite", DAM_PIERCE|DAM_BASH|DAM_SLASH, TRUE, TRUE },
{ "claw", DAM_BASH|DAM_SLASH, TRUE, TRUE },
{ "gore", DAM_PIERCE, TRUE, TRUE },
{ "kick", DAM_BASH, FALSE, TRUE },
{ "kick", DAM_BASH, FALSE, FALSE },
{ "pincer", DAM_PIERCE, FALSE, TRUE },
{ "punch", DAM_BASH, TRUE, FALSE },
{ "rake", DAM_SLASH, FALSE, TRUE },
{ "slam attack", DAM_BASH, TRUE, TRUE },
{ "sting", DAM_PIERCE, TRUE, TRUE },
{ "tail slap", DAM_BASH, FALSE, TRUE },
{ "talon", DAM_SLASH, TRUE, TRUE },
{ "tentacle", DAM_BASH|DAM_SLASH, FALSE, TRUE },
{ "wing buffet", DAM_BASH, FALSE, TRUE },
{ "attack", DAM_BASH, FALSE, TRUE },
{ "touch", DAM_NONE, TRUE, TRUE }
};
/*
* Returns TRUE if the skill number
* deals a physical attack - Kregor
*/
bool is_attack( int sn )
{
push_call("is_attack(%p)",sn);
if (sn >= TYPE_HIT)
{
pop_call();
return TRUE;
}
if (sn == TYPE_NOFIGHT)
{
pop_call();
return FALSE;
}
if (sn == gsn_whirl
|| sn == gsn_opportunist
|| sn == gsn_sunder
|| sn == gsn_coup_de_grace
|| sn == gsn_stunning_fist
|| sn == gsn_backstab
|| sn == gsn_assassinate
|| sn == gsn_quivering_palm
|| sn == gsn_martial_arts
|| sn == gsn_shield_bash)
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* Rolled all the checks for making a sneak attack
* into this one function here - Kregor
*/
bool can_backstab( CHAR_DATA *ch, CHAR_DATA *victim, int dt )
{
push_call("can_backstab(%p,%p)",ch,victim);
if (!in_same_room(ch, victim))
{
pop_call();
return FALSE;
}
if (is_spell(dt))
{
if (!IS_SET(skill_table[dt].flags, SF_RANGED_TOUCH) && !IS_SET(skill_table[dt].flags, SF_TOUCH))
{
pop_call();
return FALSE;
}
if (!learned(ch, gsn_sneaky_spell))
{
pop_call();
return FALSE;
}
}
if (!learned(ch, gsn_backstab))
{
// sleeping tiger ability gained at lvl 12
if (get_monk_style(ch) != STYLE_SLEEPING_TIGER || class_level(ch, CLASS_MONK) < 12)
{
pop_call();
return FALSE;
}
}
if (IS_AFFECTED(ch, AFF2_BERSERK)) // no precision dmg while raging
{
pop_call();
return FALSE;
}
if (!CAN_CRITICAL(victim))
{
if (!IS_UNDEAD(victim) || !is_affected(ch, gsn_channeling_attack))
{
pop_call();
return FALSE;
}
}
if (is_affected(victim, gsn_defensive_stance) && learned(victim, gsn_impenetrable_defense))
{
pop_call();
return FALSE;
}
if (!can_see(ch, victim))
{
pop_call();
return FALSE;
}
if (arcane_mastery(victim, SCHOOL_DIVINATION))
{
pop_call();
return FALSE;
}
if (!is_affected(victim, gsn_foresight))
{
pop_call();
return FALSE;
}
// if victim is too small, or too large, you can't hit critical spots
if (get_size(victim) < get_size(ch) - 1 || get_size(victim) > get_size(ch) + 1)
{
pop_call();
return FALSE;
}
if (is_mounting(ch) || is_mounted(ch))
{
pop_call();
return FALSE;
}
if (dt == gsn_backstab)
{
pop_call();
return TRUE;
}
if (!is_affected(ch, gsn_impromptu_sneak_attack))
{
if (dt != gsn_opportunist && can_dodge(victim, ch) && !is_flanking(ch, victim))
{
pop_call();
return FALSE;
}
}
pop_call();
return TRUE;
}
/*
* checks for conditions that would prevent
* ch from physically attacking victim - Kregor 2/20/11
* should be called in any function that can be
* considered a melee attack!
*/
bool can_melee_attack( CHAR_DATA *ch, CHAR_DATA *victim )
{
OBJ_DATA *wield;
push_call("can_melee_attack($p,$p,)",ch,victim);
// incorporeals cannot hit someone unless wielding a ghost touch weapon - Kregor
if (IS_INCORPOREAL(ch) && !IS_INCORPOREAL(victim))
{
// special incorp touch attack is allowed
if (ch->attack_part != ATTK_TOUCH)
{
if ((wield = get_wield(ch, FALSE)) == NULL || !WEAPON_FLAG(wield, WFLAG_GHOST_TOUCH))
{
act("You cannot touch $N to attack $M.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
}
}
//prot from alignment hedges summoned creatures IF not attacked - Kregor
if (IS_ACT(ch, ACT_SUMMONED) || race_type(ch) == RTYPE_OUTSIDER)
{
if ((IS_EVIL(ch) && get_apply(victim, APPLY_RES_EVIL))
|| (IS_GOOD(ch) && get_apply(victim, APPLY_RES_GOOD))
|| (IS_LAWFUL(ch) && get_apply(victim, APPLY_RES_LAW))
|| (IS_CHAOTIC(ch) && get_apply(victim, APPLY_RES_CHAOS)))
{
if (!victim->last_attacked || victim->last_attacked != ch)
{
act("$N's wards hedge you away!", ch, NULL, victim, TO_CHAR);
act("Your wards hedge $n away!", ch, NULL, victim, TO_VICT);
act( "$n recoils away from $N.", ch, NULL, victim, TO_NOTVICT );
pop_call();
return FALSE;
}
}
}
if (is_affected(victim, gsn_repulsion))
{
if (!ch->last_attacker || ch->last_attacker != victim)
{
if (!save_resist(ch, victim, gsn_repulsion, get_affect_level(victim, gsn_repulsion)))
{
pop_call();
return FALSE;
}
}
}
if (is_affected(victim, gsn_antilife_shell))
{
switch (race_type(ch))
{
case RTYPE_CONSTRUCT:
case RTYPE_OUTSIDER:
case RTYPE_UNDEAD:
break;
default:
if (!ch->last_attacker || ch->last_attacker != victim)
{
if (!save_resist(ch, victim, gsn_antilife_shell, get_affect_level(victim, gsn_antilife_shell)))
{
pop_call();
return FALSE;
}
}
break;
}
}
if (is_affected(victim, gsn_repel_metal))
{
if (wears_metal(ch, TRUE))
{
pop_call();
return FALSE;
}
}
pop_call();
return TRUE;
}
/*
* Actively reload a missile weapon to prepare
* it to fire without reloading delay.
*/
void do_reload(CHAR_DATA *ch, char *argument)
{
OBJ_DATA *wield;
push_call("do_reload(%p,%p)",ch,argument);
if ((wield = get_wield(ch, FALSE)) == NULL)
{
send_to_char_color("You are holding no weapon to reload.\n\r", ch);
pop_call();
return;
}
if (!is_missile_weapon(wield))
{
send_to_char_color("You aren't wielding a missile weapon.\n\r", ch);
pop_call();
return;
}
if (get_ammo(ch, wield) == NULL)
{
send_to_char_color("{138}Oops...forgot to stock up on ammunition, eh?{300}\n\r", ch);
ch->reloaded = FALSE;
pop_call();
return;
}
if (ch->reloaded)
{
send_to_char_color("Your weapon is already loaded.\n\r", ch);
pop_call();
return;
}
reload(ch, wield);
pop_call();
return;
}
/*
* Checks to see if a missile weapon is loaded
* if not, search for ammunition, if ammo found,
* set action time to reload. Returns FALSE if
* cannot fire immediately for whatever reason - Kregor
*/
bool reload( CHAR_DATA *ch, OBJ_DATA *wield )
{
OBJ_DATA *ammo = NULL;
push_call("reload(%p,%p)",ch,wield);
if (wield == NULL || !is_missile_weapon(wield))
{
ch->reloaded = FALSE;
pop_call();
return FALSE;
}
if ((ammo = get_ammo(ch, wield)) == NULL)
{
send_to_char_color("{138}Oops...forgot to stock up on ammunition, eh?{300}\n\r", ch);
ch->reloaded = FALSE;
pop_call();
return FALSE;
}
if (hands_full(ch))
{
if (!WEAPON_TYPE(wield, WEAPON_TYPE_SLING))
{
act("You cannot reload $p without a free hand.", ch, wield, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (ch->reloaded)
{
if (!WEAPON_TYPE(wield, WEAPON_TYPE_CROSSBOW_HAND)
&& !WEAPON_TYPE(wield, WEAPON_TYPE_SLING))
{
act("You cannot shoot $p without a free hand.", ch, wield, NULL, TO_CHAR);
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
}
if (ch->reloaded)
{
pop_call();
return TRUE;
}
switch(wield->value[0])
{
default:
if (!learned(ch, gsn_rapid_reload))
{
act( "You pause to reload $p.", ch, wield, NULL, TO_CHAR);
act( "$n pauses to reload $p.", ch, wield, NULL, TO_ROOM);
if (IS_SET(ch->action, ACTION_MOVE))
{
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return FALSE;
}
else
{
TAKE_ACTION(ch, ACTION_MOVE);
}
}
break;
case WEAPON_TYPE_CROSSBOW_HEAVY:
if (!learned(ch, gsn_rapid_reload))
{
act( "You pause to reload $p.", ch, wield, NULL, TO_CHAR);
act( "$n pauses to reload $p.", ch, wield, NULL, TO_ROOM);
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return FALSE;
}
else
{
act( "You pause to reload $p.", ch, wield, NULL, TO_CHAR);
act( "$n pauses to reload $p.", ch, wield, NULL, TO_ROOM);
if (IS_SET(ch->action, ACTION_MOVE))
{
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return FALSE;
}
else
{
TAKE_ACTION(ch, ACTION_MOVE);
}
}
break;
case WEAPON_TYPE_SHORTBOW:
case WEAPON_TYPE_SHORTBOW_COMPOSITE:
case WEAPON_TYPE_LONGBOW:
case WEAPON_TYPE_LONGBOW_COMPOSITE:
break;
}
ch->reloaded = TRUE;
pop_call();
return TRUE;
}
/*
* new function for manyshot, makes it work right
* in do_attack or do_shoot. Hacked one_hit and took
* out everything except finding ammo and dealing
* base damage, cuz crits and backstab don't count - Kregor
*/
bool manyshot_hit(CHAR_DATA *ch, CHAR_DATA *victim, int dt, int hit_adj, OBJ_DATA *wield)
{
OBJ_DATA *ammo;
int dam;
push_call("manyshot_hit(%p,%p,%p)",ch,victim,dt);
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
if (wield == NULL || !is_missile_weapon(wield))
{
pop_call();
return FALSE;
}
if (!COMBATMODE(ch, COMBAT_MANYSHOT))
{
pop_call();
return FALSE;
}
switch(wield->value[0])
{
case WEAPON_TYPE_SHORTBOW:
case WEAPON_TYPE_SHORTBOW_COMPOSITE:
case WEAPON_TYPE_LONGBOW:
case WEAPON_TYPE_LONGBOW_COMPOSITE:
case WEAPON_TYPE_SLING:
break;
default:
send_to_char("You can only manyshot with a bow or sling.\n\r", ch);
REMOVE_BIT(ch->combatmode, COMBAT_MANYSHOT);
pop_call();
return FALSE;
}
if (hit_adj == -5 && base_attack(ch) <= 10)
{
pop_call();
return FALSE;
}
if (hit_adj == -10 && base_attack(ch) <= 15)
{
pop_call();
return FALSE;
}
if (hit_adj < -10)
{
pop_call();
return FALSE;
}
if ((ammo = get_ammo(ch, wield)) == NULL)
{
pop_call();
return FALSE;
}
/*
Calc damage and check for specials
*/
dam = GET_DAMROLL(ch, victim, dt, wield);
if (!deflect_missile(victim, ch, ammo, -1))
{
damage(ch, victim, dam, dt, wield);
/* weapon spells on missile ammo here */
if (weapon_cast_spell(ammo->value[2], ammo->value[3], ch, victim, ammo))
ammo->value[2] = ammo->value[3] = 0;
}
junk_obj(ammo);
pop_call();
return TRUE;
}
/*
* Return throw of snatched thrown weapon using Snatch Missiles feat.
*/
void throwback( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *weapon, int dir )
{
int range, diceroll, dam, mult;
push_call("throwback(%p,%p)",ch,victim);
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (weapon->carried_by != ch)
{
pop_call();
return;
}
if (!weapon || !IS_WEAPON(weapon))
{
pop_call();
return;
}
if (dir != -1)
{
dir = rev_dir[dir];
if (!can_see_dir(ch, victim, dir))
{
send_to_char("Your attacker must have slipped out of sight.\n\r", ch);
pop_call();
return;
}
if ((range = get_missile_range(weapon)) <= 0 || findpath_search_victim(ch, victim, range) == -1)
{
send_to_char("Your target is out of range.\n\r", ch);
pop_call();
return;
}
}
CHECK_TURN(ch, victim);
int dt = TYPE_HIT;
int hit_mod = 0;
diceroll = dice(1,20);
CRIT_HIT = FALSE;
if (diceroll == 1 && !WEAPON_FLAG(weapon, WFLAG_SEEKING))
ch_printf_color(ch, "{118}You score a critical miss!\n\r");
if (number_percent() > get_apply(victim, APPLY_FORTIFICATION))
{
int threat = weapon_table[weapon->value[0]].threat;
if (WEAPON_FLAG(weapon, WFLAG_KEEN))
{
threat = (20 - threat) * 2;
threat = 20 - threat;
}
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL))
{
threat += 1;
}
if (diceroll == 20 || diceroll >= threat)
{
if (check_hit(ch, victim, dice(1,20), hit_mod, dt, weapon, FALSE, TRUE)
|| (fave_enemy_bonus(ch, victim) && learned(ch, gsn_master_hunter))
|| (can_backstab(ch, victim, dt) && learned(ch, gsn_stealth_mastery))
|| (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && (learned(ch, gsn_holy_champion) || learned(ch, gsn_unholy_champion))))
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical hit!\n\r");
}
}
}
if (!WEAPON_FLAG(weapon, WFLAG_SEEKING) && !CRIT_HIT && !check_hit(ch, victim, diceroll, hit_mod, dt, weapon, FALSE, TRUE))
{
dam_message(ch, victim, 0, dt, weapon);
if (!WEAPON_FLAG(weapon, WFLAG_RETURNING) && (!WSPEC(weapon, WSPEC_RETURNING) || !weapon_skill(ch, weapon)))
{
weapon->sac_timer = OBJ_SAC_TIME;
obj_to_room(weapon, victim->in_room->vnum);
}
else
{
act( "$p returns to $n's hand.", ch, weapon, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, weapon, NULL, TO_CHAR );
}
}
else
{
dam = GET_DAMROLL(ch, victim, dt, weapon);
mult = weapon_table[weapon->value[0]].critical;
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL) && learned(ch, gsn_weapon_grandmastery))
mult++;
if (CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
dam *= mult;
if (!deflect_missile(victim, ch, weapon, dir))
{
damage(ch, victim, dam, dt, weapon);
if (weapon_cast_spell(weapon->value[2], weapon->value[3], ch, victim, weapon))
weapon->value[2] = weapon->value[3] = 0;
if (!WEAPON_FLAG(weapon, WFLAG_RETURNING) && (!WSPEC(weapon, WSPEC_RETURNING) || !weapon_skill(ch, weapon)))
obj_to_char(weapon, victim);
else
{
act( "$p returns to $n's hand.", ch, weapon, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, weapon, NULL, TO_CHAR );
}
}
if (dir != -1 && !IS_NPC(ch) && MP_VALID_MOB(victim))
{
mprog_range_trigger(victim, ch, rev_dir[dir]);
}
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
pop_call();
return;
}
/*
* Displays a list of all your current fight participants - Kregor
*/
void fight_list(CHAR_DATA * ch)
{
char buf[MAX_STRING_LENGTH];
CHAR_DATA *unit, *vch;
int count = 0;
char colg[20], colw[20], colW[20], colG[20], colR[20];
push_call("fight_list(%p)",ch);
if (!in_combat (ch))
{
send_to_char ("You aren't part of a battle.\n\r", ch);
pop_call();
return;
}
strcpy(colg, get_color_string(ch, COLOR_ACCENT, VT102_DIM));
strcpy(colw, get_color_string(ch, COLOR_TEXT, VT102_DIM));
strcpy(colW, get_color_string(ch, COLOR_TEXT, VT102_BOLD));
strcpy(colG, get_color_string(ch, COLOR_TACT_PARTY, VT102_BOLD));
strcpy(colR, get_color_string(ch, COLOR_TACT_ENEMY, VT102_BOLD));
for (buf[0] = '\0', unit = ch->in_battle->unit_list; unit != NULL; unit = unit->next_in_battle)
{
count++;
cat_sprintf(buf, "%s[%s%2d%s] ", colg, colW, count, colg);
vch = who_fighting(unit);
if (unit != ch)
{
if (is_same_group(unit, ch))
cat_sprintf(buf, "%s%s is attacking %s.\n\r", colG, PERS(unit, ch), vch == NULL ? "Noone" : vch == ch ? "YOU" : PERS(vch, ch));
else
cat_sprintf(buf, "%s%s is attacking %s.\n\r", colR, PERS(unit, ch), vch == NULL ? "Noone" : vch == ch ? "YOU" : PERS(vch, ch));
}
else
{
cat_sprintf(buf, "%sYou are attacking %s.\n\r", colw, vch == NULL ? "Noone" : PERS(vch, ch));
}
}
send_to_char_color(buf, ch);
pop_call();
return;
}
/*
* Begins a fight against a victim, or dsplays a list of
* those in an active fight - Kregor
*/
void do_fight (CHAR_DATA * ch, char * argument)
{
CHAR_DATA *victim;
push_call("do_fight(%p,%p)",ch,argument);
if (argument[0] != '\0')
{
if (!strcasecmp(argument, "list"))
{
if (!in_combat(ch))
{
send_to_char ("You're not in combat.\n\r", ch);
pop_call();
return;
}
fight_list(ch);
pop_call();
return;
}
else
{
if (in_combat(ch))
{
send_to_char ("You're already fighting.\n\r", ch);
pop_call();
return;
}
}
}
else
{
if (in_combat(ch))
{
fight_list(ch);
pop_call();
return;
}
send_to_char ("Fight whom?\n\r", ch);
pop_call();
return;
}
if ((victim = get_char_room (ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if (victim == ch)
{
send_to_char ("You won!\n\r", ch);
pop_call();
return;
}
fight(ch, victim);
pop_call();
return;
}
/*
* mobiles use this function, PCs use the do_fun above
* to trigger this function - Kregor
*/
void fight(CHAR_DATA * ch, CHAR_DATA * victim)
{
push_call("fight(%p,%p)",ch,victim);
if (ch == NULL || victim == NULL || victim->position == POS_DEAD)
{
pop_call();
return;
}
if (ch == victim)
{
pop_call();
return;
}
if (in_combat(ch))
{
pop_call();
return;
}
if (is_safe(ch,victim))
{
pop_call();
return;
}
if (!check_murder(ch, victim))
{
pop_call();
return;
}
/*
set recently fought
*/
if (!IS_NPC(ch) && IS_NPC(victim))
{
if (ch->leader && !IS_NPC(ch->leader))
{
victim->npcdata->pvnum_last_hit = ch->leader->pcdata->pvnum;
}
else
{
victim->npcdata->pvnum_last_hit = ch->pcdata->pvnum;
}
}
if (in_combat (victim))
{
char_to_combat (ch, victim->in_battle);
set_fighting(ch, victim);
act ("You rush headlong into the fray!", ch, NULL, victim, TO_CHAR);
act ("$n rushes headlong into the fray!", ch, NULL, victim, TO_ROOM);
pop_call();
return;
}
initiate_combat (ch, victim);
if (IS_AWAKE(ch) && IS_AWAKE(victim) && can_see(victim, ch) && in_same_room(ch, victim))
{
act ("You initiate combat against $N.", ch, NULL, victim, TO_CHAR);
act ("$n initiates combat against you.", ch, NULL, victim, TO_VICT);
act ("$n initiates combat against $N.", ch, NULL, victim, TO_NOTVICT);
}
pop_call();
return;
}
/*
* Delays your turn, letting the next character go first,
* permanently swaps places with the next character.
*/
void do_delay (CHAR_DATA * ch, char * argument)
{
int initA, initB;
CHAR_DATA *rch;
push_call("do_delay(%p,%p)",ch,argument);
if (!in_combat(ch))
{
act ("You are not in combat.", ch, NULL, NULL, TO_CHAR);
pop_call();
return;
}
if (!is_active(ch))
{
act ("Just wait your turn.", ch, NULL, NULL, TO_CHAR);
pop_call();
return;
}
if (ch->action != 0)
{
act ("You cannot delay after you have already acted.", ch, NULL, NULL, TO_CHAR);
pop_call();
return;
}
if ((rch = ch->in_battle->turn_list->next->unit) == NULL)
{
bug("do_delay: null rch", 0);
pop_call();
return;
}
initA = ch->initiative;
initB = rch->initiative;
act ("You bide your time to see what $N will do.", ch, NULL, rch, TO_CHAR);
act ("$n bides $s time to see what you will do.", ch, NULL, rch, TO_VICT);
act ("$n bides $s time to see what $N will do.", ch, NULL, rch, TO_NOTVICT);
ch->initiative = initB;
rch->initiative = initA;
SET_BIT(ch->attack, ATTACK_DELAY);
// skip_turn (ch->turn, TRUE);
swap_turn (ch->turn, rch->turn);
pop_call();
return;
}
/*
* Attempts to withdraw from combat. Subject to
* attacks of opportunity unless you have the
* spring attack feat, or can tumble away.
*/
void do_withdraw (CHAR_DATA * ch, char * argument)
{
CHAR_DATA *rch, *rch_next;
EXIT_DATA *pExitData;
int dir, attempts, to_room;
push_call("do_withdraw(%p,%p)",ch,argument);
if (!in_combat(ch))
{
send_to_char("You aren't fighting anyone.\n\r", ch);
pop_call();
return;
}
if (!is_active(ch))
{
send_to_char ("Just wait your turn to run away screaming!\n\r", ch);
pop_call();
return;
}
if (IS_AFFECTED(ch, AFF2_BERSERK))
{
send_to_char( "You aren't of clear mind to do that!\n\r", ch );
pop_call();
return;
}
if (ch->position < POS_FIGHTING)
{
send_to_char("Get up first!.\n\r", ch);
pop_call();
return;
}
if ((ch->action != 0 || IS_SET(ch->attack, ATTACK_MELEE)) && !learned(ch, gsn_spring_attack))
{
send_to_char ("You cannot withdraw after acting on your turn.\n\r", ch);
pop_call();
return;
}
if (argument == NULL)
{
for (dir = -1, attempts = 0 ; attempts < 100 ; attempts++)
{
dir = number_range(0,5);
if (can_move_char(ch, dir))
break;
dir = -1;
}
if (dir == -1)
{
send_to_char("Argh! You can't find an escape!!\n\r",ch);
pop_call();
return;
}
}
else if (argument[0] == '\0')
{
send_to_char ("Withdraw in what direction?\n\r", ch);
pop_call();
return;
}
else if ((dir = direction_door(argument)) == -1 || !can_move_char(ch, dir))
{
send_to_char("There is no escape in that direction!\n\r",ch);
pop_call();
return;
}
if ((pExitData = get_exit(ch->in_room->vnum, dir)) == NULL)
{
bug("do_withwraw: NULL room",0);
pop_call();
return;
}
if (IS_SET(ch->in_room->room_flags, ROOM_FOG)
&& !can_see_smoke(ch) && number_bits(2))
{
send_to_char( "You blunder around and get lost in the cloud!\n\r", ch);
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return;
}
if (IS_SET(ch->in_room->room_flags, ROOM_ICE) && !IS_FLYING(ch) && !IS_AFFECTED(ch, AFF_WATER_WALK))
{
if (!tumble_check(ch, NULL, tumble_roll(ch), 15))
{
switch (number_bits(1))
{
case 0:
act( "You try to flee but your feet slip on the icy surface.", ch, NULL, NULL, TO_CHAR);
act( "$n tries to flee but $s feet slip on the icy surface.", ch, NULL, NULL, TO_ROOM);
break;
case 1:
act( "Your feet slip as you try to flee and you crash to the ice.", ch, NULL, NULL, TO_CHAR);
act( "$n's feet slip as $e tries to flee and $e crashes to the ice.", ch, NULL, NULL, TO_ROOM);
break;
}
TAKE_ACTION(ch, ACTION_MOVE);
update_pos(ch,POS_RESTING);
pop_call();
return;
}
}
to_room = pExitData->to_room;
act( "$n withdraws hastily from combat!", ch, NULL, NULL, TO_ROOM );
act( "You withdraw hastily from combat!", ch, NULL, NULL, TO_CHAR );
/* provoke attacks of opportunity without Spring Attack */
if (!learned(ch, gsn_spring_attack))
{
for (rch = ch->in_room->first_person ; rch != NULL ; rch = rch_next)
{
rch_next = rch->next_in_room;
// stop the loop if he dies
if (!valid_fight(rch, ch))
break;
if (rch == ch)
continue;
if (rch->last_attacker && rch->last_attacker == ch)
{
if (combat_maneuver_check(rch, ch, gsn_tumble) >= 0)
attack_of_opportunity(rch, ch);
continue;
}
if (rch->last_attacked && rch->last_attacked == ch)
{
if (combat_maneuver_check(rch, ch, gsn_tumble) >= 0)
attack_of_opportunity(rch, ch);
continue;
}
}
}
// if he gets toasted, he can't flee - Kregor
if (!IS_HELPLESS(ch))
{
pop_call();
return;
}
withdraw_combat(ch);
char_from_room(ch);
char_to_room(ch, to_room, TRUE);
act( "$n glances around for signs of pursuit.", ch, NULL, NULL, TO_ROOM);
act( "You glance around for signs of pursuit.", ch, NULL, NULL, TO_CHAR);
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return;
}
/*
* Grappling support added here. Hacked
* shadowing code for starters - Kregor
*/
void start_grapple (CHAR_DATA *ch, CHAR_DATA *victim)
{
push_call("start_grapple(%p,%p)",ch,victim);
if (ch->grappling || ch->grappled_by)
{
bug("start_grapple : already grappling.", 0);
pop_call();
return;
}
if (is_affected(victim, gsn_crushing_hand)
|| is_affected(victim, gsn_grasping_hand)
|| is_affected(ch, gsn_crushing_hand)
|| is_affected(ch, gsn_grasping_hand))
{
pop_call();
return;
}
act("$tYou grapple $N.", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), victim, TO_CHAR);
act("$t$n grapples you!", ch, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_VICT);
act("{178}$n grapples $N!", ch, NULL, victim, TO_NOTVICT);
ch->grappling = victim;
victim->grappled_by = ch;
pop_call();
return;
}
void stop_grapple (CHAR_DATA *ch)
{
push_call("stop_grapple(%p)",ch);
if (ch->grappling == NULL)
{
pop_call();
return;
}
act("{178}You stop grappling $N.", ch, NULL, ch->grappling, TO_CHAR);
act("$tYou break $n's grapple!", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), ch->grappling, TO_VICT);
act("{178}$N breaks $n's grapple!", ch, NULL, ch->grappling, TO_NOTVICT);
ch->grappling->grappled_by = NULL;
ch->grappling = NULL;
pop_call();
return;
}
/*
* Grapple maneuver from Pathfinder RPG SRD - Kregor
*/
void do_grapple( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
int check;
push_call("do_grapple(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Grapple whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if (ch->grappling)
{
send_to_char( "You are already grappling someone.\n\r", ch );
pop_call();
return;
}
if (!race_skill(ch, gsn_grab) && hands_full(ch))
{
send_to_char( "You have no free hand to grapple with.\n\r", ch );
pop_call();
return;
}
if (ch == victim)
{
send_to_char( "You give yourself a big bear hug!\n\r", ch );
act("$n gives $mself a big bear hug!", ch, NULL, NULL, TO_ROOM);
pop_call();
return;
}
if (!IS_AWAKE(victim))
{
act( "You can't grapple $N while $E's down.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (get_size(ch) + 1 < get_size(victim))
{
act( "$N is too big for you to grapple.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (get_size(victim) + 1 < get_size(ch))
{
act( "$N is too small for you to grapple.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (is_mounting(victim))
{
send_to_char( "You can't grapple a mounted target... MAYBE you could grapple the mount.\n\r", ch );
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!can_surprise(ch, victim))
{
pop_call();
return;
}
if (!has_mirror(ch, victim, gsn_grapple))
{
if (!in_combat(ch) || is_active(ch))
{
if (!learned(ch, gsn_imp_grapple) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
{
if (attack_of_opportunity(victim, ch))
{
if (!valid_fight(ch, victim))
{
pop_call();
return;
}
}
}
}
if ((check = combat_maneuver_check(ch, victim, gsn_grapple)) >= 0)
{
start_grapple(ch, victim);
}
else
{
act( "$N avoids your grappling attempt.", ch, NULL, victim, TO_CHAR);
act( "You avoid $n's grappling attempt.", ch, NULL, victim, TO_VICT);
act( "$n avoids $n's grappling attempt.", ch, NULL, victim, TO_NOTVICT);
}
}
TAKE_ACTION(ch, ACTION_STANDARD);
if (!IS_NPC(ch) && IS_NPC(victim) && !in_combat(victim))
fight(victim, ch);
pop_call();
return;
}
void do_release( CHAR_DATA *ch, char *argument )
{
push_call("do_release(%p,%p)",ch,argument);
if (!ch->grappling)
{
send_to_char( "You are not grappling anyone.\n\r", ch );
pop_call();
return;
}
stop_grapple(ch);
pop_call();
return;
}
/*
* Monster grab ability - Kregor
*/
void grab( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("grab(%p,%p)",ch,victim);
if (ch->grappling)
{
pop_call();
return;
}
if (!race_skill(ch, gsn_grab))
{
pop_call();
return;
}
if (get_size(ch) < get_size(victim))
{
act( "$N is too big for you to grab.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (is_mounting(victim))
{
send_to_char( "You can't grapple a mounted target... MAYBE you could grapple the mount.\n\r", ch );
pop_call();
return;
}
if (combat_maneuver_check(ch, victim, gsn_grapple) >= 0)
{
start_grapple(ch, victim);
}
else
{
act( "$N avoids your grappling attempt.", ch, NULL, victim, TO_CHAR);
act( "You avoid $n's grappling attempt.", ch, NULL, victim, TO_VICT);
act( "$n avoids $n's grappling attempt.", ch, NULL, victim, TO_NOTVICT);
}
pop_call();
return;
}
/*
* do_fun for attacking in melee
* also handles making a surprize attack or
* AoO out of combat - Kregor
*/
void do_attack (CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim;
push_call("do_attack(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char ("Attack whom?\n\r", ch);
pop_call();
return;
}
}
else
{
if ((victim = get_char_room (ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
}
if (in_combat(ch) && !is_active (ch))
{
if (!attack_of_opportunity(ch, victim))
{
send_to_char ("Nope. Just wait your turn.\n\r", ch);
pop_call();
return;
}
else
{
pop_call();
return;
}
}
// safeguard is my preference, take it out if you want to allow it - Kregor
if (is_same_group(ch, victim))
{
send_to_char ("You cannot attack a member of your own group.\n\r", ch);
pop_call();
return;
}
if (!in_combat(ch) && !in_combat(victim))
{
if (is_safe(ch, NULL))
{
pop_call();
return;
}
if (attack_of_opportunity(ch, victim))
{
pop_call();
return;
}
else if (!can_see(victim, ch) || !IS_AWAKE(victim) || multi(ch, gsn_backstab) != -1)
{
if (check_murder(ch, victim))
{
act( "$n attacks you by surprise!", ch, NULL, victim, TO_VICT);
act( "You attack $N by surprise!", ch, NULL, victim, TO_CHAR);
act( "$n attacks $N by surprise!", ch, NULL, victim, TO_NOTVICT);
attack(ch, victim);
pop_call();
return;
}
}
else
{
act("$N is too wary to be taken by surprise!", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
}
CHECK_TURN(ch, victim);
attack(ch, victim);
pop_call();
return;
}
/*
* Execute additional primary melee attacks after first attack or combat maneuver.
* If none of these iterations trigger, return FALSE - Kregor
*
* Wrote this for house rule to allow all attack-multiplying conditions
* to happen during a standard action. Only iterative attacks due to BAB
* happen during a secondary attack now. In return, monsters get to make
* all primary part attacks on a standard action also.
*/
bool primary_melee_attacks( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *wield, OBJ_DATA *wield2 )
{
bool iterate = FALSE;
push_call("primary_melee_attacks(%p,%p,%p,%p)",ch,victim,wield,wield2);
//give on-hand attack to shield basher - Kregor
if (valid_fight(ch, victim))
{
if (IS_SET(ch->attack, ATTACK_SHIELD_BASH))
{
one_hit(ch, victim, TYPE_UNDEFINED, 0, wield);
iterate = TRUE;
}
}
//flurry of blows or dual wield attack
if (valid_fight(ch, victim))
{
if (COMBATMODE(ch, COMBAT_FLURRY))
{
if (!wield || WSPEC(wield, WSPEC_MONK))
{
one_hit(ch, victim, TYPE_UNDEFINED, 0, wield);
iterate = TRUE;
}
}
else if (wield2)
{
one_hit(ch, victim, TYPE_UNDEFINED, 0, wield2);
iterate = TRUE;
}
}
// rapid shot attack
if (is_missile_weapon(wield) && COMBATMODE(ch, COMBAT_RAPIDSHOT) && valid_fight(ch, victim))
{
one_hit(ch, victim, TYPE_UNDEFINED, 0, wield);
iterate = TRUE;
}
pop_call();
return iterate;
}
/*
* Wrote macro to handle custom NPCs with no race
* which have attacks set in mob_index - Kregor
*/
int num_attacks( CHAR_DATA *ch, int part )
{
int race;
push_call("num_attacks(%p,%p)",ch,part);
if ((race = get_race(ch)) == RACE_NONE)
{
if (!IS_NPC(ch))
{
pop_call();
return -1;
}
pop_call();
return ch->pIndexData->attacks[part];
}
pop_call();
return race_table[race].attacks[part];
}
/*
* Large function for melee attacks. Controls both standard
* and full-round attacks, natural weapon attacks for unarmed
* creatures with them. Also controls missile weapons used
* in melee rather than at-range - Kregor
*/
bool attack(CHAR_DATA *ch, CHAR_DATA *victim)
{
OBJ_DATA *obj;
OBJ_DATA *wield = NULL;
OBJ_DATA *wield2 = NULL;
OBJ_DATA *shield = NULL;
push_call("attack(%p,%p)",ch,victim);
for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
{
if (WEAR_LOC(obj, WEAR_BOTH_HANDS) && obj->item_type == ITEM_WEAPON)
{
wield = obj;
if (WSPEC(obj, WSPEC_DOUBLE))
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_WIELD) && obj->item_type == ITEM_WEAPON)
{
wield = obj;
}
if (WEAR_LOC(obj, WEAR_DUAL_WIELD) && obj->item_type == ITEM_WEAPON)
{
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_SHIELD) && obj->item_type == ITEM_ARMOR)
{
shield = obj;
}
}
/*
Disallow fighting in different rooms and safe rooms
*/
if (victim->in_room != ch->in_room)
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
/*
* can't attack if casting spell or doing something else
*/
if (ch->casting || ch->concentrating)
{
pop_call();
return FALSE;
}
if (IS_SET(ch->action, ACTION_STANDARD) && !IS_SET(ch->attack, ATTACK_MELEE))
{
send_to_char("You cannot make a melee attack after other standard actions.\n\r", ch);
pop_call();
return FALSE;
}
// Swarms automatically hit the person engaged, deal dmg based on HD
if (rspec_req(ch, RSPEC_SWARM))
{
int numdice = ch->level / 5 + 1;
int dam = dice(numdice, 6);
ch->attack_part = get_body_part(ch, TYPE_HIT, 1);
damage(ch, victim, dam, TYPE_HIT, NULL);
SET_BIT(ch->attack, ATTACK_MELEE);
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return TRUE;
}
// nonhumanoid unarmed attack goes here.
if (!wield && has_natural_weapon(ch))
{
int part, count, numattacks;
bool HasPrimary = FALSE;
// check if has primary attack, if not use secondaries as standard with no hit_adj
for (part = 0 ; part < ATTK_MAX ; part++)
{
if (!attack_part_table[part].primary)
continue;
if ((numattacks = num_attacks(ch, part)) == -1)
{
pop_call();
return FALSE;
}
if (numattacks)
{
if (part == ATTK_TOUCH)
ch->attack_part = part; // for the can_melee check below
HasPrimary = TRUE;
break;
}
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (!IS_SET(ch->attack, ATTACK_MELEE))
{
for (ch->attack_part = -1, part = 0 ; part < ATTK_MAX ; part++)
{
if (part == ATTK_RAKE)
continue;
if (IS_RACE(ch, RACE_DRAGON))
{
if (part == ATTK_WING && ch->size < SIZE_MEDIUM)
continue;
if (part == ATTK_TAIL && ch->size < SIZE_LARGE)
continue;
}
if (!valid_fight(ch, victim))
break;
if (HasPrimary && !attack_part_table[part].primary)
continue;
if ((numattacks = num_attacks(ch, part)) > 0)
{
for (count = 0 ; count < numattacks ; count++)
{
ch->attack_part = part;
one_hit(ch, victim, TYPE_HIT, 0, NULL);
}
}
}
if (ch->attack_part != -1) // if still -1, no parts were used
{
SET_BIT(ch->attack, ATTACK_MELEE);
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return TRUE;
}
}
else if (HasPrimary) // secondaries already went off w/standard attack if not.
{
for (ch->attack_part = -1, part = 0 ; part < ATTK_MAX ; part++)
{
if (!valid_fight(ch, victim))
break;
if (part == ATTK_RAKE)
continue;
if (IS_RACE(ch, RACE_DRAGON))
{
if (part == ATTK_WING && ch->size < SIZE_MEDIUM)
continue;
if (part == ATTK_TAIL && ch->size < SIZE_LARGE)
continue;
}
if (attack_part_table[part].primary)
continue;
if ((numattacks = num_attacks(ch, part)) > 0)
{
for (count = 0 ; count < numattacks ; count++)
{
ch->attack_part = part;
one_hit(ch, victim, TYPE_HIT, -5, NULL); // secondary parts at -5 to hit
}
}
}
}
if (ch->attack_part == -1)
{
send_to_char("You have no more attacks.\n\r", ch);
if (IS_NPC(ch) && !IS_SET(ch->attack, ATTACK_MELEE)) // if we didn't get any attacks off, we need to set parts
log_build_printf(ch->pIndexData->vnum, "attack: natural attack attempt with no race parts set.");
pop_call();
return FALSE;
}
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return TRUE;
}
// check contact resistances unless a ranged attack
if (!wield || (!is_missile_weapon(wield) && !WSPEC(wield, WSPEC_NOMELEE)))
{
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
}
// if this is the first attack this round, do this, and no more.
if (!IS_SET(ch->attack, ATTACK_MELEE))
{
if (one_hit(ch, victim, TYPE_UNDEFINED, 0, wield))
{
manyshot_hit(ch, victim, TYPE_UNDEFINED, 0, wield);
}
// take this function out if changed mind about more attacks in standard action - Kregor
primary_melee_attacks(ch, victim, wield, wield2);
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_MELEE);
pop_call();
return TRUE;
}
bool iterate = FALSE;
int bab = base_attack(ch);
if (IS_SET(ch->attack, ATTACK_DISARM|ATTACK_TRIP|ATTACK_SHIELD_BASH|ATTACK_STUNNING_FIST|ATTACK_SUNDER))
{
//this is where the primary attack functions go again if we reverse the change.
}
// Barbarian rage bonus attack
if (valid_fight(ch, victim))
{
if (is_affected(ch, gsn_barbarian_rage))
{
one_hit(ch, victim, TYPE_UNDEFINED, 0, wield);
iterate = TRUE;
}
}
if ((bab > 5 || is_speedy(ch, wield, wield2)) && valid_fight(ch, victim))
{
if (one_hit(ch, victim, TYPE_UNDEFINED, -5, wield))
{
manyshot_hit(ch, victim, TYPE_UNDEFINED, -5, wield);
}
iterate = TRUE;
}
if (valid_fight(ch, victim))
{
if (COMBATMODE(ch, COMBAT_FLURRY) && learned(ch, gsn_imp_flurry))
{
if (!wield || WSPEC(wield, WSPEC_MONK))
{
one_hit(ch, victim, TYPE_UNDEFINED, -5, wield);
iterate = TRUE;
}
}
else if (wield2 && learned(ch, gsn_imp_two_weapon))
{
one_hit(ch, victim, TYPE_UNDEFINED, -5, wield2);
iterate = TRUE;
}
}
if ((bab > 10 || (bab > 5 && is_speedy(ch, wield, wield2))) && valid_fight(ch, victim))
{
if (one_hit(ch, victim, TYPE_UNDEFINED, -10, wield))
{
manyshot_hit(ch, victim, TYPE_UNDEFINED, -10, wield);
}
iterate = TRUE;
}
if (valid_fight(ch, victim))
{
if (COMBATMODE(ch, COMBAT_FLURRY) && learned(ch, gsn_greater_flurry))
{
if (!wield || WSPEC(wield, WSPEC_MONK))
{
one_hit(ch, victim, TYPE_UNDEFINED, -10, wield);
iterate = TRUE;
}
}
else if (wield2 && learned(ch, gsn_greater_two_weapon))
{
one_hit(ch, victim, TYPE_UNDEFINED, -10, wield2);
iterate = TRUE;
}
}
if ((bab > 15 || (bab > 10 && is_speedy(ch, wield, wield2))) && valid_fight(ch, victim))
{
one_hit(ch, victim, TYPE_UNDEFINED, -15, wield);
iterate = TRUE;
}
if (valid_fight(ch, victim))
{
if (wield2 && learned(ch, gsn_perfect_two_weapon))
{
one_hit(ch, victim, TYPE_UNDEFINED, -15, wield2);
iterate = TRUE;
}
}
if (iterate)
{
TAKE_ACTION(ch, ACTION_MOVE);
}
pop_call();
return iterate;
}
/*
* Rock throwing ability.
* Keeping it simple - no real obj for rock, just a virtual one
* with proper damage types and messages. Just assumes the giant
* can find SOMETHING big to throw. - Kregor
*/
bool throw_rock( CHAR_DATA *ch, CHAR_DATA *victim )
{
int dam, diceroll, range;
push_call("throw_rock(%p,%p)",ch,victim);
if (!race_skill(ch, gsn_rock_throwing))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (victim == ch)
{
pop_call();
return FALSE;
}
if (who_fighting(victim) && who_fighting(victim) == ch && IS_SET(victim->attack, ATTACK_MELEE))
{
act("$N is engaged to closely to throw anything at.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
if ((range = get_size(ch)) <= 0 || findpath_search_victim(ch, victim, range) == -1)
{
act("$N is out of range.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
TAKE_ACTION(ch, ACTION_STANDARD);
act("You grab and hurl a large projectile at $N!", ch, NULL, victim, TO_CHAR);
act("$n grabs a large projectile and hurls it at $N!", ch, NULL, victim, TO_NOTVICT);
act("$n grabs a large projectile and hurls it at you!", ch, NULL, victim, TO_VICT);
int hit_mod = 0;
diceroll = dice(1,20);
CRIT_HIT = FALSE;
if (diceroll == 1)
ch_printf_color(ch, "{118}You score a critical miss!\n\r");
if (diceroll == 20 || (diceroll >= 19 && learned(ch, gsn_imp_critical)))
{
if (check_hit(ch, victim, dice(1,20), hit_mod, gsn_rock_throwing, NULL, FALSE, TRUE)
|| (fave_enemy_bonus(ch, victim) && learned(ch, gsn_master_hunter))
|| (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && (learned(ch, gsn_holy_champion) || learned(ch, gsn_unholy_champion))))
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical hit!\n\r");
}
}
if (!CRIT_HIT && !check_hit(ch, victim, diceroll, hit_mod, gsn_rock_throwing, NULL, FALSE, TRUE))
{
act("The missile falls short of you with a THUD!!", ch, NULL, victim, TO_VICT);
act("The missile falls short of $N with a THUD!!", ch, NULL, victim, TO_VICT);
act("Your missile falls short of $N!", ch, NULL, victim, TO_CHAR);
}
else
{
dam = GET_DAMROLL(ch, victim, gsn_rock_throwing, NULL);
if (CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
dam *= 2;
damage(ch, victim, dam, gsn_rock_throwing, NULL);
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
pop_call();
return TRUE;
}
void do_throw_rock( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
int dir;
char buf1[MAX_INPUT_LENGTH];
char buf2[MAX_INPUT_LENGTH];
push_call("do_throw_rock(%p,%p)",ch,argument);
if (!race_skill(ch, gsn_rock_throwing))
{
send_to_char("You don't possess that racial ability.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, buf1);
argument = one_argument(argument, buf2);
if ((dir = direction_door(buf1)) != -1)
{
if ((victim = find_char_dir(ch, dir, buf2)) == NULL)
{
send_to_char("Your victim must have slipped out of sight.\n\r", ch);
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, buf1)) == NULL)
{
send_to_char("Your victim must have slipped away.\n\r", ch);
pop_call();
return;
}
throw_rock(ch, victim);
pop_call();
return;
}
/*
* New improved do_shoot with iterative attack support - Kregor
*/
void do_shoot(CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim;
OBJ_DATA *ammo, *weapon;
int dir, dam, mult, diceroll, hit_mod, cnt, count, range, bab;
char *name;
char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];
push_call("do_shoot(%p,%p)",ch,argument);
if ((weapon = get_eq_char(ch,WEAR_WIELD)) == NULL)
{
send_to_char("You must wield a weapon to shoot!\n\r", ch);
pop_call();
return;
}
if (!IS_OBJ_TYPE(weapon, ITEM_WEAPON) || !WSPEC(weapon, WSPEC_MISSILE))
{
send_to_char("You are not wielding a missile weapon.\n\r", ch);
pop_call();
return;
}
if (IS_SET(ch->action, ACTION_STANDARD) && !IS_SET(ch->attack, ATTACK_SHOOT))
{
send_to_char("You have already made another standard action this round.\n\r", ch);
pop_call();
return;
}
if ((ammo = get_ammo(ch, weapon)) == NULL || !reload(ch, weapon))
{
pop_call();
return;
}
if ((range = get_missile_range(weapon)) <= 0)
{
send_to_char("You cannot use that weapon at range.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, buf1);
argument = one_argument(argument, buf2);
if ((dir = direction_door(buf1)) == -1)
{
if ((dir = direction_door(buf2)) == -1)
{
send_to_char( "Shoot which direction?\n\r", ch );
pop_call();
return;
}
name = buf1;
}
else
{
name = buf2;
}
if (*name == '\0')
{
send_to_char( "Shoot at whom?\n\r", ch );
pop_call();
return;
}
if ((victim = find_char_dir(ch, dir, name)) == NULL)
{
send_to_char("Your victim must have slipped out of sight!\n\r", ch);
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (find_keeper(victim) != NULL || IS_SET(victim->in_room->room_flags, ROOM_SAFE))
{
send_to_char("You can't shoot into that room.\n\r", ch);
pop_call();
return;
}
if (findpath_search_victim(ch, victim, range) == -1)
{
send_to_char("That target is out of range.\n\r", ch);
pop_call();
return;
}
if (COMBATMODE(ch, COMBAT_MANYSHOT))
{
switch(weapon->value[0])
{
case WEAPON_TYPE_SHORTBOW:
case WEAPON_TYPE_SHORTBOW_COMPOSITE:
case WEAPON_TYPE_LONGBOW:
case WEAPON_TYPE_LONGBOW_COMPOSITE:
case WEAPON_TYPE_SLING:
break;
default:
send_to_char("You can only manyshot with a bow or sling.\n\r", ch);
REMOVE_BIT(ch->combatmode, COMBAT_MANYSHOT);
break;
}
}
bab = base_attack(ch);
bool speedy = is_speedy(ch,weapon,weapon);
if (!IS_SET(ch->action, ACTION_STANDARD))
count = 1 + (COMBATMODE(ch, COMBAT_RAPIDSHOT));
else
count = 1 + (bab > 5 || speedy) + (bab > 10 || (bab > 10 && speedy)) + (bab > 15 || (bab > 15 && speedy)) + (speedy);
for (cnt = 0 ; cnt < count ; cnt++)
{
if (!cnt && IS_SET(ch->action, ACTION_STANDARD) && count > 1)
continue;
if ((ammo = get_ammo(ch, weapon)) == NULL || !reload(ch, weapon))
{
break;
}
if ((victim = find_char_dir(ch, dir, name)) == NULL)
{
break;
}
diceroll = dice(1,20);
CRIT_HIT = FALSE;
int dt = TYPE_HIT;
if (IS_SET(ch->action, ACTION_STANDARD))
{
switch (cnt)
{
case 1:
hit_mod = -5;
break;
case 2:
hit_mod = -10;
break;
case 3:
hit_mod = -15;
break;
}
}
if (diceroll == 1 && !WEAPON_FLAG(ammo, WFLAG_SEEKING))
ch_printf_color(ch, "{118}Critical miss!\n\r");
if (number_percent() > get_apply(victim, APPLY_FORTIFICATION))
{
int threat = weapon_table[weapon->value[0]].threat;
if (WEAPON_FLAG(ammo, WFLAG_KEEN)
|| (is_bow(weapon) && learned(ch, gsn_keen_arrow)))
{
threat = (20 - threat) * 2;
threat = 20 - threat;
}
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL))
{
threat += 1;
}
if (diceroll == 20 || diceroll >= threat)
{
if (check_hit(ch, victim, dice(1,20), hit_mod, dt, weapon, FALSE, TRUE)
|| (weapon != NULL && WEAPON_TYPE(weapon, WEAPON_TYPE_SLING) && learned(ch, gsn_dead_shot))
|| (fave_enemy_bonus(ch, victim) && learned(ch, gsn_master_hunter))
|| (can_backstab(ch, victim, dt) && learned(ch, gsn_stealth_mastery))
|| (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && (learned(ch, gsn_holy_champion) || learned(ch, gsn_unholy_champion))))
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical threat!\n\r");
}
}
}
if (!WEAPON_FLAG(ammo, WFLAG_SEEKING) && diceroll != 20 && !check_hit(ch, victim, diceroll, hit_mod, dt, weapon, FALSE, TRUE))
{
act( "$n looses $p $Twards.", ch, ammo, dir_name[dir], TO_ROOM );
act( "You loose $p $Twards.", ch, ammo, dir_name[dir], TO_CHAR );
act( "$p whistles past you from $T.", victim, ammo, rev_dir_name[dir], TO_CHAR);
act( "$p whistles past $n from $T.", victim, ammo, rev_dir_name[dir], TO_ROOM);
act( "$p whistles past $N.", ch, ammo, victim, TO_CHAR );
act( "$p whistles past $N.", ch, ammo, victim, TO_ROOM );
}
else
{
dam = GET_DAMROLL(ch, victim, dt, weapon);
mult = weapon_table[ammo->value[0]].critical;
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL) && learned(ch, gsn_weapon_grandmastery))
mult++;
if (WEAPON_TYPE(weapon, WEAPON_TYPE_SLING) && learned(ch, gsn_between_the_eyes))
mult++;
if (cnt == 0 && CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
dam *= mult;
act( "$n looses $p $Twards.", ch, ammo, dir_name[dir], TO_ROOM );
act( "You loose $p $Twards.", ch, ammo, dir_name[dir], TO_CHAR );
act( "$p flies in from $T.", victim, ammo, rev_dir_name[dir], TO_CHAR );
act( "$p flies in from $T.", victim, ammo, rev_dir_name[dir], TO_ROOM );
if (!deflect_missile(victim, ch, ammo, -1))
{
damage(ch, victim, dam, dt, ammo);
if (weapon_cast_spell(ammo->value[2], ammo->value[3], ch, victim, ammo))
ammo->value[2] = ammo->value[3] = 0;
if (COMBATMODE(ch, COMBAT_MANYSHOT) && (!IS_SET(ch->action, ACTION_STANDARD) || !cnt))
{
manyshot_hit(ch, victim, dt, hit_mod, weapon);
}
}
}
if (ammo)
{
junk_obj(ammo);
}
}
if (!IS_SET(ch->action, ACTION_STANDARD))
{
TAKE_ACTION(ch, ACTION_STANDARD);
}
else
{
TAKE_ACTION(ch, ACTION_MOVE);
}
SET_BIT(ch->attack, ATTACK_SHOOT); // this gives others AoO until your next turn.
if (MP_VALID_MOB(victim) && dam > 0)
{
mprog_range_trigger(victim, ch, rev_dir[dir]);
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
pop_call();
return;
}
/*
* Thrown missile support, with D20 goodness, support for
* drawing and throwing missiles in sheaths.
*/
void do_throw( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
OBJ_DATA *weapon, *cont;
int dir, dam, mult, wielded, diceroll, range;
char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH], buf3[MAX_INPUT_LENGTH];
push_call("do_throw(%p,%p)",ch,argument);
if (IS_SET(ch->action, ACTION_STANDARD))
{
send_to_char("You have already made a standard action this round.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, buf1);
argument = one_argument(argument, buf2);
argument = one_argument(argument, buf3);
if (buf2[0] == '\0' || buf1[0] == '\0')
{
send_to_char( "Throw what at whom?\n\r", ch );
pop_call();
return;
}
if ((dir = direction_door(buf2)) == -1)
{
if ((victim = find_char_dir(ch, dir, buf2)) == NULL)
{
send_to_char("Your victim must have slipped out of sight.\n\r", ch);
pop_call();
return;
}
}
else
{
if (buf3[0] == '\0')
{
send_to_char( "Throw at whom?\n\r", ch );
pop_call();
return;
}
if ((victim = find_char_dir(ch, dir, buf3)) == NULL)
{
send_to_char("Your victim must have slipped out of sight.\n\r", ch);
pop_call();
return;
}
}
// for Rock Throwing racial ability - Kregor
if (!strcasecmp(buf1, "rock"))
{
throw_rock(ch, victim);
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
wielded = FALSE;
if ((weapon = get_wield(ch, FALSE)) != NULL)
{
if (is_name(buf1, weapon->name) || is_name_short(buf1, weapon->name))
{
wielded = TRUE;
}
}
if (wielded == FALSE)
{
if (!remove_obj(ch, WEAR_WIELD, TRUE, FALSE))
{
send_to_char("You can't throw with another weapon wielded!\n\r", ch);
pop_call();
return;
}
for (weapon = ch->first_carrying ; weapon ; weapon = weapon->next_content)
{
if (weapon->item_type == ITEM_WEAPON && (is_name(buf1, weapon->name) || is_name_short(buf1, weapon->name)))
{
break;
}
if (weapon->item_type == ITEM_SHEATH && !IS_SET(weapon->value[1], CONT_CLOSED))
{
cont = weapon;
for (weapon = weapon->first_content ; weapon ; weapon = weapon->next_content)
{
if (weapon->item_type == ITEM_WEAPON && (is_name(buf1, weapon->name) || is_name_short(buf1, weapon->name)))
{
break;
}
}
if (weapon)
{
break;
}
else
{
weapon = cont;
}
}
}
}
if (weapon == NULL)
{
send_to_char("Hmmm... you don't seem to have that weapon.\n\r", ch);
pop_call();
return;
}
if (IS_SET(weapon->extra_flags, ITEM_NODROP))
{
send_to_char("You can't let go of it.\n\r", ch);
pop_call();
return;
}
if (!WEAPON_FLAG(weapon, WFLAG_THROWING))
{
if (!WSPEC(weapon, WSPEC_THROW))
{
send_to_char("You can't throw that type of weapon.\n\r", ch);
pop_call();
return;
}
}
if (dir != -1)
{
if ((range = get_missile_range(weapon)) <= 0 || findpath_search_victim(ch, victim, range) == -1)
{
send_to_char("That target is out of range.\n\r", ch);
pop_call();
return;
}
}
CHECK_TURN(ch, victim);
if (weapon->in_obj)
{
if (!learned(ch, gsn_quick_draw))
{
if (IS_SET(ch->action, ACTION_MOVE))
{
act("You do not have enough actions to draw and throw $p.", ch, weapon, NULL, TO_CHAR);
pop_call();
return;
}
TAKE_ACTION(ch, ACTION_MOVE);
}
act( "$n pulls $p from $P.", ch, weapon, weapon->in_obj, TO_ROOM );
act( "You pull $p from $P.", ch, weapon, weapon->in_obj, TO_CHAR );
obj_from_obj(weapon);
obj_to_char(weapon, ch);
}
TAKE_ACTION(ch, ACTION_STANDARD);
/*
* The dice roll, calculate mods
*/
int dt = TYPE_HIT;
int hit_mod = 0;
diceroll = dice(1,20);
CRIT_HIT = FALSE;
if (diceroll == 1 && !WEAPON_FLAG(weapon, WFLAG_SEEKING))
ch_printf_color(ch, "{118}Critical miss!\n\r");
if (number_percent() > get_apply(victim, APPLY_FORTIFICATION))
{
int threat = weapon_table[weapon->value[0]].threat;
if (WEAPON_FLAG(weapon, WFLAG_KEEN))
{
threat = (20 - threat) * 2;
threat = 20 - threat;
}
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL))
{
threat += 1;
}
if (diceroll == 20 || diceroll >= threat)
{
if (check_hit(ch, victim, dice(1,20), hit_mod, dt, weapon, FALSE, TRUE)
|| (fave_enemy_bonus(ch, victim) && learned(ch, gsn_master_hunter))
|| (can_backstab(ch, victim, dt) && learned(ch, gsn_stealth_mastery))
|| (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && (learned(ch, gsn_holy_champion) || learned(ch, gsn_unholy_champion))))
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical threat!\n\r");
}
}
}
if (!WEAPON_FLAG(weapon, WFLAG_SEEKING) && diceroll != 20 && !check_hit(ch, victim, diceroll, hit_mod, dt, weapon, FALSE, TRUE))
{
dam_message(ch, victim, 0, dt, weapon);
if (!WEAPON_FLAG(weapon, WFLAG_RETURNING) && (!WSPEC(weapon, WSPEC_RETURNING) || !weapon_skill(ch, weapon)))
{
weapon->sac_timer = OBJ_SAC_TIME;
obj_to_room(weapon, victim->in_room->vnum);
}
else
{
act( "$p returns to $n's hand.", ch, weapon, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, weapon, NULL, TO_CHAR );
}
}
else
{
dam = GET_DAMROLL(ch, victim, dt, weapon);
mult = weapon_table[weapon->value[0]].critical;
if (IS_SET(weapon_skill(ch, weapon), WSKILL_IMP_CRITICAL) && learned(ch, gsn_weapon_grandmastery))
mult++;
if (CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
dam *= mult;
if (!deflect_missile(victim, ch, weapon, dir))
{
damage(ch, victim, dam, dt, weapon);
if (weapon_cast_spell(weapon->value[2], weapon->value[3], ch, victim, weapon))
weapon->value[2] = weapon->value[3] = 0;
if (!WEAPON_FLAG(weapon, WFLAG_RETURNING) && (!WSPEC(weapon, WSPEC_RETURNING) || !weapon_skill(ch, weapon)))
obj_to_char(weapon, victim);
else
{
act( "$p returns to $n's hand.", ch, weapon, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, weapon, NULL, TO_CHAR );
}
}
if (dir != -1 && !IS_NPC(ch) && MP_VALID_MOB(victim))
{
mprog_range_trigger(victim, ch, rev_dir[dir]);
}
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
pop_call();
return;
}
/*
* Trip, the command and the function. Mobiles use the function alone.
*/
void do_trip( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
push_call("do_trip(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Trip whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
trip(ch, victim, 0);
pop_call();
return;
}
/*
* Returns TRUE if the attempt is made, whether or not it succeeds.
*/
bool trip( CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj )
{
int check = 0;
OBJ_DATA *wield;
push_call("trip(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && !learned(ch, gsn_trip))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && IS_SET(ch->attack, ATTACK_TRIP))
{
pop_call();
return FALSE;
}
if (IS_FLYING(victim))
{
send_to_char( "How would YOU trip a flying person?\n\r", ch );
pop_call();
return FALSE;
}
if (get_size(ch) + 1 < get_size(victim))
{
act( "$N is too large for you to trip", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
if (is_mounting(victim))
{
send_to_char( "You can't trip a mounted target. MAYBE you could trip the mount.\n\r", ch );
pop_call();
return FALSE;
}
if (victim->position < POS_FIGHTING)
{
act( "$N is already down!", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
if (!can_surprise(ch, victim))
{
pop_call();
return FALSE;
}
wield = get_wield(ch, FALSE);
if (!has_mirror(ch, victim, gsn_trip))
{
if (!in_combat(ch) || is_active(ch)) // else it's his own attack of opportunity
{
if (!learned(ch, gsn_improved_trip) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
{
if (attack_of_opportunity(victim, ch))
{
if (!valid_fight(ch, victim))
{
pop_call();
return TRUE;
}
}
}
}
if ((check = combat_maneuver_check(ch, victim, gsn_trip)) == -2)
{
if (!wield || !WSPEC(wield, WSPEC_TRIP))
{
act( "$n tries to trip you and you counter $m!", ch, NULL, victim, TO_VICT);
act( "$N counters your attempt to trip $M!", ch, NULL, victim, TO_CHAR);
act( "$n tries to trip $N, who counters $s move.!", ch, NULL, victim, TO_NOTVICT);
update_pos(ch, POS_RESTING);
ch->distracted = 2;
}
else
{
act( "$n tries to trip you, but fails.", ch, NULL, victim, TO_VICT);
act( "You fail your attempt to trip $N.", ch, NULL, victim, TO_CHAR);
act( "$n tries to trip $N and fails.", ch, NULL, victim, TO_NOTVICT);
}
}
else if (check == -1)
{
act( "$n tries to trip you, but fails.", ch, NULL, victim, TO_VICT);
act( "You fail your attempt to trip $N.", ch, NULL, victim, TO_CHAR);
act( "$n tries to trip $N and fails.", ch, NULL, victim, TO_NOTVICT);
}
else
{
act( "$n trips you and you go down!", ch, NULL, victim, TO_VICT);
act( "You trip $N and $E goes down!", ch, NULL, victim, TO_CHAR);
act( "$n trips $N and $E goes down!", ch, NULL, victim, TO_NOTVICT);
update_pos(victim, POS_RESTING);
victim->distracted = 2;
}
// so these don't go off on attacks of opportunity
if (!ch->distracted && in_same_combat(ch, victim) && is_active(ch))
{
primary_melee_attacks(ch, victim, wield, get_wield(ch, TRUE));
}
}
if (!IS_NPC(ch) && IS_NPC(victim) && !in_combat(victim))
fight(victim, ch);
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_TRIP|ATTACK_MELEE);
pop_call();
return TRUE;
}
/*
* Disarm, the command and the function. Mobiles use the function alone.
*/
void do_disarm( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
OBJ_DATA *wield;
push_call("do_disarm(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Disarm whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if ((wield = get_wield(ch, FALSE)) == NULL || !WSPEC(wield, WSPEC_DISARM))
{
if ((wield = get_wield(ch, TRUE)) == NULL || !WSPEC(wield, WSPEC_DISARM))
{
if (!learned(ch, gsn_disarm))
{
send_to_char( "You are unable to perform this feat.\n\r", ch );
pop_call();
return;
}
}
}
disarm(ch, victim, 0);
pop_call();
return;
}
/*
* Returns TRUE if the attempt is made, whether or not it succeeds.
*/
bool disarm( CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj )
{
OBJ_DATA *chwield, *victwield;
int check;
push_call("disarm(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && !learned(ch, gsn_disarm))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && IS_SET(ch->attack, ATTACK_DISARM))
{
pop_call();
return FALSE;
}
if ((chwield = get_wield(ch, FALSE)) != NULL && is_missile_weapon(chwield))
{
send_to_char( "You cannot disarm with a missile weapon.\n\r", ch );
pop_call();
return FALSE;
}
if ((victwield = get_wield(victim, FALSE)) == NULL)
{
if ((victwield = get_wield(victim, TRUE)) == NULL)
{
send_to_char( "Your opponent is not wielding a weapon.\n\r", ch );
pop_call();
return FALSE;
}
}
if (IS_SET(victwield->extra_flags,ITEM_INVENTORY)
|| IS_SET(victwield->extra_flags,ITEM_NODROP)
|| IS_SET(victwield->extra_flags,ITEM_NOREMOVE))
{
send_to_char( "You cannot pry it from their hands.\n\r", ch );
SET_BIT(ch->attack, ATTACK_DISARM);
pop_call();
return FALSE;
}
if (!can_surprise(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_see_obj(ch, victwield))
{
if (number_percent() > 50)
{
act( "You fail to get a good look at $p.", ch, victwield, victim, TO_CHAR);
act( "$n attempts to disarm $N and fails.", ch, NULL, victim, TO_NOTVICT);
act( "$n attempts to disarm you and fails.", ch, NULL, victim, TO_VICT);
SET_BIT(ch->attack, ATTACK_DISARM);
}
}
else if (!has_mirror(ch, victim, gsn_disarm))
{
if (!in_combat(ch) || is_active(ch))
{
if (!learned(ch, gsn_imp_disarm) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
{
if (attack_of_opportunity(victim, ch))
{
if (!valid_fight(ch, victim))
{
pop_call();
return TRUE;
}
}
}
}
if ((check = combat_maneuver_check(ch, victim, gsn_disarm)) >= 0)
{
unequip_char(victim, victwield, TRUE);
obj_from_char(victwield);
if (!chwield)
{
act( "$n snatches $p from your hands!", ch, victwield, victim, TO_VICT);
act( "You snatches $p from $N's hands!", ch, victwield, victim, TO_CHAR);
act( "$n snatches $p from $N's hands!", ch, victwield, victim, TO_NOTVICT);
obj_to_char(victwield, ch);
}
else
{
act( "$n knocks $p from your hands!", ch, victwield, victim, TO_VICT);
act( "You knock $p from $N's hands!", ch, victwield, victim, TO_CHAR);
act( "$n knocks $p from $N's hands!", ch, victwield, victim, TO_NOTVICT);
obj_to_room(victwield, victim->in_room->vnum);
}
}
else if (check == -2)
{
act( "$n attempts to disarm you, but you counter the attempt!", ch, NULL, victim, TO_VICT);
act( "$N counters the attempt to disarm!", ch, NULL, victim, TO_CHAR);
act( "$n attempts to disarm $N, who counters the attempt!", ch, NULL, victim, TO_NOTVICT);
unequip_char(ch, chwield, TRUE);
obj_from_char(chwield);
obj_to_room(chwield, ch->in_room->vnum);
chwield = NULL;
}
else
{
act( "You fail your attempt to disarm $N.", ch, NULL, victim, TO_CHAR);
act( "$n attempts to disarm $N and fails.", ch, NULL, victim, TO_NOTVICT);
act( "$n attempts to disarm you and fails.", ch, NULL, victim, TO_VICT);
}
// so these don't go off on attacks of opportunity
if (in_same_combat(ch, victim) && is_active(ch))
{
primary_melee_attacks(ch, victim, chwield, get_wield(ch, TRUE));
}
}
if (!IS_NPC(ch) && IS_NPC(victim) && !in_combat(victim))
fight(victim, ch);
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_DISARM|ATTACK_MELEE);
pop_call();
return TRUE;
}
/*
* Returns TRUE if attempt is made, whether or not it succeeds.
* Called from the BASH command in act_obj using a char target.
*/
bool bash( CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj )
{
OBJ_DATA *shield;
push_call("bash(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && !learned(ch, gsn_imp_shield_bash))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (IS_SET(ch->attack, ATTACK_SHIELD_BASH))
{
act("You can only make one shield bash per round.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if ((shield = get_eq_char(ch, WEAR_SHIELD)) == NULL || shield->item_type != ITEM_ARMOR)
{
act("You do not have a shield to bash with.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (!ARMOR_TYPE(shield, ARMOR_TYPE_LIGHT_SHIELD) && !ARMOR_TYPE(shield, ARMOR_TYPE_HEAVY_SHIELD))
{
act("You can only bash with a light or heavy shield.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
BOOL_CHECK_TURN(ch, victim);
one_hit( ch, victim, gsn_shield_bash, hit_adj, NULL );
// so these don't go off on attacks of opportunity
if (in_same_combat(ch, victim) && is_active(ch))
{
primary_melee_attacks(ch, victim, get_wield(ch, FALSE), get_wield(ch, TRUE));
}
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_SHIELD_BASH|ATTACK_MELEE);
pop_call();
return TRUE;
}
/*
* Whirlwind Attack - command and action
*/
void do_whirl( CHAR_DATA *ch, char *argument )
{
push_call("do_whirl(%p,%p)",ch,argument);
if (!learned(ch, gsn_whirl)
&& (!learned(ch, gsn_mighty_rage) || !is_affected(ch, gsn_barbarian_rage) || armor_type_worn(ch) > ARMOR_MEDIUM))
{
send_to_char( "You do not know that maneuver.\n\r", ch);
pop_call();
return;
}
if (is_safe(ch, NULL))
{
pop_call();
return;
}
if (!in_combat(ch) || !who_fighting(ch))
{
send_to_char( "You aren't fighting anyone.\n\r", ch );
pop_call();
return;
}
if (!is_active(ch))
{
send_to_char( "Just wait your turn.\n\r", ch );
pop_call();
return;
}
whirl(ch);
pop_call();
return;
}
/*
* Returns TRUE if attempt is made, whether successful or not.
*/
bool whirl( CHAR_DATA *ch )
{
CHAR_DATA *vch, *vch_next;
OBJ_DATA *wield;
push_call("whirl(%p)",ch);
if (!who_fighting(ch))
{
pop_call();
return FALSE;
}
if (!learned(ch, gsn_whirl)
&& (!learned(ch, gsn_mighty_rage) || !is_affected(ch, gsn_barbarian_rage) || armor_type_worn(ch) > ARMOR_MEDIUM))
{
pop_call();
return FALSE;
}
if ((wield = get_wield(ch, FALSE)) != NULL && (is_missile_weapon(wield) || WSPEC(wield, WSPEC_NOMELEE)))
{
send_to_char( "You cannot whirl with a missile weapon.\n\r", ch);
pop_call();
return FALSE;
}
if (IS_SET(ch->attack, ATTACK_WHIRL))
{
send_to_char( "You can only do this maneuver once in a round.\n\r", ch);
pop_call();
return FALSE;
}
act( "You spin about in a whirlwind of movement!", ch, NULL, NULL, TO_CHAR);
act( "$n spins about in a whirlwind of movement!", ch, NULL, NULL, TO_NOTVICT);
for (vch = ch->in_room->first_person ; vch ; vch = vch_next)
{
vch_next = vch->next_in_room;
if (!who_fighting(vch) || who_fighting(vch) != ch)
continue;
one_hit( ch, vch, gsn_whirl, 0, wield );
}
SET_BIT(ch->attack, ATTACK_WHIRL);
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return TRUE;
}
/*
* Trample, the command and the function. Mobiles use the function alone.
*/
void do_trample( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
push_call("do_trample(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Trample whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
trample(ch, victim, 0);
pop_call();
return;
}
/*
* Returns TRUE if the attempt is made, whether or not it succeeds.
*/
bool trample( CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj )
{
int check = 0;
CHAR_DATA *mount, *vch, *vch_next;
push_call("trample(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (ch == victim)
{
act("You cannot trample yourself.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (!learned(ch, gsn_trample))
{
act("You don't know the trample feat.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (!quadruped(ch) && !many_legged(ch) && !is_mounting(ch))
{
act("You are not mounted.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if ((mount = ch->mounting) == NULL)
{
mount = ch;
}
else
{
if (!combat_mount(mount))
{
act("$N is not a combat-ready mount!", ch, NULL, mount, TO_CHAR);
pop_call();
return FALSE;
}
}
if (!can_melee_attack(mount, victim))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && (IS_SET(ch->attack, ATTACK_TRAMPLE) || IS_SET(mount->attack, ATTACK_TRAMPLE)))
{
pop_call();
return FALSE;
}
if (IS_FLYING(victim) || get_size(mount) + 1 < get_size(victim))
{
act( "$N cannot be trampled.", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
if (is_mounting(victim))
{
send_to_char( "You can't trample a mounted target.\n\r", ch );
pop_call();
return FALSE;
}
if (victim->position < POS_FIGHTING)
{
act( "$N is already down!", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
BOOL_CHECK_TURN(ch, victim);
if (!has_mirror(ch, victim, gsn_trample))
{
if ((check = combat_maneuver_check(mount, victim, gsn_trip)) < 0)
{
act( "$n tries to trample you, but fails.", ch, NULL, victim, TO_VICT);
act( "You fail your attempt to trample $N.", ch, NULL, victim, TO_CHAR);
act( "$n tries to trample $N and fails.", ch, NULL, victim, TO_NOTVICT);
}
else
{
act( "$n tramples you and you go down!", mount, NULL, victim, TO_VICT);
act( "You trample $N and $E goes down!", mount, NULL, victim, TO_CHAR);
act( "$n tramples $N and $E goes down!", mount, NULL, victim, TO_NOTVICT);
update_pos(victim, POS_RESTING);
victim->distracted = 2;
}
if (!IS_SET(mount->attack, ATTACK_OPPORTUNITY))
{
one_hit(mount, victim, TYPE_UNDEFINED, hit_adj, NULL);
take_opportunity(mount, victim);
}
}
SET_BIT(ch->attack, ATTACK_TRAMPLE);
SET_BIT(mount->attack, ATTACK_TRAMPLE);
// mounted onslaught = multi opponent trample!
if (learned(ch, gsn_mounted_onslaught))
{
for (vch = ch->in_room->first_person ; vch ; vch = vch_next)
{
vch_next = vch->next_in_room;
if (!can_mass_attack(ch, vch))
continue;
if (vch == victim)
continue;
if (vch->last_attacked && vch->last_attacked != ch)
continue;
// first failed attempt ends the movement
if (!has_mirror(ch, vch, gsn_trample))
{
if ((check = combat_maneuver_check(mount, vch, gsn_trip)) < 0)
{
act( "$n tries to trample you, but fails.", ch, NULL, vch, TO_VICT);
act( "You fail your attempt to trample $N.", ch, NULL, vch, TO_CHAR);
act( "$n tries to trample $N and fails.", ch, NULL, vch, TO_NOTVICT);
break;
}
else
{
act( "$n tramples you and you go down!", mount, NULL, vch, TO_VICT);
act( "You trample $N and $E goes down!", mount, NULL, vch, TO_CHAR);
act( "$n tramples $N and $E goes down!", mount, NULL, vch, TO_NOTVICT);
update_pos(vch, POS_RESTING);
vch->distracted = 2;
}
}
else
{
break;
}
}
}
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return TRUE;
}
/*
* Charge: enter next room and attack victim in one command - Kregor
*/
void do_charge(CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim, *mount;
OBJ_DATA *weapon;
int dir, range;
char *name;
char buf1[MAX_INPUT_LENGTH], buf2[MAX_INPUT_LENGTH];
push_call("do_charge(%p,%p)",ch,argument);
if (!is_mounting(ch) && !quadruped(ch) && !many_legged(ch))
{
send_to_char("You are not mounted.\n\r", ch);
pop_call();
return;
}
if ((mount = ch->mounting) == NULL)
mount = ch;
if ((weapon = get_eq_char(ch,WEAR_WIELD)) == NULL || WSPEC(weapon, WSPEC_MISSILE))
{
send_to_char("You must wield a melee weapon to charge!\n\r", ch);
pop_call();
return;
}
if (IS_OBJ_TYPE(weapon, ITEM_WEAPON) || WSPEC(weapon, WSPEC_MISSILE))
{
send_to_char("You must be holding a melee weapon to charge.\n\r", ch);
pop_call();
return;
}
if ((range = race_table[get_race(mount)].land_speed / 15) <= 0)
{
send_to_char("You cannot charge fast enough to strike anyone.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, buf1);
argument = one_argument(argument, buf2);
if ((dir = direction_door(buf1)) == -1)
{
if ((dir = direction_door(buf2)) == -1)
{
send_to_char( "Charge in which direction?\n\r", ch );
pop_call();
return;
}
name = buf1;
}
else
{
name = buf2;
}
if (!can_move_char(ch, dir))
{
send_to_char("You cannot charge in that direction!\n\r", ch);
pop_call();
return;
}
if (*name == '\0')
{
send_to_char( "Charge at whom?\n\r", ch );
pop_call();
return;
}
if ((victim = find_char_dir(ch, dir, name)) == NULL)
{
send_to_char("Your victim must have slipped out of sight!\n\r", ch);
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (find_keeper(victim) != NULL || IS_SET(victim->in_room->room_flags, ROOM_SAFE))
{
send_to_char("You can't charge into that room.\n\r", ch);
pop_call();
return;
}
if (findpath_search_victim(ch, victim, range) == -1)
{
act("You cannot reach $N quickly enough to charge $M.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (mount->move < 10)
{
if (mount != ch)
{
act("Your mount is too tired to charge.", ch, NULL, NULL, TO_CHAR);
}
else
{
act("You are too tired to charge.", ch, NULL, NULL, TO_CHAR);
}
pop_call();
return;
}
if (mount != ch)
{
act("You charge $twards upon $N!", ch, dir_name[dir], mount, TO_CHAR);
act("$n charges $twards upon $N!", ch, dir_name[dir], mount, TO_ROOM);
}
else
{
act("You charge $twards!", ch, dir_name[dir], NULL, TO_CHAR);
act("$n charges $twards!", ch, dir_name[dir], NULL, TO_ROOM);
}
char_from_room(ch);
char_to_room(ch, victim->in_room->vnum, TRUE);
if (mount != ch)
{
ch->mounting = mount;
char_from_room(mount);
char_to_room(mount, ch->in_room->vnum, TRUE);
do_look(mount, "");
}
do_look(ch, "auto");
if (mount != ch)
{
act("$n charges in from $t upon $N!", ch, dir_name[dir], mount, TO_ROOM);
}
else
{
act("$n charges in from $t!", ch, dir_name[dir], NULL, TO_ROOM);
}
if (!IS_NPC(ch))
{
mprog_greet_trigger(ch);
oprog_greet_trigger(ch);
rprog_greet_trigger(ch);
}
move_loss(ch, mount, 10);
// just in case a mprog kills someone...
if (!valid_fight(ch, victim) || !valid_fight(mount, victim))
{
pop_call();
return;
}
SET_BIT(ch->attack, ATTACK_CHARGE);
if (!can_melee_attack(mount, victim))
{
pop_call();
return;
}
one_hit(ch, victim, TYPE_UNDEFINED, 0, weapon);
// ride-by attack feat. Find second victim to iterate attack
if (learned(ch, gsn_ride_by_attack) && IS_AWAKE(ch) && IS_AWAKE(mount))
{
CHAR_DATA *fch;
if ((fch = get_next_fighting(ch, victim)) != NULL)
{
act("$n charges charges through to another target!", ch, NULL, NULL, TO_ROOM);
act("You charge charges through to another target!", ch, NULL, NULL, TO_CHAR);
one_hit(ch, victim, TYPE_UNDEFINED, -5, weapon);
}
}
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
/*
* Rescue - take over an opponent from another
* revised to use PFRPG combat maneuver - Kregor
*/
void do_rescue( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
CHAR_DATA *fch, *fch_next;
push_call("do_rescue(%p,%p)",ch,argument);
one_argument( argument, arg );
if ( arg[0] == '\0' )
{
send_to_char( "Rescue whom?\n\r", ch );
pop_call();
return;
}
if (!in_combat(ch))
{
send_to_char( "This isn't your fight, unless you join in.\n\r", ch );
pop_call();
return;
}
if ((victim = get_char_room(ch, arg)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", arg );
pop_call();
return;
}
if (victim == ch)
{
send_to_char( "What about fleeing instead?\n\r", ch );
pop_call();
return;
}
for (fch = ch->in_room->first_person ; fch != NULL ; fch = fch_next)
{
fch_next = fch->next_in_room;
if (fch->last_attacked && fch->last_attacked == victim)
{
break;
}
}
if (fch == NULL)
{
send_to_char( "That person isn't under attack.\n\r", ch );
pop_call();
return;
}
act( "{128}You attempt to rescue $N!", ch, NULL, victim, TO_CHAR );
act( "{128}$n attempts to rescue you!", ch, NULL, victim, TO_VICT );
act( "{128}$n attempts to rescue $N!", ch, NULL, victim, TO_NOTVICT );
for (fch = ch->in_room->first_person ; fch != NULL ; fch = fch_next)
{
fch_next = fch->next_in_room;
if (fch->last_attacked && fch->last_attacked == victim)
{
if (!learned(ch, gsn_spring_attack) && combat_maneuver_check(fch, ch, gsn_tumble) < 0)
{
attack_of_opportunity(fch, ch);
}
if (combat_maneuver_check(ch, fch, gsn_rescue) >= 0)
{
fch->last_attacked = ch;
act( "{128}$n switches places with your opponent!", ch, NULL, fch, TO_VICT );
}
else
{
break;
}
}
}
gain_favor(ch, DOMAIN_PROTECTION, 4);
gain_favor(ch, DOMAIN_RETRIBUTION, 4);
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return;
}
/*
* Divert - the reverse rescue, one way to flank - Kregor
*/
void do_divert( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
CHAR_DATA *fch, *fch_next;
push_call("do_divert(%p,%p)",ch,argument);
if (!IS_NPC(ch) && !learned(ch, gsn_tumble))
{
send_to_char( "What about fleeing instead?\n\r", ch );
pop_call();
return;
}
if (!in_combat(ch))
{
send_to_char( "You aren't fighting!\n\r", ch );
pop_call();
return;
}
for (fch = ch->in_room->first_person ; fch ; fch = fch->next_in_room)
{
if (fch->last_attacked && fch->last_attacked == ch)
{
break;
}
}
if (fch == NULL)
{
send_to_char( "You aren't under attack.\n\r", ch );
pop_call();
return;
}
if (argument[0] == '\0')
{
send_to_char( "Divert to whom?\n\r", ch );
pop_call();
return;
}
argument = one_argument( argument, arg );
if ((victim = get_char_room(ch, arg)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", arg );
pop_call();
return;
}
if (victim == ch)
{
send_to_char( "What about fleeing instead?\n\r", ch );
pop_call();
return;
}
if (ch->last_attacked == victim)
{
act( "Trying to make $m fight $mself?", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (!in_same_combat(ch, victim))
{
send_to_char( "That person is not fighting with you.\n\r", ch );
pop_call();
return;
}
if (!is_same_group(ch,victim))
{
act( "$N isn't a member of your group.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
for (fch = ch->in_room->first_person ; fch != NULL ; fch = fch_next)
{
fch_next = fch->next_in_room;
if (fch->last_attacked && fch->last_attacked == ch)
{
if (combat_maneuver_check(fch, ch, gsn_tumble) < 0)
{
act( "{138}You fail to divert $N.", ch, NULL, fch, TO_CHAR );
if (!learned(ch, gsn_spring_attack))
{
attack_of_opportunity(fch, ch);
}
break;
}
act( "You divert your attacker to $N!", ch, NULL, victim, TO_CHAR);
act( "$n diverts $s attacker to you!", ch, NULL, victim, TO_VICT);
act( "$n diverts $s attacker to $N!", ch, NULL, victim, TO_NOTVICT);
fch->last_attacked = victim;
}
}
gain_favor(ch, DOMAIN_PROTECTION, -4);
gain_favor(ch, DOMAIN_TRICKERY, 2);
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return;
}
/*
* Sunder, the command and the function. Mobiles use the function alone.
*/
void do_sunder( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
OBJ_DATA *wield, *vicwield;
char arg[MAX_INPUT_LENGTH];
push_call("do_sunder(%p,%p)",ch,argument);
argument = one_argument(argument, arg);
if (argument[0] == '\0' || arg[0] == '\0' )
{
send_to_char( "Sunder what on whom?\n\r", ch );
pop_call();
return;
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
else if ((vicwield = get_obj_wear(victim, arg)) == NULL)
{
act("You don't see $t on $N.", ch, arg, victim, TO_CHAR );
pop_call();
return;
}
if ((wield = get_wield(ch, FALSE)) == NULL || !IS_WEAPON(wield) || WSPEC(wield, WSPEC_MISSILE|WSPEC_NOMELEE))
{
send_to_char( "You need a melee weapon to sunder with.\n\r", ch );
pop_call();
return;
}
sunder(ch, victim, vicwield, 0);
pop_call();
return;
}
/*
* Returns TRUE if the attempt is made, whether or not it succeeds.
*/
bool sunder( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj, int hit_adj )
{
OBJ_DATA *wield;
int dt, dam;
push_call("sunder(%p,%p)",ch,victim);
if (victim == NULL || obj == NULL)
{
pop_call();
return FALSE;
}
if (victim == ch)
{
act("You could just sell it.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if ((wield = get_wield(ch, FALSE)) == NULL || !IS_WEAPON(wield) || WSPEC(wield, WSPEC_MISSILE|WSPEC_NOMELEE))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
dt = TYPE_HIT;
if (!can_see_obj(ch, obj))
{
if (number_percent() > 50)
{
act( "You fail to get a good look at $p.", ch, obj, NULL, TO_CHAR);
pop_call();
return FALSE;
}
}
if (!can_surprise(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (!has_mirror(ch, victim, gsn_sunder))
{
if (!in_combat(ch) || is_active(ch))
{
if (!learned(ch, gsn_imp_sunder) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
{
if (attack_of_opportunity(victim, ch))
{
if (!valid_fight(ch, victim))
{
pop_call();
return TRUE;
}
}
}
}
if (combat_maneuver_check(ch, victim, gsn_sunder) < 0)
{
act( "You fail your attempt to sunder $p.", ch, obj, victim, TO_CHAR);
act( "$n attempts to sunder $p and misses.", ch, obj, victim, TO_NOTVICT);
act( "$n attempts to sunder $p and misses.", ch, obj, victim, TO_VICT);
}
else
{
act( "You attempt to sunder $p on $N.", ch, obj, victim, TO_CHAR);
act( "$n attempts to sunder $p.", ch, obj, victim, TO_NOTVICT);
act( "$n attempts to sunder $p on $N.", ch, obj, victim, TO_VICT);
dam = dice(weapon_table[wield->value[0]].damnodice, weapon_table[wield->value[0]].damsizedice);
damage_equipment(ch, victim, obj, dam, dt, wield);
}
}
// so these don't go off on attacks of opportunity
if (in_same_combat(ch, victim) && is_active(ch))
{
primary_melee_attacks(ch, victim, wield, get_wield(ch, TRUE));
}
if (!IS_NPC(ch) && IS_NPC(victim) && !in_combat(victim))
fight(victim, ch);
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_SUNDER|ATTACK_MELEE);
pop_call();
return TRUE;
}
/*
* Coup de grace, final blow for
* helpless victims - Kregor
*/
void do_coupdegrace( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
OBJ_DATA *wield;
push_call("coup_de_grace(%p,%p)",ch,argument);
wield = get_wield(ch, FALSE);
one_argument( argument, arg );
if (arg[0] == '\0')
{
send_to_char( "To whom is it you wish to deliver a coup de grace?\n\r", ch );
pop_call();
return;
}
if ((victim = get_char_room(ch, arg)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", arg );
pop_call();
return;
}
if (victim == ch)
{
send_to_char( "Putting yourself out of your misery?\n\r", ch);
pop_call();
return;
}
if (!IS_HELPLESS(victim))
{
act( "$N is not as helpless as you may think.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (!CAN_CRITICAL(victim))
{
act( "$N is not subject to critical hits.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return;
}
if (!in_combat(ch) && !in_combat(victim))
{
fight(ch, victim);
}
CHECK_TURN(ch, victim);
if (!ch->concentrating && !learned(ch, gsn_swift_death))
{
if (!check_murder(ch, victim))
{
pop_call();
return;
}
act( "$n readies a coup de grace against $N.", ch, NULL, victim, TO_ROOM );
act( "You ready a coup de grace against $N.", ch, NULL, victim, TO_CHAR );
ch->concentrating = TRUE;
ch->skill_timer = 8;
ch->timer_fun = do_coupdegrace;
RESTRING(ch->cmd_argument, argument);
pop_call();
return;
}
act( "$n attempts a coup de grace on $N!", ch, wield, victim, TO_NOTVICT);
act( "$n attempts a coup de grace on you!", ch, wield, victim, TO_VICT);
act( "You attempt a coup de grace on $N!", ch, wield, victim, TO_CHAR);
one_hit( ch, victim, gsn_coup_de_grace, 0, wield );
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
/*
* Assassin death attack. Issue command once to start 3 round
* observation countdown. After countdown, three more rounds
* to issue command again to assassinate the person - Kregor
*/
void do_assassinate( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
OBJ_DATA *wield;
push_call("do_assassinate(%p,%p)",ch,argument);
if (multi(ch, gsn_assassinate) == -1)
{
send_to_char("You are not that practiced a cut-throat.\n\r", ch );
pop_call();
return;
}
if (argument[0] == '\0')
{
send_to_char( "assassinate whom?\n\r", ch );
pop_call();
return;
}
one_argument( argument, arg );
if ((victim = get_char_room(ch, arg)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", arg );
pop_call();
return;
}
if (victim == ch)
{
send_to_char( "Suicide would be easier.\n\r", ch);
pop_call();
return;
}
if (!CAN_CRITICAL(victim))
{
send_to_char( "You question your ability to land a telling blow.\n\r", ch );
pop_call();
return;
}
if (in_combat(ch) && !is_active(ch))
{
send_to_char( "You need to wait your turn.\n\r", ch );
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return;
}
if (IS_AWAKE(victim) && !IS_FLATFOOTED(victim) && !IS_HELPLESS(victim))
{
act( "You cannot take $N by surprize.", ch, NULL, victim, TO_CHAR );
pop_call();
return;
}
wield = get_wield(ch, FALSE);
if (ch->assassinate == NULL)
{
if (!learned(ch, gsn_swift_death))
{
act( "You begin to study $N's anatomy and movements...", ch, NULL, victim, TO_CHAR);
if (IS_AWAKE(victim) && can_see(victim, ch) && sense_motive_check(victim, ch, sense_motive_roll(victim), bluff_roll(ch)))
{
act( "$N eyes you suspiciously.", ch, NULL, victim, TO_CHAR);
act( "{138}$n eyes you like a stalker sizing up $s kill.", ch, NULL, victim, TO_VICT);
pop_call();
return;
}
ch->assassinate = victim;
ch->asn_count = 71;
pop_call();
return;
}
}
if (!learned(ch, gsn_swift_death))
{
if (victim != ch->assassinate)
{
act( "$N is not your mark.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (ch->asn_count > 36 || ch->asn_count <= 0)
{
act( "You have not studied $N enough to make your move.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
}
if (victim->in_room != ch->in_room)
{
act( "Your target must have slipped out of sight.", ch, NULL, NULL, TO_CHAR);
ch->assassinate = NULL;
ch->asn_count = 0;
pop_call();
return;
}
if (!check_murder(ch, victim))
{
pop_call();
return;
}
one_hit( ch, victim, gsn_assassinate, 0, wield );
ch->assassinate = NULL;
ch->asn_count = 0;
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
/*
* Stunning fist feat - Kregor
*/
void do_stun(CHAR_DATA *ch, char *argument)
{
CHAR_DATA *victim;
push_call("do_stun(%p,%p)",ch,argument);
if (IS_SET(ch->attack, ATTACK_STUNNING_FIST))
{
send_to_char("You've already used that maneuver\n\r", ch);
pop_call();
return;
}
if (!learned(ch, gsn_stunning_fist))
{
send_to_char("You don't know this feat.\n\r",ch);
pop_call();
return;
}
if (!CHECK_USES(ch, gsn_stunning_fist))
{
pop_call();
return;
}
if (hands_full(ch))
{
send_to_char("You cannot make a stunning fist without an open hand.\n\r", ch);
pop_call();
return;
}
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Stun whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
stun(ch, victim, 0);
}
bool stun(CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj)
{
push_call("stun(%p,%p)",ch,victim);
if (IS_SET(ch->attack, ATTACK_STUNNING_FIST))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (!learned(ch, gsn_stunning_fist))
{
pop_call();
return FALSE;
}
if (!CHECK_USES(ch, gsn_stunning_fist))
{
pop_call();
return FALSE;
}
if (get_wield(ch, FALSE) != NULL)
{
pop_call();
return FALSE;
}
BOOL_CHECK_TURN(ch, victim);
one_hit(ch, victim, gsn_stunning_fist, 0, NULL);
// so these don't go off on attacks of opportunity
if (in_same_combat(ch, victim) && is_active(ch))
{
primary_melee_attacks(ch, victim, NULL, NULL);
}
ch->uses[gsn_stunning_fist]++;
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_STUNNING_FIST|ATTACK_MELEE);
pop_call();
return TRUE;
}
/*
* Bullrush maneuver from Open Gaming SRD - Kregor
*/
void do_bullrush( CHAR_DATA *ch, char *argument )
{
char arg1[MAX_STRING_LENGTH];
char arg2[MAX_STRING_LENGTH];
CHAR_DATA *victim;
EXIT_DATA *pExit;
ROOM_INDEX_DATA *to_room;
int door, check;
push_call("do_bullrush(%p,%p)",ch,argument);
argument = one_argument(argument, arg1);
argument = one_argument(argument, arg2);
if (arg1[0] == '\0' || arg2[0] == '\0')
{
send_to_char("Syntax: bullrush <target> <direction>\n\r", ch);
pop_call();
return;
}
if ((victim = get_char_room(ch, arg1)) == NULL)
{
send_to_char( "Bullrush whom?\n\r", ch );
pop_call();
return;
}
if (victim == ch)
{
send_to_char( "You shouldn't shove yourself around like that.\n\r", ch );
pop_call();
return;
}
if ((door = direction_door(arg2)) == -1)
{
act("Bullrush $N in what direction?", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if ((pExit = get_exit(ch->in_room->vnum, door)) == NULL
|| (to_room = room_index[pExit->to_room]) == NULL)
{
ch_printf_color(ch, "Alas, you cannot press anyone in that direction.\n\r");
pop_call();
return;
}
if (!IS_AWAKE(victim))
{
act( "You can't bullrush $N while $E's down.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (get_size(ch) + 1 < get_size(victim))
{
act( "$N is too big for you to push around.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (is_mounting(victim))
{
send_to_char( "You can't bullrush a mounted target... MAYBE you could rush the mount.\n\r", ch );
pop_call();
return;
}
if (!can_move_char(ch, door))
{
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!can_surprise(ch, victim))
{
pop_call();
return;
}
if (!has_mirror(ch, victim, gsn_bullrush))
{
if (!in_combat(ch) || is_active(ch))
{
if (!learned(ch, gsn_imp_bullrush) || (learned(victim, gsn_combat_reflexes) && victim->level > ch->level - 4))
{
if (attack_of_opportunity(victim, ch))
{
if (!valid_fight(ch, victim))
{
pop_call();
return;
}
}
}
}
if ((check = combat_maneuver_check(ch, victim, gsn_bullrush)) >= 10)
{
act( "You press $N $tward, travelling with $M!", ch, dir_name[door], victim, TO_CHAR);
act( "$n presses you $tward, travelling with you!", ch, dir_name[door], victim, TO_VICT);
act( "$n presses $N $tward, travelling with $M!", ch, dir_name[door], victim, TO_NOTVICT);
char_from_room(ch);
char_to_room(ch, to_room->vnum, TRUE);
char_from_room(victim);
char_to_room(victim, to_room->vnum, TRUE);
act( "$n and $N arrive in a press from $T.", ch, rev_dir_name[door], victim, TO_NOTVICT);
}
else if (check >= 0)
{
act( "You press $N back away from you.", ch, NULL, victim, TO_CHAR);
act( "$n presses you back away from $m.", ch, NULL, victim, TO_VICT);
act( "$n presses $N back away from $m.", ch, NULL, victim, TO_NOTVICT);
victim->distracted = 2;
}
else
{
act( "You try to bullrush $N but $E resists your attempt.", ch, NULL, victim, TO_CHAR);
act( "$n tries to bullrush you, and you resist $s attempt", ch, NULL, victim, TO_VICT);
act( "$n tries to bulrush $N and fails!", ch, NULL, victim, TO_NOTVICT);
}
}
TAKE_ACTION(ch, ACTION_STANDARD);
SET_BIT(ch->attack, ATTACK_MELEE);
if (!IS_NPC(ch) && IS_NPC(victim) && !in_combat(victim))
fight(victim, ch);
pop_call();
return;
}
/*
* Turned gouge into a feat, trainable by
* rogues as a rogue talent - Kregor
*/
void do_gouge( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
AFFECT_DATA af;
int check;
push_call("do_gouge(%p,%p)",ch,argument);
if (!learned(ch, gsn_gouge))
{
ch_printf_color(ch, "You haven't learned this dirty trick!\n\r");
pop_call();
return;
}
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Gouge whose eyes out?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if (ch == victim)
{
ch_printf(ch, "Claw your own eyes out?\n\r", argument );
pop_call();
return;
}
if (CAN_CRITICAL(victim))
{
send_to_char("You wouldn't even begin to know where to gouge.\n\r", ch );
pop_call();
return;
}
if (IS_RACE_AFFECT(victim, AFF_BLIND))
{
act("$N cannot be blinded.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!can_surprise(ch, victim))
{
pop_call();
return;
}
if (!has_mirror(ch, victim, gsn_gouge))
{
if ((check = combat_maneuver_check(ch, victim, gsn_gouge)) < 0)
{
damage(ch, victim, 0, gsn_gouge, NULL);
}
else
{
damage(ch, victim, dice(1,4), gsn_gouge, NULL);
if (valid_fight(ch, victim))
{
if (!IS_AFFECTED(victim, AFF_BLIND))
{
if (check > 0)
{
act( "{138}$N screams as you gouge at $S eyes!", ch, NULL, victim, TO_CHAR );
act( "{138}You scream and can't see a thing as $n gouges your eyes!", ch, NULL, victim, TO_VICT );
act( "{138}$N screams as $n gouges at $S eyes!", ch, NULL, victim, TO_NOTVICT );
af.type = gsn_gouge;
af.location = APPLY_NONE;
af.modifier = 0;
af.duration = check;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF_BLIND;
affect_to_char( ch, victim, &af );
}
else
{
act( "$N seems to be able to see despite your attack!", ch, NULL, victim, TO_CHAR );
act( "You are unhindered by $n's attack.", ch, NULL, victim, TO_VICT );
}
}
}
else
{
act( "Your fingers plunge into your victim's brain, causing immediate death!", ch, NULL, NULL, TO_CHAR );
}
}
}
pop_call();
return;
}
/*
* Intimidate skill active command - Kregor 06/09/07
*/
void do_intimidate (CHAR_DATA * ch, char *argument)
{
CHAR_DATA *victim;
AFFECT_DATA af;
int check;
push_call("do_intimidate(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
send_to_char ("Intimidate whom?\n\r", ch);
pop_call();
return;
}
if (!in_combat(ch))
{
send_to_char ("You are not fighting anyone.\n\r", ch);
pop_call();
return;
}
if ((victim = get_char_room(ch, argument)) == NULL )
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if (ch == victim)
{
send_to_char ("Oooh, you've scared yourself!\n\r", ch);
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!IS_AFFECTED(victim, AFF2_FEAR))
{
if ((check = intimidate_check(ch, victim, intimidate_roll(ch), 0)) >= 0)
{
af.type = gsn_intimidate;
af.duration = 1 + check / 5;
af.location = APPLY_NONE;
af.modifier = 0;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_FEAR;
af.level = ch->level;
affect_to_char( ch, victim, &af );
victim->fear_level++;
act( "$N is visibly intimidated by your presence.", ch, NULL, victim, TO_CHAR);
act( "You are shaken at $n's intimidating presence.", ch, NULL, victim, TO_VICT);
act( "$N is visibly intimidated by $n's presence.", ch, NULL, victim, TO_NOTVICT);
}
else
{
act( "$N shrugs off your effort at being intimidating.", ch, NULL, victim, TO_CHAR);
act( "You shrug off $n's attempt at intimidating you.", ch, NULL, victim, TO_VICT);
act( "$N shrugs off $n's effort at being intimidating.", ch, NULL, victim, TO_NOTVICT);
}
}
else
{
act ("$N is already fearful.", ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return;
}
/*
* Feint, Bluff skill in combat. Mobiles use the function alone.
*/
void do_feint( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
push_call("do_feint(%p,%p)",ch,argument);
if (argument[0] == '\0')
{
if ((victim = ch->last_attacked) == NULL)
{
send_to_char( "Feint against whom?\n\r", ch );
pop_call();
return;
}
}
else if ((victim = get_char_room(ch, argument)) == NULL)
{
ch_printf(ch, "There's no %s here.\n\r", argument );
pop_call();
return;
}
if (IS_SET(ch->action, ACTION_STANDARD) && !learned(ch, gsn_improved_feint))
{
send_to_char("You have already made a standard action this round.\n\r",ch);
pop_call();
return;
}
if (victim == ch)
{
send_to_char_color( "Distract yourself?", ch );
pop_call();
return;
}
CHECK_TURN(ch, victim);
feint(ch, victim, 0);
pop_call();
return;
}
/*
* Feint command, for bluff skill in combat.
*/
bool feint( CHAR_DATA *ch, CHAR_DATA *victim, int hit_adj )
{
AFFECT_DATA af;
int diceroll, dc;
push_call("feint(%p,%p)",ch,victim);
if (IS_NPC(ch) && !learned(ch, gsn_backstab))
{
pop_call();
return FALSE;
}
if (!in_combat(ch) || !is_active(ch))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
diceroll = feint_roll(ch);
if (victim->distracted > 0 || is_affected(victim, gsn_bluff))
{
act( "$N won't be fooled again.", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
if (victim->position <= POS_SLEEPING || !can_see(victim, ch))
{
act( "$N is not paying attention to you.", ch, NULL, victim, TO_CHAR );
pop_call();
return FALSE;
}
if (get_curr_int(victim) <= 0)
{
act( "$N just ignores your attempt.",ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
else if (victim->perm_int <= 2)
diceroll -= 8;
else if (!IS_HUMANOID(victim))
diceroll -= 4;
if (learned(ch, gsn_greater_feint))
{
if (!IS_SET(ch->action, ACTION_SWIFT))
TAKE_ACTION(ch, ACTION_SWIFT);
else
TAKE_ACTION(ch, ACTION_MOVE);
}
else if (learned(ch, gsn_improved_feint))
TAKE_ACTION(ch, ACTION_MOVE);
else
TAKE_ACTION(ch, ACTION_STANDARD);
dc = UMAX(base_attack(victim), learned(victim, gsn_sense_motive)) + stat_bonus(TRUE, victim, APPLY_WIS) + dice(1,20);
if (sense_motive_check(victim, ch, dc, diceroll))
{
act( "$t$N ignores your feint attempt.", ch, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_CHAR );
act( "$tYou outsmart $n's feint attempt against you!", ch, get_color_string(victim, COLOR_YOU_HIT, VT102_BOLD), victim, TO_VICT);
victim->distracted = 0;
affect_strip(victim, gsn_bluff);
pop_call();
return TRUE;
}
act( "$t$n feints you and you drop your guard!", ch, get_color_string(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_VICT);
act( "$tYou feint against $N!", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), victim, TO_CHAR);
act( "$n feints against $N!", ch, NULL, victim, TO_NOTVICT);
af.type = gsn_bluff;
af.duration = 2;
af.location = APPLY_NONE;
af.modifier = 0;
af.bittype = AFFECT_TO_NONE;
af.bitvector = 0;
af.level = diceroll;
affect_to_char( ch, victim, &af );
pop_call();
return TRUE;
}
/*
* Taunt command, for bluff skill against casters.
*/
void do_taunt( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
int diceroll, victroll;
push_call("do_taunt(%p,%p)",ch,argument);
argument = one_argument(argument, arg);
if (arg[0] == '\0')
{
send_to_char("Syntax: taunt <target>\n\r",ch);
pop_call();
return;
}
if ((victim = get_char_room(ch, arg)) == NULL)
{
send_to_char("They are not here.\n\r",ch);
pop_call();
return;
}
if (victim == ch)
{
send_to_char_color( "You taunt yourself?", ch );
pop_call();
return;
}
if (!victim->casting && !victim->concentrating)
{
act( "$N is doing nothing to be distracted from.", ch, NULL, victim, TO_CHAR );
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (victim->distracted > 0 || is_affected(victim, gsn_bluff))
{
act( "$N won't be fooled again.", ch, NULL, victim, TO_CHAR );
pop_call();
return;
}
if (victim->position <= POS_SLEEPING || !can_see(victim, ch))
{
act( "$N is not paying attention to you.", ch, NULL, victim, TO_CHAR );
pop_call();
return;
}
diceroll = bluff_roll(ch);
victroll = concentration_roll(victim);
if (in_combat(ch) && !is_active(ch))
{
take_opportunity(ch, victim);
}
TAKE_ACTION(ch, ACTION_STANDARD);
if (get_curr_int(victim) <= 0)
{
act( "$N just ignores your attempt.",ch, NULL, victim, TO_CHAR);
pop_call();
return;
}
else if (victim->perm_int <= 2)
{
diceroll -= 8;
}
if (concentration_check(victim, ch, victroll, diceroll))
{
act( "$N ignores your taunting.", ch, NULL, victim, TO_CHAR );
act( "$n attempts to taunt you, but you ignore it.", ch, NULL, victim, TO_VICT);
act( "$n unsuccessfully tries to taunt $N.", ch, NULL, victim, TO_NOTVICT);
victim->distracted = 0;
affect_strip(victim, gsn_bluff);
pop_call();
return;
}
act( "$n's taunting distracts you from your concentration!", ch, NULL, victim, TO_VICT);
act( "Your taunting distracts $N from $S concentration!", ch, NULL, victim, TO_CHAR);
act( "$n's taunting distracts $N from $S concentration!", ch, NULL, victim, TO_NOTVICT);
if (victim->casting)
{
if (victim->cast_sn == gsn_recite_scroll && victim->reciting)
{
junk_obj(victim->reciting);
}
free_cast(victim);
}
if (victim->concentrating)
{
free_skill(victim);
}
pop_call();
return;
}
/*
* For creatures with rend attacks - Kregor 9/19/08
*/
void do_rend( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
push_call("do_rend(%p,%p)",ch,argument);
if (!learned(ch, gsn_rend))
{
send_to_char("You cannot do that.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, arg);
if (arg[0] == '\0' || (victim = get_char_room(ch, arg)) == NULL)
{
if ((victim = who_fighting(ch)) == NULL)
{
send_to_char("You are not fighting anyone.\n\r",ch);
pop_call();
return;
}
}
if (in_combat(ch) && !is_active(ch))
{
send_to_char("Just wait your turn.\n\r", ch);
pop_call();
return;
}
rend(ch, victim);
pop_call();
return;
}
/*
* For creatures with rend attacks - Kregor 9/19/08
* Does additional damage over and above the two attacks
* if both attacks hit. This is over and above DR,
* equal to the damage of both attacks, plus magic
* or STR bonuses. No other bonuses apply.
*/
bool rend( CHAR_DATA *ch, CHAR_DATA *victim )
{
bool hitone, hittwo;
int dam, dam2;
push_call("rend(%p,%p)",ch,victim);
if (!learned(ch, gsn_rend))
{
pop_call();
return FALSE;
}
if (get_wield(ch, FALSE) || get_wield(ch, TRUE) || get_hold(ch))
{
send_to_char("You must rend with two claws.\n\r", ch);
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (!check_murder(ch, victim))
{
pop_call();
return FALSE;
}
if (victim != NULL && in_combat(victim))
{
if (!in_combat(ch) || (ch->last_attacked && ch->last_attacked != victim))
{
send_to_char("You can only rend someone you are actively fighting.\n\r", ch);
pop_call();
return FALSE;
}
if (!is_active(ch))
{
send_to_char("Just wait your turn!\n\r", ch);
pop_call();
return FALSE;
}
}
if (!has_mirror(ch, victim, gsn_rend))
{
hitone = (one_hit(ch, victim, TYPE_UNDEFINED, 0, NULL));
hittwo = (one_hit(ch, victim, TYPE_UNDEFINED, 0, NULL));
//if either one doesn't connect, there's no rend
if (!hitone || !hittwo)
{
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return TRUE;
}
//calc each attack's additional damg
dam = dice(get_damnodice(ch), get_damsizedice(ch));
dam2 = dice(get_damnodice(ch), get_damsizedice(ch));
dam2 += get_apply(ch, APPLY_COMP_DAMG);
dam2 += get_apply(ch, APPLY_LUCK_DAMG);
dam2 += get_apply(ch, APPLY_MOR_DAMG);
if (IS_AFFECTED(ch, AFF2_SICKENED))
{
dam -= 2;
dam2 -= 2;
}
dam += dam2;
//it's a single effect, so mods only once
dam += stat_bonus(TRUE, ch, APPLY_STR) * 3 / 2;
dam += get_apply(ch, APPLY_COMP_DAMG);
dam += get_apply(ch, APPLY_LUCK_DAMG);
dam += get_apply(ch, APPLY_MOR_DAMG);
if (dam <= 0)
dam = 1;
dam_message(ch, victim, dam, gsn_rend, NULL);
//type nofight bypasses DR, damage is supplemental to hits - Kregor
damage(ch, victim, dam, TYPE_NOFIGHT, NULL);
}
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return TRUE;
}
/*
* For creatures with pounce attacks - Kregor 9/19/08
*/
void do_pounce( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
push_call("do_pounce(%p,%p)",ch,argument);
if (!learned(ch, gsn_pounce))
{
send_to_char("You cannot do that.\n\r", ch);
pop_call();
return;
}
argument = one_argument(argument, arg);
if (arg[0] == '\0' || (victim = get_char_room(ch, arg)) == NULL)
{
if ((victim = who_fighting(ch)) == NULL)
{
send_to_char("You are not fighting anyone.\n\r",ch);
pop_call();
return;
}
}
if (in_combat(ch) && !is_active(ch))
{
send_to_char("Just wait your turn.\n\r", ch);
pop_call();
return;
}
pounce(ch, victim);
pop_call();
return;
}
/*
* For creatures with pounce attacks - Kregor 9/19/08
* Allows a full-round action to launch all possible
* attacks including a rake against a single foe.
*/
bool pounce( CHAR_DATA *ch, CHAR_DATA *victim )
{
int part, count;
push_call("pounce(%p,%p)",ch,victim);
if (!learned(ch, gsn_pounce))
{
pop_call();
return FALSE;
}
if (is_safe(ch, victim))
{
pop_call();
return FALSE;
}
if (!can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (IS_SET(ch->action, ACTION_FULL))
{
pop_call();
return FALSE;
}
if (!check_murder(ch, victim))
{
pop_call();
return FALSE;
}
BOOL_CHECK_TURN(ch, victim);
act("You pounce onto $N!", ch, NULL, victim, TO_CHAR);
act("$n pounces onto $N!", ch, NULL, victim, TO_NOTVICT);
act("$n pounce onto you!", ch, NULL, victim, TO_VICT);
if (!has_mirror(ch, victim, gsn_pounce))
{
for (ch->attack_part = -1, part = 0 ; part < ATTK_MAX ; part++)
{
if (!valid_fight(ch, victim))
break;
if (num_attacks(ch, part) == -1)
{
pop_call();
return FALSE;
}
if (num_attacks(ch, part))
{
for (count = 0 ; count < num_attacks(ch, part) ; count++)
{
ch->attack_part = part;
one_hit(ch, victim, TYPE_HIT, 0, NULL);
}
}
}
}
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return TRUE;
}
/*
* Drain result from creatures with draining attacks - Kregor
* Note the variant of draining puts the saving throw at the
* beginning, and all successful drains are permanent unless restored.
*/
void level_drain( CHAR_DATA *ch, CHAR_DATA *victim )
{
AFFECT_DATA af;
push_call("level_drain(%p,%p,%p)",ch,victim);
if (!save_resist(ch, victim, gsn_drain, ch->level))
{
af.type = gsn_drain;
af.duration = -1;
af.modifier = 0 - learned(ch, gsn_drain);
af.location = APPLY_LEVEL;
af.bittype = AFFECT_TO_NONE;
af.bitvector = AFF_NONE;
af.level = ch->level;
affect_join( ch, victim, &af );
act( "$n wails as the life force is drawn from $m!", victim, NULL, NULL, TO_ROOM);
act( "You wail as your life force is drawn from you!", victim, NULL, NULL, TO_CHAR);
update_pos(victim, -1);
af.type = gsn_drain;
af.duration = 100;
af.bittype = AFFECT_TO_NONE;
af.bitvector = 0;
af.level = ch->level;
af.location = APPLY_HIT;
af.modifier = learned(ch, gsn_drain) * 5;
affect_join( ch, ch, &af );
}
pop_call();
return;
}
/*
* Stat damaging abilities pass through this function
* at damage function - Kregor
*/
void stat_damage( CHAR_DATA *ch, CHAR_DATA *victim, int apply, int sn )
{
AFFECT_DATA af;
int mod;
push_call("stat_damage(%p,%p,%p,%p)",ch,victim,apply,sn);
if (ch == NULL)
{
ch = victim;
}
if (sn == gsn_poison)
{
mod = dice(1,3);
}
else
{
mod = dice(1, race_skill(ch,sn));
// drains get critical hits too!
if (CRIT_HIT)
{
mod += dice(1, race_skill(ch,sn));
}
}
if (domain_apotheosis(victim, DOMAIN_REPOSE))
{
if (sn == gsn_str_drain || sn == gsn_dex_drain || sn == gsn_con_drain
|| sn == gsn_int_drain || sn == gsn_wis_drain || sn == gsn_cha_drain)
{
pop_call();
return;
}
}
if (!is_immune(victim, skill_table[sn].spell_desc))
{
af.type = sn;
af.duration = -1;
af.modifier = 0 - mod;
af.location = apply;
af.bittype = AFFECT_TO_NONE;
af.bitvector = AFF_NONE;
af.level = ch->level;
affect_join( ch, victim, &af );
if (victim != ch && (IS_SET(skill_table[sn].flags, SF_TOUCH) || ch->attack_part == ATTK_TOUCH))
{
act( "{108}$N staggers as your touch debilitates $M!", ch, NULL, victim, TO_CHAR);
act( "{108}You stagger as $n's touch debilitates you!", ch, NULL, victim, TO_VICT);
act( "{108}$N staggers as $n's touch debilitates $M!", ch, NULL, victim, TO_NOTVICT);
// so that drain affects boost the attacker
if (IS_SET(skill_table[sn].spell_desc, SDESC_NEGATIVE))
{
af.type = sn;
af.duration = 100;
af.bittype = AFFECT_TO_NONE;
af.bitvector = 0;
af.level = ch->level;
af.location = APPLY_HIT;
af.modifier = 5;
affect_join( ch, ch, &af );
}
}
// stat damage for cockatrice is gradual petrification
if (ch->race == RACE_COCKATRICE && get_curr_dex(victim) == 0)
{
af.type = sn;
af.duration = -1;
af.modifier = 0;
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_PETRIFICATION;
af.level = ch->level;
affect_join( ch, victim, &af );
act( "{108}$n freezes and turns to stone!", victim, NULL, NULL, TO_ROOM);
act( "{108}Your limbs stiffen, then everything goes black!", victim, NULL, NULL, TO_CHAR);
}
update_pos(victim,-1);
}
pop_call();
return;
}
/*
* Natural poison attack - Kregor
* This triggers upon successful hit of poisoned body part
*/
void poison_attack(CHAR_DATA *ch, CHAR_DATA *victim, int type)
{
POISON_DATA pd;
int bonus;
push_call("poison_attack(%p,%p,%p)",ch,victim,type);
pd.type = type;
pd.constant_duration = -1;
if (!IS_NPC(ch))
pd.poisoner = ch->pcdata->pvnum;
else
pd.poisoner = -1;
// calc actual DC of natural poison based on level and stat
if (get_curr_con(ch) < 0)
bonus = stat_bonus(TRUE, ch, APPLY_CHA);
else
bonus = stat_bonus(TRUE, ch, APPLY_CON);
pd.dc = ch->level / 2 + 10 + bonus;
poison_to_char(ch, victim, &pd);
pop_call();
return;
}
/*
* Save or contract disease from attack - Kregor
*/
void disease_attack(CHAR_DATA *ch, CHAR_DATA *victim, int type)
{
push_call("poison_attack(%p,%p,%p)",ch,victim,type);
if (save_resist(ch, victim, gsn_disease_attack, ch->level))
{
pop_call();
return;
}
infect_char(ch, victim, type);
pop_call();
return;
}
/*
* Frightful Presence attack for dragons etc.
* done at entry into combat - Kregor
*/
void frightful_presence(CHAR_DATA *ch)
{
CHAR_DATA *vch, *vch_next;
AFFECT_DATA af;
int sn, duration;
push_call("frightful_presence(%p)",ch);
if (!race_skill(ch, gsn_frightful_presence))
{
if (!race_skill(ch, gsn_fearful_howl))
{
if (!race_skill(ch, gsn_fear_aura))
{
pop_call();
return;
}
else
{
sn = gsn_fear_aura;
}
}
else
{
sn = gsn_fearful_howl;
act("{118}You let out a chilling howl!", ch, NULL, NULL, TO_CHAR);
act("{118}$n lets out a chilling howl!", ch, NULL, NULL, TO_ROOM);
do_shout(ch, "ArrraoooOOOooOOoo..oo..ooo!");
}
}
else
{
sn = gsn_frightful_presence;
}
for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
{
vch_next = vch->prev_in_room;
if (is_same_group(ch, vch))
continue;
if (!can_mass_cast(ch, vch, -1))
continue;
if (ch->race == vch->race)
continue;
if (race_type(ch) == RTYPE_DRAGON && race_type(vch) == RTYPE_DRAGON)
continue;
if (rspec_req(ch, RSPEC_DEMON) && rspec_req(vch, RSPEC_DEMON))
continue;
if (rspec_req(ch, RSPEC_DEVIL) && rspec_req(vch, RSPEC_DEVIL))
continue;
if (save_resist(ch, vch, sn, ch->level))
continue;
if (sn == gsn_frightful_presence)
duration = dice(5,6);
else if (sn == gsn_fearful_howl)
duration = dice(2,4);
else
duration = ch->level;
af.type = sn;
af.level = ch->level;
af.duration = duration;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_FEAR;
af.location = APPLY_NONE;
af.modifier = 0;
affect_join( ch, vch, &af);
vch->fear_level = UMIN(vch->fear_level + 2, 3);
act( "{118}You are overcome with terror in $N's presence!", vch, NULL, ch, TO_CHAR);
act( "{118}$n is overcome with terror!", vch, NULL, NULL, TO_ROOM);
if (in_combat(vch))
{
do_withdraw(vch, NULL);
}
}
pop_call();
return;
}
/*
* for the stench racial ability - Kregor
*/
void check_stench(CHAR_DATA *ch)
{
CHAR_DATA *vch, *vch_next;
AFFECT_DATA af;
push_call("check_stench(%p)",ch);
if (!race_skill(ch, gsn_stench))
{
pop_call();
return;
}
if (number_range(1,6) == 1)
act("{128}A vile stench wafts from $n.", ch, NULL, NULL, TO_ROOM);
for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
{
vch_next = vch->prev_in_room;
if (race_skill(vch, gsn_stench))
continue;
if (!can_mass_cast(ch, vch, -1))
continue;
if (save_resist(ch, vch, gsn_stench, ch->level))
{
act("You manage to ignore the smell of $N.", vch, NULL, ch, TO_CHAR);
affect_strip(ch, gsn_stench);
continue;
}
af.type = gsn_stench;
af.level = ch->level;
af.duration = 2;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_SICKENED;
af.location = APPLY_NONE;
af.modifier = 0;
affect_join( ch, vch, &af);
act( "{128}$N's stench makes you want to wretch!", vch, NULL, ch, TO_CHAR);
act( "{128}$n fights the urge to wretch!", vch, NULL, NULL, TO_ROOM);
}
pop_call();
return;
}
/*
* for aura affects - Kregor
*/
void check_aura(CHAR_DATA *ch, int sn)
{
CHAR_DATA *vch, *vch_next;
AFFECT_DATA af;
push_call("check_aura(%p,%p)",ch,sn);
if (!race_skill(ch, sn))
{
pop_call();
return;
}
for (vch = ch->in_room->last_person ; vch ; vch = vch_next)
{
vch_next = vch->prev_in_room;
if (who_fighting(vch) != ch)
continue;
if (!can_mass_cast(ch, vch, -1))
continue;
if (is_immune(vch, skill_table[sn].spell_desc))
continue;
if (will_save(vch, ch, ch->level, sn))
{
act("You manage to overcome $N's aura.", vch, NULL, ch, TO_CHAR);
affect_strip(ch, sn);
continue;
}
if (sn == gsn_aura_of_menace)
{
af.type = sn;
af.level = ch->level;
af.duration = 2;
af.bittype = AFFECT_TO_NONE;
af.bitvector = AFF_NONE;
af.location = APPLY_INS_AC;
af.modifier = -2;
affect_join( ch, vch, &af);
af.location = APPLY_MOR_TOHIT;
af.modifier = -2;
affect_join( ch, vch, &af);
af.location = APPLY_MOR_SAVES;
af.modifier = -2;
affect_join( ch, vch, &af);
act( "{138}You are repulsed by $N's righteous aura!", vch, NULL, ch, TO_CHAR);
}
if (sn == gsn_babble)
{
af.type = sn;
af.level = ch->level;
af.duration = 2;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_FASCINATED;
af.location = APPLY_NONE;
af.modifier = 0;
affect_join( ch, vch, &af);
act( "{138}You are fascinated by $N's endless babble!", vch, NULL, ch, TO_CHAR);
}
}
pop_call();
return;
}
/*
* check for the defensive roll ability - Kregor
*/
bool defensive_roll( CHAR_DATA *ch, CHAR_DATA *attacker, int dt, int dam )
{
push_call("defensive_roll(%p,%p,%p)",ch,attacker,dt,dam);
if (!IS_AWAKE(ch))
{
pop_call();
return FALSE;
}
if (IS_SET(ch->attack, ATTACK_DEF_ROLL)) // only one in a round
{
pop_call();
return FALSE;
}
if (ch->hit - dam > 0) // only if attack reduces ch below 0 hp
{
pop_call();
return FALSE;
}
if (!learned(ch, gsn_defensive_roll)) //duh!
{
pop_call();
return FALSE;
}
if (ch->uses[gsn_defensive_roll]) // one per day
{
pop_call();
return FALSE;
}
if (!is_attack(dt)) // has to be a physical attack
{
pop_call();
return FALSE;
}
if (!can_dodge(ch, attacker)) // cannot roll if denied DEX bonus
{
pop_call();
return FALSE;
}
if (!refl_save(ch, attacker, dam, gsn_defensive_roll)) // REFL save vs damage dealt
{
pop_call();
return FALSE;
}
act("{138}$n rolls with the brunt of your blow!", ch, NULL, attacker, TO_VICT);
act("{138}$n rolls with the brunt of $N's blow!", ch, NULL, attacker, TO_NOTVICT);
act("{138}you roll with the brunt of $N's blow!", ch, NULL, attacker, TO_CHAR);
SET_BIT(ch->attack, ATTACK_DEF_ROLL);
ch->uses[gsn_defensive_roll]++;
pop_call();
return TRUE;
}
/*
* Rust monsters, Black Puddings and other corrosive things - Kregor
*/
void corrosive_touch( CHAR_DATA *ch, CHAR_DATA *victim, int level )
{
OBJ_DATA *obj;
int cnt, pick;
push_call("spell_rusting_grasp(%p,%p,%p)",ch,victim,level);
switch (race_table[get_race(victim)].material)
{
case MATERIAL_STEEL:
case MATERIAL_COLD_IRON:
damage( ch, victim, dice(3, 6) + UMIN(level, 15), gsn_corrosion, NULL );
pop_call();
return;
default:
break;
}
for (cnt = 0, obj = victim->first_carrying ; obj ; obj = obj->next_content)
{
if (!IS_WORN(obj))
{
continue;
}
if (material_table[obj->material].parent != MATERIAL_TYPE_METAL)
{
continue;
}
if (!IS_OBJ_TYPE(obj, ITEM_ARMOR) && !IS_OBJ_TYPE(obj, ITEM_WEAPON))
{
continue;
}
if (IS_OBJ_STAT(obj, ITEM_MAGIC|ITEM_BROKEN))
{
continue;
}
cnt++;
}
if (cnt)
{
pick = number_range(1,cnt);
for (cnt = 0, obj = victim->first_carrying ; obj ; obj = obj->next_content)
{
if (!IS_WORN(obj))
{
continue;
}
if (material_table[obj->material].parent != MATERIAL_TYPE_METAL)
{
continue;
}
if (!IS_OBJ_TYPE(obj, ITEM_ARMOR) && !IS_OBJ_TYPE(obj, ITEM_WEAPON))
{
continue;
}
if (IS_OBJ_STAT(obj, ITEM_MAGIC|ITEM_BROKEN))
{
continue;
}
if (cnt == pick)
{
damage_equipment(ch, victim, obj, dice(1,6) + UMIN(level/2,10), gsn_corrosion, NULL);
break;
}
}
}
pop_call();
return;
}
/*
* For the SPLIT defense of oozes - Kregor
*/
bool split( CHAR_DATA *ch, int dam_type )
{
MOB_INDEX_DATA *pMob;
CHAR_DATA *mh;
int lev, vnum, hp, hp_max;
push_call("split(%p)",ch);
if (!IS_NPC(ch))
{
pop_call();
return FALSE;
}
// because splitting mset npc's will cause unexpected results - Kregor
if (ch->race != ch->pIndexData->race)
{
pop_call();
return FALSE;
}
if (!race_skill(ch, gsn_split))
{
pop_call();
return FALSE;
}
if (ch->level <= 1)
{
pop_call();
return FALSE;
}
if (!IS_SET(dam_type, DAM_SLASH|DAM_PIERCE))
{
pop_call();
return FALSE;
}
vnum = ch->pIndexData->vnum;
lev = ch->level/2;
hp = ch->hit/2;
hp_max = ch->max_hit/2;
pMob = get_mob_index( vnum );
mh = create_mobile( pMob );
char_to_room( mh, ch->in_room->vnum, TRUE );
ch->level = lev;
ch->hit = hp;
ch->max_hit = hp_max;
mh->level = lev;
mh->hit = hp;
mh->max_hit = hp_max;
act( "$n splits in two!", ch, NULL, NULL, TO_CHAR);
act( "$n splits in two!", ch, NULL, NULL, TO_ROOM);
if (who_fighting(ch))
fight(mh, who_fighting(ch));
pop_call();
return TRUE;
}
/*
* Sever a limb - Kregor
*/
bool sever_limb( CHAR_DATA *ch, CHAR_DATA *victim, lg_int part )
{
OBJ_DATA *obj;
push_call("sever_limb(%p,%p)",ch,victim);
switch(part)
{
case CAN_WEAR_WRIST:
if (IS_SEVERED(victim, CAN_WEAR_WRIST))
part = CAN_WEAR_HANDS;
break;
case CAN_WEAR_ANKLE:
if (IS_SEVERED(victim, CAN_WEAR_ANKLE))
part = CAN_WEAR_FEET;
break;
case CAN_WEAR_FINGER:
if (IS_SEVERED(victim, CAN_WEAR_FINGER))
part = CAN_WEAR_HANDS;
break;
default:
act("That part cannot be severed.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
SET_BIT(victim->severed, part);
victim->hit -= victim->hit / 4;
act("$n viciously severs your $T!", ch, flag_string(part, w_flags), victim, TO_VICT);
act("$n viciously severs $N's $T!", ch, flag_string(part, w_flags), victim, TO_NOTVICT);
act("You viciously sever $N's $T!", ch, flag_string(part, w_flags), victim, TO_CHAR);
// unequip items on severed part(s)
for (obj = victim->first_carrying ; obj ; obj = obj->next_content)
{
if (part == CAN_WEAR_HANDS)
{
if (!WEAR_LOC(obj, WEAR_HANDS)
&& !WEAR_LOC(obj, WEAR_WIELD)
&& !WEAR_LOC(obj, WEAR_HOLD)
&& !WEAR_LOC(obj, WEAR_SHIELD)
&& !WEAR_LOC(obj, WEAR_DUAL_WIELD)
&& !WEAR_LOC(obj, WEAR_FINGER_L)
&& !WEAR_LOC(obj, WEAR_FINGER_R)
&& !WEAR_LOC(obj, WEAR_WRIST_L)
&& !WEAR_LOC(obj, WEAR_WRIST_R))
continue;
}
if (part == CAN_WEAR_FINGER)
{
if (!WEAR_LOC(obj, WEAR_FINGER_R))
continue;
}
if (part == CAN_WEAR_FEET)
{
if (!WEAR_LOC(obj, WEAR_FEET)
&& !WEAR_LOC(obj, WEAR_ANKLE_L)
&& !WEAR_LOC(obj, WEAR_ANKLE_R))
continue;
}
if (part == CAN_WEAR_ANKLE)
{
if (!WEAR_LOC(obj, WEAR_ANKLE_R))
continue;
}
if (part == CAN_WEAR_FEET)
{
if (!WEAR_LOC(obj, WEAR_FEET)
&& !WEAR_LOC(obj, WEAR_ANKLE_L)
&& !WEAR_LOC(obj, WEAR_ANKLE_R))
continue;
}
if (part == CAN_WEAR_WRIST)
{
if (!WEAR_LOC(obj, WEAR_WRIST_R)
&& !WEAR_LOC(obj, WEAR_FINGER_R)
&& !WEAR_LOC(obj, WEAR_WIELD)
&& !WEAR_LOC(obj, WEAR_BOTH_HANDS))
continue;
}
unequip_char(victim, obj, TRUE);
obj_from_char(obj);
obj_to_room(obj, victim->in_room->vnum);
act("$p falls onto the ground!", ch, NULL, NULL, TO_ALL);
}
pop_call();
return TRUE;
}
/*
* Attack roll calculation and check, optional touch and ranged options
*/
bool check_hit( CHAR_DATA *ch, CHAR_DATA *victim, int diceroll, int hit_adj, int dt, OBJ_DATA *wield, bool fTouch, bool fRanged )
{
int ac = 0;
int adj = 0;
bool hit = FALSE;
push_call("check_hit(%p,%p,%p,%p,%p,%p)",ch,victim,hit_adj,dt,wield,fTouch,fRanged);
if (dt == gsn_coup_de_grace) // coup de grace is automatic hit
hit = TRUE;
else if (diceroll == 1 && !IS_PLR(ch, PLR_HOLYLIGHT)) // 1 always misses
hit = FALSE;
else if (number_percent() <= can_miss(ch, victim, dt, fRanged, wield)) // % roll against concealment
hit = FALSE;
else if (diceroll == 20) // 20 is automatic hit
{
wiz_printf_room(ch, "Perfect 20 roll! diceroll: %d, adj: %d, ac: %d\n\r", diceroll, adj, ac);
if (!IS_NPC(ch) && !IS_SET(ch->pcdata->spam, SPAM_DICE_ROLLS))
{
ch_printf_color(ch, "%sRolled perfect 20! + adjs: %d vs. AC %d\n\r", get_color_string(ch, COLOR_ACCENT, VT102_DIM), adj, ac);
}
hit = TRUE;
}
else
{
ac = calc_ac(victim, ch, fTouch);
adj = GET_HITROLL(ch, victim, dt, wield);
if (fRanged)
{
if (wield && ch->in_room)
{
if (ch->in_room->area->weather_info->wind_speed >= 9)
{
act("The raging winds blow make your ranged attack useless.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
else if (ch->in_room->area->weather_info->wind_speed >= 8)
{
adj -= 4;
}
else if (ch->in_room->area->weather_info->wind_speed >= 7)
{
adj -= 2;
}
}
adj -= range_mod(ch, victim, wield);
}
wiz_printf_room(ch, "Attack Roll(%s): diceroll: %d, adj: %d, mod: %d vs. ac: %d\n\r", get_name(ch), diceroll, adj, hit_adj, ac);
if (victim && victim->in_room != ch->in_room)
wiz_printf_room(victim, "Attack Roll(%s): diceroll: %d, adj: %d, mod: %d vs. ac: %d\n\r", get_name(ch), diceroll, adj, hit_adj, ac);
if (!IS_NPC(ch) && !IS_SET(ch->pcdata->spam, SPAM_DICE_ROLLS))
{
ch_printf_color(ch, "%sAttack roll: %d + adjs: %d vs. AC %d\n\r", get_color_string(ch, COLOR_ACCENT, VT102_DIM), diceroll, adj, ac);
}
diceroll += adj;
diceroll += hit_adj;
hit = diceroll >= ac;
}
// True Strike only good for one hit, so, once it's checked, strip the effect
if (is_affected(ch, gsn_true_strike))
affect_strip(ch, gsn_true_strike);
pop_call();
return(hit);
}
/*
* check for combat maneuvers. Roll of 1 by attacker or failure
* by 10 or more are both critical failures, and return -2.
* otherwise, return -1 on failure. Roll of 20, is a critical
* success, same as beating the defender roll by 10 or more. - Kregor
*/
int combat_maneuver_check( CHAR_DATA *ch, CHAR_DATA *victim, int sn )
{
int chroll, victroll, bonus;
push_call("combat_maneuver_check(%p,%p)",ch,victim);
// 1 = crit fail, 20 = crit success
if ((chroll = dice(1,20)) == 1)
{
pop_call();
return -2;
}
else if (chroll == 20 || IS_HELPLESS(victim))
{
pop_call();
return 10;
}
if (sn != gsn_crushing_hand && sn != gsn_grasping_hand && sn != gsn_forceful_hand)
chroll += combat_maneuver_bonus(ch);
victroll = dice(1,20);
victroll += combat_maneuver_defense(victim, ch);
/* skill-dependant adjustments */
if (sn != gsn_grapple)
{
if (ch->grappling || ch->grappled_by)
{
chroll -= 2;
}
if (victim->grappling || victim->grappled_by)
{
victroll -= 2;
}
}
if (sn == gsn_trip)
{
if (can_trip(victim))
{
pop_call();
return -1;
}
if (learned(ch, gsn_improved_trip))
chroll += 2;
if (get_monk_style(ch) == STYLE_PASSIVE_WAY && class_level(ch, CLASS_MONK) >= 12)
chroll += 4;
if (IS_SET(ch->attack, ATTACK_TRAMPLE)) // mod for Mounted Onslaught feat - Kregor
chroll -= 5;
if (!IS_FLYING(victim) && !is_mounting(victim))
{
if (learned(victim, gsn_stability))
victroll += 4;
if (is_affected(victim, gsn_defensive_stance) && (bonus = multi_class_level(victim, gsn_greater_stability)) > 0)
victroll += UMIN(stat_bonus(TRUE, ch, APPLY_CON), bonus);
}
if (quadruped(victim))
victroll += 4;
if (many_legged(victim))
victroll += 8;
if (learned(victim, gsn_improved_trip))
victroll += 2;
if (get_monk_style(victim) == STYLE_HAND_AND_FOOT && class_level(victim, CLASS_MONK) >= 12)
victroll += 2;
victroll += learned(victim, gsn_tumble);
}
if (sn == gsn_bullrush)
{
if (learned(ch, gsn_imp_bullrush))
chroll += 2;
if (!IS_FLYING(victim) && !is_mounting(victim))
{
if (learned(victim, gsn_stability))
victroll += 4;
if (is_affected(victim, gsn_defensive_stance) && (bonus = multi_class_level(victim, gsn_greater_stability)) > 0)
victroll += UMIN(stat_bonus(TRUE, ch, APPLY_CON), bonus);
}
if (quadruped(victim))
victroll += 4;
if (many_legged(victim))
victroll += 8;
if (learned(victim, gsn_imp_bullrush))
victroll += 2;
if (get_monk_style(victim) == STYLE_HAND_AND_FOOT && class_level(victim, CLASS_MONK) >= 12)
victroll += 2;
victroll += synergy_bonus(victim, gsn_tumble);
}
if (sn == gsn_grapple)
{
if (learned(ch, gsn_imp_grapple))
chroll += 2;
if (get_monk_style(ch) == STYLE_DENYING_STANCE && class_level(ch, CLASS_MONK) >= 12)
chroll += 2;
chroll += synergy_bonus(ch, gsn_escape_artist);
if (race_skill(ch, gsn_grab))
chroll += 4;
victroll = UMAX(victroll, escape_artist_roll(victim));
if (learned(ch, gsn_imp_grapple))
victroll += 2;
if (get_monk_style(victim) == STYLE_DENYING_STANCE && class_level(victim, CLASS_MONK) >= 12)
victroll += 2;
}
if (sn == gsn_grasping_hand || sn == gsn_crushing_hand)
{
if (is_affected(victim, sn))
chroll += get_affect_level(victim, sn) + 11;
else
chroll += get_caster_level(ch, sn) + 11;
if (sn == gsn_crushing_hand)
chroll += 2;
victroll = UMAX(victroll, escape_artist_roll(victim));
if (learned(ch, gsn_imp_grapple))
victroll += 2;
if (get_monk_style(victim) == STYLE_DENYING_STANCE && class_level(victim, CLASS_MONK) >= 12)
victroll += 2;
}
if (sn == gsn_forceful_hand)
{
if (can_trip(victim))
{
pop_call();
return -1;
}
if (is_affected(victim, sn))
chroll += get_affect_level(victim, sn) + 9;
else
chroll += get_caster_level(ch, sn) + 9;
if (!IS_FLYING(victim) && !is_mounting(victim))
{
if (learned(victim, gsn_stability))
victroll += 4;
if (is_affected(victim, gsn_defensive_stance) && (bonus = multi_class_level(victim, gsn_greater_stability)) > 0)
victroll += UMIN(stat_bonus(TRUE, ch, APPLY_CON), bonus);
}
if (quadruped(victim))
victroll += 4;
if (many_legged(victim))
victroll += 8;
if (learned(victim, gsn_imp_bullrush))
victroll += 2;
if (get_monk_style(victim) == STYLE_HAND_AND_FOOT && class_level(victim, CLASS_MONK) >= 12)
victroll += 2;
victroll += synergy_bonus(victim, gsn_tumble);
}
if (sn == gsn_disarm)
{
OBJ_DATA *chwield, *victwield;
if ((chwield = get_wield(ch, FALSE)) != NULL)
chwield = get_wield(ch, TRUE);
if (chwield)
{
if (WEAR_LOC(chwield, WEAR_BOTH_HANDS))
chroll += 4;
if (WSPEC(chwield, WSPEC_DISARM))
chroll += 2;
}
else
chroll -= 4;
if (learned(ch, gsn_imp_disarm))
chroll += 2;
if (get_monk_style(ch) == STYLE_DENYING_STANCE && class_level(ch, CLASS_MONK) >= 12)
chroll += 2;
if ((victwield = get_wield(victim, FALSE)) == NULL)
{
if ((victwield = get_wield(victim, TRUE)) == NULL)
{
if ((victwield = get_hold(victim)) == NULL)
{
bug( "combat_maneuver_check: funtion passed NULL object", 0 );
pop_call();
return -1;
}
}
}
//weapon grandmastery defeats disarm.
if (IS_SET(weapon_skill(victim, victwield), WSKILL_IMP_CRITICAL) && learned(victim, gsn_weapon_grandmastery))
{
pop_call();
return -1;
}
if (victwield && WEAR_LOC(victwield, WEAR_BOTH_HANDS))
victroll += 4;
if (IS_SET(weapon_skill(victim, victwield), WSKILL_FOCUS) && learned(victim, gsn_weapon_mastery))
victroll += (multi_skill_level(ch, gsn_weapon_mastery) / 4) + 1;
if (learned(victim, gsn_imp_disarm))
victroll += 2;
}
if (sn == gsn_sunder)
{
OBJ_DATA *chwield;
if ((chwield = get_wield(ch, FALSE)) != NULL && WEAR_LOC(chwield, WEAR_BOTH_HANDS))
chroll += 4;
else if (!chwield)
chroll -= 4;
if (learned(ch, gsn_imp_sunder))
chroll += 2;
}
if (sn == gsn_rescue)
{
chroll += synergy_bonus(ch, gsn_tumble);
if (learned(ch, gsn_mobility))
chroll += 4;
}
if (sn == gsn_tumble)
{
victroll = tumble_roll(victim);
if (learned(victim, gsn_mobility))
victroll += 4;
}
/* the moment of truth */
if (chroll + 10 < victroll)
{
pop_call();
return -2;
}
else if (chroll < victroll)
{
pop_call();
return -1;
}
pop_call();
return (chroll - victroll);
}
/*
* Taken from the Pathfider RPG System (www.paizo.com)
* Alternative resolution for all special combat maneuvers
*/
int combat_maneuver_bonus( CHAR_DATA *ch )
{
int bonus;
push_call("combat_maneuver_bonus(%p)",ch);
bonus = base_attack(ch);
if (get_size(ch) <= SIZE_TINY || learned(ch, gsn_agile_combatant))
bonus += stat_bonus(TRUE, ch, APPLY_DEX);
else
bonus += stat_bonus(TRUE, ch, APPLY_STR);
if (learned(ch, gsn_mounted_combat))
{
if (ch->mounting || quadruped(ch))
{
bonus += 3;
}
}
switch (get_size(ch))
{
case SIZE_FINE:
bonus -= 8;
break;
case SIZE_DIMINUTIVE:
bonus -= 4;
break;
case SIZE_TINY:
bonus -= 2;
break;
case SIZE_SMALL:
bonus -= 1;
break;
case SIZE_LARGE:
bonus += 1;
break;
case SIZE_HUGE:
bonus += 2;
break;
case SIZE_GARGANTUAN:
bonus += 4;
break;
case SIZE_COLOSSAL:
bonus += 8;
break;
}
switch(drunk_level(ch))
{
case DRUNK_SOBER:
break;
case DRUNK_TIPSY:
bonus -= 1;
break;
case DRUNK_MERRY:
bonus -= 2;
break;
case DRUNK_DRUNK:
bonus -= 4;
break;
case DRUNK_HAMMERED:
bonus -= 8;
break;
default:
bonus -= 16;
break;
}
bonus += get_apply(ch, APPLY_COMP_TOHIT);
bonus += get_apply(ch, APPLY_INS_TOHIT);
bonus += get_apply(ch, APPLY_LUCK_TOHIT);
bonus += get_apply(ch, APPLY_MOR_TOHIT);
if (IS_AFFECTED(ch, AFF2_SICKENED))
bonus -= 2;
if (IS_DAZZLED(ch))
bonus -= 1;
if (IS_ENTANGLED(ch))
bonus -= 2;
if (ch->fear_level)
bonus -= UMIN(ch->fear_level, 3) * 2;
bonus += get_apply(ch, APPLY_LEVEL);
pop_call();
return bonus;
}
/*
* Taken from the Pathfider RPG System (www.paizo.com)
* Alternative resolution for all special combat maneuvers
*/
int combat_maneuver_defense( CHAR_DATA *ch, CHAR_DATA *attacker )
{
OBJ_DATA *shield;
int bonus, deflect;
push_call("combat_maneuver_bonus(%p)",ch);
bonus = base_attack(ch);
bonus += stat_bonus(TRUE, ch, APPLY_STR);
deflect = 0;
if (ch->mounting && learned(ch, gsn_mounted_combat))
bonus += 3;
switch (get_size(ch))
{
case SIZE_FINE:
bonus -= 8;
break;
case SIZE_DIMINUTIVE:
bonus -= 4;
break;
case SIZE_TINY:
bonus -= 2;
break;
case SIZE_SMALL:
bonus -= 1;
break;
case SIZE_LARGE:
bonus += 1;
break;
case SIZE_HUGE:
bonus += 2;
break;
case SIZE_GARGANTUAN:
bonus += 4;
break;
case SIZE_COLOSSAL:
bonus += 8;
break;
}
switch(drunk_level(ch))
{
case DRUNK_SOBER:
break;
case DRUNK_TIPSY:
bonus -= 1;
break;
case DRUNK_MERRY:
bonus -= 2;
break;
case DRUNK_DRUNK:
bonus -= 4;
break;
case DRUNK_HAMMERED:
bonus -= 8;
break;
default:
bonus -= 16;
break;
}
if (attacker != NULL)
{
if (IS_EVIL(attacker))
deflect = UMAX(deflect, get_apply(ch, APPLY_RES_EVIL));
if (IS_GOOD(attacker))
deflect = UMAX(deflect, get_apply(ch, APPLY_RES_GOOD));
if (IS_LAWFUL(attacker))
deflect = UMAX(deflect, get_apply(ch, APPLY_RES_LAW));
if (IS_CHAOTIC(attacker))
deflect = UMAX(deflect, get_apply(ch, APPLY_RES_CHAOS));
if (!can_see(ch, attacker) && !learned(ch,gsn_blind_fight))
bonus -= 2;
if (can_dodge(ch, attacker))
{
bonus += dodge_bonus(ch);
if (rspec_req(attacker, RSPEC_GIANT) && learned(ch, gsn_defensive_training))
bonus += 4;
}
else
{
bonus += dodge_penalty(ch);
}
}
if ((shield = get_eq_char(ch, WEAR_SHIELD)) != NULL && learned(ch, gsn_shield_ward))
bonus += shield_bonus(ch);
if (class_level(ch, CLASS_MONK)
&& armor_type_worn(ch) == ARMOR_NONE
&& !shield && !IS_ENCUMBERED(ch))
{
bonus += class_level(ch, CLASS_MONK) / 5;
bonus += stat_bonus(TRUE, ch, APPLY_WIS);
}
deflect = UMAX(deflect, race_table[get_race(ch)].deflection);
deflect = UMAX(deflect, get_apply(ch, APPLY_DEFLECT));
if (domain_apotheosis(ch, DOMAIN_PROTECTION))
deflect = stat_bonus(TRUE, ch, APPLY_WIS);
bonus += deflect;
bonus += get_apply(ch, APPLY_LEVEL);
pop_call();
return bonus;
}
/*
* Hit one guy once.
* Changed to bool, returns TRUE if a hit is successful - Kregor
*/
bool one_hit(CHAR_DATA *ch, CHAR_DATA *victim, int dt, int hit_adj, OBJ_DATA *wield)
{
OBJ_DATA *ammo;
int dam, diceroll, threat;
bool fRanged = FALSE;
bool QuiverHit = FALSE;
bool VorpalHit = FALSE;
bool fTouch = FALSE;
bool BackStab = FALSE;
push_call("one_hit(%p,%p,%p)",ch,victim,dt);
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
if (is_safe(ch, NULL))
{
pop_call();
return FALSE;
}
ammo = NULL;
if (wield != NULL && is_missile_weapon(wield))
{
if ((ammo = get_ammo(ch, wield)) == NULL || !reload(ch, wield))
{
pop_call();
return FALSE;
}
ch->reloaded = FALSE;
fRanged = TRUE;
}
// shuriken and chakram, that you can't beat someone with
if (wield && WSPEC(wield, WSPEC_NOMELEE))
{
fRanged = TRUE;
}
if (!fRanged && !can_melee_attack(ch, victim))
{
pop_call();
return FALSE;
}
/*
Make them a bit tired
*/
if (!move_loss(ch, NULL, 1))
{
pop_call();
return FALSE;
}
/*
Figure out the type of damage message.
*/
if (dt == TYPE_UNDEFINED || dt == gsn_whirl || dt == gsn_opportunist)
{
if (IS_SET(ch->attack, ATTACK_MIRROR))
{
pop_call();
return FALSE;
}
if (wield == NULL || (wield->item_type != ITEM_WEAPON && wield->item_type != ITEM_AMMO))
{
if ((ch->attack_part = get_body_part(ch, TYPE_HIT, 0)) == -1)
{
send_to_char("You have no body parts to attack with!\n\r", ch);
pop_call();
return FALSE;
}
if (learned(ch, gsn_martial_arts))
{
if (dt == gsn_whirl)
martial_arts_attack = 5;
else
martial_arts_attack = number_range(0,11);
}
else if (learned(ch, gsn_imp_unarmed_strike))
{
if (dt == gsn_whirl)
brawling_attack = 4;
else
brawling_attack = number_range(0, 8);
}
}
if (dt != gsn_opportunist && dt != gsn_coup_de_grace)
{
dt = TYPE_HIT;
// if (wield != NULL && (wield->item_type == ITEM_WEAPON || wield->item_type == ITEM_AMMO))
// {
// dt += weapon_table[wield->value[0]].dam_type;
// }
}
}
if (is_attack(dt) && !wield && !is_armed(ch, FALSE))
attack_of_opportunity(victim, ch);
if (!valid_fight(ch, victim))
{
pop_call();
return FALSE;
}
// check for slip and fall in slippery rooms to ruin attack
if (IS_SET(ch->in_room->room_flags, ROOM_ICE) && !IS_FLYING(ch) && !IS_AFFECTED(ch, AFF_WATER_WALK))
{
if (!tumble_check(ch, NULL, tumble_roll(ch), 10))
{
if (!refl_save(ch, NULL, 10, -1))
{
switch (number_bits(1))
{
case 0:
act( "You slip and fall flat on your back.", ch, NULL, NULL, TO_CHAR);
act( "$n slips and falls flat on $s back.", ch, NULL, NULL, TO_ROOM);
break;
case 1:
act( "You lose your footing and fall.", ch, NULL, NULL, TO_CHAR);
act( "$n loses $s footing and falls.", ch, NULL, NULL, TO_ROOM);
break;
}
TAKE_ACTION(ch, ACTION_MOVE);
update_pos(ch, POS_RESTING);
}
else
{
act( "You lose your balance, and manage to right yourself.", ch, NULL, NULL, TO_CHAR);
TAKE_ACTION(ch, ACTION_MOVE);
}
pop_call();
return FALSE;
}
}
/*
* The dice roll, possible critical miss or threat
*/
diceroll = dice(1,20);
CRIT_HIT = FALSE;
if (wield != NULL)
{
threat = weapon_table[wield->value[0]].threat;
}
else
{
threat = 20;
}
if ((wield != NULL && WEAPON_FLAG(wield, WFLAG_KEEN))
|| (wield != NULL && is_bow(wield) && learned(ch, gsn_keen_arrow)))
{
threat = (20 - threat) * 2;
threat = 20 - threat;
}
if (IS_SET(weapon_skill(ch, wield), WSKILL_IMP_CRITICAL))
{
threat += 1;
}
// if touch attack, then use touch AC on check_hit
if (!wield && ch->attack_part == ATTK_TOUCH)
{
fTouch = TRUE;
}
// crit miss - nothing else happens, just end it here.
if (diceroll == 1 && !IS_PLR(ch, PLR_HOLYLIGHT))
{
ch_printf_color(ch, "{118}Critical miss!\n\r");
damage(ch, victim, 0, dt, wield);
if (ammo)
{
junk_obj(ammo);
}
pop_call();
return FALSE;
}
if (number_percent() > get_apply(victim, APPLY_FORTIFICATION))
{
if (can_backstab(ch, victim, dt))
BackStab = TRUE;
if (dt == gsn_coup_de_grace)
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical hit!\n\r");
}
else if (diceroll == 20 || diceroll >= threat)
{
if (check_hit(ch, victim, dice(1,20), hit_adj, dt, wield, fTouch, fRanged)
|| (wield != NULL && WEAPON_TYPE(wield, WEAPON_TYPE_SLING) && learned(ch, gsn_dead_shot))
|| (fave_enemy_bonus(ch, victim) && learned(ch, gsn_master_hunter))
|| (BackStab && learned(ch, gsn_stealth_mastery))
|| (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && (learned(ch, gsn_holy_champion) || learned(ch, gsn_unholy_champion))))
{
CRIT_HIT = TRUE;
ch_printf_color(ch, "{118}You score a critical threat!\n\r");
}
}
}
else if (dt == gsn_assassinate || dt == gsn_backstab || dt == gsn_coup_de_grace)
{
dt = TYPE_HIT;
}
/*
Calc damage and check for specials
*/
dam = GET_DAMROLL(ch, victim, dt, wield);
if (CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
{
if (wield != NULL)
{
int mult = weapon_table[wield->value[0]].critical;
if (IS_SET(weapon_skill(ch, wield), WSKILL_IMP_CRITICAL) && learned(ch, gsn_weapon_grandmastery))
mult++;
if (WEAPON_TYPE(wield, WEAPON_TYPE_SLING) && learned(ch, gsn_between_the_eyes))
mult++;
else if (learned(ch, gsn_thrust_home) && (is_light_weapon(ch, wield) || WSPEC(wield, WSPEC_FINESSE)) && get_eq_char(ch, WEAR_SHIELD) == NULL)
mult++;
dam *= mult;
if (WEAPON_FLAG(wield, WFLAG_VORPAL) && diceroll == 20) /* vorpal weapon flag - DECAPITATED!! */
{
VorpalHit = TRUE;
}
}
else
{
// now added Imp Crit to unarmed strikes!!
if (IS_SET(weapon_skill(ch, wield), WSKILL_IMP_CRITICAL) && learned(ch, gsn_weapon_grandmastery))
dam *= 3;
else
dam *= 2;
// monk's quivering palm, per mud20 specs - Kregor
if (learned(ch, gsn_quivering_palm) && !COMBATMODE(ch, COMBAT_NONLETHAL)
&& multi_class_level(ch, gsn_quivering_palm) >= victim->level)
{
QuiverHit = TRUE;
}
}
}
int bs_dice = 0;
int bs_dam = 0;
if (BackStab)
{
bs_dice = ROUNDUP(multi_skill_level(ch, gsn_backstab) / 2);
if (COMBATMODE(ch, COMBAT_ARTERIAL))
bs_dice -= 1;
if (COMBATMODE(ch, COMBAT_CRIPPLE))
bs_dice -= 1;
if (get_monk_style(ch) == STYLE_SLEEPING_TIGER && class_level(ch, CLASS_MONK) >= 12)
bs_dice++;
if (bs_dice > 0)
{
if (learned(ch, gsn_greater_sneak_attack))
{
bs_dam = dice(bs_dice, 8);
}
else
{
bs_dam = dice(bs_dice, 6);
}
}
if (dt == gsn_assassinate)
{
dam += bs_dam;
}
else if (is_flanking(ch, victim))
{
if (dt != gsn_coup_de_grace)
{
act( "{118}You attack $N from $S flank!", ch, NULL, victim, TO_CHAR);
act( "$n {118}attacks you from your flank!", ch, NULL, victim, TO_VICT);
dam += bs_dam;
dt = gsn_backstab;
}
}
else
{
dam += bs_dam;
if (dt != gsn_coup_de_grace)
{
dt = gsn_backstab;
act( "{118}You attempt a sneak attack on $N!", ch, NULL, victim, TO_CHAR);
act( "$n {118}attempts a sneak attack on you!", ch, NULL, victim, TO_VICT);
}
}
}
if (diceroll != 20 && !check_hit(ch, victim, diceroll, hit_adj, dt, wield, fTouch, fRanged))
{
damage(ch, victim, 0, dt, wield);
if (ammo)
{
junk_obj(ammo);
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
pop_call();
return FALSE;
}
if (is_affected(ch, gsn_impromptu_sneak_attack))
affect_strip(ch, gsn_impromptu_sneak_attack);
bool fHit = TRUE;
if (dam <= 0)
{
dam = 1;
}
// because ranged doesn't need to consider cleave,
// damage shields and other crap - Kregor
if (fRanged)
{
if (is_missile_weapon(wield) && !ammo)
{
bug("(one_hit): Missile wield hit with no ammo.", 0);
act( "{138}Where did your ammo go???", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (WSPEC(wield, WSPEC_NOMELEE))
{
if (!deflect_missile(victim, ch, wield, -1))
{
damage(ch, victim, dam, dt, wield);
/* weapon spells on ranged weapon here */
weapon_cast_spell(wield->value[2], wield->value[3], ch, victim, wield);
if (WSPEC(wield, WSPEC_RETURNING) || WEAPON_FLAG(wield, WFLAG_RETURNING))
{
act("$p returns to your hand.", ch, wield, NULL, TO_CHAR);
act("$p returns to $n's hand.", ch, wield, NULL, TO_ROOM);
}
else
{
obj_from_char(wield);
obj_to_char(wield, victim);
}
}
else
{
pop_call();
return FALSE;
}
}
else if (!deflect_missile(victim, ch, ammo, -1))
{
damage(ch, victim, dam, dt, wield);
/* weapon spells on missile ammo here */
if (weapon_cast_spell(ammo->value[2], ammo->value[3], ch, victim, ammo))
ammo->value[2] = ammo->value[3] = 0;
junk_obj(ammo);
}
else
{
pop_call();
return FALSE;
}
}
else
{
// check for parry feat
if (!check_parry(victim, ch, wield, diceroll + hit_adj + GET_HITROLL(ch, victim, dt, wield)))
{
damage(ch, victim, dam, QuiverHit ? gsn_quivering_palm : VorpalHit ? gsn_vorpal_hit : dt, wield);
/* weapon spells on melee weapons here */
if (wield && valid_fight(ch, victim) && weapon_cast_spell(wield->value[2], wield->value[3], ch, victim, wield))
wield->value[2] = wield->value[3] = 0;
}
else
{
fHit = FALSE; // make sure no cleave on this parried attack - Kregor
}
// make acidic creatures damage weapons - Kregor
if (wield)
{
if (race_skill(victim, gsn_acid_touch) && race_skill(victim, gsn_corrosion))
{
if (IS_OBJ_TYPE(wield, ITEM_WEAPON))
{
if (!IS_OBJ_STAT(wield, ITEM_MAGIC) && !refl_save(ch, NULL, victim->level, gsn_acid_touch))
{
damage_equipment(victim, ch, wield, dice(1,race_skill(victim, gsn_acid_touch)), gsn_acid_touch, NULL);
}
}
}
}
/* damage shields kick in here. Only the first to affect ch counts. */
if (valid_fight(ch, victim) && !has_reach(ch, wield)) // only damage if not using reach weapon.
{
int lvl;
if ((lvl = get_affect_level(victim, gsn_fire_shield)) > 0 && !save_resist(victim, ch, gsn_fire_shield, lvl))
{
damage(victim, ch, dice(1,6) + UMIN(lvl,15), gsn_fire_shield, NULL);
}
else if ((lvl = get_affect_level(victim, gsn_blade_barrier)) > 0 && !save_resist(victim, ch, gsn_blade_barrier, lvl))
{
damage(victim, ch, dice(1,6) + UMIN(lvl,15), gsn_blade_barrier, NULL);
}
else if ((lvl = get_affect_level(victim, gsn_death_armor)) > 0 && !save_resist(victim, ch, gsn_death_armor, lvl))
{
damage(victim, ch, dice(1,6) + UMIN(lvl,15), gsn_death_armor, NULL);
}
else if ((lvl = get_affect_level(victim, gsn_aura_of_retribution)) > 0 && !save_resist(victim, ch, gsn_aura_of_retribution, lvl))
{
damage(victim, ch, dice(1,6) + UMIN(lvl,15), gsn_aura_of_retribution, NULL);
}
else if ((lvl = get_affect_level(victim, gsn_thorn_body)) > 0 && !save_resist(victim, ch, gsn_thorn_body, lvl))
{
damage(victim, ch, dice(1,6) + UMIN(lvl,15), gsn_thorn_body, NULL);
}
else if (!wield) // damage touches only affect barehanded attacks
{
if ((lvl = race_skill(victim, gsn_barbed_touch)) > 0)
{
damage(victim, ch, dice(1,lvl), gsn_barbed_touch, NULL);
}
else if ((lvl = race_skill(victim, gsn_fire_touch)) > 0)
{
damage(victim, ch, dice(1,lvl), gsn_fire_touch, NULL);
}
else if ((lvl = race_skill(victim, gsn_frost_touch)) > 0)
{
damage(victim, ch, dice(1,lvl), gsn_frost_touch, NULL);
}
else if ((lvl = race_skill(victim, gsn_shock_touch)) > 0)
{
damage(victim, ch, dice(1,lvl), gsn_shock_touch, NULL);
}
else if ((lvl = race_skill(victim, gsn_acid_touch)) > 0)
{
damage(victim, ch, dice(1,lvl), gsn_acid_touch, NULL);
}
}
}
// grab ability = automatic grapple on a hit
if (race_skill(ch, gsn_grab) && !wield && valid_fight(ch, victim))
{
grab(ch, victim);
pop_call();
return TRUE;
}
/* cleave kicks in here - Kregor */
// but not if damage shields above took him out - Kregor
if (!valid_fight(ch, victim))
{
pop_call();
return TRUE;
}
//don't be giving cleaves to whirlwinds, coups, stunning fists, etc - Kregor
if (dt < TYPE_HIT)
{
pop_call();
return TRUE;
}
if (!learned(ch, gsn_cleave))
{
if (!IS_AFFECTED(ch, AFF2_BERSERK) || !learned(ch, gsn_cloud_of_cleaves))
{
pop_call();
return TRUE;
}
}
if (IS_SET(ch->attack, ATTACK_CLEAVE))
{
if (learned(ch, gsn_great_cleave) || (IS_AFFECTED(ch, AFF2_BERSERK) && learned(ch, gsn_cloud_of_cleaves)))
REMOVE_BIT(ch->attack, ATTACK_CLEAVE);
pop_call();
return TRUE;
}
if (fHit && COMBATMODE(ch, COMBAT_POWER))
{
CHAR_DATA *fch;
if ((fch = get_next_fighting(ch, victim)) == NULL)
{
pop_call();
return TRUE;
}
act("{138}You cleave through to another opponent!", ch, NULL, NULL, TO_CHAR);
act("$n {138}cleaves through to another opponent!", ch, NULL, NULL, TO_ROOM);
one_hit( ch, fch, dt, hit_adj, wield );
SET_BIT(ch->attack, ATTACK_CLEAVE);
}
}
pop_call();
return TRUE;
}
/*
* Calculate the range of a missile weapon in no. of rooms - Kregor
*/
int get_missile_range( OBJ_DATA *weapon )
{
int range;
push_call("get_weapon_range(%p)",weapon);
if (weapon == NULL)
{
bug ("get_missile_range: no weapon.", 0);
pop_call();
return -1;
}
if ((range = weapon_table[weapon->value[0]].range) <= 0)
{
if (WEAPON_FLAG(weapon, WFLAG_THROWING))
{
range = 10;
}
else
{
pop_call();
return 0;
}
}
range = UMAX(1, range / 10);
if (WEAPON_FLAG(weapon, WFLAG_DISTANCE))
{
range *= 2;
}
pop_call();
return range;
}
/*
* calculate range penalty to hit - Kregor
*/
int range_mod(CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *wield)
{
int range;
push_call("range_mod(%p,%p)",ch,victim);
if (ch->in_room == victim->in_room)
{
pop_call();
return 0;
}
if ((range = get_range(ch, victim)) == -1)
{
pop_call();
return 0;
}
if (learned(ch, gsn_far_shot))
{
if (is_missile_weapon(wield))
range /= 2;
else
range = range * 2 / 3;
}
if (wield && (WEAPON_FLAG(wield, WFLAG_DISTANCE) || (is_bow(wield) && learned(ch, gsn_distance_arrow))))
range /= 2;
pop_call();
return range;
}
/*
* Damage a victim
*/
void damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, OBJ_DATA *wield )
{
AFFECT_DATA af;
bool hit = FALSE;
int diceroll;
push_call("damage(%p,%p,%p,%p,%p)",ch,victim,dam,dt,wield);
// really, there's just no need...
if (victim->position == POS_DEAD)
{
pop_call();
return;
}
if (is_affected(victim, gsn_time_stop))
{
pop_call();
return;
}
if (dt != TYPE_NOFIGHT)
{
if (is_safe(ch, NULL))
{
// Disallow damage in shops and safe places.
if (in_combat(ch))
clean_combat(ch->in_battle);
pop_call();
return;
}
user_from_furniture(ch);
if (victim != ch)
{
if (IS_AFFECTED(ch, AFF_HIDE))
{
if (wield && is_missile_weapon(wield) && !IS_SET(ch->action, ACTION_STANDARD) && learned(ch, gsn_sniper))
{
act( "You take a single shot from your concealment.", ch, NULL, NULL, TO_CHAR );
}
else
{
AFFECT_STRIP(ch, AFF_HIDE);
act( "$n steps out from $s hiding spot.", ch, NULL, NULL, TO_ROOM );
act( "You step out from your hiding spot.", ch, NULL, NULL, TO_CHAR );
}
}
if (IS_AFFECTED(ch, AFF_INVISIBLE))
{
affect_strip(ch, gsn_invis);
REMOVE_BIT(ch->affected_by, AFF_INVISIBLE);
}
if (IS_AFFECTED(ch, AFF_SANCTUARY) && is_attack(dt))
{
AFFECT_STRIP(ch, AFF_SANCTUARY);
}
if (IS_AFFECTED(victim, AFF2_CHARMED) && is_attack(dt))
{
if (is_affected(victim, gsn_charm_monster) && get_caster(victim, gsn_charm_monster) == ch)
affect_strip(victim, gsn_charm_monster);
if (is_affected(victim, gsn_charm_person) && get_caster(victim, gsn_charm_person) == ch)
affect_strip(victim, gsn_charm_person);
if (is_affected(victim, gsn_charm_plant) && get_caster(victim, gsn_charm_plant) == ch)
affect_strip(victim, gsn_charm_plant);
if (is_affected(victim, gsn_charm_animal) && get_caster(victim, gsn_charm_animal) == ch)
affect_strip(victim, gsn_charm_animal);
}
if (IS_AFFECTED(victim, AFF_DOMINATE) && is_attack(dt))
{
if (is_affected(victim, gsn_dominate_monster) && get_caster(victim, gsn_dominate_monster) == ch)
affect_strip(victim, gsn_dominate_monster);
if (is_affected(victim, gsn_dominate_person) && get_caster(victim, gsn_dominate_person) == ch)
affect_strip(victim, gsn_dominate_person);
if (is_affected(victim, gsn_dominate_animal) && get_caster(victim, gsn_dominate_animal) == ch)
affect_strip(victim, gsn_dominate_animal);
}
if (!in_combat(ch))
{
fight(ch,victim);
}
// strip out bluffs and feints
affect_strip(victim, gsn_bluff);
victim->distracted = 0;
// Nope, you just got a mirror image!
if (has_mirror(ch, victim, dt))
{
pop_call();
return;
}
victim->last_attacker = ch;
}
}
/*
* Set recently fought info.
*/
if (IS_NPC(victim) && !IS_NPC(ch) && !confused && dam > 0 && dt != TYPE_NOFIGHT)
{
if (ch->leader && !IS_NPC(ch->leader))
{
victim->npcdata->pvnum_last_hit = ch->leader->pcdata->pvnum;
}
else
{
victim->npcdata->pvnum_last_hit = ch->pcdata->pvnum;
}
}
if (dam && !IS_NPC(ch) && IS_NPC(victim) && !confused && ch != victim && dt != TYPE_NOFIGHT)
{
if (ch->level > 1 && victim->npcdata->hate_fear != ch->pcdata->pvnum)
{
victim->npcdata->hate_fear = ch->pcdata->pvnum;
}
}
/*
* Weapon flags support - Kregor 7/13/07
* damage that is subject to reduction and only
* triggers on successful hit.
*/
if (dam > 0)
{
/*
* remember real hit result, because it matters after damage reductions - Kregor
*/
hit = TRUE;
if (wield)
{
// concat weapon flags from missile weapons onto ammo
if (IS_WEAPON(wield) && is_missile_weapon(wield))
{
OBJ_DATA *ammo;
if ((ammo = get_ammo(ch, wield)) == NULL)
{
bug("damage: missile weapon returned NULL ammo!", 0);
pop_call();
return;
}
if (wield->value[1])
{
SET_BIT(ammo->value[1], wield->value[1]);
}
wield = ammo;
}
if (WEAPON_FLAG(wield, WFLAG_ANARCHIC))
{
if (IS_LAWFUL(victim))
{
dam += dice(2, 6);
}
}
if (WEAPON_FLAG(wield, WFLAG_AXIOMATIC))
{
if (IS_CHAOTIC(victim))
{
dam += dice(2, 6);
}
}
if (WEAPON_FLAG(wield, WFLAG_BANE))
{
if (race_table[victim->race].type == wield->value[2])
{
if (wield->value[3] > 0 && IS_SET(race_table[victim->race].flags, wield->value[3]))
{
dam += dice(2, 6);
}
}
}
if (WEAPON_FLAG(wield, WFLAG_HOLY))
{
if (IS_EVIL(victim))
{
dam += dice(2, 6);
}
}
if (WEAPON_FLAG(wield, WFLAG_MERCIFUL))
{
dam += dice(1, 6);
}
if (WEAPON_FLAG(wield, WFLAG_UNHOLY))
{
if (IS_GOOD(victim))
{
dam += dice(2, 6);
}
}
}
if (IS_SET(weapon_skill(ch, wield), WSKILL_PWR_CRITICAL) && IS_SET(ch->attack, ATTACK_MELEE) && COMBATMODE(ch, COMBAT_POWER))
{
if (CRIT_HIT && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
{
if (wield)
dam += dice(weapon_table[wield->value[0]].critical - 1, 6);
else
dam += dice(1,6);
}
}
}
// modify the physical damage before adding further effects.
dam = damage_modify(ch, victim, dt, dam, wield);
/*
* elemental damage, drains and touch attacks do
* damage whether the physical hit is resisted or not - Kregor
* each element and affect gets modified individually
* before concating back onto physical damage.
*/
if (hit)
{
if (wield)
{
if (WEAPON_FLAG(wield, WFLAG_DISRUPTION))
{
if (IS_UNDEAD(victim) && !will_save(victim, NULL, actual_tohit_bonus(wield) * 2 + 10, -1))
{
dam += victim->hit + 10;
}
}
if (WEAPON_FLAG(wield, WFLAG_FLAMING))
{
dam += damage_modify(ch, victim, gsn_fire_hit, dice(1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_FLAMING_BURST))
{
dam += damage_modify(ch, victim, gsn_fire_hit, dice(CRIT_HIT ? weapon_table[wield->value[0]].critical : 1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_FROST))
{
dam += damage_modify(ch, victim, gsn_frost_hit, dice(1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_ICY_BURST))
{
dam += damage_modify(ch, victim, gsn_frost_hit, dice(CRIT_HIT ? weapon_table[wield->value[0]].critical : 1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_SHOCK))
{
dam += damage_modify(ch, victim, gsn_shock_hit, dice(1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_SHOCK_BURST))
{
dam += damage_modify(ch, victim, gsn_shock_hit, dice(CRIT_HIT ? weapon_table[wield->value[0]].critical : 1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_VICIOUS))
{
dam += damage_modify(ch, victim, gsn_sonic_blast, dice(1, 6), wield);
}
if (WEAPON_FLAG(wield, WFLAG_CAUSTIC))
{
dam += damage_modify(ch, victim, gsn_acid_hit, dice(1, 6), wield);
}
}
else if (is_attack(dt))
{
int sizedie = 0;
if (ch->attack_part == ATTK_TOUCH)
{
wiz_printf_room(ch, "damage: incorporeal touch hit!!\n\r");
}
if (race_skill(ch, gsn_corrosion))
{
corrosive_touch(ch, victim, ch->level);
}
if ((sizedie = race_skill(ch, gsn_barbed_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_barbed_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_fire_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_fire_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_frost_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_frost_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_shock_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_shock_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_acid_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_acid_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_divine_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_divine_touch, dice(1, sizedie), NULL);
}
if ((sizedie = race_skill(ch, gsn_negative_energy_touch)) > 0)
{
dam += damage_modify(ch, victim, gsn_negative_energy_touch, ch->level / 2 + dice(sizedie, 8), NULL);
}
if (race_skill(ch, gsn_drain))
level_drain(ch, victim);
if (race_skill(ch, gsn_cha_drain))
stat_damage(ch, victim, APPLY_CHA_DRAIN, gsn_cha_drain);
if (race_skill(ch, gsn_con_drain))
stat_damage(ch, victim, APPLY_CON_DRAIN, gsn_con_drain);
if (race_skill(ch, gsn_dex_drain))
stat_damage(ch, victim, APPLY_DEX_DRAIN, gsn_dex_drain);
if (race_skill(ch, gsn_int_drain))
stat_damage(ch, victim, APPLY_INT_DRAIN, gsn_int_drain);
if (race_skill(ch, gsn_str_drain))
stat_damage(ch, victim, APPLY_STR_DRAIN, gsn_str_drain);
if (race_skill(ch, gsn_wis_drain))
stat_damage(ch, victim, APPLY_WIS_DRAIN, gsn_wis_drain);
if (race_skill(ch, gsn_paralysis_touch))
{
if (!save_resist(ch, victim, gsn_paralysis_touch, ch->level))
{
af.type = gsn_paralysis_touch;
af.duration = dice(1, race_skill(ch, gsn_paralysis_touch));
af.bitvector = AFF2_PARALYSIS;
af.modifier = 0;
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
affect_join( ch, victim, &af );
act( "$t$N's touch leaves you paralyzed!", victim, get_color_string(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), ch, TO_VICT);
act( "{118}$n is paralyzed!", victim, NULL, NULL, TO_ROOM);
}
}
}
}
dam_message(ch, victim, (hit && IS_INCORPOREAL(ch) && !dam) ? -1 : dam, dt, wield);
damage_hurt(ch, victim, dam, dt);
if (hit && victim != ch && is_attack(dt) && victim->position > POS_DEAD) // if victim is dead, none of this matters.
{
if (dam > 0) // these only work if actual physical damage happens
{
if (wield != NULL && wield->poison != NULL && dam > 0)
{
if (IS_NPC(ch) || in_area(ch, ROOM_VNUM_ARENA))
{
wield->poison->poisoner = -1;
}
else
{
wield->poison->poisoner = ch->pcdata->pvnum;
}
obj_affect_poison(wield, victim);
}
if (CAN_CRITICAL(victim))
{
if (get_apply(victim, APPLY_REGENERATION) <= 0) // regenerating creatures do not take bleed damage
{
int bleed;
if ((bleed = learned(ch, gsn_bleed_damage)) > 0)
{
af.type = gsn_bleed_damage;
af.duration = -1;
af.modifier = dice(1,bleed);
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_BLEEDING;
if (!IS_NPC(ch))
{
victim->critical_hit_by = ch->pcdata->pvnum;
}
else
{
victim->critical_hit_by = 0;
}
affect_join( ch, victim, &af );
}
if ((dt == gsn_backstab && wield != NULL && learned(ch, gsn_arterial_strike) && COMBATMODE(ch, COMBAT_ARTERIAL))
|| (wield != NULL && WEAPON_FLAG(wield, WFLAG_WOUNDING)))
{
af.type = gsn_bleed_damage;
af.duration = -1;
af.modifier = 1;
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_BLEEDING;
if (!IS_NPC(ch))
{
victim->critical_hit_by = ch->pcdata->pvnum;
}
else
{
victim->critical_hit_by = 0;
}
affect_join( ch, victim, &af );
}
if (race_skill(ch, gsn_wounding_attack))
{
// horned devil only gets the tail
if (ch->race != RACE_CORNUGON || ch->attack_part == ATTK_TAIL)
{
af.type = gsn_festering_wounds;
af.duration = -1;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF_CURSE;
af.location = APPLY_NONE;
af.modifier = 0;
affect_join( ch, victim, &af );
act( "{118}The wound begins to fester!", victim, NULL, NULL, TO_CHAR );
act( "{118}$n's wound festers and bleeds!", victim, NULL, NULL, TO_ROOM );
}
}
}
if (dt == gsn_backstab && learned(ch, gsn_cripple) && COMBATMODE(ch, COMBAT_CRIPPLE))
{
if (!fort_save(victim, ch, dam, -1))
{
af.type = gsn_cripple;
af.duration = -1;
af.modifier = -2;
af.location = APPLY_STR_DAMAGE;
af.bittype = AFFECT_TO_NONE;
af.bitvector = AFF_NONE;
affect_join( ch, victim, &af );
act( "$tYour crippling blow weakens $N", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), victim, TO_CHAR);
act( "$t$n's crippling blow weakens you.", ch, get_color_string(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_CHAR);
}
}
}
if (!wield && is_attack(dt))
{
/* natural poison attack */
if (race_table[ch->race].poison) // shapechange does not grant poison
{
if (ch->attack_part == race_table[ch->race].poison_part) // must be hit w/ the right part
{
poison_attack(ch, victim, race_table[ch->race].poison);
}
}
if (race_table[ch->race].disease) // shapechange does not grant disease
{
if (ch->attack_part == race_table[ch->race].disease_part) // must be hit w/ the right part
{
disease_attack(ch, victim, race_table[ch->race].disease);
}
}
/* stat damage only happens along w physical dmg */
if (race_skill(ch, gsn_cha_damage))
stat_damage(ch, victim, APPLY_CHA_DAMAGE, gsn_cha_damage);
if (race_skill(ch, gsn_con_damage))
stat_damage(ch, victim, APPLY_CON_DAMAGE, gsn_con_damage);
if (race_skill(ch, gsn_dex_damage))
stat_damage(ch, victim, APPLY_DEX_DAMAGE, gsn_dex_damage);
if (race_skill(ch, gsn_int_damage))
stat_damage(ch, victim, APPLY_INT_DAMAGE, gsn_int_damage);
if (race_skill(ch, gsn_str_damage))
stat_damage(ch, victim, APPLY_STR_DAMAGE, gsn_str_damage);
if (race_skill(ch, gsn_wis_damage))
stat_damage(ch, victim, APPLY_WIS_DAMAGE, gsn_wis_damage);
}
if (is_mounting(victim))
{
int roll = mount_roll(victim);
if (learned(victim, gsn_mounted_combat))
roll += 3;
if (!mount_check(victim, victim->mounting, roll, 5 + dam))
{
act( "You fall off of $N.", victim, NULL, victim->mounting, TO_CHAR);
act( "$n falls off of you.", victim, NULL, victim->mounting, TO_VICT);
act( "$n falls off of $N.", victim, NULL, victim->mounting, TO_NOTVICT);
if (!mount_check(victim, victim->mounting, roll, 15))
damage(victim, victim, dice(1,6), TYPE_NOFIGHT, NULL);
victim->mounting = NULL;
}
}
}
}
if (dam > 0 && dt != TYPE_NOFIGHT)
{
if (wield && WEAPON_FLAG(wield, WFLAG_VAMPIRIC))
{
int heal = UMIN(dam, wield->value[2]);
ch->hit = UMIN( ch->hit + heal, get_max_hit(ch));
ch->nonlethal = UMAX(0, ch->nonlethal - heal);
update_pos(ch,-1);
act( "You feel a chill as $N's life force heals you.", ch, NULL, victim, TO_CHAR);
act( "$n's wounds seem to heal themselves.", ch, NULL, victim, TO_ROOM);
}
if (victim->position == POS_SLEEPING)
{
if (!IS_AFFECTED(victim, AFF_SLEEP))
{
update_pos(victim, POS_RESTING);
}
}
}
if (valid_fight(ch, victim))
{
if (dam > 0 && victim->casting)
{
if (dt == TYPE_NOFIGHT)
dam /= 2;
diceroll = concentration_roll(victim);
if (class_ability(ch, gsn_warmage))
diceroll += 4;
if (!concentration_check(victim, ch, diceroll, 10 + dam + get_spell_circle(NULL, victim->cast_sn)))
{
act( "{138}You lost your concentration!", victim, NULL, NULL, TO_CHAR);
act( "$n stops casting.", victim, NULL, NULL, TO_ROOM);
if (victim->cast_sn == gsn_recite_scroll && victim->reciting)
junk_obj(victim->reciting);
free_cast(victim);
}
}
if (dam > 0 && victim->concentrating)
{
if (dt == TYPE_NOFIGHT)
dam /= 2;
diceroll = concentration_roll(victim);
if (!concentration_check(victim, ch, diceroll, 10 + dam))
{
act( "{138}You lost your concentration!", victim, NULL, NULL, TO_CHAR);
act( "$n loses $s concentration.", victim, NULL, NULL, TO_ROOM);
free_skill(victim);
}
}
}
pop_call();
return;
}
/*
* Scandum 4-6-2003
* Heavily modified by Kregor 2006
*/
int damage_modify( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int dam, OBJ_DATA *wield )
{
int dam_type = DAM_BASH;
push_call("damage_modify(%p,%p,%p,%p)",ch,victim,dt,dam,wield);
// incorporeals pass 1 point dmg to here just to treat it as a hit - Kregor
if (dam <= 0 || (!wield && is_attack(dt) && IS_INCORPOREAL(ch)))
{
pop_call();
return 0;
}
if (dt == TYPE_NOFIGHT)
{
pop_call();
return dam;
}
if (wield != NULL && (wield->item_type == ITEM_WEAPON || wield->item_type == ITEM_AMMO))
{
dam_type = weapon_table[wield->value[0]].dam_type;
}
else if (is_attack(dt))
{
if (learned(ch, gsn_martial_arts))
{
dam_type = martial_arts_table[martial_arts_attack].dam_type;
}
else if (learned(ch, gsn_imp_unarmed_strike))
{
dam_type = brawling_table[brawling_attack].dam_type;
}
else if (ch->attack_part != -1)
{
dam_type = attack_part_table[ch->attack_part].dam_type;
}
else if ((dam_type = skill_table[dt].dam_type) == DAM_NONE)
{
dam_type = DAM_BASH;
}
}
else
{
if (valid_skill(dt))
{
dam_type = skill_table[dt].dam_type;
if (is_spell(dt) && IS_SET(ch->cast_feats, METAMAGIC_MERCIFUL)
&& !IS_SET(skill_table[dt].spell_desc, SDESC_DEATH))
{
SET_BIT(dam_type, DAM_NONLETHAL);
}
}
else if (dt >= TYPE_HIT)
{
bug("dam_modify: Do we even use this anymore?", 0);
}
else
{
if (dt != TYPE_UNDEFINED)
{
bug("damage_modify: bad dt %d.", dt);
}
}
}
if (!is_spell(dt) && (dam_type == DAM_NONE || dam_type == DAM_NONLETHAL))
{
bug("damage_modify: no dam_type %d.", dt);
send_to_char("Something went wrong with the damage... contact an immortal!\n\r", ch);
}
if (dam <= 0)
{
dam = 1;
}
/*
* Here's where ALL the physical damage types
* get concatenated for damage reduction purposes
* Kregor 7/25/07
*/
if (is_attack(dt))
{
if (dt == gsn_shield_bash)
{
wield = get_eq_char(ch, WEAR_SHIELD);
}
if (COMBATMODE(ch, COMBAT_NONLETHAL)
&& dt != gsn_assassinate && dt != gsn_backstab)
{
SET_BIT(dam_type, DAM_NONLETHAL);
}
if (wield != NULL)
{
if (WSPEC(wield, WSPEC_NONLETHAL))
{
SET_BIT(dam_type, DAM_NONLETHAL);
}
if (material_table[wield->material].parent == MATERIAL_TYPE_METAL && domain_apotheosis(ch, DOMAIN_METAL))
{
SET_BIT(dam_type, DAM_ADAMANTINE|DAM_IRON|DAM_SILVER);
}
if (WEAPON_FLAG(wield, WFLAG_KI_FOCUS))
{
if (learned(ch, gsn_ki_strike_magic))
{
SET_BIT(dam_type, DAM_MAGICAL);
}
if (learned(ch, gsn_ki_strike_lawful))
{
SET_BIT(dam_type, DAM_LAWFUL);
}
if (learned(ch, gsn_ki_strike_adamant))
{
SET_BIT(dam_type, DAM_ADAMANTINE);
}
}
if (WEAPON_FLAG(wield, WFLAG_MERCIFUL))
{
SET_BIT(dam_type, DAM_NONLETHAL);
}
if (IS_OBJ_STAT(wield, ITEM_MAGIC) || (is_bow(wield) && learned(ch, gsn_enhance_arrow)))
{
SET_BIT(dam_type, DAM_MAGICAL);
}
if (IS_OBJ_EVIL(wield) || rspec_req(ch, RSPEC_EVIL) || learned(ch, gsn_aura_of_blasphemy) || actual_tohit_bonus(wield) >= 5)
{
SET_BIT(dam_type, DAM_EVIL);
}
if (IS_OBJ_GOOD(wield) || rspec_req(ch, RSPEC_GOOD) || learned(ch, gsn_aura_of_faith) || actual_tohit_bonus(wield) >= 5)
{
SET_BIT(dam_type, DAM_GOOD);
}
if (IS_OBJ_CHAOTIC(wield) || rspec_req(ch, RSPEC_CHAOTIC) || actual_tohit_bonus(wield) >= 5)
{
SET_BIT(dam_type, DAM_CHAOTIC);
}
if (IS_OBJ_LAWFUL(wield) || rspec_req(ch, RSPEC_LAWFUL) || actual_tohit_bonus(wield) >= 5)
{
SET_BIT(dam_type, DAM_LAWFUL);
}
if (wield->material == MATERIAL_COLD_IRON || actual_tohit_bonus(wield) >= 3)
{
SET_BIT(dam_type, DAM_IRON);
}
if (wield->material == MATERIAL_MITHRAL || wield->material == MATERIAL_SILVER || actual_tohit_bonus(wield) >= 3)
{
SET_BIT(dam_type, DAM_SILVER);
}
if (wield->material == MATERIAL_ADAMANTINE || actual_tohit_bonus(wield) >= 4)
{
SET_BIT(dam_type, DAM_ADAMANTINE);
}
}
else
{
if (!COMBATMODE(ch, COMBAT_NONLETHAL) && IS_SET(dam_type, DAM_NONLETHAL))
{
if (has_natural_weapon(ch) || is_armed(ch, FALSE))
{
REMOVE_BIT(dam_type, DAM_NONLETHAL);
}
}
if (race_table[get_race(ch)].apply[APPLY_DR_MAGIC]
|| learned(ch, gsn_ki_strike_magic))
{
SET_BIT(dam_type, DAM_MAGICAL);
}
if (rspec_req(ch, RSPEC_EVIL) || learned(ch, gsn_aura_of_blasphemy) || (IS_EVIL(ch) && learned(ch, gsn_align_ki)))
{
SET_BIT(dam_type, DAM_EVIL);
}
if (rspec_req(ch, RSPEC_GOOD) || learned(ch, gsn_aura_of_faith) || (IS_GOOD(ch) && learned(ch, gsn_align_ki)))
{
SET_BIT(dam_type, DAM_GOOD);
}
if (rspec_req(ch, RSPEC_CHAOTIC))
{
SET_BIT(dam_type, DAM_CHAOTIC);
}
if (rspec_req(ch, RSPEC_LAWFUL) || learned(ch, gsn_ki_strike_lawful))
{
SET_BIT(dam_type, DAM_LAWFUL);
}
if (learned(ch, gsn_ki_strike_adamant))
{
SET_BIT(dam_type, DAM_ADAMANTINE);
}
}
}
/* incorporeal creatures can only be hit by
* magical effects and weapons, and only 50% damage,
* or 100% with the ghost touch weapon flag - Kregor 7/23/07
*/
if (IS_INCORPOREAL(victim) && !IS_INCORPOREAL(ch) && dt != TYPE_NOFIGHT)
{
if (!wield)
{
if (is_attack(dt) && !IS_SET(dam_type, DAM_MAGICAL))
{
dam = 0;
}
if (!is_spell(dt))
{
dam = 0;
}
else
{
dam /= 2;
}
}
else
{
if (!WEAPON_FLAG(wield, WFLAG_GHOST_TOUCH))
{
if (!IS_SET(dam_type, DAM_MAGICAL))
{
dam = 0;
}
else
{
dam /= 2;
}
}
}
}
if (dam > 0 && IS_AREA_SPELL(dt))
{
dam = dam * 3 / 2;
}
if (dam_type == DAM_NONE)
{
pop_call();
return dam;
}
if (is_spell(dt))
{
wiz_printf("dam_type %s", flag_string(dam_type, dtype_flags));
wiz_printf("spell_desc %s", flag_string(skill_table[dt].spell_desc, sdesc_flags));
}
else
wiz_printf("dam_type %s", flag_string(dam_type, dtype_flags));
if (is_immune(victim, skill_table[dt].spell_desc))
{
dam = 0;
}
else
{
if (IS_SET(dam_type, DAM_PHYSICAL) && (!is_affected(victim, gsn_smite) || get_caster(victim, gsn_smite) != ch))
dam -= dam_reduction(ch, victim, dam, dam_type);
/* ablative immunity first, followed by resistance, followed by innate immunity */
if (IS_SET(dam_type, DAM_ENERGY))
{
if ((dam = energy_immunity(victim, dam, dam_type)) > 0)
{
dam -= energy_resistance(victim, dam, dam_type);
}
}
if (dam < 0)
{
dam = 0;
}
if (dam > 0 && is_vulnerable(ch, victim, dam_type))
{
dam = dam * 3 / 2;
}
}
DAM_TYPE = dam_type;
pop_call();
return dam;
}
/*
* hurt the victim and inform him of new state, award exp
*/
void damage_hurt( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt )
{
char buf[MAX_INPUT_LENGTH];
push_call("damage_hurt(%p,%p,%p,%p)",ch,victim,dam,dt);
dam = UMIN(dam, victim->hit + 10);
/* insta-kill attacks cannot be nonlethal */
if (dt == gsn_coup_de_grace || dt == gsn_assassinate || dt == gsn_vorpal_hit || dt == gsn_quivering_palm)
REMOVE_BIT(DAM_TYPE, DAM_NONLETHAL);
if (IS_SET(DAM_TYPE, DAM_NONLETHAL))
{
if (race_type(victim) != RTYPE_CONSTRUCT && race_type(victim) != RTYPE_UNDEAD && !IS_GOD(victim))
victim->nonlethal = UMIN(victim->nonlethal + dam, get_max_hit(victim) + 10);
}
else if (!split(victim, DAM_TYPE)) // should check for split, else do actual dmg - Kregor
{
if (dt == gsn_vorpal_hit /* vorpal weapon flag - DECAPITATED!! - Kregor */
|| (CRIT_HIT && IS_SET(DAM_TYPE, DAM_SLASH) && ch->race == RACE_BALOR)) // any slashing weapon balor wields is vorpal
{
act( "$n's slash severs your head!!", ch, NULL, victim, TO_VICT);
act( "$n's slash severs $N's head!!", ch, NULL, victim, TO_NOTVICT);
act( "Your slash severs $N's head!!", ch, NULL, victim, TO_CHAR);
dam = victim->hit + 10;
}
else if (dt == gsn_quivering_palm && !is_immune(victim, SDESC_DEATH)) /* quivering palm - save or die! - Kregor */
{
if (!will_save(victim, ch, multi_class_level(ch, gsn_quivering_palm) / 2 + stat_bonus(TRUE, ch, APPLY_WIS) + 10, -1))
{
act( "{118}Your body starts to quiver and spasm, then... blackness!", victim, NULL, NULL, TO_CHAR);
act( "{118}$n starts to quiver and spasm, then collapses!", victim, NULL, NULL, TO_ROOM);
dam = victim->hit + 10;
}
}
// vorpal and quivering palm trump regen - Kregor
else if (get_apply(victim, APPLY_REGENERATION) <= 0 || IS_SET(DAM_TYPE, DAM_ACID|DAM_FIRE) || is_vulnerable(ch, victim, DAM_TYPE))
{
if (dt == gsn_coup_de_grace)
{
if (!fort_save(victim, ch, 10 + dam, -1))
{
act( "{118}$n ends your life with a single strike!", ch, NULL, victim, TO_VICT);
act( "{118}$n ends $N's life with a single strike!", ch, NULL, victim, TO_NOTVICT);
act( "{128}You end $N's life with a single strike!", ch, NULL, victim, TO_CHAR);
dam = victim->hit + 10;
}
}
else if (dt == gsn_assassinate) /* assasination - instant stun - Kregor */
{
if (!fort_save(victim, ch, 10 + multi_skill_level(ch, gsn_assassinate) + stat_bonus(TRUE, ch, APPLY_INT), -1))
{
dam = victim->hit;
if (learned(ch, gsn_true_death))
{
dam += 11;
}
}
}
else if (dam > UMAX(50, get_max_hit(victim) / 2) && CAN_CRITICAL(victim)) /* Pathfinder RPG massive damage rule */
{
if (!fort_save(victim, ch, 15, -1))
{
act( "{118}You succumb to massive damage!", ch, NULL, victim, TO_VICT);
act( "{128}$N succumbs to massive damage!", ch, NULL, victim, TO_CHAR);
act( "{118}$N succumbs to massive damage!", ch, NULL, victim, TO_NOTVICT);
dam = victim->hit + 1;
}
else
{
send_to_char( "{018}***That really did HURT!***{300}\n\r", victim);
}
}
}
//defensive roll here!
if (defensive_roll(victim, ch, dt, dam))
dam /= 2;
victim->hit -= dam;
}
if (!IS_NPC(victim) && (victim->level >= LEVEL_IMMORTAL || NEW_AUTH(victim) || victim->in_room->vnum == ROOM_VNUM_LIMBO) && victim->hit < 1)
{
victim->hit = 1;
}
if (victim->hit < 0 && get_apply(victim, APPLY_REGENERATION) > 0 && !IS_SET(DAM_TYPE, DAM_ACID|DAM_FIRE) && !is_vulnerable(ch, victim, DAM_TYPE))
{
victim->hit = 0;
}
// domain/race abilities to restore hit points at mortal wounding - Kregor
if (victim->hit < 0 && victim->hit > -10)
{
if (domain_skill(victim, gsn_divine_renewal) && !victim->uses[gsn_divine_renewal])
{
if ((victim->hit += 8 + UMIN(class_level(ch, CLASS_CLERIC), stat_bonus(TRUE, victim, APPLY_WIS))) >= 0)
{
act("{138}You feel a surge of divine renewal!", victim, NULL, NULL, TO_CHAR);
act("{138}$n beams with divine renewal!", victim, NULL, NULL, TO_CHAR);
victim->uses[gsn_divine_renewal]++;
}
}
else if (race_skill(victim, gsn_ferocity) && !victim->uses[gsn_ferocity])
{
act("{118}You keep going, in spite of grievous wounds!", victim, NULL, NULL, TO_CHAR);
act("{118}$n presses on in spite of grievous wounds!", victim, NULL, NULL, TO_CHAR);
}
}
update_pos(victim,-1);
if (victim->position == POS_MORTAL)
{
if (!IS_NPC(ch))
{
victim->critical_hit_by = ch->pcdata->pvnum;
}
else
{
victim->critical_hit_by = 0;
}
}
if (dt == gsn_vorpal_hit) /* Here goes the head! */
{
if (!collect_bounty(ch, victim))
{
OBJ_DATA *obj;
char *name;
name = IS_NPC(ch) ? victim->short_descr : victim->name;
obj = create_object( get_obj_index( OBJ_VNUM_SEVERED_HEAD ), 0 );
sprintf(buf, obj->short_descr, name);
RESTRING(obj->short_descr, buf);
sprintf(buf, obj->long_descr, name);
RESTRING(obj->long_descr, buf);
obj_to_room(obj, ch->in_room->vnum);
act ("$p plops to the ground.", ch, obj, victim, TO_CHAR);
act ("$p plops to the ground.", ch, obj, victim, TO_ROOM);
}
else
{
ch->pcdata->bounties++;
}
}
if (victim->position > POS_STUNNED && dam > 0)
{
if (dt == gsn_stunning_fist)
{
int dc = (ch->level / 2) + 10 + stat_bonus(TRUE, ch, APPLY_WIS);
if (CAN_CRITICAL(victim) && !fort_save(victim, ch, dc, -1))
{
AFFECT_DATA af;
af.type = gsn_stunning_fist;
af.location = APPLY_NONE;
af.modifier = 0;
af.level = ch->level;
af.duration = 1;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_STUNNED;
affect_join( ch, victim, &af );
act( "$tYour punch sends $N reeling!", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), victim, TO_CHAR);
act( "$t$n's punch sends $N reeling!", ch, get_color_string(ch, COLOR_TEXT, VT102_BOLD), victim, TO_NOTVICT);
act( "$t$n's punch sends you reeling!", ch, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_VICT);
}
update_pos(victim,-1);
}
if (dt == gsn_shield_bash && learned(ch, gsn_shield_slam))
{
if (CAN_CRITICAL(victim) && !fort_save(victim, ch, 10 + dam, -1))
{
AFFECT_DATA af;
af.type = gsn_shield_slam;
af.location = APPLY_NONE;
af.modifier = 0;
af.level = ch->level;
af.duration = 1;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_DAZED;
affect_join( ch, victim, &af );
act( "$tYour shield bash sends $N reeling!", ch, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), victim, TO_CHAR);
act( "$t$n's shield bash sends $N reeling!", ch, get_color_string(ch, COLOR_TEXT, VT102_BOLD), victim, TO_NOTVICT);
act( "$t$n's shield bash sends you reeling!", ch, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), victim, TO_VICT);
}
update_pos(victim,-1);
}
if (victim->position > POS_STUNNED)
show_char_to_char( victim, NULL, FALSE );
}
switch (victim->position)
{
case POS_MORTAL:
act( "$n is mortally wounded, and will die soon, if not aided.", victim, NULL, NULL, TO_ROOM);
send_to_char( "You are mortally wounded, and will die soon, if not aided.\n\r", victim);
break;
case POS_INCAP:
act( "$n is incapacitated, but stable.", victim, NULL, NULL, TO_ROOM );
send_to_char("You are incapacitated, but stable.\n\r",victim );
break;
case POS_STUNNED:
act( "$n is stunned, but will probably recover.",victim, NULL, NULL, TO_ROOM );
send_to_char("You are stunned, but will probably recover.\n\r", victim );
break;
case POS_DEAD:
if (in_combat(victim))
{
if (IS_NPC(ch) && ch->master)
{
victim->last_attacker = ch->master;
}
else
{
victim->last_attacker = ch;
}
}
mprog_death_trigger(ch, victim);
rprog_death_trigger(ch, victim);
mprog_kill_trigger(ch, victim);
rprog_kill_trigger(ch, victim);
if (IS_NPC(victim) && dt != gsn_death_throes && race_skill(victim, gsn_death_throes)) // added going out with a blast!
{
act("{118}$n is consumed in a blast of flames!!", victim, NULL, NULL, TO_ROOM);
spell_damage(victim, ch, victim->level*5, gsn_death_throes, victim->level);
}
else
{
act( "$n is DEAD!!", victim, NULL, NULL, TO_ROOM );
send_to_char_color( "{138}You have been KILLED!!\n\r\n\r", victim );
}
break;
default:
break;
}
/*
* Sleep spells and extremely wounded folks.
*/
if (!IS_AWAKE(victim) || ch == victim)
{
if (dt != TYPE_NOFIGHT)
{
stop_hate_fear(victim);
}
}
else if (!IS_NPC(victim) && is_attack(dt))
{
oprog_hit_trigger(victim);
}
else if (IS_NPC(victim))
{
mprog_hit_trigger(victim, ch);
}
if (!IS_NPC(ch) && IS_AWAKE(ch) && ch != victim)
{
if (is_attack(dt))
{
oprog_damage_trigger(ch);
}
}
/*
* Payoff for killing things.
*/
if (victim->position == POS_DEAD)
{
if (!IS_NPC(victim))
{
if (!IS_NPC(ch))
{
if (ch != victim)
{
spam_attack_list(victim);
if (in_area(victim, ROOM_VNUM_ARENA))
{
add_to_victory_list( ch, victim );
}
else if (dt != gsn_vorpal_hit && collect_bounty(ch, victim))
{
ch->pcdata->bounties++;
}
}
}
else
{
victim->pcdata->mdeaths++;
if (victim->pcdata->clan != NULL)
{
victim->pcdata->clan->mdeaths++;
save_clan( victim->pcdata->clan );
}
}
}
if (IS_NPC(victim) && !IS_NPC(ch))
{
ch->pcdata->mkills++;
if (ch->pcdata->clan != NULL)
{
ch->pcdata->clan->mkills++;
}
ch->pcdata->murder_grudge[victim->pIndexData->vnum]++;
if ((IS_CITIZEN(victim) || IS_GUARD(victim)) && OPPOSITE_ALIGN(ch, victim) < 75)
{
gain_reputation(ch, -3);
}
if (IS_CITIZEN(victim) || IS_GUARD(victim))
{
set_bounty(victim, ch, CRIME_LOW_MURDER);
}
}
if (!IS_NPC(victim))
{
log_printf("%s killed by %s at %u", victim->name, get_name( ch ), victim->in_room->vnum );
save_char_obj(victim, BACKUP_SAVE);
if (!PERM_DEATH) // disabled perm death means xp & level loss at the time of death.
{
if (IS_NPC(ch))
{
int exp_loss = victim->pcdata->exp - exp_level(victim, victim->level - 1);
if (exp_loss > 0)
{
gain_exp(victim, 0 - exp_loss / 2);
}
if (victim->level > 1)
lose_level(victim, TRUE);
}
}
if (!in_area(victim, ROOM_VNUM_ARENA))
{
// killer and thief flags only go away if killed by PC who isn't one himself.
if (!IS_PLR(ch, PLR_KILLER|PLR_THIEF))
{
if (IS_SET(victim->act, PLR_KILLER))
{
REMOVE_BIT(victim->act, PLR_KILLER);
}
if (IS_SET(victim->act, PLR_THIEF))
{
REMOVE_BIT(victim->act, PLR_THIEF);
}
}
}
}
else
{
group_gain( ch, victim );
}
if (ch == victim)
{
if (ch->position == POS_DEAD)
{
if (IS_NPC(ch))
{
log_printf("[%u] %s just killed itself using damage type %d.", ch->pIndexData->vnum, ch->short_descr, dt);
}
else
{
log_printf("%s just killed itself using damage type %d.", get_name(ch), dt);
}
raw_kill(ch, dt);
}
pop_call();
return;
}
if (!confused)
set_fighting(victim, ch);
if (!IS_NPC(ch) && !IS_NPC(victim) && in_area(ch, ROOM_VNUM_ARENA))
{
PLAYER_GAME *gpl;
for (gpl = mud->f_player ; gpl ; gpl = gpl->next )
{
if (gpl->ch != ch && gpl->ch != victim)
{
ch_printf( gpl->ch, "Announcers exclaim throughout the city, %s'%s has defeated %s in the arena!'\n\r", get_color_string(gpl->ch, COLOR_SPEECH, VT102_BOLD), ch->name, victim->name );
}
}
ch->pcdata->akills++;
}
raw_kill( victim, dt );
if (!IS_NPC(victim) && !IS_NPC(ch))
{
OBJ_DATA *corpse;
if ((corpse = victim->pcdata->corpse) != NULL)
{
corpse->value[5] = ch->pcdata->pvnum; //marks the killer of PC for single-item loot.
}
}
if (ch->desc && CH(ch->desc)->pcdata->vt100 == 1)
{
vt100prompt( ch );
}
pop_call();
return;
}
else if (!IS_AWAKE(victim)) // jail or sever if disabled - Kregor
{
if (collect_bounty(ch, victim))
{
ch->pcdata->bounties++;
}
}
if (ch->desc)
{
if (CH(ch->desc)->pcdata->vt100 == 1)
{
vt100prompt( ch );
}
}
if (victim->desc)
{
if (CH(victim->desc)->pcdata->vt100 == 1)
{
vt100prompt( victim );
}
}
pop_call();
return;
}
bool has_mirror(CHAR_DATA *ch, CHAR_DATA *victim, int dt)
{
AFFECT_DATA *paf;
char message[MAX_INPUT_LENGTH];
const char *attack;
bool all;
push_call("has_mirror(%p,%p)",ch,victim);
attack = NULL;
all = FALSE;
if (IS_AFFECTED(ch, AFF_TRUESIGHT))
{
pop_call();
return FALSE;
}
if (!is_affected(victim, gsn_mirror_image))
{
pop_call();
return FALSE;
}
if (dt == TYPE_NOFIGHT)
{
pop_call();
return FALSE;
}
SET_BIT(ch->attack, ATTACK_MIRROR);
for (paf = victim->first_affect ; paf ; paf = paf->next)
{
if (paf->type == gsn_mirror_image)
{
if (number_range(0, paf->modifier))
{
paf->modifier -= 1;
if (paf->modifier <= 0)
{
all = TRUE;
affect_strip(victim, paf->type);
}
}
else
{
act( "You discover the real $N.", ch, NULL, short_to_name(PERS(victim,ch), 1), TO_CHAR);
pop_call();
return FALSE;
}
break;
}
}
attack = get_dam_nounce(ch, victim, dt, NULL);
if (attack[0] != '\0')
{
sprintf(message, "Your %s destroys $N in one blow!", attack);
act( message, ch, NULL, victim, TO_CHAR);
sprintf(message, "$n's %s destroys $N in one blow!", attack);
act( message, ch, NULL, victim, TO_NOTVICT);
act( "One of your images is dispelled!", ch, NULL, victim, TO_VICT);
}
else
{
act( "You destroy $N in one blow!", ch, NULL, victim, TO_CHAR);
act( "$n destroys $N in one blow!", ch, NULL, victim, TO_NOTVICT);
act( "One of your images is dispelled!", ch, NULL, victim, TO_VICT);
}
if (all)
{
act( "You discover the real $T.", ch, NULL, short_to_name(PERS(victim,ch), 1), TO_CHAR );
act( "All your images are gone!", ch, NULL, victim, TO_VICT );
act( "All $N's images have vanished.", ch, NULL, victim, TO_NOTVICT );
}
pop_call();
return TRUE;
}
/*
* Make a corpse out of a character.
* Added dt for effects that leave corpse or prevent raising on corpse - Kregor
*/
void make_corpse( CHAR_DATA *ch, int dt )
{
char buf[MAX_INPUT_LENGTH];
OBJ_DATA *corpse;
OBJ_DATA *obj;
OBJ_DATA *obj_next;
int size;
push_call("make_corpse(%p)",ch);
if (IS_UNDEAD(ch) || IS_ACT(ch, ACT_NOCORPSE) || (valid_skill(dt) && IS_SET(skill_table[dt].flags, SF_NOCORPSE)))
{
for (obj = ch->first_carrying ; obj ; obj = obj_next)
{
obj_next = obj->next_content;
if (IS_SET(obj->extra_flags, ITEM_INVENTORY))
{
if (IS_NPC(ch))
junk_obj(obj);
}
else
{
obj_to_room( obj, ch->in_room->vnum );
}
}
pop_call();
return;
}
size = UMAX(0, ch->size - 2);
if (IS_NPC(ch))
{
corpse = create_object(get_obj_index(OBJ_VNUM_CORPSE_NPC), 0);
corpse->timer = 4;
corpse->level = ch->level;
corpse->owned_by = 0;
corpse->value[4] = ch->pIndexData->vnum;
corpse->value[5] = size * size;
corpse->value[6] = size;
corpse->weight = ch->weight;
corpse->size = ch->size;
if (IS_ACT(ch, ACT_PET) && ch->master != NULL && ch->master->pcdata != NULL )
{
corpse->owned_by = ch->master->pcdata->pvnum;
}
if (ch->gold > 0)
{
obj_to_obj(create_money(ch->gold), corpse);
ch->gold = 0;
}
RESTRING(corpse->description, ch->description);
}
else
{
corpse = create_object(get_obj_index(OBJ_VNUM_CORPSE_PC), 0);
corpse->timer = 60;
if (ch->pcdata->corpse == NULL)
{
ch->pcdata->corpse = corpse;
ch->pcdata->corpse_room = ch->in_room->vnum;
}
corpse->owned_by = ch->pcdata->pvnum;
RESTRING(corpse->description, ch->description);
corpse->value[4] = ch->pcdata->pvnum;
corpse->weight = ch->weight;
corpse->size = ch->size;
if (is_spell(dt) && IS_SET(skill_table[dt].spell_desc, SDESC_DEATH))
SET_BIT(corpse->extra_flags, ITEM_NO_RAISE);
}
sprintf(buf, "corpse %s %s", IS_NPC(ch) ? "npc_corpse" : "pc_corpse", short_to_name(get_name(ch), 1));
RESTRING(corpse->name, buf);
sprintf(buf, "the corpse of %s", adjective(ch));
RESTRING(corpse->short_descr, buf);
sprintf(buf, "The corpse of %s is lying here.", adjective(ch));
RESTRING(corpse->long_descr, buf);
for (obj = ch->first_carrying ; obj ; obj = obj_next)
{
obj_next = obj->next_content;
// sorry, but shopkeepers' inventory gets purged - Kregor
if (is_keeper(ch) && obj->reset && obj->reset->command == 'G')
{
junk_obj(obj);
continue;
}
if (IS_NPC(ch))
{
obj_to_obj( obj, corpse );
continue;
}
// nodrop items will stay with dead character - Kregor
if (!IS_SET(obj->extra_flags, ITEM_INVENTORY) && !IS_SET(obj->extra_flags, ITEM_NODROP))
{
obj_to_obj( obj, corpse );
}
}
if (ch->gold > 0)
{
obj_to_obj(create_money(ch->gold), corpse);
ch->gold = 0;
}
obj_to_room(corpse, ch->in_room->vnum);
if (valid_skill(dt) && IS_SET(skill_table[dt].spell_desc, SDESC_DEATH))
SET_BIT(corpse->extra_flags, ITEM_NO_RAISE);
if (!IS_NPC(ch))
{
ch->pcdata->time_of_death = mud->current_time;
ch->pcdata->just_died_ctr = 10;
ch->pcdata->condition[COND_FULL] = max_hunger(ch);
ch->pcdata->condition[COND_THIRST] = max_thirst(ch);
ch->pcdata->condition[COND_DRUNK] = 0;
ch->pcdata->condition[COND_AIR] = max_air(ch);
if (PERM_DEATH)
ch_printf_color(ch, "You feel yourself leaving your body as everything goes black...\n\r");
else
ch_printf_color(ch, "The gods have retreived your soul from oblivion.\n\r");
}
pop_call();
return;
}
/*
* Kills a character, for whatever reason.
*/
void raw_kill( CHAR_DATA *victim, int dt )
{
push_call("raw_kill(%p)",victim);
if (victim == supermob)
{
log_printf("The SuperMob is immortal, he cannot die!!");
pop_call();
return;
}
if (IS_NPC(victim))
{
if (IS_SET(victim->act, ACT_WILL_DIE))
{
pop_call();
return;
}
SET_BIT(victim->act, ACT_WILL_DIE);
}
if (victim->furniture)
{
user_from_furniture(victim);
}
if (victim->hitched)
{
user_from_cart(victim);
}
if (victim->poison)
{
POISON_DATA *pd;
while ((pd = victim->poison) != NULL)
{
victim->poison = victim->poison->next;
FREEMEM( pd );
}
}
while (victim->first_disease)
{
disease_from_char(victim, victim->first_disease);
}
while (victim->first_affect)
{
affect_from_char(victim, victim->first_affect);
}
victim->affected_by = 0;
victim->affected2_by = 0;
if (victim->tracked_by)
stop_tracking(victim->tracked_by);
if (victim->grappling)
{
stop_grapple(victim->grappled_by);
}
if (victim->grappled_by)
{
stop_grapple(victim->grappled_by);
}
if (!IS_NPC(victim))
{
if (victim->pcdata->shadowing)
{
stop_shadow(victim);
}
if (victim->pcdata->tracking)
{
stop_tracking(victim);
}
if (victim->pcdata->shadowed_by)
{
stop_shadow(victim->pcdata->shadowed_by);
}
if (in_area(victim, ROOM_VNUM_ARENA))
{
arena_death(victim);
}
else if (in_area(victim, ROOM_VNUM_SCHOOL))
{
newbie_death(victim);
}
else
{
player_death(victim, dt);
}
pop_call();
return;
}
make_corpse(victim, dt);
stop_fighting(victim, TRUE);
if (IS_SET(victim->act, ACT_PET) && victim->master)
{
send_to_char("Your pet dies a horrible death.\n\r", victim->master);
}
/* Familiar death, costs 200 exp per level, save for half */
if (IS_ACT(victim, ACT_FAMILIAR|ACT_WARHORSE) && victim->master)
{
int exp_loss = victim->level * 200;
if (fort_save(victim->master, NULL, 10 + victim->level, -1))
exp_loss /= 2;
if (exp_loss > 0)
{
gain_exp(victim, 0 - exp_loss);
if (IS_ACT(victim, ACT_FAMILIAR))
act( "You suffer from the death of your familiar!", victim->master, NULL, NULL, TO_CHAR);
if (IS_ACT(victim, ACT_WARHORSE))
act( "You suffer from the death of your mount!", victim->master, NULL, NULL, TO_CHAR);
act( "$n writhes in pain and looks drained!", victim->master, NULL, NULL, TO_ROOM);
}
}
junk_mob(victim);
pop_call();
return;
}
/*
* Gutted the death cry, no more chunks and gibs - Kregor
*/
void death_cry( CHAR_DATA *ch )
{
EXIT_DATA *pExit;
char *msg;
int door, temp_vnum;
bool cry = FALSE;
push_call("death_cry(%p)",ch);
if (IS_UNDEAD(ch))
{
msg = "$n turns to dust.";
}
else if (IS_ACT(ch, ACT_NOCORPSE))
{
msg = "$n vanishes into thin air.";
}
else
{
msg = "$n lets out a death cry.";
cry = TRUE;
}
act( msg, ch, NULL, NULL, TO_ROOM );
if (IS_NPC(ch) && !CAN_TALK(ch))
{
msg = "You hear something's death cry.";
}
else
{
msg = "You hear someone's death cry.";
}
if (cry && !in_area(ch, ROOM_VNUM_ARENA))
{
temp_vnum = ch->in_room->vnum;
for (door = 0 ; door <= 5 ; door++)
{
if ((pExit = get_exit(temp_vnum, door)) != NULL)
{
ch->in_room = room_index[pExit->to_room];
act( msg, ch, NULL, NULL, TO_ROOM );
}
}
ch->in_room = room_index[temp_vnum];
}
pop_call();
return;
}
/*
* Newbies do not perm death, because we're nice - Kregor.
*/
void newbie_death( CHAR_DATA *victim )
{
push_call("arena_death(%p)",victim);
victim->position = POS_RESTING;
victim->hit = UMAX( 1, victim->hit );
destroy_mana( victim );
victim->move = UMAX( 1, victim->move );
char_from_room(victim);
char_to_room(victim, ROOM_VNUM_SCHOOL, TRUE);
char_reset(victim);
pop_call();
return;
}
/*
* Defeat in arena is not lethal. Sends back
* to PCs death room.
*/
void arena_death( CHAR_DATA *victim )
{
push_call("arena_death(%p)",victim);
victim->position = POS_RESTING;
victim->hit = UMAX( 1, victim->hit );
destroy_mana( victim );
victim->move = UMAX( 1, victim->move );
char_from_room(victim);
char_to_room(victim, ROOM_VNUM_TEMPLE, TRUE);
char_reset(victim);
pop_call();
return;
}
/*
* Very lethal death. Depending on perm death option,
* character is either stuck on fugue plane, or respawned
* with level loss back to a safe room - Kregor
*/
void player_death( CHAR_DATA *victim, int dt )
{
OBJ_DATA *obj, *obj_next;
push_call("player_death(%p)",victim);
if (victim->pcdata->death_room == ROOM_VNUM_SCHOOL)
{
if (!in_area(victim, ROOM_VNUM_SCHOOL))
{
victim->pcdata->death_room = ROOM_VNUM_TEMPLE;
}
}
make_corpse(victim, dt);
for (obj = victim->first_carrying ; obj ; obj = obj_next)
{
obj_next = obj->next_content;
if (!IS_SET(obj->extra_flags, ITEM_INVENTORY))
{
junk_obj( obj );
}
}
char_from_room(victim);
if (PERM_DEATH)
{
SET_BIT(victim->act, PLR_DEAD);
char_to_room(victim, ROOM_VNUM_DEATH, TRUE);
victim->pcdata->recall = ROOM_VNUM_TEMPLE;
}
else
{
if (victim->level < 2)
{
char_to_room(victim, ROOM_VNUM_SCHOOL, TRUE);
}
else
{
char_to_room(victim, victim->pcdata->death_room, TRUE);
}
victim->pcdata->recall = victim->in_room->vnum;
}
victim->position = POS_RESTING;
victim->hit = UMAX( 1, victim->hit );
destroy_mana( victim );
victim->move = UMAX( 1, victim->move );
char_reset(victim);
save_char_obj(victim, NORMAL_SAVE);
do_look(victim, "");
pop_call();
return;
}
/*
* Make the spam string for an attack, base verb
* on percent HP damaged - Kregor
*/
void dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, OBJ_DATA *wield )
{
CHAR_DATA *fch;
PLAYER_GAME *fpl;
char buf1[MAX_INPUT_LENGTH];
char buf2[MAX_INPUT_LENGTH];
char buf3[MAX_INPUT_LENGTH];
char youHit[41], hitYou[41];
const char *vp;
const char *attack;
char punct;
int percent;
int diceroll = 0;
push_call("dam_message(%p,%p,%d,%d,%d)",ch,victim,dam,dt,wield);
if (dt == TYPE_NOFIGHT || dam == -1)
{
pop_call();
return;
}
if (ch == NULL) /* crash prevention */
ch = victim;
percent = 100 * dam / UMAX(1, get_max_hit(victim));
attack = NULL;
if ( dam == 0 )
{
if ( dt == gsn_acid_hit || dt == gsn_fire_hit || dt == gsn_frost_hit || dt == gsn_shock_hit )
{
vp = "is resisted by";
}
else
{
vp = "misses";
}
}
else if ( dt == gsn_acid_hit )
{
vp = "eats away at";
}
else if ( dt == gsn_fire_hit )
{
vp = "burns";
}
else if ( dt == gsn_frost_hit )
{
vp = "chills";
}
else if ( dt == gsn_shock_hit )
{
vp = "shocks";
}
else if ( percent <= 5 )
{
vp = "barely touches";
}
else if ( percent <= 10 )
{
vp = "scratches";
}
else if ( percent <= 15 )
{
vp = "grazes";
}
else if ( percent <= 20 )
{
vp = "hits";
}
else if ( percent <= 30 )
{
vp = "solidly hits";
}
else if ( percent <= 40 )
{
vp = "hurts";
}
else if ( percent <= 50 )
{
vp = "MUTILATES";
}
else if ( percent <= 60 )
{
vp = "*EVISCERATES*";
}
else if ( percent <= 70 )
{
vp = "**MASSACRES**";
}
else if ( percent <= 80 )
{
vp = "***DEMOLISHES***";
}
else if ( percent <= 90 )
{
vp = "****ANNIHILATES****";
}
else
{
vp = "*****OBLITERATES*****";
}
punct = (percent <= 20) ? '.' : '!';
strcpy(hitYou, (dam <= 0) ? "" : get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD));
if (ch != victim)
strcpy(youHit, (dam <= 0) ? "" : get_color_code(ch, COLOR_YOU_HIT, VT102_BOLD));
else
strcpy(hitYou, (dam <= 0) ? "" : get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD));
if (dt == TYPE_HIT)
{
attack = get_dam_nounce(ch, victim, dt, wield);
if (attack[0] != '\0')
{
sprintf(buf1, "%s %s", attack, vp);
if (dam > 0)
{
sprintf(buf2, "%sYour %s %s $N for %dhp%c", youHit, attack, vp, dam, punct);
}
else
{
sprintf(buf2, "%sYour %s %s $N%c", youHit, attack, vp, punct);
}
if (dam > 0)
{
sprintf(buf3, "%s$n's %s %s you for %dhp%c", hitYou, attack, vp, dam, punct);
}
else
{
sprintf(buf3, "%s$n's %s %s you%c", hitYou, attack, vp, punct);
}
}
else
{
sprintf(buf1, "attack %s", vp);
if (dam > 0)
{
sprintf(buf2, "%sYour attack %s $N for %dhp%c", youHit, vp, dam, punct );
}
else
{
sprintf(buf2, "%sYour attack %s $N%c", youHit, vp, punct);
}
if (dam > 0)
{
sprintf(buf3, "%s$n's attack %s you for %dhp%c", hitYou, vp, dam, punct);
}
else
{
sprintf(buf3, "%s$n's attack %s you%c", hitYou, vp, punct);
}
}
}
else
{
attack = get_dam_nounce(ch, victim, dt, wield);
sprintf(buf1, "%s %s", attack, vp);
if (dam > 0)
{
sprintf( buf2, "%sYour %s %s $N for %dhp%c", youHit, attack, vp, dam, punct );
}
else
{
sprintf( buf2, "%sYour %s %s $N%c", youHit, attack, vp, punct );
}
if (dam > 0)
{
sprintf(buf3, "%s%s's %s %s you for %dhp%c", hitYou, ch == victim ? "Someone" : "$n", attack, vp, dam, punct);
}
else
{
sprintf(buf3,"%s%s's %s %s you%c", hitYou, ch == victim ? "Someone" : "$n", attack, vp, punct);
}
}
if (dt == gsn_assassinate && learned(ch, gsn_silent_death))
diceroll = hide_roll(ch);
else
diceroll = 0;
/*
Spam code for combat - Chaos 12/20/94
*/
for (fch = ch->in_room->first_person ; fch ; fch = fch->next_in_room)
{
if (fch->desc && fch != ch && fch != victim)
{
if (diceroll && hide_check(ch, fch, diceroll, perception_roll(fch, SENSE_SIGHT)))
continue;
if (fch->position > POS_SLEEPING)
{
bool gch, gvic;
gch = is_same_group( ch, fch );
gvic = is_same_group( victim, fch );
if (gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_PARTY_MISS))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_PARTY_HIT))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_YOU_HIT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_THEY_MISS_PARTY))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
else if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_THEY_HIT_PARTY))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_YOU_ARE_HIT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_OTHER_MISS))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_OTHER_HIT))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_BOLD), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
}
}
if (in_area(ch, ROOM_VNUM_ARENA))
{
for (fpl = mud->f_player ; fpl ; fpl = fpl->next)
{
if (!IS_IMMORTAL(fpl->ch) && blocking(fpl->ch, ch))
continue;
if (!fpl->ch->desc || fpl->ch == ch || fpl->ch == victim)
continue;
if (fpl->ch == ch)
continue;
if (fpl->ch->in_room == NULL || in_same_room(ch, fpl->ch)
|| !in_area(fpl->ch, ROOM_VNUM_ARENA)
|| !IS_SET(fpl->ch->in_room->room_flags, ROOM_AUDIENCE))
continue;
if (diceroll && hide_check(ch, fpl->ch, diceroll, perception_roll(fpl->ch, SENSE_SIGHT)))
continue;
if (fpl->ch->position > POS_SLEEPING)
{
bool gch, gvic;
gch = is_same_group( ch, fpl->ch );
gvic = is_same_group( victim, fpl->ch );
if (gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_PARTY_MISS))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_PARTY_HIT))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_YOU_HIT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && gvic)
{
if (dam <= 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_THEY_MISS_PARTY))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
else if (dam > 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_THEY_HIT_PARTY))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_YOU_ARE_HIT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_OTHER_MISS))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fpl->ch->desc)->pcdata->spam, SPAM_OTHER_HIT))
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_TEXT, VT102_BOLD), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
}
else
{
ch_printf_color(fpl->ch, "%s%s's %s %s%c\n\r", get_color_string(fpl->ch, COLOR_TEXT, VT102_DIM), ch == victim ? "Someone" : capitalize(PERS(ch, fpl->ch)), buf1, PERS(victim, fpl->ch), (percent <= 18) ? '.' : '!');
}
}
}
}
if (ch->in_room->vnum != victim->in_room->vnum)
{
for (fch = victim->in_room->first_person ; fch ; fch = fch->next_in_room)
{
if (fch->desc && fch != ch && fch != victim)
{
if (diceroll && hide_check(ch, fch, diceroll, perception_roll(fch, SENSE_SIGHT)))
continue;
if (fch->position > POS_SLEEPING)
{
bool gch, gvic;
gch = is_same_group( ch, fch );
gvic = is_same_group( victim, fch );
if (gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_PARTY_MISS))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_PARTY_HIT))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_YOU_HIT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_THEY_MISS_PARTY))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
else if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_THEY_HIT_PARTY))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_YOU_ARE_HIT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else if (!gch && !gvic)
{
if (dam <= 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_OTHER_MISS))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
if (dam > 0 && !IS_SET(CH(fch->desc)->pcdata->spam, SPAM_OTHER_HIT))
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_BOLD), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
else
{
ch_printf_color(fch, "%s%s's %s %s%c\n\r", get_color_string(fch, COLOR_TEXT, VT102_DIM), capitalize(PERS(ch, fch)), buf1, PERS(victim, fch), (percent <= 18) ? '.' : '!');
}
}
}
}
}
if (dam == 0)
{
if (ch->desc && !IS_SET(CH(ch->desc)->pcdata->spam, SPAM_YOU_MISS))
{
if (ch->position > POS_SLEEPING)
{
act( ch == victim ? buf3 : buf2, ch, NULL, victim, TO_CHAR);
}
}
if (victim->desc && !IS_SET(CH(victim->desc)->pcdata->spam, SPAM_THEY_MISS))
{
if (victim->position > POS_SLEEPING)
{
act( buf3, ch, NULL, victim, TO_VICT);
}
}
}
else
{
if (ch->desc && !IS_SET(CH(ch->desc)->pcdata->spam, SPAM_YOU_HIT))
{
if (ch->position > POS_SLEEPING)
{
act( ch == victim ? buf3 : buf2, ch, NULL, victim, TO_CHAR);
}
}
if (victim->desc && !IS_SET(CH(victim->desc)->pcdata->spam, SPAM_THEY_HIT))
{
if (victim->position > POS_SLEEPING)
{
act( buf3, ch, NULL, victim, TO_VICT);
}
}
}
pop_call();
return;
}
/*
* revamped function significantly - Kregor
*
* pass type 1 to select a primary attack part,
* or the first body part if no primary attack.
* pass type 0 to select a random attack part.
*/
int get_body_part( CHAR_DATA *ch, int dt, int type )
{
int race, part, pick, cnt, rdm;
push_call("get_body_part(%p,%p,%p)",ch,dt,type);
if (dt != TYPE_HIT)
{
pop_call();
return -1;
}
if ((race = get_race(ch)) == RACE_NONE && !IS_NPC(ch))
{
pop_call();
return -1;
}
part = -1;
if (type == 1)
{
for (cnt = 0 ; cnt < ATTK_MAX ; cnt++)
{
if (num_attacks(ch, cnt) == -1)
{
pop_call();
return -1;
}
if (cnt == ATTK_RAKE)
continue;
if (num_attacks(ch, cnt) && attack_part_table[cnt].primary)
{
part = cnt;
break;
}
}
if (part == -1) // if no primary part, grab the first actual attack part
{
for (cnt = 0 ; cnt < ATTK_MAX ; cnt++)
{
if (cnt == ATTK_RAKE)
continue;
if (num_attacks(ch, cnt))
{
part = cnt;
break;
}
}
}
}
else
{
for (rdm = cnt = 0 ; rdm < ATTK_MAX ; rdm++)
{
if (rdm == ATTK_RAKE)
continue;
if (num_attacks(ch, rdm))
{
cnt++;
}
}
pick = number_range(1, cnt);
for (rdm = cnt = 0 ; rdm < ATTK_MAX ; rdm++)
{
if (rdm == ATTK_RAKE)
continue;
if (num_attacks(ch, rdm) && ++cnt == pick)
{
part = rdm;
break;
}
}
}
pop_call();
return part;
}
/*
* loop through set attack parts, if one is flagged
* as a natural weapon, return TRUE - Kregor
*/
bool has_natural_weapon(CHAR_DATA *ch)
{
int part, race;
push_call("has_natural_weapon(%p)",ch);
if (IS_NPC(ch) && ch->pIndexData->unique && !ch->pIndexData->race)
{
pop_call();
return FALSE;
}
else if ((race = get_race(ch)) == RACE_NONE)
{
pop_call();
return FALSE;
}
for (part = 0; part < ATTK_MAX ; part++)
{
if (num_attacks(ch, part) && attack_part_table[part].natural)
{
pop_call();
return TRUE;
}
}
pop_call();
return FALSE;
}
/*
* Snarf the noun message for the damage done.
* if wield is passed, it copies the name of the weapon,
* if no weapon, it copies the name of the unarmed attack,
* or the name of the damage in skill_table for spells and
* non-physical damage - Kregor
*/
char *get_dam_nounce( CHAR_DATA *ch, CHAR_DATA *victim, int dt, OBJ_DATA *wield )
{
static char *attack;
push_call("get_dam_nounce(%p,%p,%p,%p)",ch,victim,dt,wield);
attack = "";
if (wield)
{
if (wield->pIndexData->attack_string[0] != '\0')
{
attack = wield->pIndexData->attack_string;
}
else
{
attack = weapon_table[wield->value[0]].name;
}
}
else if (is_attack(dt))
{
if (learned(ch, gsn_martial_arts))
{
attack = martial_arts_table[martial_arts_attack].message;
}
else if (learned(ch, gsn_imp_unarmed_strike))
{
attack = brawling_table[brawling_attack].message;
}
else if (ch->attack_part != -1)
{
attack = attack_part_table[ch->attack_part].attack;
}
else if (valid_skill(dt))
{
attack = skill_table[dt].noun_damage;
}
}
else
{
if (mpdamstring[0] != '\0')
{
attack = &mpdamstring[0];
}
else if (valid_skill(dt))
{
attack = skill_table[dt].noun_damage;
}
else
{
if (dt != TYPE_UNDEFINED)
{
bug("get_dam_nounce: bad dt %d.", dt);
}
attack = STRALLOC("<BAD ATTACK NAME>");
}
}
pop_call();
return attack;
}
/*
* get the number of damage dice for an
* unarmed attack - Kregor
*/
int get_damnodice( CHAR_DATA *ch )
{
int dam, bodypart, level, size;
push_call("get_damnodice(%p)",ch);
size = get_size(ch);
if ((bodypart = ch->attack_part) != -1)
{
if (race_skill(ch, gsn_imp_natural_attack)
|| is_affected(ch, gsn_stone_fist)
|| is_affected(ch, gsn_iron_body))
{
size = UMIN(size + 1, SIZE_COLOSSAL);
}
dam = dam_no_dice[bodypart].size[size];
}
else if ((bodypart = get_body_part(ch, TYPE_HIT, 1)) == -1)
{
bug("get_damnodice(%s): char has no body parts or race setting", ch->name);
pop_call();
return 0;
}
if (race_skill(ch, gsn_golem_slam))
{
dam = UMIN(2, ch->level / 6);
}
else if ((level = multi_skill_level(ch, gsn_martial_arts)) > 0)
{
switch (size)
{
case SIZE_TINY:
if (level >= 20)
dam = 2;
else
dam = 1;
break;
case SIZE_SMALL:
if (level >= 16)
dam = 2;
else
dam = 1;
break;
case SIZE_MEDIUM:
if (level >= 12)
dam = 2;
else
dam = 1;
break;
case SIZE_LARGE:
if (level >= 20)
dam = 3;
else if (level >= 8)
dam = 2;
else
dam = 1;
break;
case SIZE_HUGE:
if (level >= 16)
dam = 3;
else if (level >= 4)
dam = 2;
else
dam = 1;
break;
case SIZE_GARGANTUAN:
if (level >= 20)
dam = 4;
else if (level >= 12)
dam = 3;
else
dam = 2;
break;
case SIZE_COLOSSAL:
if (level >= 20)
dam = 5;
else if (level >= 12)
dam = 4;
else if (level >= 4)
dam = 3;
else
dam = 2;
break;
default:
dam = 1;
break;
}
}
pop_call();
return dam;
}
/*
* get the size of damage dice for an
* unarmed attack - Kregor
*/
int get_damsizedice( CHAR_DATA *ch )
{
int dam, bodypart, level, size;
push_call("get_damsizedice(%p)",ch);
size = get_size(ch);
if ((bodypart = ch->attack_part) != -1)
{
if ((bodypart = get_body_part(ch, TYPE_HIT, 1)) == -1)
{
bug("get_damsizedice(%s): char has no body parts or race setting", ch->name);
pop_call();
return 0;
}
if (race_skill(ch, gsn_imp_natural_attack)
|| is_affected(ch, gsn_stone_fist)
|| is_affected(ch, gsn_iron_body))
{
size = UMIN(size + 1, SIZE_COLOSSAL);
}
dam = dam_size_dice[bodypart].size[size];
}
if (race_skill(ch, gsn_golem_slam))
{
switch (get_size(ch))
{
default:
dam = 1;
break;
case SIZE_TINY:
dam = 3;
break;
case SIZE_SMALL:
dam = 4;
break;
case SIZE_MEDIUM:
dam = 6;
break;
case SIZE_LARGE:
dam = 8;
break;
case SIZE_HUGE:
dam = 10;
break;
case SIZE_GARGANTUAN:
case SIZE_COLOSSAL:
dam = 12;
break;
}
}
else if ((level = multi_skill_level(ch, gsn_martial_arts)) > 0)
{
switch (size)
{
case SIZE_TINY:
if (level >= 20)
dam = 6;
else if (level >= 16)
dam = 10;
else if (level >= 12)
dam = 8;
else if (level >= 8)
dam = 6;
else if (level >= 4)
dam = 4;
else
dam = 3;
break;
case SIZE_SMALL:
if (level >= 20)
dam = 8;
else if (level >= 16)
dam = 6;
else if (level >= 12)
dam = 10;
else if (level >= 8)
dam = 8;
else if (level >= 4)
dam = 6;
else
dam = 4;
break;
case SIZE_MEDIUM:
if (level >= 20)
dam = 10;
else if (level >= 16)
dam = 8;
else if (level >= 12)
dam = 6;
else if (level >= 8)
dam = 10;
else if (level >= 4)
dam = 8;
else
dam = 6;
break;
case SIZE_LARGE:
if (level >= 20)
dam = 8;
else if (level >= 16)
dam = 10;
else if (level >= 12)
dam = 8;
else if (level >= 8)
dam = 6;
else if (level >= 4)
dam = 10;
else
dam = 8;
break;
case SIZE_HUGE:
if (level >= 20)
dam = 10;
else if (level >= 16)
dam = 8;
else if (level >= 12)
dam = 10;
else if (level >= 8)
dam = 8;
else if (level >= 4)
dam = 6;
else
dam = 10;
break;
case SIZE_GARGANTUAN:
if (level >= 20)
dam = 10;
else if (level >= 16)
dam = 8;
else if (level >= 12)
dam = 10;
else if (level >= 8)
dam = 8;
else if (level >= 4)
dam = 10;
else
dam = 8;
break;
case SIZE_COLOSSAL:
if (level >= 16)
dam = 10;
else if (level >= 12)
dam = 8;
else if (level >= 8)
dam = 10;
else if (level >= 4)
dam = 8;
else
dam = 10;
break;
default:
if (level >= 20)
dam = 8;
else if (level >= 16)
dam = 6;
else if (level >= 20)
dam = 4;
else if (level >= 20)
dam = 3;
else
dam = 2;
break;
}
}
pop_call();
return dam;
}
/*
* Return TRUE if ch is wielding
* or considered a lethal weapon - Kregor
*/
bool is_armed( CHAR_DATA *ch, bool wield )
{
OBJ_DATA *obj = NULL;
push_call("is_armed(%p)", ch);
if (is_affected(ch, gsn_stone_fist)
|| is_affected(ch, gsn_thorn_body)
|| is_affected(ch, gsn_iron_body))
{
pop_call();
return TRUE;
}
if (learned(ch, gsn_martial_arts)
|| learned(ch, gsn_imp_unarmed_strike))
{
pop_call();
return TRUE;
}
if (!has_natural_weapon(ch))
{
if (!wield)
{
pop_call();
return FALSE;
}
if ((obj = get_wield(ch, FALSE)) == NULL)
{
if ((obj = get_wield(ch, TRUE)) == NULL)
{
pop_call();
return FALSE;
}
}
}
if (obj && !IS_WEAPON(obj))
{
pop_call();
return FALSE;
}
if (obj && (is_missile_weapon(obj) || WSPEC(obj, WSPEC_NOMELEE)) && !learned(ch, gsn_combat_archery))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/************************
* Attacks of Opportunity - D20 style - Kregor
*/
/*
* First, is CH able?
*/
bool can_take_opportunity( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("can_take_opportunity(%p,%p)",ch,victim);
// crash insurance:
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
/* DUH! */
if (!IS_AWAKE(ch))
{
pop_call();
return FALSE;
}
if (ch == victim)
{
pop_call();
return FALSE;
}
if (is_affected(ch, gsn_time_stop))
{
pop_call();
return FALSE;
}
/* confused will not take AoO */
if (IS_AFFECTED(ch, AFF2_CONFUSION))
{
act("You are too confused to take an opportunity.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* no threat unless actively fighting victim */
if (who_fighting(ch) != victim)
{
act("You are not actively engaging $N.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
/* grappled can't take opportunity */
if (ch->grappling || ch->grappled_by)
{
act("You too restrained to take the opportunity.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (!IS_AFFECTED(ch, AFF_FREEDOM) && (is_affected(ch, gsn_crushing_hand) || is_affected(ch, gsn_grasping_hand)))
{
act("You too restrained to take the opportunity.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't take opportunity with missile weapon */
if (MISSILE_WIELD(ch))
{
act("You cannot threaten with a missile weapon.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't take opportunity if considered unarmed */
if (!is_armed(ch, TRUE))
{
act("You are unarmed and cannot take the opportunity.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't interrupt your concentration to take opportunity */
if (ch->casting || ch->concentrating)
{
act("You are concentrating on something else.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't attack flatfooted without combat reflexes */
if (IS_FLATFOOTED(ch) && !learned(ch, gsn_combat_reflexes))
{
act("You missed your attack of opportunity.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't attack if you've cast this round */
if (IS_SET(ch->attack, ATTACK_CAST))
{
act("You can't take opportunity after casting this round.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
/* can't attack what you can't see */
if (!can_see(ch, victim))
{
act("You can't see $N to take an opportunity.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
/* ONE AoO per round */
if (IS_SET(ch->attack, ATTACK_OPPORTUNITY))
{
act("You've already made your attack of opportunity this round.", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* Next, is VICT vulnerable?
* (Only used for manual actions, auto AoO are controlled in functions)
*/
bool has_opportunity( CHAR_DATA *ch, CHAR_DATA *victim )
{
bool fOpportunity = FALSE;
push_call("has_opportunity(%p,%p)",ch,victim);
// crash insurance:
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
if (!can_take_opportunity(ch, victim))
{
pop_call();
return FALSE;
}
// qualifiers for opportunity
if (victim->casting && !learned(victim, gsn_warmage))
{
fOpportunity = TRUE;
}
else if (victim->concentrating)
{
fOpportunity = TRUE;
}
else if (IS_SET(victim->attack, ATTACK_SHOOT) && !learned(victim, gsn_combat_archery))
{
fOpportunity = TRUE;
}
else if (is_affected(victim, gsn_irresistible_dance))
{
fOpportunity = TRUE;
}
else if (in_combat(victim))
{
if (!is_armed(victim, TRUE))
{
fOpportunity = TRUE;
}
}
if (!fOpportunity)
{
pop_call();
return FALSE;
}
// After all this, check for murder allowance:
if (!check_murder(ch, victim))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* Auto attacks of opportunity
*/
bool attack_of_opportunity( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("attack_of_opportunity(%p,%p)",ch,victim);
if (!has_opportunity(ch, victim))
{
pop_call();
return FALSE;
}
act( "{138}You make an attack of opportunity against $N.", ch, NULL, victim, TO_CHAR);
act( "{138}$n makes an attack of opportunity against you.", ch, NULL, victim, TO_VICT);
act( "{138}$n makes an attack of opportunity against $N.", ch, NULL, victim, TO_NOTVICT);
one_hit(ch, victim, learned(ch, gsn_opportunist) ? gsn_opportunist : TYPE_UNDEFINED, 0, get_wield(ch, FALSE));
SET_BIT(ch->attack, ATTACK_OPPORTUNITY);
pop_call();
return TRUE;
}
/*
* Used to take elective attacks of opportunity.
*/
void take_opportunity( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("take_opportunity(%p,%p)",ch,victim);
act( "{138}You make an attack of opportunity against $N.", ch, NULL, victim, TO_CHAR);
act( "{138}$n makes an attack of opportunity against you.", ch, NULL, victim, TO_VICT);
act( "{138}$n makes an attack of opportunity against $N.", ch, NULL, victim, TO_NOTVICT);
SET_BIT(ch->attack, ATTACK_OPPORTUNITY);
pop_call();
return;
}
/*
* Conditions for ch to receive dodge bonus to AC
*/
bool can_dodge( CHAR_DATA *ch, CHAR_DATA *attacker )
{
push_call("can_dodge(%p,%p)",ch,attacker);
if (IS_HELPLESS(ch))
{
pop_call();
return FALSE;
}
if (IS_AFFECTED(ch, AFF2_STUNNED))
{
pop_call();
return FALSE;
}
if (IS_FLATFOOTED(ch) && !learned(ch,gsn_uncanny_dodge))
{
pop_call();
return FALSE;
}
if (!IS_AWAKE(ch))
{
pop_call();
return FALSE;
}
if (encumberance(ch) == OVERLOADED)
{
pop_call();
return FALSE;
}
if (attacker == NULL)
{
pop_call();
return TRUE;
}
if (is_affected(attacker, gsn_impromptu_sneak_attack))
{
pop_call();
return FALSE;
}
if (!can_see(ch, attacker) && !learned(ch, gsn_uncanny_dodge) && !learned(ch,gsn_blind_fight))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* This is where the improved uncanny dodge shines - Kregor
*/
bool is_flanking (CHAR_DATA *ch, CHAR_DATA *victim)
{
int dodge_level, flank_level;
push_call("is_flanking(%p,%p)",ch,victim);
if (IS_PLR(victim, PLR_HOLYLIGHT))
{
pop_call();
return FALSE;
}
if (who_fighting(victim) != NULL && who_fighting(victim) != ch)
{
if (multi_skill_level(victim, gsn_uncanny_dodge) >= 4)
{
dodge_level = multi_class_level(victim, gsn_uncanny_dodge);
flank_level = multi_skill_level(ch, gsn_backstab);
if (dodge_level + 4 >= flank_level)
{
pop_call();
return FALSE;
}
}
}
if (who_fighting(victim) && who_fighting(victim) == ch)
{
pop_call();
return FALSE;
}
pop_call();
return race_type_table[race_type(victim)].can_flank;
}
/*
* Conditions for parry and riposte feat - Kregor
*/
bool check_parry ( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *weapon, int attack )
{
OBJ_DATA *wield;
push_call("check_parry(%p,%p)",ch,victim);
if (IS_SET(ch->attack, ATTACK_PARRY) || !COMBATMODE(ch, COMBAT_DEFENSIVE) || !learned(ch, gsn_parry))
{
pop_call();
return FALSE;
}
if (!can_dodge(ch, victim))
{
pop_call();
return FALSE;
}
if (get_size(victim) > get_size(ch) + 1)
{
pop_call();
return FALSE;
}
if (get_size(ch) > get_size(victim) + 2)
{
pop_call();
return FALSE;
}
if ((wield = get_wield(ch, FALSE)) == NULL)
wield = get_wield(ch, TRUE);
if (weapon)
{
if (!wield)
attack += 4;
else
attack += (weapon->size - wield->size);
}
else
{
attack += (get_size(victim) - get_size(ch));
}
if (!refl_save(ch, NULL, attack, -1))
{
pop_call();
return FALSE;
}
if (learned(ch, gsn_riposte) && attack_of_opportunity(ch, victim))
{
act( "{138}$n makes an agile riposte against your attack!", ch, NULL, victim, TO_VICT);
act( "{138}$n makes an agile riposte against $N!", ch, NULL, victim, TO_NOTVICT);
act( "{138}You make an agile riposte against $N!", ch, NULL, victim, TO_CHAR);
SET_BIT(ch->attack, ATTACK_RIPOSTE);
}
else
{
act( "{138}$n parries your attack!!", ch, NULL, victim, TO_VICT);
act( "{138}$n parries $N's attack!", ch, NULL, victim, TO_NOTVICT);
act( "{138}You parry $N's attack!", ch, NULL, victim, TO_CHAR);
}
SET_BIT(ch->attack, ATTACK_PARRY);
pop_call();
return TRUE;
}
/*
* Code for the Deflect Missiles/Snatch Missiles feats - Kregor
* added support for missile deflection from spells
*/
bool deflect_missile ( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *missile, int dir )
{
AFFECT_DATA *paf;
push_call("deflect_missile(%p,%p,%p)",ch,victim,missile);
if (missile == NULL)
{
log_printf("deflect_missile(): NULL ammo or weapon.");
pop_call();
return FALSE;
}
else if (ch == NULL || victim == NULL)
{
log_printf("deflect_missile(): NULL actor, ch or victim.");
pop_call();
return FALSE;
}
if (!IS_OBJ_STAT(missile, ITEM_MAGIC))
{
if ((paf = get_affect_sn(victim, gsn_missile_deflection)) != NULL)
{
--paf->modifier;
if (paf->modifier <= 0)
{
act( skill_table[paf->type].msg_off, victim, NULL, NULL, TO_CHAR);
affect_strip(victim, gsn_missile_deflection);
}
act( "$p deflects harmlessly off of $n.", victim, missile, NULL, TO_ROOM);
act( "$p is deflected harmlessly away.", victim, missile, NULL, TO_CHAR);
if (ch->in_room != victim->in_room)
act( "$p deflects harmlessly off of $N.", ch, missile, NULL, TO_ROOM);
obj_from_char(missile);
if (!IS_AMMO(missile))
{
if (WEAPON_FLAG(missile, WFLAG_RETURNING) || (WSPEC(missile, WSPEC_RETURNING) && weapon_skill(ch, missile)))
{
act( "$p returns to $n's hand.", ch, missile, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, missile, NULL, TO_CHAR );
obj_to_char(missile, ch);
}
else
{
missile->sac_timer = OBJ_SAC_TIME;
obj_to_room(missile, victim->in_room->vnum);
}
}
else
{
junk_obj(missile);
}
pop_call();
return TRUE;
}
}
//repel metal bounces metal missiles away, guaranteed! - Kregor
if (is_affected(victim, gsn_repel_metal))
{
if (missile->material && material_table[missile->material].parent == MATERIAL_TYPE_METAL)
{
act( "$p is repelled by $n.", victim, missile, NULL, TO_ROOM);
act( "$p is repelled harmlessly away.", victim, missile, NULL, TO_CHAR);
if (ch->in_room != victim->in_room)
act( "$p is repelled by $N.", ch, missile, NULL, TO_ROOM);
obj_from_char(missile);
if (!IS_AMMO(missile))
{
if (WEAPON_FLAG(missile, WFLAG_RETURNING) || (WSPEC(missile, WSPEC_RETURNING) && weapon_skill(ch, missile)))
{
act( "$p returns to $n's hand.", ch, missile, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, missile, NULL, TO_CHAR );
obj_to_char(missile, ch);
}
else
{
missile->sac_timer = OBJ_SAC_TIME;
obj_to_room(missile, victim->in_room->vnum);
}
}
else
{
junk_obj(missile);
}
pop_call();
return TRUE;
}
}
if (IS_SET(ch->attack, ATTACK_DEFLECT))
{
pop_call();
return FALSE;
}
if (IS_FLATFOOTED(ch) || IS_HELPLESS(ch))
{
pop_call();
return FALSE;
}
if (learned(ch, gsn_ranged_parry))
{
OBJ_DATA *weapon;
if (armor_type_worn(ch) == ARMOR_NONE
&& get_eq_char(ch, WEAR_SHIELD) == NULL
&& (weapon = get_wield(ch, FALSE)) != NULL
&& WSPEC(weapon, WSPEC_FINESSE))
{
if (!refl_save(ch, victim, 20, -1))
{
pop_call();
return FALSE;
act( "$n deflects $p with $P.", ch, missile, weapon, TO_ROOM);
act( "You deflect $p with $P.", ch, missile, weapon, TO_CHAR);
if (ch->in_room != victim->in_room)
act( "$N deflects $p with $P.", victim, missile, weapon, TO_ROOM);
if (!IS_AMMO(missile))
{
if (WEAPON_FLAG(missile, WFLAG_RETURNING) || (WSPEC(missile, WSPEC_RETURNING) && weapon_skill(ch, missile)))
{
act( "$p returns to $n's hand.", ch, missile, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, missile, NULL, TO_CHAR );
obj_to_char(missile, ch);
}
else
{
missile->sac_timer = OBJ_SAC_TIME;
obj_to_room(missile, victim->in_room->vnum);
}
}
else
{
junk_obj(missile);
}
pop_call();
return TRUE;
}
else
{
pop_call();
return FALSE;
}
}
}
if (!learned(ch, gsn_deflect_arrows) && !learned(ch, gsn_snatch_arrows))
{
pop_call();
return FALSE;
}
if (!is_handy(ch) || hands_full(ch))
{
pop_call();
return FALSE;
}
if (!refl_save(ch, victim, 20, -1))
{
pop_call();
return FALSE;
}
obj_from_char(missile);
if (learned(ch, gsn_snatch_arrows) && !IS_AMMO(missile))
{
act( "$n snatches $p from midair.", ch, missile, victim, TO_ROOM);
act( "You snatch $p from midair.", ch, missile, victim, TO_CHAR);
if (ch->in_room != victim->in_room)
act( "$N snatches $p from midair.", victim, missile, ch, TO_ROOM);
obj_to_char(missile, victim);
throwback(victim, ch, missile, dir);
}
else
{
act( "$n deflects $p.", ch, missile, victim, TO_ROOM);
act( "You deflect $p.", ch, missile, victim, TO_CHAR);
if (ch->in_room != victim->in_room)
act( "$N deflects $p.", victim, missile, ch, TO_ROOM);
if (!IS_AMMO(missile))
{
if (WEAPON_FLAG(missile, WFLAG_RETURNING) || (WSPEC(missile, WSPEC_RETURNING) && weapon_skill(ch, missile)))
{
act( "$p returns to $n's hand.", ch, missile, NULL, TO_ROOM );
act( "$p returns to your hand.", ch, missile, NULL, TO_CHAR );
obj_to_char(missile, ch);
}
else
{
missile->sac_timer = OBJ_SAC_TIME;
obj_to_room(missile, victim->in_room->vnum);
}
}
else
{
junk_obj(missile);
}
}
pop_call();
return TRUE;
}
/*
* AC conditional calculations for attack rolls
*/
int calc_ac( CHAR_DATA *victim, CHAR_DATA *ch, bool touch )
{
int ac, bonus;
push_call("calc_ac(%p,%p)",victim,ch);
ac = GET_AC(victim, touch);
bonus = 0;
if (IS_EVIL(ch))
bonus = UMAX(bonus, get_apply(victim, APPLY_RES_EVIL));
if (IS_GOOD(ch))
bonus = UMAX(bonus, get_apply(victim, APPLY_RES_GOOD));
if (IS_LAWFUL(ch))
bonus = UMAX(bonus, get_apply(victim, APPLY_RES_LAW));
if (IS_CHAOTIC(ch))
bonus = UMAX(bonus, get_apply(victim, APPLY_RES_CHAOS));
if (race_type(ch) == RTYPE_OUTSIDER)
{
if (learned(victim, gsn_divine_defense))
bonus += ROUNDUP(multi_skill_level(victim, gsn_divine_defense) / 2);
}
ac += bonus;
if (can_dodge(victim, ch))
{
ac += dodge_bonus(victim);
if (rspec_req(ch, RSPEC_GIANT) && learned(victim, gsn_defensive_training))
ac += 4;
}
else
{
ac += dodge_penalty(victim);
}
if ((who_fighting(victim) == NULL && can_see(victim, ch)) || who_fighting(victim) == ch)
{
if (!touch || learned(victim, gsn_shield_ward))
ac += shield_bonus(victim);
}
pop_call();
return ac;
}
/*
* Find weapon wielded, return NULL if item is not a weapon - Kregor
*/
OBJ_DATA *get_wield( CHAR_DATA *ch, bool fDual )
{
OBJ_DATA *wield;
push_call("get_wield(%p,%p)",ch,fDual);
for (wield = ch->first_carrying ; wield ; wield = wield->next_content)
{
if (!IS_WEAPON(wield))
continue;
if (!IS_WORN(wield))
continue;
if (fDual && WEAR_LOC(wield, WEAR_DUAL_WIELD))
{
pop_call();
return wield;
}
if (WEAR_LOC(wield, WEAR_BOTH_HANDS))
{
if (!fDual || WSPEC(wield, WSPEC_DOUBLE))
{
pop_call();
return wield;
}
}
if (!fDual && WEAR_LOC(wield, WEAR_WIELD))
{
pop_call();
return wield;
}
}
pop_call();
return NULL;
}
/*
* Check if missile weapon wielded - Kregor
*/
bool MISSILE_WIELD( CHAR_DATA *ch )
{
OBJ_DATA *wield;
push_call("MISSILE_WIELD(%p)",ch);
if ((wield = get_wield(ch, FALSE)) != NULL)
{
pop_call();
return WSPEC(wield, WSPEC_MISSILE|WSPEC_NOMELEE);
}
pop_call();
return FALSE;
}
/*
* Check if object is a missile weapon - Kregor
*/
bool is_missile_weapon( OBJ_DATA *wield )
{
push_call("is_missile_weapon(%p)",wield);
if (wield == NULL)
{
pop_call();
return FALSE;
}
pop_call();
return WSPEC(wield, WSPEC_MISSILE);
}
/*
* Check if object is a bow - Kregor
*/
bool is_bow( OBJ_DATA *wield )
{
push_call("is_missile_weapon(%p)",wield);
if (wield == NULL)
{
pop_call();
return FALSE;
}
if (WEAPON_TYPE(wield, WEAPON_TYPE_LONGBOW)
|| WEAPON_TYPE(wield, WEAPON_TYPE_LONGBOW_COMPOSITE)
|| WEAPON_TYPE(wield, WEAPON_TYPE_SHORTBOW)
|| WEAPON_TYPE(wield, WEAPON_TYPE_SHORTBOW_COMPOSITE))
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* Checks for ammo for missile weapon
* returns NULL if none found in inventory or quiver
*/
OBJ_DATA *get_ammo( CHAR_DATA *ch, OBJ_DATA *wield )
{
OBJ_DATA *ammo, *cont;
push_call("get_ammo(%p,%p)",ch,wield);
ammo = NULL;
if (is_missile_weapon(wield))
{
for (ammo = ch->first_carrying ; ammo ; ammo = ammo->next_content)
{
if (ammo->item_type == ITEM_AMMO && ammo->value[0] == wield->value[0] && ammo->level <= wield->level)
{
break;
}
if (ammo->item_type == ITEM_QUIVER && !IS_SET(ammo->value[1], CONT_CLOSED))
{
cont = ammo;
for (ammo = ammo->first_content ; ammo ; ammo = ammo->next_content)
{
if (ammo->item_type == ITEM_AMMO && ammo->value[0] == wield->value[0])
{
break;
}
}
if (ammo)
{
break;
}
else
{
ammo = cont;
}
}
}
}
pop_call();
return (ammo);
}
/*
* Calculate all the possible modifiers to attack roll - Kregor
*/
int GET_HITROLL(CHAR_DATA *ch, CHAR_DATA *victim, int dt, OBJ_DATA *wield)
{
int val, wskill;
OBJ_DATA *obj;
OBJ_DATA *ammo = NULL;
OBJ_DATA *wield1 = NULL;
OBJ_DATA *wield2 = NULL;
OBJ_DATA *shield = NULL;
push_call("GET_HITROLL(%p)",ch);
val = 0;
val = base_attack(ch);
val += get_apply(ch, APPLY_COMP_TOHIT);
val += get_apply(ch, APPLY_INS_TOHIT);
val += get_apply(ch, APPLY_LUCK_TOHIT);
val += get_apply(ch, APPLY_MOR_TOHIT);
for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
{
if (WEAR_LOC(obj, WEAR_BOTH_HANDS) && obj->item_type == ITEM_WEAPON && WSPEC(obj, WSPEC_DOUBLE))
{
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_WIELD) && obj->item_type == ITEM_WEAPON)
{
wield1 = obj;
}
if (WEAR_LOC(obj, WEAR_DUAL_WIELD) && obj->item_type == ITEM_WEAPON)
{
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_SHIELD) && obj->item_type == ITEM_ARMOR)
{
shield = obj;
}
}
if (IS_SET(ch->attack, ATTACK_SHIELD_BASH) && !learned(ch, gsn_imp_shield_bash))
val -= 2;
// apply mount's STR bonus to hit during charge, or double ch if quadruped
if (IS_SET(ch->attack, ATTACK_CHARGE))
{
if (is_mounting(ch))
{
val += UMAX(0, stat_bonus(TRUE, ch->mounting, APPLY_STR));
}
else
{
val += UMAX(0, stat_bonus(TRUE, ch, APPLY_STR));
}
}
switch (get_size(ch))
{
case SIZE_FINE:
val += 8;
break;
case SIZE_DIMINUTIVE:
val += 4;
break;
case SIZE_TINY:
val += 2;
break;
case SIZE_SMALL:
val += 1;
break;
case SIZE_LARGE:
val -= 1;
break;
case SIZE_HUGE:
val -= 2;
break;
case SIZE_GARGANTUAN:
val -= 4;
break;
case SIZE_COLOSSAL:
val -= 8;
break;
}
if (IS_AFFECTED(ch, AFF2_SICKENED))
val -= 2;
if (IS_DAZZLED(ch))
val -= 1;
if (IS_ENTANGLED(ch))
val -= 2;
if (is_affected(ch, gsn_barbarian_rage) && !learned(ch, gsn_mighty_rage))
val -= 2;
switch(drunk_level(ch))
{
case DRUNK_SOBER:
break;
case DRUNK_TIPSY:
val -= 1;
break;
case DRUNK_MERRY:
val -= 2;
break;
case DRUNK_DRUNK:
val -= 4;
break;
case DRUNK_HAMMERED:
val -= 8;
break;
default:
val -= 16;
break;
}
// penalty to hit -2 for each level of fear
if (ch->fear_level)
val -= UMIN(ch->fear_level, 3) * 2;
// armor penalty applies if wearing non-proficient armor
if (has_armor_penalty(ch))
val -= armor_check_penalty(ch);
// weapon proficiency feats
wskill = weapon_skill(ch, wield);
if (IS_SET(wskill, WSKILL_EPIC_FOCUS))
val += 2;
if (IS_SET(wskill, WSKILL_GREATER_FOCUS))
val += 1;
if (IS_SET(wskill, WSKILL_FOCUS))
val += 1;
if (learned(ch, gsn_weapon_mastery) && IS_SET(wskill, WSKILL_FOCUS))
val += (multi_skill_level(ch, gsn_weapon_mastery) / 4) + 1;
// These modifiers only apply to missile weapons
if (is_missile_weapon(wield))
{
if (COMBATMODE(ch, COMBAT_CALLED))
val -= UMIN(stat_bonus(TRUE, ch, APPLY_DEX), base_attack(ch));
if (COMBATMODE(ch, COMBAT_RAPIDSHOT))
val -= 2;
if (COMBATMODE(ch, COMBAT_MANYSHOT))
val -= 2;
if (is_mounting(ch) && !learned(ch, gsn_mounted_archery))
val -= 2;
}
else // these modifiers only apply in melee
{
if (COMBATMODE(ch, COMBAT_POWER))
val -= UMIN(stat_bonus(TRUE, ch, APPLY_STR), base_attack(ch));
if (COMBATMODE(ch, COMBAT_DEFENSIVE))
{
if (learned(ch, gsn_combat_expertise))
val -= UMIN(stat_bonus(TRUE, ch, APPLY_DEX), base_attack(ch));
else
val -= 4;
}
if (COMBATMODE(ch, COMBAT_FLURRY))
{
if (!wield || WSPEC(wield, WSPEC_MONK))
{
if (class_level(ch, CLASS_MONK) < 5)
val -= 2;
else if (class_level(ch, CLASS_MONK) < 10)
val -= 1;
}
}
}
if (wield != NULL)
{
// nonproficient penalty
if (!wskill)
val -= 4;
if (WEAPON_TYPE(wield, WEAPON_TYPE_SLING))
val += ROUNDUP(multi_class_level(ch, gsn_crack_shot)/2);
if (is_missile_weapon(wield))
{
val += stat_bonus(TRUE, ch, APPLY_DEX);
if (victim != NULL)
{
if (learned(ch, gsn_point_blank) && in_same_room(ch, victim))
val += 1;
// firing into melee without precise shot -4 to hit
if (victim->position == POS_FIGHTING && !learned(ch, gsn_precise_shot))
val -= 4;
}
// apply only the greater of weapon or ammo enchantment
int bonus = 0;
bonus = weapon_tohit_bonus(wield);
if ((ammo = get_ammo(ch, wield)) != NULL)
bonus = UMAX(weapon_tohit_bonus(ammo), bonus);
if (is_bow(wield) && learned(ch, gsn_enhance_arrow))
bonus = UMAX(ROUNDUP(multi_class_level(ch, gsn_enhance_arrow) / 2), bonus);
val += bonus;
}
else
{
val += weapon_tohit_bonus(wield);
// weapon finesse - DEX bonus instead of STR for light weapons
if ((is_light_weapon(ch, wield) || WSPEC(wield, WSPEC_FINESSE))
&& learned(ch,gsn_weapon_finesse) && shield == NULL
&& get_curr_dex(ch) >= 13 && !COMBATMODE(ch, COMBAT_POWER))
{
val += stat_bonus(TRUE, ch, APPLY_DEX);
}
else
{
val += stat_bonus(TRUE, ch, APPLY_STR);
}
}
/* -4 if fighting nonlethal with lethal weapon */
if (COMBATMODE(ch, COMBAT_NONLETHAL)
&& !WSPEC(wield, WSPEC_NONLETHAL) && !WEAPON_FLAG(wield, WFLAG_MERCIFUL))
val -= 4;
/*
* two-weapon fighting penalties:
* light weapon w/feat - -2 -2
* light weapon w/o feat - -4 -8
* normal weapons w/feat - -4 -4
* normal weapons w/o feat - -6 -10
* double weapon w feat - -3 -3
* double weapon w/o feat - -6 -6
* Besides making coding easier for double weapons, I rationale the
* fact that a PC has to spend an exotic weapon feat to use a double
* weapon enough for a lesser penalty even without TWF (since w/o
* Exotic Weapon feat the penalties would be -10/-10 - Kregor
*/
else if (!learned(ch, gsn_perfect_two_weapon))
{
if (WEAR_LOC(wield, WEAR_DUAL_WIELD))
{
if (learned(ch, gsn_twin_sword) && wield1 && wield1->value[0] == wield->value[0])
{
val -= 2;
}
else if (is_light_weapon(ch, wield))
{
if (learned(ch, gsn_two_weapon))
val -= 2;
else
val -= 8;
}
else if (learned(ch, gsn_two_weapon))
val -= 4;
else
val -= 10;
}
else if (WEAR_LOC(wield, WEAR_BOTH_HANDS) && WSPEC(wield, WSPEC_DOUBLE))
{
if (learned(ch, gsn_two_weapon))
val -= 3;
else
val -= 6;
}
else if (wield2 != NULL)
{
if (learned(ch, gsn_twin_sword) && wield2 && wield2->value[0] == wield->value[0])
{
val -= 2;
}
else if (is_light_weapon(ch, wield2))
{
if (learned(ch, gsn_two_weapon))
val -= 2;
else
val -= 4;
}
else if (learned(ch, gsn_two_weapon))
val -= 4;
else
val -= 6;
}
}
if (wield->material == MATERIAL_COLD_IRON)
{
val -= 1;
}
}
else
{
/* -4 to hit if fighting lethal unarmed */
if (!has_natural_weapon(ch) && !is_armed(ch, FALSE)
&& !COMBATMODE(ch, COMBAT_NONLETHAL))
{
val -= 4;
}
/* unarmed strike = light weapon */
if (learned(ch,gsn_weapon_finesse) && shield == NULL
&& get_curr_dex(ch) >= 13 && !COMBATMODE(ch, COMBAT_POWER))
val += stat_bonus(TRUE, ch, APPLY_DEX);
else
val += stat_bonus(TRUE, ch, APPLY_STR);
// added here, to keep from adding to weapon bonus
val += get_apply(ch, APPLY_HITROLL);
}
if (victim != NULL)
{
if (!can_see(victim, ch))
val += 2;
if (is_flanking(ch, victim))
val += 2;
/* paladin/blackguard smite ability */
if (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch && stat_bonus(TRUE, ch, APPLY_CHA) > 0)
{
val += stat_bonus(TRUE, ch, APPLY_CHA);
}
/* retribution domain strike */
if (is_affected(ch, gsn_retributive_strike) && ch->last_attacker && ch->last_attacker == victim)
{
val += stat_bonus(TRUE, ch, APPLY_WIS);
}
if (is_affected(ch, gsn_divine_wrath))
{
val += UMAX(1, stat_bonus(TRUE, ch, APPLY_CHA));
}
}
val += get_apply(ch, APPLY_LEVEL);
if (ch->grappling)
val -= 2;
else if (!IS_AFFECTED(ch, AFF_FREEDOM))
{
if (ch->grappled_by || IS_UNDERWATER(ch) || is_affected(ch, gsn_crushing_hand) || is_affected(ch, gsn_grasping_hand))
val -= 2;
}
pop_call();
return(UMIN(val, 99));
}
/*
* Get the damage dice for a weapon or attack, plus bonuses
*/
int GET_DAMROLL(CHAR_DATA *ch, CHAR_DATA *victim, int dt, OBJ_DATA *wield)
{
OBJ_DATA *obj;
OBJ_DATA *ammo = NULL;
OBJ_DATA *wield2 = NULL;
OBJ_DATA *shield = NULL;
int dam = 0;
push_call("GET_DAMROLL(%p)",ch);
for (obj = ch->first_carrying ; obj ; obj = obj->next_content)
{
if (WEAR_LOC(obj, WEAR_BOTH_HANDS) && obj->item_type == ITEM_WEAPON && WSPEC(obj, WSPEC_DOUBLE))
{
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_DUAL_WIELD) && obj->item_type == ITEM_WEAPON)
{
wield2 = obj;
}
if (WEAR_LOC(obj, WEAR_SHIELD) && obj->item_type == ITEM_ARMOR)
{
shield = obj;
}
}
// first, get base weapon damage - Kregor
if (wield != NULL && (IS_WEAPON(wield) || IS_AMMO(wield)))
{
dam = dice(weapon_table[wield->value[0]].damnodice, weapon_table[wield->value[0]].damsizedice);
// charge damage multiplied for lance and/or spirited charge
if (IS_SET(ch->attack, ATTACK_CHARGE))
{
if (learned(ch, gsn_spirited_charge) && WEAPON_TYPE(wield, WEAPON_TYPE_LANCE))
{
dam *= 3;
}
else if (learned(ch, gsn_spirited_charge) || WEAPON_TYPE(wield, WEAPON_TYPE_LANCE))
{
dam *= 2;
}
// apply mount's STR bonus to dam during charge, or double ch STR bonus if quadruped
if (is_mounting(ch))
{
dam += UMAX(0, stat_bonus(TRUE, ch->mounting, APPLY_STR));
}
else
{
dam += UMAX(0, stat_bonus(TRUE, ch, APPLY_STR));
}
}
if (WEAPON_TYPE(wield, WEAPON_TYPE_SLING))
dam += ROUNDUP(multi_class_level(ch, gsn_crack_shot)/2) * 2;
// bows & slings have STR penalties, slings & composite bows grant STR bonus
if (is_missile_weapon(wield))
{
switch(wield->value[0])
{
case WEAPON_TYPE_SHORTBOW:
case WEAPON_TYPE_LONGBOW:
if (get_curr_str(ch) < 10)
dam += stat_bonus(TRUE, ch, APPLY_STR);
break;
case WEAPON_TYPE_SHORTBOW_COMPOSITE:
case WEAPON_TYPE_LONGBOW_COMPOSITE:
case WEAPON_TYPE_SLING:
dam += stat_bonus(TRUE, ch, APPLY_STR);
break;
}
// point-blank shot feat
if (victim != NULL && learned(ch, gsn_point_blank) && in_same_room(ch, victim))
dam += 1;
// take greater of weapon or ammo enchantment
int bonus = weapon_dam_bonus(wield);
if ((ammo = get_ammo(ch, wield)) != NULL)
bonus = UMAX(weapon_dam_bonus(ammo), bonus);
if (is_bow(wield) && learned(ch, gsn_enhance_arrow))
bonus = UMAX(ROUNDUP(multi_class_level(ch, gsn_enhance_arrow) / 2), bonus);
dam += bonus;
}
// imp weapon finesse - DEX bonus instead of STR for light weapons
else if ((is_light_weapon(ch, wield) || WSPEC(wield, WSPEC_FINESSE))
&& learned(ch, gsn_imp_weapon_finesse) && shield == NULL
&& get_curr_dex(ch) >= 13 && !COMBATMODE(ch, COMBAT_POWER))
{
if (wield->wear_loc == WEAR_DUAL_WIELD)
dam += stat_bonus(TRUE, ch, APPLY_DEX) / 2;
else
dam += stat_bonus(TRUE, ch, APPLY_DEX);
}
else
{
switch(wield->wear_loc)
{
case WEAR_BOTH_HANDS:
dam += stat_bonus(TRUE, ch, APPLY_STR) * 1.5;
break;
case WEAR_DUAL_WIELD:
dam += stat_bonus(TRUE, ch, APPLY_STR) / 2;
break;
default:
dam += stat_bonus(TRUE, ch, APPLY_STR);
break;
}
}
switch (wield->material)
{
case MATERIAL_SILVER:
case MATERIAL_SOFTWOOD:
case MATERIAL_STONE:
dam -= 1;
break;
case MATERIAL_BONE:
dam -= 2;
break;
default:
break;
}
}
else if (dt == gsn_shield_bash)
{
if (shield == NULL || shield->item_type != ITEM_ARMOR)
{
bug("GET_DAMROLL(%s): dt shield_bash with no shield!", get_name(ch));
pop_call();
return 0;
}
if (ARMOR_TYPE(shield, ARMOR_TYPE_LIGHT_SHIELD))
dam = dice(1,4);
if (ARMOR_TYPE(shield, ARMOR_TYPE_HEAVY_SHIELD))
dam = dice(1,6);
dam += stat_bonus(TRUE, ch, APPLY_STR) / 2;
}
else if (dt == gsn_rock_throwing)
{
dam = dice(dam_no_dice[ATTK_SLAM].size[get_size(ch)] * 2, dam_size_dice[ATTK_SLAM].size[get_size(ch)]);
dam += stat_bonus(TRUE, ch, APPLY_STR) * 3 / 2;
}
else
{
dam = dice(get_damnodice(ch), get_damsizedice(ch));
int stat;
if (learned(ch, gsn_imp_weapon_finesse) && shield == NULL
&& get_curr_dex(ch) >= 13 && !COMBATMODE(ch, COMBAT_POWER))
stat = APPLY_DEX;
else
stat = APPLY_STR;
if (ch->attack_part > -1)
{
if (attack_part_table[ch->attack_part].primary)
{
if (race_skill(ch, gsn_imp_natural_attack))
dam += stat_bonus(TRUE, ch, stat) * 3 / 2;
else
dam += stat_bonus(TRUE, ch, stat);
}
else
dam += (stat_bonus(TRUE, ch, stat) / 2);
}
else
dam += (stat_bonus(TRUE, ch, stat));
wiz_printf("get_damdice(%s): body part %d - %dd%d+%d", get_name(ch), ch->attack_part, get_damnodice(ch), get_damsizedice(ch), stat_bonus(TRUE, ch, stat));
}
// then, add modifiers
int wskill = weapon_skill(ch, wield);
if (IS_SET(wskill, WSKILL_SPECIALIZED))
dam += 2;
if (IS_SET(wskill, WSKILL_GREATER_SPEC))
dam += 2;
if (IS_SET(wskill, WSKILL_EPIC_SPEC))
dam += 4;
if (learned(ch, gsn_weapon_mastery) && IS_SET(wskill, WSKILL_FOCUS))
dam += (multi_skill_level(ch, gsn_weapon_mastery) / 4) + 1;
dam += get_apply(ch, APPLY_COMP_DAMG);
dam += get_apply(ch, APPLY_LUCK_DAMG);
dam += get_apply(ch, APPLY_MOR_DAMG);
if (IS_AFFECTED(ch, AFF2_SICKENED))
dam -= 2;
if (is_missile_weapon(wield))
{
if (COMBATMODE(ch, COMBAT_CALLED))
dam += UMIN(stat_bonus(TRUE, ch, APPLY_DEX), base_attack(ch));
}
else
{
if (COMBATMODE(ch, COMBAT_POWER))
{
if (wield && (is_light_weapon(ch, wield) || WEAR_LOC(wield, WEAR_DUAL_WIELD)))
dam += UMIN(stat_bonus(TRUE, ch, APPLY_STR), base_attack(ch)) / 2;
else if (wield && WEAR_LOC(wield, WEAR_BOTH_HANDS))
dam += UMIN(stat_bonus(TRUE, ch, APPLY_STR), base_attack(ch)) * 2;
else
dam += UMIN(stat_bonus(TRUE, ch, APPLY_STR), base_attack(ch));
}
else if (learned(ch, gsn_precise_strike) && !IS_AFFECTED(ch, AFF2_BERSERK) && CAN_CRITICAL(victim) && (!is_affected(victim, gsn_defensive_stance) || !learned(victim, gsn_impenetrable_defense)))
{
if (wield && (is_light_weapon(ch, wield) || WSPEC(wield, WSPEC_FINESSE))
&& wield2 == NULL && shield == NULL)
{
dam += dice (multi_class_level(ch, gsn_precise_strike)/5, 6);
}
}
}
if (victim != NULL)
{
/* paladin/blackguard smite ability */
if (is_affected(victim, gsn_smite) && get_caster(victim, gsn_smite) == ch)
{
if (race_type(victim) == RTYPE_OUTSIDER
|| race_type(victim) == RTYPE_DRAGON
|| race_type(victim) == RTYPE_UNDEAD)
dam += multi_class_level(ch, gsn_smite) * 2;
else
dam += multi_class_level(ch, gsn_smite);
}
if (is_affected(ch, gsn_retributive_strike) && ch->last_attacker && ch->last_attacker == victim)
{
dam += class_level(ch, CLASS_CLERIC);
}
if (is_affected(ch, gsn_divine_wrath))
{
dam += UMAX(1, stat_bonus(TRUE, ch, APPLY_CHA));
}
/* ranger favored enemy bonus */
dam += fave_enemy_bonus(ch, victim);
}
if (IS_UNDERWATER(ch) && !IS_AFFECTED(ch, AFF_FREEDOM))
dam /= 2;
pop_call();
return(UMAX(1,dam));
}
int weapon_tohit_bonus( OBJ_DATA *wield )
{
push_call("weapon_tohit_bonus(%p)",wield);
if (!IS_WEAPON(wield) && !IS_AMMO(wield))
{
pop_call();
return 0;
}
if (!wield->apply[APPLY_HITROLL] && IS_OBJ_STAT(wield, ITEM_MASTERWORK))
{
pop_call();
return 1;
}
pop_call();
return wield->apply[APPLY_HITROLL];
}
int weapon_dam_bonus( OBJ_DATA *wield )
{
push_call("weapon_enhance_bonus(%p)",wield);
if (!IS_WEAPON(wield) && !IS_AMMO(wield))
{
pop_call();
return 0;
}
pop_call();
return wield->apply[APPLY_DAMROLL];
}
/*
* Count only permanent weapon bonus
* because only permanent enchantment breaks other DR - Kregor
*/
int actual_tohit_bonus( OBJ_DATA *wield )
{
AFFECT_DATA *paf;
int mod = 0;
push_call("actual_tohit_bonus(%p)",wield);
for (paf = wield->first_affect ; paf ; paf = paf->next)
{
if (paf->location == APPLY_HITROLL)
{
if (paf->modifier <= 0 || paf->duration >= 0)
{
continue;
}
mod = UMAX(mod, paf->modifier);
}
}
pop_call();
return mod;
}
/*
* returns the miss chance for an attack
* against a concealed/hidden target
* -1 means can't miss - Kregor
*/
int can_miss( CHAR_DATA *ch, CHAR_DATA *victim, int dt, bool fRanged, OBJ_DATA *wield )
{
int miss = -1;
push_call("can_miss(%p,%p,%p,%p,%p)",ch,victim,dt,fRanged,wield);
//ethereal creatures cannot affect or be affected by non-ethereal
if ((CAN_ETHEREAL_WALK(ch) && !CAN_ETHEREAL_WALK(victim))
|| (CAN_ETHEREAL_WALK(victim) && !CAN_ETHEREAL_WALK(ch)))
{
pop_call();
return 100;
}
if (IS_AFFECTED(ch, AFF_GASEOUS))
{
pop_call();
return 100;
}
// mundane partial concealment that trumps True Sight
if (IS_AFFECTED(victim, AFF_HIDE)
|| IS_BLIND(ch)
|| IS_SET(ch->in_room->room_flags, ROOM_FOG)
|| IS_SET(victim->in_room->room_flags, ROOM_FOG)
|| (learned(victim, gsn_self_concealment) && !IS_FLATFOOTED(victim) && !IS_HELPLESS(victim))
|| (arcane_mastery(victim, SCHOOL_ILLUSION) && !IS_HELPLESS(victim)))
{
miss = 50;
}
else if (IS_AFFECTED(ch, AFF_TRUESIGHT))
{
pop_call();
return -1;
}
if (race_skill(victim, gsn_light_invisibility))
{
switch (get_room_light(victim->in_room))
{
default:
break;
case LIGHT_BRIGHT:
miss = UMAX(50, miss);
break;
case LIGHT_NORMAL:
miss = UMAX(20, miss);
break;
case LIGHT_DARKNESS:
miss = UMIN(miss, 20);
break;
}
}
// Light blindness = 50% miss in bright, 20% miss in normal - Kregor
if (ch->in_room && race_skill(ch, gsn_light_blindness))
{
switch (get_room_light(ch->in_room))
{
default:
break;
case LIGHT_BRIGHT:
miss = UMIN(miss, 50);
break;
case LIGHT_NORMAL:
miss = UMIN(miss, 20);
break;
}
}
if (wield && WEAPON_FLAG(wield, WFLAG_SEEKING))
{
pop_call();
return -1;
}
if (!can_see(ch, victim))
miss = UMAX(50, miss);
miss = UMAX(get_apply(victim, APPLY_CONCEALMENT), miss);
if (fRanged)
{
if (is_affected(victim, gsn_entropic_shield))
miss = UMAX(20, miss);
if (learned(ch, gsn_imp_precise_shot))
miss = -1;
}
else
{
// blind fight only works against miss chance in melee
if (miss > 0 && learned(ch,gsn_blind_fight))
miss /= 2;
}
if (is_affected(ch, gsn_true_strike))
miss = -1;
// blindsight does not require sight at all
if (race_skill(ch, gsn_blindsight) || domain_apotheosis(ch, DOMAIN_CAVERN))
miss = -1;
// faerie fire foils concealment
if (is_affected(victim, gsn_faerie_fire))
miss = -1;
pop_call();
return miss;
}
/*
* virtually from scratch object damage codes,
* consistent with d20 guildelines for sundering and
* failed saves - Kregor
*/
/*
* dmg gets passed thru here for resistancs and vulnerabilities,
* just like damage_modify for chars
*/
int obj_damage_modify( OBJ_DATA *obj, OBJ_DATA *wield, int dam, int dam_type )
{
int material, hardness;
AFFECT_DATA *paf, *paf_next;
push_call("obj_damage_modify(%p,%p,%p,%p)",obj,wield,dam,dam_type);
if (!obj || !wield)
{
pop_call();
return dam;
}
material = obj->material;
if (IS_SET(material_table[material].vulnerable, dam_type))
dam *= 2;
else if (IS_SET(material_table[material].resist_lo, dam_type))
dam /= 2;
else if (IS_SET(material_table[material].resist_hi, dam_type))
dam /= 4;
else if (IS_SET(material_table[material].immune, dam_type))
dam = 0;
hardness = material_table[material].hardness;
hardness += obj->hardness;
hardness += actual_tohit_bonus(obj) * 2;
/* adamantine bypasses all hardness less than 20 */
if (wield && wield->material == MATERIAL_ADAMANTINE && hardness < 20)
hardness = 0;
if (!IS_SET(material_table[material].vulnerable, dam_type))
dam -= hardness;
if (dam <= 0)
{
pop_call();
return 0;
}
/*
* it stands to reason that one elemental damage weapon is going
* to have at least as much resistance to that element as it deals - Kregor
*/
if (WEAPON_FLAG(obj, WFLAG_FLAMING|WFLAG_FLAMING_BURST) && IS_SET(dam_type, DAM_FIRE))
dam -= 6;
else if (WEAPON_FLAG(obj, WFLAG_SHOCK|WFLAG_SHOCK_BURST) && IS_SET(dam_type, DAM_ELECTRIC))
dam -= 6;
else if (WEAPON_FLAG(obj, WFLAG_FROST|WFLAG_ICY_BURST) && IS_SET(dam_type, DAM_COLD))
dam -= 6;
else if (WEAPON_FLAG(obj, WFLAG_VICIOUS) && IS_SET(dam_type, DAM_SONIC))
dam -= 6;
else if (WEAPON_FLAG(obj, WFLAG_CAUSTIC) && IS_SET(dam_type, DAM_ACID))
dam -= 6;
/* no need to loop thru the effects if there's already no damage left */
if (dam <= 0)
{
pop_call();
return 0;
}
if (obj->first_affect != NULL)
{
for ( paf = obj->first_affect; paf != NULL; paf = paf_next )
{
paf_next = paf->next;
switch (paf->location)
{
case APPLY_DR_FIRE:
if (IS_SET(dam_type, DAM_FIRE))
dam -= paf->modifier;
break;
case APPLY_DR_ACID:
if (IS_SET(dam_type, DAM_ACID))
dam -= paf->modifier;
break;
case APPLY_DR_COLD:
if (IS_SET(dam_type, DAM_COLD))
dam -= paf->modifier;
break;
case APPLY_DR_ELECTRIC:
if (IS_SET(dam_type, DAM_ELECTRIC))
dam -= paf->modifier;
break;
case APPLY_DR_SONIC:
if (IS_SET(dam_type, DAM_SONIC))
dam -= paf->modifier;
break;
case APPLY_IMM_FIRE:
if (IS_SET(dam_type, DAM_FIRE))
dam = 0;
break;
case APPLY_IMM_ACID:
if (IS_SET(dam_type, DAM_ACID))
dam = 0;
break;
case APPLY_IMM_COLD:
if (IS_SET(dam_type, DAM_COLD))
dam = 0;
break;
case APPLY_IMM_ELECTRIC:
if (IS_SET(dam_type, DAM_ELECTRIC))
dam = 0;
break;
case APPLY_IMM_SONIC:
if (IS_SET(dam_type, DAM_SONIC))
dam = 0;
break;
}
}
}
if (dam <= 0)
{
pop_call();
return 0;
}
pop_call();
return dam;
}
/*
* currently, only weapons, armor and treasure can be damaged
* and/or repaired. Mostly what would matter for getting damaged
* or sundered anyway.
*/
void damage_equipment( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj, int dam, int dt, OBJ_DATA *wield )
{
int condition;
int dam_type = DAM_BASH;
push_call("damage_equipment(%p,%p,%p,%p,%p,%p)",ch,victim,obj,dt,dam,wield);
if ( obj == NULL )
{
pop_call();
return;
}
if ( (obj->item_type != ITEM_WEAPON
&& obj->item_type != ITEM_ARMOR
&& obj->item_type != ITEM_TREASURE)
|| IS_OBJ_STAT(obj, ITEM_INVENTORY) || IS_OBJ_STAT(obj, ITEM_NODROP)) //plot items can't be damaged
{
pop_call();
return;
}
if (is_attack(dt))
{
if (wield != NULL)
{
dam_type = weapon_table[wield->value[0]].dam_type;
}
}
else if (valid_skill(dt))
{
dam_type = skill_table[dt].dam_type;
}
else
{
if (dt != TYPE_UNDEFINED)
{
bug("damage_equipment: bad dt %d.", dt);
}
}
int wskill = weapon_skill(ch, wield);
if (IS_SET(wskill, WSKILL_SPECIALIZED))
dam += 2;
if (IS_SET(wskill, WSKILL_GREATER_SPEC))
dam += 2;
if (IS_SET(wskill, WSKILL_EPIC_SPEC))
dam += 4;
if (learned(ch, gsn_weapon_mastery) && IS_SET(wskill, WSKILL_FOCUS))
dam += (multi_skill_level(ch, gsn_weapon_mastery) / 4) + 1;
if (dam > 0)
{
if (wield != NULL && IS_WEAPON(wield))
{
if (WEAR_LOC(wield, WEAR_BOTH_HANDS))
dam += stat_bonus(TRUE, ch, APPLY_STR) * 1.5;
else if (WEAR_LOC(wield, WEAR_DUAL_WIELD))
dam += stat_bonus(TRUE, ch, APPLY_STR) / 2;
else
dam += stat_bonus(TRUE, ch, APPLY_STR);
dam += weapon_dam_bonus(wield);
dam += get_apply(ch, APPLY_COMP_DAMG);
dam += get_apply(ch, APPLY_LUCK_DAMG);
dam += get_apply(ch, APPLY_MOR_DAMG);
if (IS_AFFECTED(ch, AFF2_SICKENED))
dam -= 2;
if (COMBATMODE(ch, COMBAT_POWER))
dam += UMIN(stat_bonus(TRUE, ch, APPLY_STR), base_attack(ch));
//makes sense that anti-alignment weapons would damage weapons of that alignment, doesn't it?
if (WEAPON_FLAG(wield, WFLAG_HOLY))
{
if (IS_OBJ_STAT(obj, ITEM_EVIL))
{
dam += dice(2, 6);
}
}
if (WEAPON_FLAG(wield, WFLAG_UNHOLY))
{
if (IS_OBJ_STAT(obj, ITEM_GOOD))
{
dam += dice(2, 6);
}
}
if (WEAPON_FLAG(wield, WFLAG_FLAMING) || WEAPON_FLAG(wield, WFLAG_FLAMING_BURST))
{
dam += obj_damage_modify(obj, wield, dice(1, 6), DAM_FIRE);
}
if (WEAPON_FLAG(wield, WFLAG_FROST) || WEAPON_FLAG(wield, WFLAG_ICY_BURST))
{
dam += obj_damage_modify(obj, wield, dice(1, 6), DAM_COLD);
}
if (WEAPON_FLAG(wield, WFLAG_SHOCK) || WEAPON_FLAG(wield, WFLAG_SHOCK_BURST))
{
dam += obj_damage_modify(obj, wield, dice(1, 6), DAM_ELECTRIC);
}
if (WEAPON_FLAG(wield, WFLAG_VICIOUS))
{
dam += obj_damage_modify(obj, wield, dice(1, 6), DAM_SONIC);
}
if (WEAPON_FLAG(wield, WFLAG_CAUSTIC))
{
dam += obj_damage_modify(obj, wield, dice(1, 6), DAM_ACID);
}
}
}
if ((dam = obj_damage_modify(obj, wield, dam, dam_type)) <= 0)
{
pop_call();
return;
}
obj->hit_points -= dam;
if ( obj->hit_points < 0 )
obj->hit_points = 0;
if (obj->hit_points > 0)
{
if ((condition = what_percent(obj->hit_points, UMAX(1, get_obj_max_hit(obj)))) >= 90)
{
act("$p is damaged, but still in great condition.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged, but still in great condition.", victim, obj, NULL, TO_CHAR);
}
else if ( condition >= 70 )
{
act("$p is damaged, but is in good condition.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged, but is in good condition.", victim, obj, NULL, TO_CHAR);
}
else if ( condition >= 50 )
{
act("$p is damaged and is in fair condition.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged and is in fair condition.", victim, obj, NULL, TO_CHAR);
}
else if ( condition >= 30 )
{
act("$p is damaged and in poor condition.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged and in poor condition.", victim, obj, NULL, TO_CHAR);
}
else if ( condition >= 10 )
{
act("$p is damaged and in bad condition.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged and in bad condition.", victim, obj, NULL, TO_CHAR);
}
else
{
act("$p is damaged and about to fall apart.", ch, obj, NULL, TO_CHAR);
if (victim)
act("$p is damaged and about to fall apart.", victim, obj, NULL, TO_CHAR);
}
}
else
{
act( "$p is damaged beyond any use.", ch, obj, NULL, TO_ALL);
SET_BIT( obj->extra_flags, ITEM_BROKEN );
if (victim && IS_WORN(obj))
remove_obj(victim, obj->wear_loc, TRUE, FALSE);
}
pop_call();
return;
}
/*
* For breath weapon attacks by DM chars - Kregor
*/
void do_breath( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
push_call("do_breath(%p,%p)",ch,argument);
argument = one_argument(argument, arg);
if (arg[0] == '\0' || (victim = get_char_room(ch, arg)) == NULL)
{
if ((victim = who_fighting(ch)) == NULL)
{
send_to_char("You are not fighting anyone.\n\r",ch);
pop_call();
return;
}
}
if (!check_murder(ch, victim))
{
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!breath_weapon(ch, victim))
send_to_char("Try as you might, you cannot muster a breath weapon.\n\r", ch);
pop_call();
return;
}
/*
* Replaces dragon spec_fun - Kregor
*/
bool breath_weapon( CHAR_DATA *ch, CHAR_DATA *victim )
{
int sn, Breathe;
push_call("breath_weapon(%p,%p)",ch,victim);
if (!in_combat(ch))
{
pop_call();
return FALSE;
}
if (IS_SET(ch->action, ACTION_STANDARD))
{
pop_call();
return FALSE;
}
for (Breathe = FALSE, sn = gsn_acid_breath ; sn <= gsn_slow_gas ; sn++)
{
if (learned(ch, sn) && (dice(1,4) == 1)) // simulate 1d4 rounds cooldown
{
Breathe = TRUE;
break;
}
}
if (!Breathe)
{
pop_call();
return FALSE;
}
if (victim == NULL)
{
pop_call();
return FALSE;
}
if ((*skill_table[sn].spell_fun) ( sn, ch->level, ch, victim, 0 ))
{
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* For gaze weapon attacks by DM chars - Kregor
*/
void do_gaze( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
push_call("do_gaze(%p,%p)",ch,argument);
argument = one_argument(argument, arg);
if (arg[0] == '\0' || (victim = get_char_room(ch, arg)) == NULL)
{
if ((victim = who_fighting(ch)) == NULL)
{
send_to_char("You are not fighting anyone.\n\r",ch);
pop_call();
return;
}
}
if (is_safe(ch, victim))
{
pop_call();
return;
}
if (!check_murder(ch, victim))
{
pop_call();
return;
}
CHECK_TURN(ch, victim);
if (!gaze_attack(ch, victim))
send_to_char("Even with your best stink eye, you cannot muster a gaze attack.\n\r", ch);
pop_call();
return;
}
/*
* For creatures with gaze attacks.
*/
bool gaze_attack( CHAR_DATA *ch, CHAR_DATA *victim )
{
AFFECT_DATA af;
int dc, sn, level;
push_call("do_gaze(%p,%p)",ch,victim);
if (IS_SET(ch->action, ACTION_STANDARD))
{
pop_call();
return FALSE;
}
if ((sn = gsn_petri_gaze) && !learned(ch, sn))
{
if ((sn = gsn_death_gaze) && !learned(ch, sn))
{
if ((sn = gsn_fear_gaze) && !learned(ch, sn))
{
pop_call();
return FALSE;
}
}
}
if (!in_combat(ch) || (dice(1,4) != 1)) // simulate 1d4 rounds cooldown
{
pop_call();
return FALSE;
}
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (!who_fighting(victim) || who_fighting(victim) != ch)
{
if (number_percent() > 50)
{
pop_call();
return FALSE;
}
}
level = ch->level;
dc = (level / 2) + 10;
dc += stat_bonus(TRUE, ch, APPLY_CHA);
if (sn == gsn_petri_gaze)
{
if (!save_resist(ch, victim, sn, level))
{
af.type = sn;
af.duration = -1;
af.modifier = 0;
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_PETRIFICATION;
af.level = dc;
affect_join( ch, victim, &af );
act( "$n freezes and turns to stone!", victim, NULL, NULL, TO_ROOM);
act( "Your limbs begin to stiffen, then everything goes black!", victim, NULL, NULL, TO_CHAR);
update_pos(victim,-1);
}
}
if (sn == gsn_fear_gaze)
{
if (!save_resist(ch, victim, sn, level))
{
af.type = sn;
af.duration = dice(1,8);
af.modifier = 0;
af.location = APPLY_NONE;
af.bittype = AFFECT_TO_CHAR;
af.bitvector = AFF2_FEAR;
af.level = dc;
affect_join( ch, victim, &af );
victim->fear_level += UMIN(3, victim->fear_level + 2);
act( "$n is paralyzed with fear!", victim, NULL, NULL, TO_ROOM);
act( "You freeze in your tracks with fear!", victim, NULL, NULL, TO_CHAR);
}
}
if (sn == gsn_death_gaze)
{
if (!save_resist(ch, victim, sn, level))
{
act( "$N falls dead from $n's deadly gaze!", ch, NULL, victim, TO_NOTVICT);
act( "$N falls dead from your deadly gaze!", ch, NULL, victim, TO_CHAR);
act( "You die from $n's deadly gaze!", ch, NULL, victim, TO_VICT);
damage(ch, victim, victim->hit + 11, sn, NULL);
}
}
TAKE_ACTION(ch, ACTION_STANDARD);
pop_call();
return TRUE;
}