/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA fight.c Combat related file, pc/npc fight code, some skills, etc. ******** Heavily modified and expanded ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structures.h" #include "utils.h" #include "comm.h" #include "handler.h" #include "interpreter.h" #include "acmd.h" #include "db.h" #include "magic.h" #include "mudlimits.h" #include "nature.h" #include "objsave.h" #include "bard.h" #include "fight.h" #include "affect.h" #include "lists.h" #include "quest.h" #include "global.h" #include "darkenelf.h" /* Global structures */ chdata *combat_list = NULL; /* head of l-list of fighting chars*/ chdata *combat_next_dude = NULL; /* Next dude global trick */ /* External structures */ extern int exp_table[NUM_CLASSES][MAX_LEVELS]; /* External functions */ void forget(chdata *ch, chdata *victim); void remember(chdata *ch, chdata *victim); int check_arena_state(BOOL award); /* any left? */ void damage_object(obdata *ob, chdata *ch); extern void send_combat_messages(int dam, chdata *ch, chdata *vict, int spl); extern void send_position_messages(int dam, chdata *ch, chdata *vict); /* The Fight related routines */ /* New appear() takes care of spelleq now - SL */ void appear(chdata *ch) { BOOL i = IS_AFFECTED(ch, AFF_INVISIBLE); BOOL j; affect_from_char(ch, SPELL_INVISIBLE); REMOVE_BIT(AFF_FLAGS(ch), AFF_INVISIBLE); affect_total(ch); j = IS_AFFECTED(ch, AFF_INVISIBLE); if(i && !j) act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM); } // set position based on current hit points void update_pos(chdata *victim) { if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POS_STUNNED)) return; else if (GET_HIT(victim) > 0) GET_POS(victim) = POS_STANDING; else if (GET_HIT(victim) <= -11) GET_POS(victim) = POS_DEAD; else if (GET_HIT(victim) <= -6) GET_POS(victim) = POS_MORTALLYW; else if (GET_HIT(victim) <= -3) GET_POS(victim) = POS_INCAP; else GET_POS(victim) = POS_STUNNED; } /* start one char fighting another (yes, it is horrible, I know... ) */ void set_fighting(chdata *ch, chdata *vict) { if (ch == vict) return; assert(!FIGHTING(ch)); ch->next_fighting = combat_list; combat_list = ch; affect_from_char(ch, SPELL_SLEEP); FIGHTING(ch) = vict; GET_POS(ch) = POS_FIGHTING; } /* remove a char from the list of fighting chars */ void stop_fighting(chdata *ch) { chdata *temp; if (ch == combat_next_dude) combat_next_dude = ch->next_fighting; REMOVE_FROM_LIST(ch, combat_list, next_fighting); ch->next_fighting = NULL; FIGHTING(ch) = NULL; VWAIT(ch) = 0; if (IS_PC(ch)) { ch->pc_specials->whirlwind = 0; ch->pc_specials->cyclone = 0; } // remove some combat character flags REMOVE_BIT(CHAR_FLAGS(ch), CH_DISEMBOWEL | CH_SIDESTEP | CH_SUBDUED | CH_REDIRECTED | CH_LOWSTRIKE | CH_BLADEDANCE | CH_ONEATTACK); // yank the tumbling... 4/21/98 -jtrhone affect_from_char(ch, SKILL_TUMBLE); GET_POS(ch) = POS_STANDING; update_pos(ch); } // from a chdata structure, make an object filled with ch's stuff void make_corpse(chdata *ch) { obdata *corpse, *o; obdata *money; int i; static char buf[MAX_STRING_LENGTH]; obdata *create_money(int amount); CREATE(corpse, obdata, 1); clear_object(corpse); corpse->touched = TRUE; /* don't purge this on normal reset */ corpse->item_number = NOWHERE; corpse->in_room = NOWHERE; if (IS_PC(ch)) corpse->plr_num = GET_IDNUM(ch); else corpse->plr_num = 0; if (IS_NPC(ch)) corpse->name = str_dup("corpse remains"); else corpse->name = str_dup("pc corpse remains"); sprintf(buf, "The remains of %s are lying here.", GET_NAME(ch)); corpse->description = str_dup(buf); sprintf(buf, "the remains of %s", GET_NAME(ch)); corpse->shdesc = str_dup(buf); corpse->contains = ch->carrying; if (GET_GOLD(ch) > 0) { /* following 'if' clause added to fix gold duplication loophole */ if (IS_NPC(ch) || (IS_PC(ch) && ch->desc)) { money = create_money(GET_GOLD(ch)); obj_to_obj(money, corpse); } GET_GOLD(ch) = 0; } corpse->type_flag = ITEM_CONTAINER; OBJ_WEARS(corpse) = ITEM_TAKE; OBJ_EXTRAS(corpse) = ITEM_NODONATE; corpse->value[0] = 0; /* You can't store stuff in a corpse */ corpse->value[3] = 1; /* corpse identifyer */ corpse->weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch); if (IS_NPC(ch)) corpse->timer = max_npc_corpse_time; else corpse->timer = max_pc_corpse_time; for (i = 0; i < MAX_WEAR; i++) if (EQ(ch, i)) obj_to_obj(unequip_char(ch, i, TRUE), corpse); ch->carrying = 0; IS_CARRYING_N(ch) = 0; IS_CARRYING_W(ch) = 0; corpse->next = object_list; object_list = corpse; for (o = corpse->contains; o; o->in_obj = corpse, o = o->next_content) ; object_list_new_owner(corpse, 0); float_sink_object(corpse, ch->in_room); /* will sink RoA*/ } /* for a corpseless death jtrhone aka Vall*/ void drop_all(chdata *ch) { obdata *o; obdata *money; int i; obdata *create_money(int amount); if (GET_GOLD(ch) > 0) { /* following 'if' clause added to fix gold duplication loophole */ if (IS_NPC(ch) || (IS_PC(ch) && ch->desc)) { money = create_money(GET_GOLD(ch)); float_sink_object(money, ch->in_room); } GET_GOLD(ch) = 0; } while(ch->carrying) { o = ch->carrying; obj_from_char(o); float_sink_object(o, ch->in_room); } for (i = 0; i < MAX_WEAR; i++) if (EQ(ch, i)) float_sink_object(unequip_char(ch, i, TRUE), ch->in_room); ch->carrying = NULL; IS_CARRYING_N(ch) = 0; IS_CARRYING_W(ch) = 0; } /* When ch kills victim -adopted from 3.0 */ void change_alignment(chdata *ch, chdata *victim) { /* * new alignment change algorithm: if you kill a monster with alignment A, * you move 1/16th of the way to having alignment -A. Simple and fast. */ GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) >> 4; } // send cry of death to room, and to rooms nearby // Added check for undead. 08/13/98 -callahan void death_cry(chdata *ch) { int door, was_in; if (IN_NOWHERE(ch)) return; if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD)) strcpy(buf, "$n begins to crumble, and falls into a pile of dust."); else { /* add a bit of randomness */ switch(number(0, 4)) { case 0: strcpy(buf, "Your blood %B%6freezes%0 as you hear $n's death cry."); strcpy(buf2, "Your blood %B%6freezes%0 as you hear someone's death cry."); break; case 1: strcpy(buf, "A blood %4chilling%0 scream accompanies $n's death."); strcpy(buf2, "A blood %4chilling%0 scream accompanies someone's death."); break; case 2: strcpy(buf, "$n %Bscreams%0 out in anguish as $e visits death."); strcpy(buf2, "Someone %Bscreams%0 out in anguish as they visit death."); break; case 3: strcpy(buf, "$n's %4cry of death%0 chills you to the bone."); strcpy(buf2, "Someone's %4cry of death%0 chills you to the bone."); break; default: strcpy(buf, "You hear $n's %4death cry%0, then %4silence...%0"); strcpy(buf2, "You hear someone's %4death cry%0, then %4silence...%0"); break; } } act(buf, FALSE, ch, 0, 0, TO_ROOM); was_in = ch->in_room; for (door = 0; door < NUM_OF_DIRS; door++) if (CAN_GO(ch, door)) { ch->in_room = DIR(was_in, door)->to_room; act(buf2, FALSE, ch, 0, 0, TO_ROOM); ch->in_room = was_in; } } // the actual killing of chdata // Added check for undead. 08/13/98 -callahan void raw_kill(chdata *ch) { void CharToDust(CharData *ch); if (!INVALID_ROOM(ch->in_room) && IS_PC(ch) && REAL_ZONE(world[ch->in_room].zone)) zone_table[world[ch->in_room].zone].pc_deaths++; if (FIGHTING(ch)) stop_fighting(ch); while (ch->affected) affect_remove(ch, ch->affected); death_cry(ch); if (MOB_FLAGGED(ch, MOB_NO_CORPSE) || MOB_CLASS_FLAGGED(ch, MOB_UNDEAD)) drop_all(ch); /* else if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD)) CharToDust(ch); */ else make_corpse(ch); extract_char(ch); } // die without penalty, remove possible arena harmful affects void die_arena_style(chdata *ch) { extern struct arena_data arena_info; dsdata *pt; arena_info.ticks_since_kill = 0; char_from_room(ch); if (ch->pc_specials->preroom > 0 && ch->pc_specials->preroom < top_of_world) char_to_room(ch, ch->pc_specials->preroom); else char_to_room(ch, real_room(GET_LOADROOM(ch))); do_look_at_room(ch, 0, 0); // restore old hit/move/mana only if they were less than now GET_HIT(ch) = ch->pc_specials->prehit; GET_MANA(ch) = ch->pc_specials->premana; GET_MOVE(ch) = ch->pc_specials->premove; ch->pc_specials->prehit = 1; ch->pc_specials->premana = 1; ch->pc_specials->premove = 1; update_pos(ch); send_to_char("You have been eliminated!!!\n\r",ch); // remove ill arena affects -roa jtrhone affect_from_char(ch, SPELL_HOLD_PERSON); affect_from_char(ch, SPELL_BLINDNESS); affect_from_char(ch, SPELL_POISON); affect_from_char(ch, SPELL_POISON_DART); affect_from_char(ch, SPELL_ENSNARE); REMOVE_BIT(PLR_FLAGS(ch), PLR_ARENA); REMOVE_BIT(PRF_FLAGS(ch), PRF_AREN_CH); if (FIGHTING(ch)) stop_fighting(ch); if (ch->followers || ch->master) die_follower(ch); sprintf(buf, "%s has been eliminated from the ARENA!!\n\r",GET_NAME(ch)); for (pt = descriptor_list; pt; pt=pt->next) if (D_CHECK(pt) && pt->character != ch && SEND_OK(pt->character) && !PRF2_FLAGGED(pt->character, PRF2_NOARENA)) send_to_char(buf, pt->character); act("$n arrives amidst a flash of light.", TRUE, ch, 0, 0, TO_ROOM); check_arena_state(TRUE); } /* do_die */ // die! distributes exp loss, resets violence wait, updates rent/reimb files // calls the raw_kill - actual death function void die(chdata *ch, BOOL akill) { VWAIT(ch) = 0; if (IN_ARENA(ch)) die_arena_style(ch); else { if (akill) gain_exp(ch, -(GET_EXP(ch)/12)); /* an assassin kill */ else { if (GET_LEVEL(ch) < 50) gain_exp(ch, -GET_EXP(ch) / 8); /* div by 6 for now less loss */ else gain_exp(ch, -GET_EXP(ch) / 6); // higher levels LOSE way more now } if (GET_LEVEL(ch) >= number(0, 50)) /* level 50+ WILL lose CON */ GET_CON(ch) -= 1; /* save objlist for players here in text file for reimb command */ write_reimb_file(ch); delete_object_file(ch); raw_kill(ch); } } /* group gain rewritten RoA James Rhone 1/11/96 */ /* use levels of group as percentage bases */ void group_gain(chdata *ch, chdata *victim) { int share = 0, tot_levels = 0, lev_diff = 0; float percentage = 0.0, roughshare = 0.0; int avail_exp = 0, highest_level = 0; chdata *k; while ((k = group_member(ch, TRUE))) { tot_levels += GET_LEVEL(k); highest_level = MAX(highest_level, GET_LEVEL(k)); } /* find available EXP based on highest level in the group */ if (!(lev_diff = highest_level - GET_LEVEL(victim))) avail_exp = GET_EXP(victim); else if (lev_diff > 0) /* ch is a higher level */ { percentage = (5.0 * (float) lev_diff) / 100.0; roughshare = (float) GET_EXP(victim) * percentage; avail_exp = GET_EXP(victim) - (int) roughshare; } else /* mob is a higher level */ if (lev_diff < 0) { lev_diff = -(lev_diff); percentage = (5.0 * (float) lev_diff) / 100.0; roughshare = (float) GET_EXP(victim) * percentage; avail_exp = GET_EXP(victim) + (int) roughshare; } avail_exp = MAX(avail_exp, 1); while ((k = group_member(ch, TRUE))) { percentage = (float) GET_LEVEL(k) / (float) tot_levels; roughshare = percentage * (float) avail_exp; share = (int) roughshare; share = MAX(share, 1); // have HOLD check here... if (IS_HELD(victim) && HOLDER(victim) != k) share /= 10; if (IS_ASSASSIN(victim) || IN_ARENA(victim)) share = gain_exp(k, 1); else if (highest_level - GET_LEVEL(k) > 20) /* must be within 20 levels! -roa */ share = gain_exp(k, 1); else share = gain_exp(k, share); if (share > 1) sprintf(buf2, "You receive your share of experience -- %d points.", share); else strcpy(buf2, "You receive no experience from the battle."); act(buf2, FALSE, k, 0, 0, TO_CHAR); change_alignment(k, victim); if (auto_save && !(number(0, 4))) save_char(k, NOWHERE); } } // when a nongrouped character kills someone other than him/herself // NEW SOLO ALGORITHM RoA James Rhone (now solo will yield // more exp than grouping) void solo_gain_exp(chdata *ch, chdata *victim) { int lev_diff = 0, exp = 0; float percentage = 0.0, roughgain = 0.0; if (!(lev_diff = GET_LEVEL(ch) - GET_LEVEL(victim))) exp = GET_EXP(victim); else if (lev_diff > 0) /* ch is a higher level */ { percentage = (5.0 * (float) lev_diff) / 100.0; roughgain = (float) GET_EXP(victim) * percentage; exp = GET_EXP(victim) - (int) roughgain; } else /* mob is a higher level */ if (lev_diff < 0) { lev_diff = -(lev_diff); percentage = (5.0 * (float) lev_diff) / 100.0; roughgain = (float) GET_EXP(victim) * percentage; exp = GET_EXP(victim) + (int) roughgain; } if (IS_ASSASSIN(victim)) { send_to_char("You receive no experience for killing other assassins.\n\r",ch); change_alignment(ch,victim); } else if (IN_ARENA(victim)) send_to_char("You receive no experience for defeating ARENA combatants.\n\r",ch); else { exp = MAX(exp, 1); // have HOLD check here... if (IS_HELD(victim) && HOLDER(victim) != ch) exp /= 10; exp = gain_exp(ch, exp); change_alignment(ch, victim); if (exp > 1) sprintf(buf2, "You receive %d experience points.\n\r", exp); else strcpy(buf2, "You receive no experience from the battle.\n\r"); send_to_char(buf2, ch); } if (auto_save && !number(0, 1)) save_char(ch, NOWHERE); } // returns one member of ch's group, possibly ch chdata *random_group_member(chdata *ch) { chdata *k; struct follow_type *f; int num = 0, pick = 0; int i = 0; if (!IS_AFFECTED(ch, AFF_GROUP)) return (ch); if (!(k = ch->master)) k = ch; /* tally levels for group starting with leader */ if (IS_AFFECTED(k, AFF_GROUP) && SAME_ROOM(k, ch)) num++; for (f = k->followers; f; f = f->next) if (IS_AFFECTED(f->follower, AFF_GROUP) && SAME_ROOM(f->follower, ch) && (f->follower != k)) /* we counted K already */ num++; if (num <= 1) return (ch); pick = number(1, num); for (f = k->followers; f && i != pick; f = f->next) if (IS_AFFECTED(f->follower, AFF_GROUP) && SAME_ROOM(f->follower, ch)) { i++; if (i == pick) break; } if (f && f->follower) return(f->follower); else return(ch); } /* return true if (both PC and both ASSASSINS or both in ARENA) */ BOOL check_mortal_combat(chdata *ch, chdata *vict) { if (IS_NPC(ch) || IS_NPC(vict)) return TRUE; if (IN_ARENA(ch) && IN_ARENA(vict)) return TRUE; if (IS_ASSASSIN(ch) && IS_ASSASSIN(vict)) return TRUE; act("%1Assassin%0 vs %1Assassin%0 only.",FALSE,ch, 0, 0, TO_CHAR); return FALSE; } int spell_adjust_damage(chdata *victim, int dam) { if (IS_AFFECTED(victim, AFF_SANCTUARY)) dam /= 2; /* 1/2 damage when sanctuary */ else if (IS_AFFECTED2(victim, AFF2_CROW)) dam /= 3; /* 1/3 damage when protected by crow */ return dam; } // takes damage into account and adjusts it according to spells/skills etc // returns adjusted damage int adjust_damage(chdata *ch, chdata *victim, int dam, int *spellnum, BOOL regardless, int *shock_dam) { chdata *tmp_ch; sh_int pack; if (regardless) { // max damage is a range from 250 to 350, random :) -jtrhone roa dam = MAX(0, MIN(dam, number(250, 350))); GET_HIT(victim) -= dam; return dam; } dam = spell_adjust_damage(victim, dam); /* drow fight better outside in the dark James Rhone RoA*/ if (IS_DROW(ch) && OUTSIDE(ch)) { if (global_weather.sunlight == SUN_LIGHT) dam -= (dam / 4); else if (global_weather.sunlight == SUN_DARK) dam += (dam / 4); } /* orcs fight better in packs James Rhone RoA*/ if (ch != victim && IS_ORC(ch)) { for (tmp_ch = world[ch->in_room].people, pack = 0; tmp_ch; tmp_ch=tmp_ch->next_in_room) if (tmp_ch != ch && IS_ORC(tmp_ch) && FIGHTING(tmp_ch) == FIGHTING(ch)) pack++; if (pack) dam += 2 * pack; } /* If victim has mirror images, see if ch hit one of them */ if (victim->specials.num_of_images > 0 && number(0, victim->specials.num_of_images) && !saves_spell(ch, SPELL_MIRRORIMAGE)) // add sv_vs_magic now 11/29/97 -jtrhone { victim->specials.num_of_images--; dam = 0; *spellnum = SPELL_MIRRORIMAGE; } /* if amitars or baals, reduce mana each hit */ if (affected_by_spell(victim, SPELL_SONG_AMITAR) && bard_song_near_char(victim, SONG_AMITAR)) { GET_MANA(victim) -= dam / 4; GET_MANA(victim) = MAX(0, GET_MANA(victim)); if (!GET_MANA(victim) && IS_PC(victim)) { if (SINGING(victim)) do_finish(ch, " song", 0, 0); if (PLAYING(victim)) do_finish(ch, " tune", 0, 0); } dam = (int) (dam * 0.6); *spellnum = SPELL_SONG_AMITAR; } else if (affected_by_spell(victim, SPELL_SONG_BALM) && bard_song_near_char(victim, SONG_BALM)) { GET_MANA(victim) -= dam / 8; GET_MANA(victim) = MAX(0, GET_MANA(victim)); if (!GET_MANA(victim) && IS_PC(victim)) { if (SINGING(victim)) do_finish(ch, " song", 0, 0); if (PLAYING(victim)) do_finish(ch, " tune", 0, 0); } dam -= GET_LEVEL(victim); dam = MAX(0, MIN(dam, 250)); *shock_dam = dam; GET_HIT(victim) -= dam; dam = 0; *spellnum = SPELL_SONG_AMITAR; /* same combat messages as amitar */ } // max damage is a range from 250 to 350, random :) -jtrhone roa dam = MAX(0, MIN(dam, number(250, 350))); GET_HIT(victim) -= dam; if (!*shock_dam) *shock_dam = dam; return dam; } void send_combat_mortlog(chdata *ch, chdata *victim) { dsdata *jt; if (!INCOG(victim) && ch != victim) { for (jt = descriptor_list; jt; jt = jt->next) if (D_CHECK(jt) && jt->character != victim && PRF_FLAGGED(jt->character,PRF_MORTLOG) && SEND_OK(jt->character)) { sprintf(buf2, "[ %s killed by %s -> %s ]\n\r", GET_NAME(victim), GET_NAME(ch), world[victim->in_room].name); send_to_char(buf2, jt->character); } } else for (jt = descriptor_list; jt; jt = jt->next) if (D_CHECK(jt) && jt->character != victim && PRF_FLAGGED(jt->character,PRF_MORTLOG) && SEND_OK(jt->character)) { sprintf(buf2, "[ %s embraces %%1death%%0. ]\n\r",GET_NAME(victim)); send_to_char(buf2, jt->character); } sprintf(buf2, "%s killed by %s rm#%d", GET_NAME(victim), GET_NAME(ch), world[victim->in_room].number); mudlog(buf2, BRF, LEV_IMM, TRUE); } void do_combat_autos(chdata *ch) { long money1, money2, money_looted; if (IS_PC(ch)) // can have a SPC_AUTOLOOT here? (roa) { money1 = GET_GOLD(ch); if (PRF_FLAGGED(ch, PRF_AGOLD)) { sprintf(buf, "all.%s from corpse", currency_name); do_get(ch, buf, 0, 0); } if (PRF_FLAGGED(ch, PRF_ALOOT)) do_get(ch, "all from corpse", 0, 0); money2 = GET_GOLD(ch); money_looted = MAX(0, (money2 - money1)); if (PRF_FLAGGED(ch, PRF_ASPLIT) && IS_AFFECTED(ch, AFF_GROUP)) { sprintf(buf, "%ld", money_looted); do_split(ch, buf, 0, 0); } } } // small proc for doing the shaman wolf thing void do_shaman_wolf(int dam, chdata *ch, chdata *victim) { if (GET_LEVEL(ch) < 60) GET_HIT(ch) -= dam; else GET_HIT(ch) -= dam/2; GET_HIT(ch) = MAX(1, GET_HIT(ch)); act("The %B%1WOLF%0 lashes out at you from $M!", FALSE, ch, 0, victim, TO_CHAR); act("The %B%1WOLF%0 lashes out at $N!", FALSE, victim, 0, ch, TO_CHAR); act("The %B%1WOLF%0 lashes out from $n towards $N!", FALSE, victim, 0, ch, TO_NOTVICT); update_pos(ch); } // if pc and fighting linkless... void linkless_flee(chdata *victim) { do_flee(victim, "", 0, 0); if (!FIGHTING(victim)) { act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM); victim->specials.was_in_room = victim->in_room; char_from_room(victim); char_to_room(victim, 0); } } // if in shock, send a little message void send_shock_message(chdata *ch, chdata *victim) { act("$N falters under your deadly blow!", FALSE, ch, 0, victim,TO_CHAR); act("AHHH! You %1falter%0 under the blow!", FALSE, victim, 0, 0, TO_CHAR); act("$n drops to $s knees in %Bshock%0!", FALSE, victim, 0, 0, TO_ROOM); VWAIT(victim) += 1; /* in case it wasnt 0 :) */ } // these are the things done if victim dies in damage() void do_death_procs(chdata *ch, chdata *victim) { BOOL loot_ok = FALSE; if (IS_NPC(victim) || victim->desc) { if (IS_AFFECTED(ch, AFF_GROUP)) group_gain(ch, victim); else if (ch != victim) // if suicide, no exp, i.e. DTs and such solo_gain_exp(ch, victim); } if (IS_PC(victim)) send_combat_mortlog(ch, victim); if (ch != victim && (IS_NPC(victim) || IS_ASSASSIN(victim))) loot_ok = TRUE; if (IS_PC(victim) && (MOB_FLAGGED(ch, MOB_MEMORY) || ch->npc_specials.memory)) forget(ch, victim); if (FIGHTING(ch) == victim) stop_fighting(ch); // ok, ch killed victim, check SLAYMOB quests see if ch finished one... if (IS_NPC(victim)) qcheck_slaymob(ch, GET_MOB_VNUM(victim)); if (ch != victim && IS_ASSASSIN(ch) && IS_ASSASSIN(victim) && !IN_ARENA(victim) && !IN_ARENA(ch)) die(victim, TRUE); /* yes an assassin kill */ else die(victim, FALSE); if (loot_ok) do_combat_autos(ch); } // Some spells are touch-sensitive. 05/03/98 -callahan int spell_touch_affects(chdata *vict, chdata *ch) { int dam = 0; if (!vict) return 0; if (affected_by_spell(ch, SPELL_WALL_FIRE)) { act("You are burned by the flames surrounding $n.", FALSE, ch , 0, vict, TO_VICT); act("$N is burned by the flames surrounding you.", FALSE, ch, 0, vict, TO_CHAR); act("$N is burned by the flames surrounding $n.", FALSE, ch, 0, vict, TO_NOTVICT); dam += (int) ((float) Level(ch) * 0.02); } else if (affected_by_spell(ch, SPELL_WALL_BRAMBLES)) { act("You are scathed by the brambles which surround $n.", FALSE, ch , 0, vict, TO_VICT); act("$N is scathed by the brambles which surround you.", FALSE, ch, 0, vict, TO_CHAR); act("$N is scathed by the brambles which surround $n.", FALSE, ch, 0, vict, TO_NOTVICT); dam += (int) ((float) Level(ch) * 0.05); } return dam; } /* do_damage */ int damage(chdata *ch, chdata *victim, int dam, int spellnum, BOOL regardless) { int shock_dam = 0; extern struct con_app_type con_app[]; if (GET_POS(victim) <= POS_DEAD) { log("SYSERR: Attempt to damage a corpse."); return CHAR_OK; /* -je, 7/7/92 */ } /* You can't damage an immortal! */ if (IS_IMMORTAL(victim)) dam = 0; if (victim != ch) { if (GET_POS(ch) > POS_STUNNED) { if (!(FIGHTING(ch))) set_fighting(ch, victim); // if both fighters are NPCs, see if the master of one charmie is in room // to attack :) if (IS_NPC(ch) && IS_NPC(victim) && victim->master && CHARMED(victim) && SAME_ROOM(victim->master, ch) && !number(0, 10)) { if (FIGHTING(ch)) stop_fighting(ch); hit(ch, victim->master, TYPE_UNDEFINED, FALSE); return CHAR_OK; } } if (GET_POS(victim) > POS_STUNNED) { if (!FIGHTING(victim)) set_fighting(victim, ch); // immortal memory safety check if (MOB_FLAGGED(victim, MOB_MEMORY) && IS_PC(ch) && (GET_LEVEL(ch) < LEV_IMM)) remember(victim, ch); if (PC_MOUNTING(ch)) do_dismount(ch, "", 0, 0); /* for now dismount in combat */ if (IS_NPC(ch)) GET_POS(ch) = POS_FIGHTING; } } // Added undead check. 08/14/98 -callahan if (victim->master == ch) { if (MOB_CLASS_FLAGGED(ch, MOB_UNDEAD) && affected_by_spell(ch, SPELL_ANIMATE_DEAD)) raw_kill(victim); else stop_follower(victim); } if (IS_AFFECTED(ch, AFF_INVISIBLE)) appear(ch); // If the victim is affected by any spells that take effect upon // contact, initiate them now // Added to facilitate wall of fire and brambles 05/03/98 -callahan dam += spell_touch_affects(victim, ch); dam = adjust_damage(ch, victim, dam, &spellnum, regardless, &shock_dam); /* does character go into shock because of blow? -roa based on con */ if (shock_dam && shock_dam > (int) ((float)GET_MAX_HIT(victim) * ((float)con_app[GET_CON(victim)].shock / 100.0))) send_shock_message(ch, victim); if (GET_LEVEL(ch) >= LEV_GOD) { sprintf(buf, "[%d points of damage]\n\r",dam); S2C(); } if (ch != victim && IS_AFFECTED2(victim, AFF2_WOLF)) do_shaman_wolf(dam, ch, victim); if ((ch != victim) && dam && !regardless) gain_exp(ch, GET_LEVEL(victim) + dam); update_pos(victim); // message sending related to combat and positions in fightmes.c send_combat_messages(dam, ch, victim, spellnum); send_position_messages(dam, ch, victim); if (ch != victim && IS_PC(victim) && !(victim->desc)) linkless_flee(victim); if (!AWAKE(victim) && FIGHTING(victim)) stop_fighting(victim); if (GET_POS(victim) == POS_DEAD) { do_death_procs(ch, victim); return CHAR_DIED; } else return CHAR_OK; } int successful_parry(chdata *victim) { int percent; if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim)) return FALSE; percent = 5 + (3 * (IS_THIEF(victim))); // if tumbling, double chance to parry 4/21/98 -jtrhone if (affected_by_spell(victim, SKILL_TUMBLE)) percent *= 2; if (IS_PC(victim) || IS_AFFECTED(victim, AFF_PARRY) ) if ((GET_SKILL(victim, SKILL_PARRY) > number(1,100) || IS_NPC(victim)) && (number(1,100) < percent)) { do_parry(victim, "", 0, 0); return TRUE; } return FALSE; } int successful_dodge(chdata *victim) { int percent; if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim)) return FALSE; percent = 5 + (3 * (IS_THIEF(victim))); if (IS_PC(victim) || IS_AFFECTED(victim, AFF_DODGE)) if ((GET_SKILL(victim, SKILL_DODGE) > number(1,100) || IS_NPC(victim)) && (number(1,100) < percent)) { do_dodge(victim, "", 0, 0); return TRUE; } return FALSE; } int successful_block(chdata *victim) { int percent; if (IS_HELD(victim) || !AWAKE(victim) || IN_A_RITUAL(victim)) return FALSE; percent = 5 + (3 * (IS_THIEF(victim))); // conform to slot revamp 5/28/98 -jtrhone if (IS_PC(victim) && EQ(victim, W_HOLD) && IS_ARMOR(EQ(victim, W_HOLD))) if (GET_SKILL(victim, SKILL_SHIELDBLOCK) > number(1,100) && number(1,100) < percent) { do_shieldblock(victim, "", 0, 0); return TRUE; } return FALSE; } /* weapon type + base (types range from roughly 0 - 10) */ int get_weapon_type(obdata *weap) { return (weap->value[3] + TYPE_HIT); } int get_thaco(chdata *ch) { float class_thacos[NUM_CLASSES + 1] = { 0.0, // class 0 is undefined 0.18, // mage 0.20, // cleric 0.23, // thief 0.28, // warrior 0.24, // shaman 0.26, // ranger 0.20, // bard 0.25, // warlock 0.27 // monk }; if (IS_NPC(ch)) return 20; if (IS_IMMORTAL(ch)) return 0; return (20 - (int) (class_thacos[(int) GET_CLASS(ch)] * GET_LEVEL(ch)) ); } // modular function to determine if one player hits another // uses thaco/ac/etc etc etc etc return TRUE if yep // updated to check some CH_ flags 4/21/98 -jtrhone int successful_hit(chdata *ch, chdata *victim) { int victim_ac, calc_thaco; byte diceroll; extern struct str_app_type str_app[]; extern struct dex_app_type dex_app[]; if (!AWAKE(victim) || IS_HELD(victim)) return TRUE; diceroll = number(1, 20); if (diceroll == 20) return TRUE; // always hit on 20 roll if (diceroll == 1) return FALSE; // always miss on 1 roll calc_thaco = get_thaco(ch); calc_thaco -= str_app[TRUE_STRENGTH(ch)].tohit; calc_thaco -= GET_HITROLL(ch); victim_ac = GET_AC(victim) / 10; victim_ac += dex_app[GET_DEX(victim)].defensive; if (IS_EVIL(ch) && IS_AFFECTED(victim, AFF_PROTECT_EVIL)) victim_ac -= 10; if (IS_GOOD(ch) && IS_AFFECTED(victim, AFF_PROTECT_GOOD)) victim_ac -= 10; // updated to look at mob class undead as well 4/26/98 -jtrhone if (affected_by_spell(victim, SPELL_PROTECT_UNDEAD) && (SPC_FLAGGED(ch, SPC_UNDEAD) || MOB_CLASS_FLAGGED(ch, MOB_UNDEAD))) victim_ac -= 10; if (IS_AFFECTED2(victim, AFF2_ILLUMINATE)) /* easier to hit */ victim_ac += GET_LEVEL(ch)/2; // if hamstrung... easier to hit... 4/26/98 -jtrhone if (affected_by_spell(victim, SKILL_HAMSTRING)) victim_ac += GET_LEVEL(ch)/2; victim_ac = MAX(-49, victim_ac); /* -49 is lowest */ if ((calc_thaco - diceroll) > victim_ac) return FALSE; // MISSED else // if vict is about to sidestep, roll ch DEX against it // if ch makes dex roll, nullify sidestep 4/21/98 -jtrhone if (CHAR_FLAGGED(victim, CH_SIDESTEP)) { // if ch fails the roll, then ch will miss no matter what if ((GET_DEX(ch)-13) < number(1, 10)) return FALSE; else { REMOVE_BIT(CHAR_FLAGS(victim), CH_SIDESTEP); act("$n avoids your sidestep!", FALSE, ch, 0, victim, TO_VICT); act("You avoid $N's sidestep!", FALSE, ch, 0, victim, TO_CHAR); act("$n avoid $N's sidestep!", FALSE, ch, 0, victim, TO_NOTVICT); return TRUE; // HIT } } else return TRUE; // HIT } /* do_hit char hits another */ // updated return type to let us know when victim dies... int hit(chdata *ch, chdata *victim, int type, BOOL dual) { obdata *wielded = 0; obdata *held = 0; int w_type, h_type = 0; int dam = 0; int hdam = 0; byte isthief = FALSE; extern struct str_app_type str_app[]; if (!SAME_ROOM(ch, victim)) { log("SYSERR: NOT SAME ROOM WHEN FIGHTING!"); if (FIGHTING(ch)) stop_fighting(ch); if (FIGHTING(victim)) stop_fighting(victim); return CHAR_OK; } if (IS_PC(ch) && SUMMONED(victim) && SUMMONER(victim) == GET_IDNUM(ch)) { REMOVE_BIT(CHAR_FLAGS(victim), CH_SUMMONED); SUMMONER(victim) = -1; } if (IS_PC(ch) && IN_A_RITUAL(ch)) return CHAR_OK; /* cant attack in ritual */ if (!check_mortal_combat(ch, victim)) { if (FIGHTING(ch)) stop_fighting(ch); if (FIGHTING(victim)) stop_fighting(victim); return CHAR_OK; } if (!check_truce(ch, victim) || IS_HELD(ch)) return CHAR_OK; /* cant hit truced players, cant hit if held */ // if they are brewing, stop them here... 4/19/98 -jtrhone affect_from_char(ch, SKILL_BREW); // if they have the loseattack flag, remove it and return 4/26/98 -jtrhone if (CHAR_FLAGGED(ch, CH_LOSEATTACK)) { REMOVE_BIT(CHAR_FLAGS(ch), CH_LOSEATTACK); return CHAR_OK; } if (successful_dodge(victim)) return CHAR_OK; if (successful_parry(victim)) return CHAR_OK; if (successful_block(victim)) return CHAR_OK; if ((held = EQ(ch, W_HOLD)) && IS_WEAPON(held) && IS_THIEF(ch)) { isthief = TRUE; h_type = get_weapon_type(held); } if ((wielded = EQ(ch, W_WIELD)) && IS_WEAPON(wielded)) w_type = get_weapon_type(wielded); else w_type = TYPE_HIT; // if ch is sidestepping, next hit is backstab 4/21/98 -jtrhone // must remove this before return... if (CHAR_FLAGGED(ch, CH_SIDESTEP)) type = SKILL_BACKSTAB; // similar, types to backstab, however, lasts entire round, // not just one hit... 4/22/98 -jtrhone if (CHAR_FLAGGED(ch, CH_DISEMBOWEL)) type = SKILL_BACKSTAB; if (!successful_hit(ch, victim)) { REMOVE_BIT(CHAR_FLAGS(ch), CH_SIDESTEP); if (type == SKILL_BACKSTAB) damage(ch, victim, 0, SKILL_BACKSTAB, FALSE); else damage(ch, victim, 0, w_type, FALSE); } else // or else we HIT { REMOVE_BIT(CHAR_FLAGS(ch), CH_SIDESTEP); dam = str_app[TRUE_STRENGTH(ch)].todam; dam += GET_DAMROLL(ch); if (isthief) { hdam = str_app[TRUE_STRENGTH(ch)].todam; hdam += GET_DAMROLL(ch); } if (!wielded) { if (IS_NPC(ch)) dam += dice(ch->npc_specials.damnodice, ch->npc_specials.damsizedice); else { if (IS_AFFECTED2(ch, AFF2_CLAWS)) /* if cougar claws 11d3 */ dam += dice(11, 3); else if (IS_NAT_MONK(ch)) dam += dice(MAX(1, (GET_LEVEL(ch) / 10)), 4); /* 1d4 per 10 levels */ else dam += number(0, 2); /* Max. 2 dam with bare hands non monk*/ } } else { damage_object(wielded, ch); dam += dice(wielded->value[1], wielded->value[2]); if (isthief) { damage_object(held, ch); hdam += dice(held->value[1], held->value[2]); } } if (GET_POS(victim) < POS_FIGHTING) dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3; if (isthief && GET_POS(victim) < POS_FIGHTING) hdam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3; /* If the char was stun touched, multiply the dam */ if (CHAR_FLAGGED(victim, CH_STUNTOUCHED)) { REMOVE_BIT(CHAR_FLAGS(victim), CH_STUNTOUCHED); dam = (int) (dam * 1.66); if (isthief) hdam = (int) (hdam * 1.66); } // If the the attacker is in lowstrike mode, multiply the dam // 4/19/98 -jtrhone if (CHAR_FLAGGED(ch, CH_LOWSTRIKE)) { REMOVE_BIT(CHAR_FLAGS(ch), CH_LOWSTRIKE); dam = (int) (dam * 1.66); if (isthief) hdam = (int) (hdam * 1.66); act("%B%1OOOF! $n lowstrikes you!%0", FALSE, ch, 0, victim, TO_VICT); } dam = MAX(1, dam); /* Not less than 0 damage */ hdam = MAX(1, hdam); /* Not less than 0 damage */ // if either weapon is poisoned, apply poison vs save and // and decrement poison on object 4/19/98 -jtrhone if (wielded && wielded->poisoned) { (*spell_info[SPELL_POISON].spell_pointer) (ch, "", victim, NULL); if (--wielded->poison_duration <= 0) { wielded->poisoned = FALSE; act("The poison on $p has dried up.", FALSE, ch, wielded, 0, TO_CHAR); } } if (isthief && held && held->poisoned) { (*spell_info[SPELL_POISON].spell_pointer) (ch, "", victim, NULL); if (--held->poison_duration <= 0) { held->poisoned = FALSE; act("The poison on $p has dried up.", FALSE, ch, held, 0, TO_CHAR); } } if (type == SKILL_BACKSTAB) { dam *= MAX(1, (GET_LEVEL(ch) / 10)); /* multiplier lev/10 */ if (damage(ch, victim, dam, SKILL_BACKSTAB, FALSE) == CHAR_DIED) return CHAR_DIED; } else if (IS_AFFECTED2(ch, AFF2_CLAWS)) { if (damage(ch, victim, dam, SKILL_CLAWS, FALSE) == CHAR_DIED) return CHAR_DIED; } else { if (damage(ch, victim, dam, w_type, FALSE) == CHAR_DIED) return CHAR_DIED; } if (isthief && dual && FIGHTING(ch) && GET_POS(FIGHTING(ch)) > POS_DEAD && GET_SKILL(ch, SKILL_DUAL) > number(1, 100)) if (damage(ch, victim, hdam, h_type, FALSE) == CHAR_DIED) return CHAR_DIED; if (GET_POS(victim) == POS_SITTING) do_stand(victim, "", 0, 0); } return CHAR_OK; } void perform_whirlwind(chdata *ch) { chdata *t, *next_t; int n; if (IN_NOWHERE(ch)) return; if (IS_NPC(ch)) return; for (n = 0, t = world[ch->in_room].people; t; t = next_t) { next_t = t->next_in_room; if (IS_NPC(t) && (can_see(ch, t) || FIGHTING(t))) { if (!n) { act("$n WHIRLS around in blurred movements!",FALSE, ch, 0, 0, TO_ROOM); act("You WHIRL around in blurred movements!",FALSE, ch, 0, 0, TO_CHAR); } n++; hit(ch, t, TYPE_UNDEFINED, FALSE); } } if (!n) { act("$n swings wildly at the air.", FALSE, ch, 0, 0, TO_ROOM); act("You swing wildly at the air.", FALSE, ch, 0, 0, TO_CHAR); } ch->pc_specials->whirlwind--; } // enhanced whirlwind, 3/27/98 -jtrhone void perform_cyclone(chdata *ch) { chdata *t, *next_t; int n; if (IN_NOWHERE(ch)) return; if (IS_NPC(ch)) return; for (n = 0, t = world[ch->in_room].people; t; t = next_t) { next_t = t->next_in_room; if (IS_NPC(t) && (can_see(ch, t) || FIGHTING(t))) { if (!n) // first time thru, send these... { act("You WHIRL around like a %Bcyclone%0!",FALSE, ch, 0, 0, TO_CHAR); act("$n WHIRLS around like a %Bcyclone%0!",FALSE, ch, 0, 0, TO_ROOM); } n++; // two hits in cyclone... hit(ch, t, TYPE_UNDEFINED, FALSE); hit(ch, t, TYPE_UNDEFINED, FALSE); } } if (!n) { act("$n swings wildly at the air.", FALSE, ch, 0, 0, TO_ROOM); act("You swing wildly at the air.", FALSE, ch, 0, 0, TO_CHAR); } ch->pc_specials->cyclone--; } // use this function to see if ch can multi attack based on affectuals BOOL no_extra_attacks(chdata *ch) { int res; if (CHAR_FLAGGED(ch, CH_ONEATTACK)) { REMOVE_BIT(CHAR_FLAGS(ch), CH_ONEATTACK); return TRUE; } if (affected_by_spell(ch, SPELL_SONG_BOG)) { act("Your actions feel slow and sluggish!", FALSE, ch, 0, 0, TO_CHAR); act("$n's actions look slow and sluggish!", FALSE, ch, 0, 0, TO_ROOM); return TRUE; } if (affected_by_spell(ch, SKILL_CRIPPLE)) { act("You are in severe pain!", FALSE, ch, 0, 0, TO_CHAR); act("$n seems to be in severe pain!", FALSE, ch, 0, 0, TO_ROOM); return TRUE; } if (affected_by_spell(ch, SPELL_FOREST_EMBRACE)) { act("The grip of the vines hinders your movements.", FALSE, ch, 0, 0, TO_CHAR); act("$n's movements seem hindered by the vines which bind $m.", FALSE, ch, 0, 0, TO_ROOM); return TRUE; } // added rangers entangle, must decrement and check affect here! 3/27/98 -jtrhone res = decrement_affect(ch, SPELL_ENTANGLE); switch (res) { case AFFECT_DECREMENTED: act("You are entangled!", FALSE, ch, 0, 0, TO_CHAR); act("$n seems to be entangled!", FALSE, ch, 0, 0, TO_ROOM); return TRUE; case AFFECT_REMOVED: act("You break free of the entanglement!", FALSE, ch, 0, 0, TO_CHAR); act("$n breaks free of the entanglement!", FALSE, ch, 0, 0, TO_ROOM); return TRUE; case AFFECT_NOTFOUND: default: break; } return FALSE; // ch attacks normally... } void do_combat_songs(chdata *ch, chdata *vict) { switch (SINGING(ch)) { case 0: break; case 10: /* harsh dissonance */ if (GET_MANA(ch) < 2) { send_to_char("You're out of mana!\n\r",ch); do_finish(ch, " song", 0, 0); break; } GET_MANA(ch) -= 2; GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch)/2; GET_HIT(ch) = MAX(1, GET_HIT(ch)); act("$N's song forces you to kneel in pain!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR); act("Your song forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$n's song forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM); update_pos(ch); break; case 23: /* tortured soul */ if (GET_MANA(ch) < 5) { send_to_char("You're out of mana!\n\r",ch); do_finish(ch, " song", 0, 0); break; } GET_MANA(ch) -= 5; GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch); GET_HIT(ch) = MAX(1, GET_HIT(ch)); act("$N's song tortures your soul!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR); act("Your song tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$n's song tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM); update_pos(ch); break; default: break; } } void do_combat_tunes(chdata *ch, chdata *vict) { switch (PLAYING(ch)) { case 0: break; case 10: /* harsh dissonance */ if (GET_MANA(ch) < 2) { send_to_char("You're out of mana!\n\r",ch); do_finish(ch, " tune", 0, 0); break; } GET_MANA(ch) -= 2; GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch)/2; GET_HIT(ch) = MAX(1, GET_HIT(ch)); act("$N's tune forces you to kneel in pain!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR); act("Your tune forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$n's tune forces $N to kneel in pain!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM); update_pos(ch); break; case 23: /* tortured soul */ if (GET_MANA(ch) < 5) { send_to_char("You're out of mana!\n\r",ch); do_finish(ch, " tune", 0, 0); break; } GET_MANA(ch) -= 5; GET_HIT(FIGHTING(ch)) -= GET_LEVEL(ch); GET_HIT(ch) = MAX(1, GET_HIT(ch)); act("$N's tune tortures your soul!", FALSE, FIGHTING(ch), 0, ch, TO_CHAR); act("Your tune tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$n's tune tortures $N's soul!", FALSE, ch, 0, FIGHTING(ch), TO_ROOM); update_pos(ch); break; default: break; } } // things that get removed or stopped after a round of combat for ch // 4/22/98 -jtrhone // remove brambles and wall fire... corrected infinite loop 5/7/98 -jtrhone void end_combat_round(chdata *ch) { chdata *person; AffType *af, *next_af; // yank the DISEMBOWEL bit REMOVE_BIT(CHAR_FLAGS(ch), CH_DISEMBOWEL); // any per round affects to be yanked? // Get rid of walls if no more attackers. 05/05/98 -callahan if (FIGHTING(ch)) return; CharsInRoom(ch, person) { if (FIGHTING(person) == ch) return; else continue; } for (af = ch->affected; af; af = next_af) { next_af = af->next; if (af->type == SPELL_WALL_FIRE || af->type == SPELL_WALL_BRAMBLES) { if (spell_info[af->type].wear_off) send_to_char(tprintf("%s\r\n", spell_info[af->type].wear_off), ch); affect_remove(ch, af); } } } // take care of combat, called every PULSE_VIOLENCE (roughly 2 seconds) void perform_violence(void) { void diag_char_to_char(chdata *i, chdata *ch); chdata *ch, *vict; obdata *wielded; int spell = -1; int i; for (ch = combat_list; ch; ch = combat_next_dude) { combat_next_dude = ch->next_fighting; assert(FIGHTING(ch)); // try to remove CH_SUBDUED via DEX roll if (CHAR_FLAGGED(ch, CH_SUBDUED)) if (GET_DEX(ch) > number(3, 18)) REMOVE_BIT(CHAR_FLAGS(ch), CH_SUBDUED); // check affects that last # of rounds... 4/18/98 -jtrhone check_per_round_affects(ch); if (!VWAIT(ch)) { if (!AWAKE(ch)) { if (FIGHTING(ch)) stop_fighting(ch); continue; } if (IS_PC(ch) && ch->pc_specials->whirlwind > 0) perform_whirlwind(ch); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } if (SAME_ROOM(ch, FIGHTING(ch))) { if((wielded = EQ(ch, W_WIELD)) && (IS_WEAPON(wielded)) && OBJ_FLAGGED(wielded, ITEM_SPELLEQ)) { spell = wielded->eqspell; if(spell >= 0 && !number(0, 3)) /* roughly 25% */ { /* once every 5 or so rounds, launch a spell - SL */ act("$p shivers mightily in your hands!", TRUE, ch, wielded, NULL, TO_CHAR); act("$p shivers mightily in the hands of $n!", TRUE, ch, wielded, NULL, TO_ROOM); // if evil weapon and violent spell, swap ch and vict if (SPELL_FLAGGED(spell, S_VIOLENT) && !OBJ_FLAGGED(wielded, ITEM_EVIL)) vict = FIGHTING(ch); else vict = ch; do_cast_spell(spell, ch, NULL, SPELL_TYPE_SPELL, vict, NULL); } } /* end if weapon && spelleq */ // we must continually check for death... if(!FIGHTING(ch)) { end_combat_round(ch); continue; } if (CHAR_FLAGGED(ch, CH_REDIRECTED)) { act("$n's slips and strikes $mself!", FALSE, ch, 0, 0, TO_ROOM); // minor bug fix if ch kills himself we were still removing a bit... 4/22/98 -jtrhone if (hit(ch, ch, TYPE_UNDEFINED, TRUE) == CHAR_DIED) continue; REMOVE_BIT(CHAR_FLAGS(ch), CH_REDIRECTED); continue; } // if hamstrung... set one attack loss now 4/26/98 -jtrhone if (CHAR_FLAGGED(ch, CH_HAMSTRUNG)) SET_BIT(CHAR_FLAGS(ch), CH_LOSEATTACK); // the NORMAL hit here if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, TRUE) == CHAR_DIED) { end_combat_round(ch); continue; } // check extra attack reduction affects... if (no_extra_attacks(ch)) { end_combat_round(ch); continue; } // Modified for Druid spell 'forest warrior' 06/01/98 -callahan if (((IS_WARRIOR(ch) || IS_RANGER(ch) || IS_THIEF(ch) || IS_MONK(ch) || IS_SHAMAN(ch) || IS_BARD(ch)) && GET_SKILL(ch, SKILL_DOUBLE)) || GET_SKILL(ch, SPELL_FOREST_WARRIOR)) do_double(ch, "", 0, 0); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } if ((IS_WARRIOR(ch) || IS_MONK(ch) || IS_RANGER(ch)) && GET_SKILL(ch, SKILL_TRIPLE)) do_triple(ch, "", 0, 0); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } // check for multiples from thief bladedance 4/21/98 -jtrhone if (CHAR_FLAGGED(ch, CH_BLADEDANCE)) { REMOVE_BIT(CHAR_FLAGS(ch), CH_BLADEDANCE); SET_BIT(CHAR_FLAGS(ch), CH_ONEATTACK); act("%BYou bladedance around $N!%0", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("%B$n bladedances around you!%0", TRUE, ch, 0, FIGHTING(ch), TO_VICT); act("%B$n bladedances around $N!%0", TRUE, ch, 0, FIGHTING(ch), TO_NOTVICT); if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE) == CHAR_DIED) { end_combat_round(ch); continue; } if(!FIGHTING(ch)) { end_combat_round(ch); continue; } if (hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE) == CHAR_DIED) { end_combat_round(ch); continue; } if(!FIGHTING(ch)) { end_combat_round(ch); continue; } } /* BARDs check the singing and playing stuff RoA*/ if (IS_PC(ch)) do_combat_songs(ch, FIGHTING(ch)); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } if (IS_PC(ch)) do_combat_tunes(ch, vict); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } /* If a character is affected by haste, give him an extra attack 50% of the time */ if(IS_AFFECTED(ch, AFF_HASTE) && number(0, 1)) hit(ch, FIGHTING(ch), TYPE_UNDEFINED, FALSE); if(!FIGHTING(ch)) { end_combat_round(ch); continue; } /* MH0 - 1 extra attack 90% of the time */ if(MOB_FLAGGED(ch, MOB_MH0) && number(0, 9)) { if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK)) vict = random_group_member(FIGHTING(ch)); else vict = FIGHTING(ch); hit(ch, vict, TYPE_UNDEFINED, FALSE); } if(!FIGHTING(ch)) { end_combat_round(ch); continue; } /* MH1 - 2 extra attacks, each with 90% */ if(MOB_FLAGGED(ch, MOB_MH1)) for(i = 0; i < 2; i++) if(number(0, 9)) { if(!FIGHTING(ch)) break; if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK)) vict = random_group_member(FIGHTING(ch)); else vict = FIGHTING(ch); hit(ch, vict, TYPE_UNDEFINED, FALSE); } if(!FIGHTING(ch)) { end_combat_round(ch); continue; } /* MH2 - 4 extra attacks, each with 90% */ if(MOB_FLAGGED(ch, MOB_MH2)) for(i = 0; i < 4; i++) if(number(0, 9)) { if(!FIGHTING(ch)) break; if (IS_AFFECTED(FIGHTING(ch), AFF_GROUP) && MOB_FLAGGED(ch, MOB_SPLITATTACK)) vict = random_group_member(FIGHTING(ch)); else vict = FIGHTING(ch); hit(ch, vict, TYPE_UNDEFINED, FALSE); } if(!FIGHTING(ch)) { end_combat_round(ch); continue; } /* showing the condition of enemy here JRhone*/ if (FIGHTING(ch)) diag_char_to_char(FIGHTING(ch), ch); // this round is over for this char 4/22/98 -jtrhone end_combat_round(ch); } else /* Not in same room */ stop_fighting(ch); } else /* they have a violence wait */ VWAIT(ch)--; if (!FIGHTING(ch)) VWAIT(ch) = 0; } }