/*************************************************************************** * 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; }