/**************************************************************************** * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | * * -----------------------------------------------------------| \\._.// * * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) * * -----------------------------------------------------------| ).:.( * * SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} * * -----------------------------------------------------------| / ' ' \ * * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~* * Scryn, Swordbearer, Rennard, Tricops, and Gorog. | * * ------------------------------------------------------------------------ * * 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 Staerfeldt, Tom Madsen, and Katja Nyboe. * * ------------------------------------------------------------------------ * * Battle & death module * ****************************************************************************/ #include "stdafx.h" #include "smaug.h" #include "SysData.h" #include "skill.h" #include "mobiles.h" #include "objects.h" #include "rooms.h" #include "deity.h" #include "area.h" #include "races.h" #include "class.h" #include "Exits.h" #include "SmaugWizDoc.h" #include "descriptor.h" #include "character.h" #include "smaugx.h" extern CCharacter * gch_prev; // Local functions. void dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt, CObjData* pObj = NULL); void death_cry (CCharacter *ch); void group_gain (CCharacter *ch, CCharacter *victim); int xp_compute (CCharacter *gch, CCharacter *victim); int align_compute (CCharacter *gch, CCharacter *victim); ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt); int obj_hitroll (CObjData *obj); BOOL dual_flip = FALSE; BOOL HandleAttackFlags (CCharacter* ch, CCharacter* victim); BOOL HandleDefenseFlags (CCharacter* ch, CCharacter* victim); // Check to see if weapon is poisoned. BOOL is_wielding_poisoned (CCharacter *ch) { CObjData *obj; if ((obj = get_eq_char (ch, WEAR_WIELD)) && obj->IsPoisoned ()) return TRUE; return FALSE; } // hunting, hating and fearing code -Thoric BOOL is_hunting (CCharacter *ch, CCharacter *victim) { if (!ch->hunting || ch->hunting->who != victim) return FALSE; return TRUE; } BOOL is_hating (CCharacter *ch, CCharacter *victim) { if (!ch->hating || ch->hating->who != victim) return FALSE; return TRUE; } BOOL is_fearing (CCharacter *ch, CCharacter *victim) { if (!ch->fearing || ch->fearing->who != victim) return FALSE; return TRUE; } void stop_hunting (CCharacter *ch) { if (ch->hunting) { STRFREE (ch->hunting->name); delete ch->hunting; ch->hunting = NULL; } } void stop_hating (CCharacter *ch) { if (ch->hating) { STRFREE (ch->hating->name); delete ch->hating; ch->hating = NULL; } } void stop_fearing (CCharacter *ch) { if (ch->fearing) { STRFREE (ch->fearing->name); delete ch->fearing; ch->fearing = NULL; } } void start_hunting (CCharacter *ch, CCharacter *victim) { if (ch->hunting) stop_hunting (ch); ch->hunting = new HHF_DATA; ch->hunting->name = QUICKLINK (victim->GetName ()); ch->hunting->who = victim; } void start_hating (CCharacter *ch, CCharacter *victim) { if (ch->hating) stop_hating (ch); ch->hating = new HHF_DATA; ch->hating->name = QUICKLINK (victim->GetName ()); ch->hating->who = victim; } void start_fearing (CCharacter *ch, CCharacter *victim) { if (ch->fearing) stop_fearing (ch); ch->fearing = new HHF_DATA; ch->fearing->name = QUICKLINK (victim->GetName ()); ch->fearing->who = victim; } // Get the current armor class for a vampire based on time of day short VAMP_AC (CCharacter * ch) { if (ch->IsVampire () && ch->IsOutside ()) { switch (weather_info.sunlight) { case SUN_DARK: return -10; case SUN_RISE: return 5; case SUN_LIGHT: return 10; case SUN_SET: return 2; default: return 0; } } return 0; } int max_fight (CCharacter *ch) { return 8; } // Control the fights going on. // Called periodically by update_handler. // Many hours spent fixing bugs in here by Thoric, as noted by residual // debugging checks. If you never get any of these error messages again // in your logs... then you can comment out some of the checks without // worry. void violence_update (void) { char buf [MAX_STRING_LENGTH]; CCharacter *ch; CCharacter *lst_ch; CCharacter *victim; CCharacter *rch, *rch_next; CTimerData *timer, *timer_next; ch_ret retcode; CSkill *skill; try { lst_ch = NULL; for (ch = last_char; ch; lst_ch = ch, ch = gch_prev) { set_cur_char (ch); gch_prev = ch->GetPrev (); if (gch_prev && gch_prev->GetNext () != ch) { bug ("FATAL: violence_update: %s->GetPrev ()->GetNext () " "doesn't point to ch.", ch->GetName ()); bug ("Short-cutting here"); ch->SetPrev (NULL); gch_prev = NULL; sprintf (buf, "%s says, 'Prepare for the worst!'", SysData.GetSupremeEntity ()); do_shout (ch, buf); } // See if we got a pointer to someone who recently died... // if so, either the pointer is bad... or it's a player who // "died", and is back at the healer... // Since he/she's in the char_list, it's likely to be the later... // and should not already be in another fight already if (char_died (ch)) continue; // See if we got a pointer to some bad looking data... if (! ch->GetInRoom () || ! ch->GetName ()) { gpDoc->LogString ("violence_update: bad ch record! " "(Shortcutting.)", LOG_BUG); sprintf (buf, "ch: %d ch->GetInRoom (): %d ch->GetPrev (): " "%d ch->GetNext (): %d", (int) ch, (int) ch->GetInRoom (), (int) ch->GetPrev (), (int) ch->GetNext ()); gpDoc->LogString (buf, LOG_BUG); gpDoc->LogString (lastplayercmd, LOG_BUG); if (lst_ch) sprintf (buf, "lst_ch: %d lst_ch->GetPrev (): " "%d lst_ch->GetNext (): %d", (int) lst_ch, (int) lst_ch->GetPrev (), (int) lst_ch->GetNext ()); else strcpy (buf, "lst_ch: NULL"); gpDoc->LogString (buf, LOG_BUG); gch_prev = NULL; continue; } // Experience gained during battle deceases as battle drags on if (ch->GetFightData ()) if ((++ch->GetFightData ()->duration % 24) == 0) ch->GetFightData ()->xp = ((ch->GetFightData ()->xp * 9) / 10); for (timer = ch->first_timer; timer; timer = timer_next) { timer_next = timer->GetNext (); if (timer->CountDown () <= 0) { if (timer->GetType () == TIMER_ASUPRESSED) { if (timer->GetValue () == -1) { timer->SetCount (1000); continue; } } if (timer->GetType () == TIMER_DO_FUN) { int tempsub; tempsub = ch->GetSubstate (); ch->SetSubstate (timer->GetValue ()); timer->DoFun (ch, ""); if (char_died (ch)) break; ch->SetSubstate (tempsub); } extract_timer (ch, timer); } } if (char_died (ch)) continue; // We need spells that have shorter durations than an hour. // So a melee round sounds good to me... -Thoric POSITION pos = ch->m_AffectList.GetHeadPosition (); while (pos) { CAffectData *paf = ch->m_AffectList.GetNext (pos); if (paf->duration > 0) { --paf->duration; continue; } if (paf->duration == 0) { CAffectData *pNext = NULL; if (pos) pNext = ch->m_AffectList.GetAt (pos); if (! pNext || pNext->type != paf->type || pNext->duration > 0) { skill = SkillTable.GetValidSkill (paf->type); if (paf->type > 0 && skill && skill->ValidOffMsg ()) { set_char_color (AT_WEAROFF, ch); ch->SendText (skill->GetOffMsg ()); ch->SendText ("\n\r"); } } if (paf->type == gsn_possess) { ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal; ch->GetDesc ()->m_pOriginal = NULL; ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ()); ch->GetDesc ()->m_pCharacter->switched = NULL; ch->SetDesc (NULL); } affect_remove (ch, paf); } } if (char_died (ch)) continue; // check for exits moving players around // if ((retcode = pullcheck (ch, pulse)) == rCHAR_DIED // || char_died (ch)) // continue; if ((victim = ch->GetFightWho ()) == NULL || ch->IsParalysed ()) continue; // Let the battle begin! retcode = rNONE; if (ch->GetInRoom ()->IsSafe ()) { sprintf (buf, "violence_update: %s fighting %s in a SAFE room.", ch->GetName (), victim->GetName ()); gpDoc->LogString (buf); stop_fighting (ch, TRUE); } else if (ch->IsAwake () && ch->GetInRoom () == victim->GetInRoom ()) retcode = multi_hit (ch, victim, TYPE_UNDEFINED); else stop_fighting (ch, FALSE); if (char_died (ch)) continue; if (retcode == rCHAR_DIED || (victim = ch->GetFightWho ()) == NULL) continue; // Mob triggers rprog_rfight_trigger (ch); if (char_died (ch)) continue; mprog_hitprcnt_trigger (ch, victim); if (char_died (ch)) continue; mprog_fight_trigger (ch, victim); if (char_died (ch)) continue; if (ch->IsNpc ()) { // NPC special attack flags -Thoric if (ch->HasAttacks ()) if (! HandleAttackFlags (ch, victim)) then continue; // NPC special defense flags -Thoric if (ch->HasDefenses ()) if (! HandleDefenseFlags (ch, victim)) then continue; } // Fun for the whole family! for (rch = ch->GetInRoom ()->first_person; rch; rch = rch_next) { rch_next = rch->GetNextInRoom (); // Group Fighting Styles Support: // If ch is tanking // If rch is using a more aggressive style than ch // Then rch is the new tank -h if ((! ch->IsNpc () && ! rch->IsNpc ()) && rch != ch && rch->GetFightWho () == ch && ! rch->GetFightWho ()->IsAutonomous () && rch->GetStyle () < ch->GetStyle ()) rch->GetFightWho ()->SetFightWho (rch); if (rch->IsAwake () && !rch->GetFightData ()) { // PC's auto-assist others in their group. if (! ch->IsNpc () || ch->IsCharmed ()) { if ((!rch->IsNpc () || rch->IsCharmed ()) && is_same_group (ch, rch)) multi_hit (rch, victim, TYPE_UNDEFINED); continue; } // NPC's assist NPC's of same type or 12.5% chance regardless. if (rch->IsNpc () && !rch->IsCharmed () && ! rch->IsNoAssist ()) { if (char_died (ch)) break; if (rch->GetMobIndex () == ch->GetMobIndex () || number_bits (3) == 0) { CCharacter *vch; CCharacter *target; int number; target = NULL; number = 0; vch = ch->GetInRoom ()->first_person; for ( ; vch; vch = vch->GetNext ()) { if (can_see (rch, vch) && is_same_group (vch, victim) && number_range (0, number) == 0) { if (vch->mount && vch->mount == rch) target = NULL; else { target = vch; number++; } } } if (target) multi_hit (rch, target, TYPE_UNDEFINED); } } } } } } catch (...) { CSwException ex; ex.Printf ("violence_update Exception."); throw ex; } } BOOL HandleAttackFlags (CCharacter* ch, CCharacter* victim) { if (30 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE; int attacktype = -1; int cnt = 0; int retcode; for (;;) { if (cnt++ > 10) { attacktype = -1; break; } attacktype = number_range (7, MAX_ATTACK_TYPE-1); if (ch->CanAttack (attacktype)) break; } switch (attacktype) { case ATCK_BASH: do_bash (ch, ""); retcode = global_retcode; break; case ATCK_STUN: do_stun (ch, ""); retcode = global_retcode; break; case ATCK_GOUGE: do_gouge (ch, ""); retcode = global_retcode; break; case ATCK_FEED: do_gouge (ch, ""); retcode = global_retcode; break; case ATCK_DRAIN: retcode = spell_energy_drain (SkillTable.Lookup ("energy drain"), ch->GetLevel (), ch, victim); break; case ATCK_FIREBREATH: retcode = spell_fire_breath (SkillTable.Lookup ("fire breath"), ch->GetLevel (), ch, victim); break; case ATCK_FROSTBREATH: retcode = spell_frost_breath (SkillTable.Lookup ("frost breath"), ch->GetLevel (), ch, victim); break; case ATCK_ACIDBREATH: retcode = spell_acid_breath (SkillTable.Lookup ("acid breath"), ch->GetLevel (), ch, victim); break; case ATCK_LIGHTNBREATH: retcode = spell_lightning_breath (SkillTable.Lookup ("lightning breath"), ch->GetLevel (), ch, victim); break; case ATCK_GASBREATH: retcode = spell_gas_breath (SkillTable.Lookup ("gas breath"), ch->GetLevel (), ch, victim); break; case ATCK_SPIRALBLAST: retcode = spell_spiral_blast (SkillTable.Lookup ("spiral blast"), ch->GetLevel (), ch, victim); break; case ATCK_POISON: retcode = spell_poison (gsn_poison, ch->GetLevel (), ch, victim); break; case ATCK_NASTYPOISON: /* retcode = spell_nasty_poison (SkillTable.Lookup ("nasty poison"), ch->GetLevel (), ch, victim); */ break; case ATCK_GAZE: /* retcode = spell_gaze (SkillTable.Lookup ("gaze"), ch->GetLevel (), ch, victim); */ break; case ATCK_BLINDNESS: retcode = spell_blindness (gsn_blindness, ch->GetLevel (), ch, victim); break; case ATCK_CAUSESERIOUS: retcode = spell_cause_serious (SkillTable.Lookup ("cause serious"), ch->GetLevel (), ch, victim); break; case ATCK_EARTHQUAKE: retcode = spell_earthquake (SkillTable.Lookup ("earthquake"), ch->GetLevel (), ch, victim); break; case ATCK_CAUSECRITICAL: retcode = spell_cause_critical (SkillTable.Lookup ("cause critical"), ch->GetLevel (), ch, victim); break; case ATCK_CURSE: retcode = spell_curse (SkillTable.Lookup ("curse"), ch->GetLevel (), ch, victim); break; case ATCK_FLAMESTRIKE: retcode = spell_flamestrike (SkillTable.Lookup ("flamestrike"), ch->GetLevel (), ch, victim); break; case ATCK_HARM: retcode = spell_harm (SkillTable.Lookup ("harm"), ch->GetLevel (), ch, victim); break; case ATCK_FIREBALL: retcode = spell_fireball (SkillTable.Lookup ("fireball"), ch->GetLevel (), ch, victim); break; case ATCK_COLORSPRAY: retcode = spell_colour_spray (SkillTable.Lookup ("colour spray"), ch->GetLevel (), ch, victim); break; case ATCK_WEAKEN: retcode = spell_weaken (SkillTable.Lookup ("weaken"), ch->GetLevel (), ch, victim); break; } if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch))) return FALSE; return TRUE; } BOOL HandleDefenseFlags (CCharacter* ch, CCharacter* victim) { if (50 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE; int attacktype = -1; int cnt = 0; int retcode; for (;;) { if (cnt++ > 10) { attacktype = -1; break; } attacktype = number_range (2, MAX_DEFENSE_TYPE-1); if (ch->CanDefend (attacktype)) break; } switch (attacktype) { case DFND_CURELIGHT: act (AT_MAGIC, "$n mutters a few incantations...and looks " "a little better.", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("cure light"), ch->GetLevel (), ch, ch); break; case DFND_CURESERIOUS: act (AT_MAGIC, "$n mutters a few incantations...and looks " "a bit better.", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("cure serious"), ch->GetLevel (), ch, ch); break; case DFND_CURECRITICAL: act (AT_MAGIC, "$n mutters a few incantations...and looks " "a bit healthier.", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("cure critical"), ch->GetLevel (), ch, ch); break; case DFND_DISPELMAGIC: act (AT_MAGIC, "$n mutters a few incantations...and " "waves $s arms about.", ch, NULL, NULL, TO_ROOM); retcode = spell_dispel_magic (SkillTable.Lookup ( "dispel magic"), ch->GetLevel (), ch, victim); break; case DFND_DISPELEVIL: act (AT_MAGIC, "$n mutters a few incantations...and " "waves $s arms about.", ch, NULL, NULL, TO_ROOM); retcode = spell_dispel_evil (SkillTable.Lookup ( "dispel evil"), ch->GetLevel (), ch, victim); break; case DFND_SHOCKSHIELD: if (! ch->HasShockShield ()) { act (AT_MAGIC, "$n utters a few incantations...", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("shockshield"), ch->GetLevel (), ch, ch); } else retcode = rNONE; break; case DFND_FIRESHIELD: if (! ch->HasFireShield ()) { act (AT_MAGIC, "$n utters a few incantations...", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("fireshield"), ch->GetLevel (), ch, ch); } else retcode = rNONE; break; case DFND_ICESHIELD: if (! ch->HasIceShield ()) { act (AT_MAGIC, "$n utters a few incantations...", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("iceshield"), ch->GetLevel (), ch, ch); } else retcode = rNONE; break; case DFND_TRUESIGHT: if (! ch->HasTrueSight ()) { act (AT_MAGIC, "$n utters a few incantations...", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("true"), ch->GetLevel (), ch, ch); } else retcode = rNONE; break; case DFND_SANCTUARY: if (! victim->HasSanctuary ()) { act (AT_MAGIC, "$n mutters a few incantations...", ch, NULL, NULL, TO_ROOM); retcode = spell_smaug (SkillTable.Lookup ("sanctuary"), ch->GetLevel (), ch, ch); } else retcode = rNONE; break; } if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch))) return FALSE; return TRUE; } // Do one group of attacks. ch_ret multi_hit (CCharacter *ch, CCharacter *victim, int dt) { int chance; int dual_bonus; ch_ret retcode; /* add timer if player is attacking another player */ if (!ch->IsNpc () && !victim->IsNpc ()) add_timer (ch, TIMER_RECENTFIGHT, 20, NULL, 0); if (!ch->IsNpc () && ch->IsNice () && !victim->IsNpc ()) return rNONE; if ((retcode = one_hit (ch, victim, dt)) != rNONE) return retcode; if (ch->GetFightWho () != victim || dt == gsn_backstab || dt == gsn_circle) return rNONE; // Very high chance of hitting compared to chance of going berserk // 40% or higher is always hit.. don't learn anything here though. // -- Altrag chance = ch->IsNpc () ? 100 : (ch->GetPcData ()->learned[gsn_berserk]*5/2); if (ch->IsBeserk () && number_percent () < chance) if ((retcode = one_hit (ch, victim, dt)) != rNONE || ch->GetFightWho () != victim) return retcode; if (get_eq_char (ch, WEAR_DUAL_WIELD)) { dual_bonus = ch->IsNpc () ? (ch->GetLevel () / 10) : (ch->GetPcData ()->learned[gsn_dual_wield] / 10); chance = ch->IsNpc () ? ch->GetLevel () : ch->GetPcData ()->learned[gsn_dual_wield]; if (number_percent () < chance) { learn_from_success (ch, gsn_dual_wield); retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } else learn_from_failure (ch, gsn_dual_wield); } else dual_bonus = 0; if (ch->GetMove () < 10) dual_bonus = -20; /* * NPC predetermined number of attacks -Thoric */ if (ch->IsNpc () && ch->numattacks > 0) { for (chance = 0; chance <= ch->numattacks; chance++) { retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } return retcode; } chance = ch->IsNpc () ? ch->GetLevel () : (int) ((ch->GetPcData ()->learned[gsn_second_attack]+dual_bonus)/1.5); if (number_percent () < chance) { learn_from_success (ch, gsn_second_attack); retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } else learn_from_failure (ch, gsn_second_attack); chance = ch->IsNpc () ? ch->GetLevel () : (int) ((ch->GetPcData ()->learned[gsn_third_attack]+ (dual_bonus*1.5))/2); if (number_percent () < chance) { learn_from_success (ch, gsn_third_attack); retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } else learn_from_failure (ch, gsn_third_attack); chance = ch->IsNpc () ? ch->GetLevel () : (int) ((ch->GetPcData ()->learned[gsn_fourth_attack]+ (dual_bonus*2))/3); if (number_percent () < chance) { learn_from_success (ch, gsn_fourth_attack); retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } else learn_from_failure (ch, gsn_fourth_attack); chance = ch->IsNpc () ? ch->GetLevel () : (int) ((ch->GetPcData ()->learned[gsn_fifth_attack]+ (dual_bonus*3))/4); if (number_percent () < chance) { learn_from_success (ch, gsn_fifth_attack); retcode = one_hit (ch, victim, dt); if (retcode != rNONE || ch->GetFightWho () != victim) return retcode; } else learn_from_failure (ch, gsn_fifth_attack); retcode = rNONE; chance = ch->IsNpc () ? (int) (ch->GetLevel () / 2) : 0; if (number_percent () < chance) retcode = one_hit (ch, victim, dt); if (retcode == rNONE) { int move; if (! ch->IsFlying () && ! ch->IsFloating ()) move = encumbrance (ch, movement_loss[UMIN (SECT_MAX-1, ch->GetInRoom ()->sector_type)]); else move = encumbrance (ch, 1); if (ch->GetMove ()) ch->SetMove (UMAX (0, ch->GetMove () - move)); } return retcode; } /* * Weapon types, haus */ int weapon_prof_bonus_check (CCharacter *ch, CObjData *wield, int *gsn_ptr) { int bonus; bonus = 0; *gsn_ptr = -1; if (!ch->IsNpc () && ch->GetLevel () > 5 && wield) { switch (wield->value[3]) { default: *gsn_ptr = -1; break; case 0: case 12: case 10: case 6: *gsn_ptr = gsn_pugilism; break; case 3: case 1: *gsn_ptr = gsn_long_blades; break; case 11: case 2: *gsn_ptr = gsn_short_blades; break; case 4: *gsn_ptr = gsn_flexible_arms; break; case 5: *gsn_ptr = gsn_talonous_arms; break; case 7: case 8: *gsn_ptr = gsn_bludgeons; break; } if (*gsn_ptr != -1) bonus = (int) ((ch->GetPcData ()->learned[*gsn_ptr] -50)/10); /* Reduce weapon bonuses for misaligned clannies. if (ch->IsClanned ()) { bonus = bonus / (1 + abs (ch->alignment - ch->GetPcData ()->clan->alignment) / 1000); }*/ if (ch->IsDevoted ()) { bonus = bonus - abs (ch->GetPcData ()->favor) / -100 ; } } return bonus; } // Calculate the tohit bonus on the object and return RIS values. // -- Altrag int obj_hitroll (CObjData *obj) { int tohit = 0; CAffectData *paf; CAffectList &IList = obj->pIndexData->AffList; POSITION apos = IList.GetHeadPosition (); while (apos) { paf = IList.GetNext (apos); if (paf->location == APPLY_HITROLL) tohit += paf->modifier; } CAffectList &AList = obj->AffList; apos = AList.GetHeadPosition (); while (apos) { paf = AList.GetNext (apos); if (paf->location == APPLY_HITROLL) tohit += paf->modifier; } return tohit; } /* * Offensive shield level modifier */ short off_shld_lvl (CCharacter *ch, CCharacter *victim) { short lvl; if (!ch->IsNpc ()) /* players get much less effect */ { lvl = UMAX (1, (ch->GetLevel () - 10) / 2); if (number_percent () + (victim->GetLevel () - lvl) < 35) return lvl; else return 0; } else { lvl = ch->GetLevel () / 2; if (number_percent () + (victim->GetLevel () - lvl) < 70) return lvl; else return 0; } } // Hit one guy once. ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt) { CObjData *wield; int victim_ac; int thac0; int thac0_00; int thac0_32; int plusris; int dam; int diceroll; int attacktype, cnt; int prof_bonus; int prof_gsn; ch_ret retcode; // Can't beat a dead char! // Guard against weird room-leavings. if (victim->GetPosition () == POS_DEAD || ch->GetInRoom () != victim->GetInRoom ()) return rVICT_DIED; // Figure out the weapon doing the damage -Thoric // Dual wield support -- switch weapons each attack if ((wield = get_eq_char (ch, WEAR_DUAL_WIELD)) != NULL) { if (dual_flip == FALSE) { dual_flip = TRUE; wield = get_eq_char (ch, WEAR_WIELD); } else dual_flip = FALSE; } else wield = get_eq_char (ch, WEAR_WIELD); prof_bonus = weapon_prof_bonus_check (ch, wield, &prof_gsn); // make sure fight is already started if (ch->GetFightData () && dt == TYPE_UNDEFINED && ch->IsNpc () && ch->HasAttacks ()) { cnt = 0; for (;;) { attacktype = number_range (0, 6); if (ch->CanAttack (attacktype)) break; if (cnt++ > 16) { attacktype = -1; break; } } if (attacktype == ATCK_BACKSTAB) attacktype = -1; if (wield && number_percent () > 25) attacktype = -1; if (! wield && number_percent () > 50) attacktype = -1; switch (attacktype) { default: break; case ATCK_BITE: do_bite (ch, ""); retcode = global_retcode; break; case ATCK_CLAWS: do_claw (ch, ""); retcode = global_retcode; break; case ATCK_TAIL: do_tail (ch, ""); retcode = global_retcode; break; case ATCK_STING: do_sting (ch, ""); retcode = global_retcode; break; case ATCK_PUNCH: do_punch (ch, ""); retcode = global_retcode; break; case ATCK_KICK: do_kick (ch, ""); retcode = global_retcode; break; case ATCK_TRIP: attacktype = 0; break; } if (attacktype) return retcode; } if (dt == TYPE_UNDEFINED) { dt = TYPE_HIT; if (wield && wield->item_type == ITEM_WEAPON) dt += wield->value [3]; } // Calculate to-hit-armor-class-0 versus armor. if (ch->IsNpc ()) { thac0_00 = ch->mobthac0; thac0_32 = 0; } else { thac0_00 = ClassTable.GetThac0 (ch->GetClass ()); thac0_32 = ClassTable.GetThac32 (ch->GetClass ()); } thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32) - GET_HITROLL (ch); victim_ac = UMAX (-19, (int) (victim->GetAc () / 10)); // if you can't see what's coming... if (wield && ! can_see_obj (victim, *wield)) victim_ac += 1; if (! can_see (ch, victim)) victim_ac -= 4; // "learning" between combatients. Takes the intelligence difference, // and multiplies by the times killed to make up a learning bonus // given to whoever is more intelligent -Thoric if (ch->GetFightData () && ch->GetFightData ()->who == victim) { short times = ch->GetFightData ()->timeskilled; if (times) { short intdiff = ch->GetIntelligence () - victim->GetIntelligence (); if (intdiff != 0) victim_ac += (intdiff*times)/10; } } // Weapon proficiency bonus victim_ac += prof_bonus; // The moment of excitement! while ((diceroll = number_bits (5)) >= 20); if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) { // Miss. if (prof_gsn != -1) learn_from_failure (ch, prof_gsn); damage (ch, victim, 0, dt); return rNONE; } // Hit. // Calc damage. if (!wield) // bare hand dice formula fixed by Thoric dam = number_range (ch->barenumdie, ch->baresizedie * ch->barenumdie); else dam = number_range (wield->value [1], wield->value [2]); // Bonuses. dam += GET_DAMROLL (ch); if (prof_bonus) dam += prof_bonus / 4; // Calculate Damage Modifiers from Victim's Fighting Style switch (victim->GetPosition ()) { case POS_BERSERK: dam = (int) (1.2 * dam); break; case POS_AGGRESSIVE: dam = (int) (1.1 * dam); break; case POS_DEFENSIVE: dam = (int) (.85 * dam); break; case POS_EVASIVE: dam = (int) (.8 * dam); } // Calculate Damage Modifiers from Attacker's Fighting Style switch (ch->GetPosition ()) { case POS_BERSERK: dam = (int) (1.2 * dam); break; case POS_AGGRESSIVE: dam = (int) (1.1 * dam); break; case POS_DEFENSIVE: dam = (int) (.85 * dam); break; case POS_EVASIVE: dam = (int) (.8 * dam); } if (!ch->IsNpc () && ch->GetPcData ()->learned[gsn_enhanced_damage] > 0) { dam += (int) (dam * ch->GetPcData ()->learned [gsn_enhanced_damage] / 120); learn_from_success (ch, gsn_enhanced_damage); } if (! victim->IsAwake ()) dam *= 2; if (dt == gsn_backstab) dam *= (2 + URANGE (2, ch->GetLevel () - (victim->GetLevel ()/4), 30) / 8); if (dt == gsn_circle) dam *= (2 + URANGE (2, ch->GetLevel () - (victim->GetLevel ()/4), 30) / 16); if (dam <= 0) dam = 1; plusris = 0; if (wield) { if (wield->IsMagic ()) dam = ris_damage (victim, dam, RIS_MAGIC); else dam = ris_damage (victim, dam, RIS_NONMAGIC); // Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll -Thoric plusris = obj_hitroll (wield); } else dam = ris_damage (victim, dam, RIS_NONMAGIC); // check for RIS_PLUSx - Thoric if (dam) { int x, res, imm, sus, mod; if (plusris) plusris = RIS_PLUS1 << UMIN (plusris, 7); // initialize values to handle a zero plusris imm = res = -1; sus = 1; // find high ris for (x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1) { if (victim->IsImmune (x)) imm = x; if (victim->CanResist (x)) res = x; if (victim->IsSusceptible (x)) sus = x; } mod = 10; if (imm >= plusris) mod -= 10; if (res >= plusris) mod -= 2; if (sus <= plusris) mod += 2; // check if immune if (mod <= 0) dam = -1; if (mod != 10) dam = (dam * mod) / 10; } if (prof_gsn != -1) { if (dam > 0) learn_from_success (ch, prof_gsn); else learn_from_failure (ch, prof_gsn); } // immune to damage if (dam == -1) { if (dt >= 0 && dt < SkillTable.GetCount ()) { CSkill *skill = SkillTable.GetSkill (dt); BOOL found = FALSE; if (skill->ValidImmuneCharMsg ()) { act (AT_HIT, skill->GetImmuneCharMsg (), ch, NULL, victim, TO_CHAR); found = TRUE; } if (skill->ValidImmuneVictMsg ()) { act (AT_HITME, skill->GetImmuneVictMsg (), ch, NULL, victim, TO_VICT); found = TRUE; } if (skill->ValidImmuneRoomMsg ()) { act (AT_ACTION, skill->GetImmuneRoomMsg (), ch, NULL, victim, TO_NOTVICT); found = TRUE; } if (found) return rNONE; } dam = 0; } if ((retcode = damage (ch, victim, dam, dt)) != rNONE) return retcode; if (char_died (ch)) return rCHAR_DIED; if (char_died (victim)) return rVICT_DIED; retcode = rNONE; if (dam == 0) return retcode; // weapon spells -Thoric // Each successful hit casts a spell if (wield && ! victim->IsImmuneMagic () && ! victim->GetInRoom ()->IsNoMagic ()) { CAffectData *aff; CAffectList &IList = wield->pIndexData->AffList; POSITION apos = IList.GetHeadPosition (); while (apos) { aff = IList.GetNext (apos); if (aff->location == APPLY_WEAPONSPELL && SkillTable.IsValid (aff->modifier) && SkillTable.GetSpellFunction (aff->modifier)) retcode = (*SkillTable.GetSpellFunction (aff->modifier)) (aff->modifier, (wield->level+3)/3, ch, victim); } if (retcode != rNONE || char_died (ch) || char_died (victim)) return retcode; CAffectList &AList = wield->AffList; apos = AList.GetHeadPosition (); while (apos) { aff = AList.GetNext (apos); if (aff->location == APPLY_WEAPONSPELL && SkillTable.IsValid (aff->modifier) && SkillTable.GetSpellFunction (aff->modifier)) retcode = (*SkillTable.GetSpellFunction ( aff->modifier)) (aff->modifier, (wield->level+3)/3, ch, victim); } if (retcode != rNONE || char_died (ch) || char_died (victim)) return retcode; } // magic shields that retaliate -Thoric if (victim->IsAffected (AFF_FIRESHIELD) && !ch->IsAffected (AFF_FIRESHIELD)) retcode = spell_fireball (gsn_fireball, off_shld_lvl (victim, ch), victim, ch); if (retcode != rNONE || char_died (ch) || char_died (victim)) return retcode; if (victim->IsAffected (AFF_ICESHIELD) && ! ch->IsAffected (AFF_ICESHIELD)) retcode = spell_chill_touch (gsn_chill_touch, off_shld_lvl (victim, ch), victim, ch); if (retcode != rNONE || char_died (ch) || char_died (victim)) return retcode; if (victim->IsAffected (AFF_SHOCKSHIELD) && ! ch->IsAffected (AFF_SHOCKSHIELD)) retcode = spell_lightning_bolt (gsn_lightning_bolt, off_shld_lvl (victim, ch), victim, ch); if (retcode != rNONE || char_died (ch) || char_died (victim)) return retcode; return retcode; } // Hit one guy with a projectile. // Handles use of missile weapons (wield = missile weapon) // or thrown items/weapons ch_ret projectile_hit (CCharacter *ch, CCharacter *victim, CObjData *wield, CObjData *projectile, short dist) { int victim_ac; int thac0; int thac0_00; int thac0_32; int plusris; int dam; int diceroll; int prof_bonus; int prof_gsn = -1; int proj_bonus; int dt; ch_ret retcode; if (! projectile) return rNONE; if (projectile->item_type == ITEM_PROJECTILE || projectile->item_type == ITEM_WEAPON) { dt = TYPE_HIT + projectile->value [3]; proj_bonus = number_range (projectile->value [1], projectile->value [2]); } else { dt = TYPE_UNDEFINED; proj_bonus = number_range (1, URANGE (2, get_obj_weight (projectile), 100)); } // Can't beat a dead char! if (victim->IsDead () || char_died (victim)) { extract_obj (projectile); return rVICT_DIED; } if (wield) prof_bonus = weapon_prof_bonus_check (ch, wield, &prof_gsn); else prof_bonus = 0; if (dt == TYPE_UNDEFINED) { dt = TYPE_HIT; if (wield && wield->item_type == ITEM_MISSILE_WEAPON) dt += wield->value [3]; } // Calculate to-hit-armor-class-0 versus armor. if (ch->IsNpc ()) { thac0_00 = ch->mobthac0; thac0_32 = 0; } else { thac0_00 = ClassTable.GetThac0 (ch->GetClass ()); thac0_32 = ClassTable.GetThac32 (ch->GetClass ()); } thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32) - GET_HITROLL (ch) + (dist*2); victim_ac = UMAX (-19, (victim->GetAc () / 10)); // if you can't see what's coming... if (! can_see_obj (victim, *projectile)) ++victim_ac; if (! can_see (ch, victim)) victim_ac -= 4; // Weapon proficiency bonus victim_ac += prof_bonus; // The moment of excitement! while ((diceroll = number_bits (5)) >= 20) ; if (diceroll == 0 || (diceroll != 19 && diceroll < thac0 - victim_ac)) { // Miss. if (prof_gsn != -1) learn_from_failure (ch, prof_gsn); // Do something with the projectile if (number_percent () < 50) extract_obj (projectile); else { if (projectile->in_obj) obj_from_obj (projectile); if (projectile->carried_by) obj_from_char (projectile); obj_to_room (projectile, victim->GetInRoom ()); } damage (ch, victim, 0, dt); return rNONE; } // Hit, Calc damage. if (! wield) // dice formula fixed by Thoric dam = proj_bonus; else dam = number_range (wield->value [1], wield->value [2]) + (proj_bonus / 10); // Bonuses. dam += GET_DAMROLL (ch); if (prof_bonus) dam += prof_bonus / 4; // Calculate Damage Modifiers from Victim's Fighting Style if (victim->GetPosition () == POS_BERSERK) dam = (int) (dam * 1.2); else if (victim->GetPosition () == POS_AGGRESSIVE) dam = (int) (dam * 1.1); else if (victim->GetPosition () == POS_DEFENSIVE) dam = (int) (dam * .85); else if (victim->GetPosition () == POS_EVASIVE) dam = (int) (dam * .8); if (! ch->IsNpc () && ch->GetPcData ()->learned [gsn_enhanced_damage] > 0) { dam += ch->GetLearnedPercent (gsn_enhanced_damage) / 120; learn_from_success (ch, gsn_enhanced_damage); } if (! victim->IsAwake ()) dam *= 2; if (dam <= 0) dam = 1; plusris = 0; if (projectile->IsMagic ()) dam = ris_damage (victim, dam, RIS_MAGIC); else dam = ris_damage (victim, dam, RIS_NONMAGIC); // Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll -Thoric if (wield) plusris = obj_hitroll (wield); // check for RIS_PLUSx -Thoric if (dam) { if (plusris) plusris = RIS_PLUS1 << UMIN (plusris, 7); // initialize values to handle a zero plusris int imm = -1; int res = -1; int sus = 1; // find high ris for (int x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1) { if (victim->IsImmune (x)) imm = x; if (victim->CanResist (x)) res = x; if (victim->IsSusceptible (x)) sus = x; } int mod = 10; if (imm >= plusris) mod -= 10; if (res >= plusris) mod -= 2; if (sus <= plusris) mod += 2; // check if immune if (mod <= 0) dam = -1; if (mod != 10) dam = (dam * mod) / 10; } if (prof_gsn != -1) { if (dam > 0) learn_from_success (ch, prof_gsn); else learn_from_failure (ch, prof_gsn); } // immune to damage if (dam == -1) { CSkill *pSkill = SkillTable.GetSkill (dt); if (pSkill) { BOOL bFound = FALSE; if (pSkill->ValidImmuneCharMsg ()) { act (AT_HIT, pSkill->GetImmuneCharMsg (), ch, NULL, victim, TO_CHAR); bFound = TRUE; } if (pSkill->ValidImmuneVictMsg ()) { act (AT_HITME, pSkill->GetImmuneVictMsg (), ch, NULL, victim, TO_VICT); bFound = TRUE; } if (pSkill->ValidImmuneRoomMsg ()) { act (AT_ACTION, pSkill->GetImmuneRoomMsg (), ch, NULL, victim, TO_NOTVICT); bFound = TRUE; } if (bFound) { if (number_percent() < 50) extract_obj (projectile); else { if (projectile->in_obj) obj_from_obj (projectile); if (projectile->carried_by) obj_from_char (projectile); obj_to_room (projectile, victim->GetInRoom ()); } return rNONE; } } dam = 0; } if ((retcode = damage (ch, victim, dam, dt)) != rNONE) { extract_obj (projectile); return retcode; } if (char_died (ch)) { extract_obj (projectile); return rCHAR_DIED; } if (char_died (victim)) { extract_obj (projectile); return rVICT_DIED; } retcode = rNONE; if (dam == 0) { if (number_percent() < 50) extract_obj (projectile); else { if (projectile->in_obj) obj_from_obj (projectile); if (projectile->carried_by) obj_from_char (projectile); obj_to_room (projectile, victim->GetInRoom ()); } return retcode; } // weapon spells -Thoric if (wield && ! victim->IsImmuneMagic () && ! victim->GetInRoom ()->IsNoMagic ()) { CAffectList &IList = wield->pIndexData->AffList; POSITION pos = IList.GetHeadPosition (); while (pos) { CAffectData &Af = *IList.GetNext (pos); if (Af.location == APPLY_WEAPONSPELL && SkillTable.IsValid (Af.modifier) && SkillTable.GetSpellFunction (Af.modifier)) retcode = (*SkillTable.GetSpellFunction (Af.modifier)) (Af.modifier, (wield->level+3)/3, ch, victim); } if (retcode != rNONE || char_died (ch) || char_died (victim)) { extract_obj (projectile); return retcode; } CAffectList &AList = wield->AffList; pos = AList.GetHeadPosition (); while (pos) { CAffectData &Af = *AList.GetNext (pos); if (Af.location == APPLY_WEAPONSPELL && SkillTable.IsValid (Af.modifier) && SkillTable.GetSpellFunction (Af.modifier)) retcode = (*SkillTable.GetSpellFunction (Af.modifier)) (Af.modifier, (wield->level+3)/3, ch, victim); } if (retcode != rNONE || char_died (ch) || char_died (victim)) { extract_obj (projectile); return retcode; } } extract_obj (projectile); return retcode; } // Calculate damage based on resistances, immunities and suceptibilities // -Thoric short ris_damage (CCharacter *ch, short dam, int ris) { short modifier; modifier = 10; if (ch->IsImmune (ris)) modifier -= 10; if (ch->CanResist (ris)) modifier -= 2; if (ch->IsSusceptible (ris)) modifier += 2; if (modifier <= 0) return -1; if (modifier == 10) return dam; return (dam * modifier) / 10; } // Inflict damage from a hit. ch_ret damage (CCharacter *ch, CCharacter *victim, int dam, int dt) { char buf1 [MAX_STRING_LENGTH]; short dameq; short maxdam; BOOL npcvict; BOOL loot; int xp_gain; CObjData *damobj; short dampmod; CCharacter *gch; int init_gold, new_gold, gold_diff; ch_ret retcode = rNONE; if (! ch) { bug ("Damage: null ch!"); return rERROR; } if (! victim) { bug ("Damage: null victim!"); return rVICT_DIED; } if (victim->GetPosition () == POS_DEAD) return rVICT_DIED; npcvict = victim->IsNpc (); // Check damage types for RIS -Thoric if (dam && dt != TYPE_UNDEFINED) { if (IS_FIRE (dt)) dam = ris_damage (victim, dam, RIS_FIRE); else if (IS_COLD (dt)) dam = ris_damage (victim, dam, RIS_COLD); else if (IS_ACID (dt)) dam = ris_damage (victim, dam, RIS_ACID); else if (IS_ELECTRICITY (dt)) dam = ris_damage (victim, dam, RIS_ELECTRICITY); else if (IS_ENERGY (dt)) dam = ris_damage (victim, dam, RIS_ENERGY); else if (IS_DRAIN (dt)) dam = ris_damage (victim, dam, RIS_DRAIN); else if (dt == gsn_poison || IS_POISON (dt)) dam = ris_damage (victim, dam, RIS_POISON); else if (dt == (TYPE_HIT+DAM_POUND) || dt == (TYPE_HIT+DAM_CRUSH) || dt == (TYPE_HIT+DAM_STONE) || dt == (TYPE_HIT+DAM_PEA)) dam = ris_damage (victim, dam, RIS_BLUNT); else if (dt == (TYPE_HIT+DAM_STAB) || dt == (TYPE_HIT+DAM_PIERCE) || dt == (TYPE_HIT+DAM_BITE) || dt == (TYPE_HIT+DAM_BOLT) || dt == (TYPE_HIT+DAM_DART) || dt == (TYPE_HIT+DAM_ARROW)) dam = ris_damage (victim, dam, RIS_PIERCE); else if (dt == (TYPE_HIT+DAM_SLICE) || dt == (TYPE_HIT+DAM_SLASH) || dt == (TYPE_HIT+DAM_WHIP) || dt == (TYPE_HIT+DAM_CLAW)) dam = ris_damage (victim, dam, RIS_SLASH); if (dam == -1) { if (dt >= 0 && dt < SkillTable.GetCount ()) { BOOL found = FALSE; CSkill *skill = SkillTable.GetSkill (dt); if (skill->ValidImmuneCharMsg ()) { act (AT_HIT, skill->GetImmuneCharMsg (), ch, NULL, victim, TO_CHAR); found = TRUE; } if (skill->ValidImmuneVictMsg ()) { act (AT_HITME, skill->GetImmuneVictMsg (), ch, NULL, victim, TO_VICT); found = TRUE; } if (skill->ValidImmuneRoomMsg ()) { act (AT_ACTION, skill->GetImmuneRoomMsg (), ch, NULL, victim, TO_NOTVICT); found = TRUE; } if (found) return rNONE; } dam = 0; } } // Precautionary step mainly to prevent people in Hell from finding // a way out. --Shaddai if (victim->GetInRoom ()->IsSafe ()) dam = 0; if (dam && npcvict && ch != victim) { if (! victim->IsAction (ACT_SENTINEL)) { if (victim->hunting) { if (victim->hunting->who != ch) { STRFREE (victim->hunting->name); victim->hunting->name = QUICKLINK (ch->GetName ()); victim->hunting->who = ch; } } else if (! victim->IsPacifist ()) // Gorog start_hunting (victim, ch); } if (victim->hating) { if (victim->hating->who != ch) { STRFREE (victim->hating->name); victim->hating->name = QUICKLINK (ch->GetName ()); victim->hating->who = ch; } } else if (! victim->IsPacifist ()) // Gorog start_hating (victim, ch); } // Stop up any residual loopholes. maxdam = (dt == gsn_backstab) ? ch->GetLevel () * 80 : ch->GetLevel () * 30; if (dam > maxdam) { bug ("Damage: %d more than %d points!", dam, maxdam); bug ("** %s (lvl %d) -> %s **", ch->GetName (), ch->GetLevel (), victim->GetName ()); dam = maxdam; } if (victim != ch) { // Certain attacks are forbidden. // Most other attacks are returned. if (is_safe (ch, victim)) return rNONE; check_attacker (ch, victim); if (victim->GetPosition () > POS_STUNNED) { if (! victim->GetFightData ()) set_fighting (victim, ch); if (victim->GetFightData ()) { if (victim->IsNpc ()) victim->SetPosition (POS_FIGHTING); else switch (victim->GetStyle ()) { case (STYLE_EVASIVE): victim->SetPosition (POS_EVASIVE); break; case (STYLE_DEFENSIVE): victim->SetPosition (POS_DEFENSIVE); break; case (STYLE_AGGRESSIVE): victim->SetPosition (POS_AGGRESSIVE); break; case (STYLE_BERSERK): victim->SetPosition (POS_BERSERK); break; default: victim->SetPosition (POS_FIGHTING); } } } if (victim->GetPosition () > POS_STUNNED) { if (! ch->GetFightData ()) set_fighting (ch, victim); // If victim is charmed, ch might attack victim's master. if (ch->IsNpc () && npcvict && victim->IsCharmed () && victim->GetMaster () && victim->GetMaster ()->GetInRoom () == ch->GetInRoom () && number_bits (3) == 0) { stop_fighting (ch, FALSE); retcode = multi_hit (ch, victim->GetMaster (), TYPE_UNDEFINED); return retcode; } } // More charm stuff. if (victim->GetMaster () == ch) stop_follower (victim); // Pkill stuff. If a deadly attacks another deadly or is attacked // by one, then ungroup any nondealies. Disabled untill I can figure // out the right way to do it. #ifdef XXXX /* short anopc = 0; // # of (non-pkill) pc in a (ch) short bnopc = 0; // # of (non-pkill) pc in b (victim) CCharacter *lch; // leader ch // count the # of non-pkill pc in a (not including == ch) for (gch = ch->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (ch, gch) && ! gch->IsNpc () && ! gch->IsPkiller () && (ch != gch)) anopc++; // count the # of non-pkill pc in b (not including == victim) for (gch = victim->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (victim, gch) && ! gch->IsNpc () && ! gch->IsPkiller () && (victim != gch)) bnopc++; // only consider disbanding if both groups have 1 (+) non-pk pc if ((bnopc > 0) && (anopc > 0)) { // look at group a through ch's leader first lch = ch->GetLeader () ? ch->GetLeader () : ch; if (lch != ch) { // stop following leader if it isn't pk if (! IS_NPC (lch) && ! IS_PKILL (lch)) stop_follower (ch); else { // disband non-pk members from lch's group if it is pk for (gch = ch->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) { if (is_same_group (lch, gch) && (lch != gch) && ! gch->IsNpc () && ! gch->IsPkiller ()) stop_follower (gch); } } } else for (gch = ch->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) { // ch is leader - disband non-pks from group if (is_same_group (ch, gch) && (ch != gch) && (! gch->IsPkiller () && ! gch->IsNpc ())) stop_follower (gch); } // time to look at the victims group through its leader lch = victim->GetLeader () ? victim->GetLeader () : victim; if (lch != victim) { // if leader isn't deadly, stop following lch if (! IS_PKILL (lch) && ! IS_NPC (lch)) stop_follower (victim); else { // lch is pk, disband non-pk's from group for (gch = victim->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) { if (is_same_group (lch, gch) && (lch != gch) && (! gch->IsPkiller () && ! gch->IsNpc ())) stop_follower (gch); } } } else { // victim is leader of group - disband non-pks for (gch = victim->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) { if (is_same_group (victim, gch) && (victim != gch) && ! gch->IsPkiller () && ! gch->IsNpc ()) stop_follower (gch); } } } */ #endif short anopc = 0; // # of (non-pkill) pc in a (ch) short bnopc = 0; // # of (non-pkill) pc in b (victim) // count the # of non-pkill pc in a (not including == ch) for (gch = ch->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (ch, gch) && ! gch->IsNpc () && ! gch->IsPkiller () && (ch != gch)) anopc++; // count the # of non-pkill pc in b (not including == victim) for (gch = victim->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (victim, gch) && ! gch->IsNpc () && ! gch->IsPkiller () && (victim != gch)) bnopc++; // only consider disbanding if both groups have 1 (+) non-pk pc, // or when one participant is pc, and the other group has 1 (+) // pk pc's (in the case that participant is only pk pc in group) if ((bnopc > 0 && anopc > 0) || (bnopc > 0 && !ch->IsNpc ()) || (anopc > 0 && !victim->IsNpc ())) { // Disband from same group first if (is_same_group (ch, victim)) { // Messages to char and master handled in stop_follower act (AT_ACTION, "$n disbands from $N's group.", (ch->GetLeader () == victim) ? victim : ch, NULL, (ch->GetLeader () == victim) ? victim->GetMaster () : ch->GetMaster (), TO_NOTVICT); if (ch->GetLeader () == victim) stop_follower (victim); else stop_follower (ch); } // if leader isnt pkill, leave the group and disband ch if (ch->GetLeader () != NULL && ! ch->GetLeader ()->IsNpc () && ! ch->GetLeader ()->IsPkiller ()) { act (AT_ACTION, "$n disbands from $N's group.", ch, NULL, ch->GetMaster (), TO_NOTVICT); stop_follower (ch); } else { for (gch = ch->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (gch, ch) && !gch->IsNpc () && ! gch->IsPkiller () && gch != ch) { act (AT_ACTION, "$n disbands from $N's group.", ch, NULL, gch->GetMaster (), TO_NOTVICT); stop_follower (gch); } } // if leader isnt pkill, leave the group and disband victim if (victim->GetLeader () != NULL && ! victim->GetLeader ()->IsNpc () && ! victim->GetLeader ()->IsPkiller ()) { act (AT_ACTION, "$n disbands from $N's group.", victim, NULL, victim->GetMaster (), TO_NOTVICT); stop_follower (victim); } else { for (gch = victim->GetInRoom ()->first_person; gch; gch = gch->GetNextInRoom ()) if (is_same_group (gch, victim) && !gch->IsNpc () && ! gch->IsPkiller () && gch != victim) { act (AT_ACTION, "$n disbands from $N's group.", gch, NULL, gch->GetMaster (), TO_NOTVICT); stop_follower (gch); } } } // Inviso attacks ... not. if (ch->IsInvis ()) { affect_strip (ch, gsn_invis); affect_strip (ch, gsn_mass_invis); ch->ClrInvis (); act (AT_MAGIC, "$n fades into existence.", ch, NULL, NULL, TO_ROOM); } // Take away Hide if (ch->IsHidden ()) ch->ClrHide (); // Damage modifiers. if (victim->HasSanctuary ()) dam /= 2; if (victim->IsProtected () && ch->IsEvil ()) dam -= (int) (dam / 4); if (dam < 0) dam = 0; // Check for disarm, trip, parry, and dodge. if (dt >= TYPE_HIT && ch->GetInRoom () == victim->GetInRoom ()) { if (ch->IsNpc () && ch->CanDisarm () && ch->GetLevel () > 9 && number_percent () < ch->GetLevel () / 3) disarm (ch, victim); if (ch->IsNpc () && ch->CanTrip () && ch->GetLevel () > 5 && number_percent () < ch->GetLevel () / 2) trip (ch, victim); if (check_parry (ch, victim)) return rNONE; if (check_dodge (ch, victim)) return rNONE; // if (check_tumble (ch, victim)) // return rNONE; } // Check control panel settings and modify damage if (ch->IsNpc ()) { if (npcvict) dampmod = SysData.MobMobDamage; else dampmod = SysData.MobPlrDamage; } else { if (npcvict) dampmod = SysData.PlrMobDamage; else dampmod = SysData.PlrPlrDamage; } if (dampmod > 0) dam = (dam * dampmod) / 100; dam_message (ch, victim, dam, dt); } // Code to handle equipment getting damaged, and also support -Thoric // bonuses/penalties for having or not having equipment where hit if (dam > 10 && dt != TYPE_UNDEFINED) { // get a random body eq part dameq = number_range (WEAR_LIGHT, WEAR_EYES); damobj = get_eq_char (victim, dameq); if (damobj) { if (dam > get_obj_resistance (damobj) && number_bits (1) == 0) { set_cur_obj (damobj); damage_obj (damobj); } dam -= 5; // add a bonus for having something to block the blow } else dam += 5; // add penalty for bare skin! } // Hurt the victim. // Inform the victim of his new state. victim->AddHp (-dam); // Get experience based on % of damage done -Thoric if (dam && ch != victim && !ch->IsNpc () && ch->GetFightData () && ch->GetFightData ()->xp) { if (ch->GetFightData ()->who == victim) xp_gain = (int) (ch->GetFightData ()->xp * dam) / victim->GetMaxHp (); else xp_gain = (int) (xp_compute (ch, victim) * 0.85 * dam) / victim->GetMaxHp (); gain_exp (ch, xp_gain); } if (! victim->IsNpc () && victim->GetLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1) victim->SetHp (1); // Make sure newbies dont die if (!victim->IsNpc () && !victim->IsAuthed () && victim->GetHp () < 1) victim->SetHp (1); if (dam > 0 && dt > TYPE_HIT && ! victim->IsPoisoned () && is_wielding_poisoned (ch) && ! victim->IsImmunePoison () && !saves_poison_death (ch->GetLevel (), victim)) { CAffectData af; af.type = gsn_poison; af.duration = 20; af.location = APPLY_STR; af.modifier = -2; af.bitvector = AFF_POISON; affect_join (victim, &af); victim->SetMentalState (URANGE (20, victim->GetMentalState () + victim->IsPkiller () ? 1 : 2, 100)); } // Vampire self preservation -Thoric if (victim->IsVampire ()) { if (dam >= (victim->GetMaxHp () / 10)) // get hit hard, lose blood gain_condition (victim, COND_BLOODTHIRST, -1 - (victim->GetLevel () / 20)); if (victim->GetHp () <= (victim->GetMaxHp () / 8) && victim->GetPcData ()->condition[COND_BLOODTHIRST] > 5) { gain_condition (victim, COND_BLOODTHIRST, -URANGE (3, victim->GetLevel () / 10, 8)); victim->AddHp (URANGE (4, (victim->GetMaxHp () / 30), 15)); set_char_color (AT_BLOOD, victim); victim->SendText ("You howl with rage as the beast within stirs!\n\r"); } } if (! npcvict && victim->GetTrustLevel () >= LEVEL_IMMORTAL && ch->GetTrustLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1) victim->SetHp (1); update_pos (victim); switch (victim->GetPosition ()) { case POS_MORTAL: act (AT_DYING, "$n is mortally wounded, and will die soon, if not aided.", victim, NULL, NULL, TO_ROOM); act (AT_DANGER, "You are mortally wounded, and will die soon, if not aided.", victim, NULL, NULL, TO_CHAR); break; case POS_INCAP: act (AT_DYING, "$n is incapacitated and will slowly die, if not aided.", victim, NULL, NULL, TO_ROOM); act (AT_DANGER, "You are incapacitated and will slowly die, if not aided.", victim, NULL, NULL, TO_CHAR); break; case POS_STUNNED: if (! victim->IsParalysed ()) { act (AT_ACTION, "$n is stunned, but will probably recover.", victim, NULL, NULL, TO_ROOM); act (AT_HURT, "You are stunned, but will probably recover.", victim, NULL, NULL, TO_CHAR); } break; case POS_DEAD: if (dt >= 0 && dt < SkillTable.GetCount ()) { CSkill *skill = SkillTable.GetSkill (dt); if (skill->ValidDieCharMsg ()) act (AT_DEAD, skill->GetDieCharMsg (), ch, NULL, victim, TO_CHAR); if (skill->ValidDieVictMsg ()) act (AT_DEAD, skill->GetDieVictMsg (), ch, NULL, victim, TO_VICT); if (skill->ValidDieRoomMsg ()) act (AT_DEAD, skill->GetDieRoomMsg (), ch, NULL, victim, TO_NOTVICT); } act (AT_DEAD, "$n is DEAD!", victim, 0, 0, TO_ROOM); act (AT_DEAD, "You have been KILLED!\n\r", victim, 0, 0, TO_CHAR); break; default: if (dam > victim->GetMaxHp () / 4) { act (AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR); if (number_bits (3) == 0) worsen_mental_state (victim, 1); } if (victim->GetHp () < victim->GetMaxHp () / 4) { act (AT_DANGER, "You wish that your wounds would stop BLEEDING " "so much!", victim, 0, 0, TO_CHAR); if (number_bits (2) == 0) worsen_mental_state (victim, 1); } break; } // Sleep spells and extremely wounded folks. if (! victim->IsAwake () // lets make NPC's not slaughter PC's && ! victim->IsParalysed ()) { if (victim->GetFightData () && victim->GetFightData ()->who->hunting && victim->GetFightData ()->who->hunting->who == victim) stop_hunting (victim->GetFightData ()->who); if (victim->GetFightData () && victim->GetFightData ()->who->hating && victim->GetFightData ()->who->hating->who == victim) stop_hating (victim->GetFightData ()->who); if (! npcvict && ch->IsNpc ()) stop_fighting (victim, TRUE); else stop_fighting (victim, FALSE); } // Payoff for killing things. if (victim->GetPosition () == POS_DEAD) { group_gain (ch, victim); if (! npcvict) { sprintf (log_buf, "%s killed by %s at %d", victim->GetName (), (ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()), victim->GetInRoom ()->vnum); gpDoc->LogString (log_buf, LOG_PLAYER); to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL); // Dying penalty: // 1/2 way back to previous level. if (victim->GetExp () > exp_level (victim, victim->GetLevel ())) gain_exp (victim, (exp_level (victim, victim->GetLevel ()) - victim->GetExp ())/2); // New penalty... go back to the beginning of current level. // victim->GetExp () = exp_level (victim, victim->GetLevel ()); } else if (! ch->IsNpc ()) // keep track of mob vnum killed add_kill (ch, victim); check_killer (ch, victim); if (ch->GetInRoom () == victim->GetInRoom ()) loot = legal_loot (ch, victim); else loot = FALSE; set_cur_char (victim); raw_kill (ch, victim); victim = NULL; if (! ch->IsNpc () && loot) { // Autogold by Scryn 8/12 if (ch->IsAction (PLR_AUTOGOLD)) { init_gold = ch->GetGold (); do_get (ch, "coins corpse"); new_gold = ch->GetGold (); gold_diff = (new_gold - init_gold); if (gold_diff > 0) { sprintf (buf1,"%d",gold_diff); do_split (ch, buf1); } } if (ch->IsAction (PLR_AUTOLOOT) && victim != ch) do_get (ch, "all corpse"); else do_look (ch, "in corpse"); if (ch->IsAction (PLR_AUTOSAC)) do_sacrifice (ch, "corpse"); } if (SysData.IsSaveOnKill ()) save_char_obj (ch); return rVICT_DIED; } if (victim == ch) return rNONE; // Take care of link dead people. if (! npcvict && !victim->GetDesc () && ! victim->IsNoRecall ()) { if (number_range (0, victim->GetWait ()) == 0) { do_recall (victim, ""); return rNONE; } } // Wimp out? if (npcvict && dam > 0) { if ((victim->IsWimpy () && number_bits (1) == 0 && victim->GetHp () < victim->GetMaxHp () / 2) || (victim->IsCharmed () && victim->GetMaster () && victim->GetMaster ()->GetInRoom () != victim->GetInRoom ())) { start_fearing (victim, ch); stop_hunting (victim); do_flee (victim, ""); } } if (! npcvict && victim->GetHp () > 0 && victim->GetHp () <= victim->GetWimpLevel () && victim->GetWait () == 0) do_flee (victim, ""); else if (! npcvict && victim->IsAction (PLR_FLEE)) do_flee (victim, ""); return rNONE; } BOOL is_safe (CCharacter *ch, CCharacter *victim) { // Thx Josh! if (ch->GetFightWho () == ch) return FALSE; if (victim->GetInRoom ()->IsSafe ()) { set_char_color (AT_MAGIC, ch); ch->SendText ("A magical force prevents you from attacking.\n\r"); return TRUE; } if (ch->IsPacifist ()) { // Fireblade set_char_color (AT_MAGIC, ch); ch->SendText ("You are a pacifist and will not fight.\n\r"); return TRUE; } if (ch->GetLevel () > LEVEL_IMMORTAL) return FALSE; if (victim->IsNpc () && victim->IsPacifist ()) { // Gorog set_char_color (AT_MAGIC, ch); ch->SendTextf ("%s is a pacifist and will not fight.\n\r", capitalize (victim->GetShortDescr ())); return TRUE; } if (! ch->IsNpc () && ! victim->IsNpc () && ch != victim && victim->GetInRoom ()->GetArea ()->IsNoPkill ()) { set_char_color (AT_IMMORT, ch); ch->SendText ("The gods have forbidden player killing in this area.\n\r"); return TRUE; } if (ch->IsNpc () || victim->IsNpc ()) return FALSE; if (ch->GetAge () < 18 || ch->GetLevel () < 5) { set_char_color (AT_WHITE, ch); ch->SendText ("You are not yet ready, needing age or experience, " "if not both. \n\r"); return TRUE; } if (victim->GetAge () < 18 || victim->GetLevel () < 5) { set_char_color (AT_WHITE, ch); ch->SendText ("They are yet too young to die.\n\r"); return TRUE; } if (ch->GetLevel () - victim->GetLevel () > 5 || victim->GetLevel () - ch->GetLevel () > 5) { set_char_color (AT_IMMORT, ch); ch->SendText ("The gods do not allow murder when there is such " "a difference in level.\n\r"); return TRUE; } if (get_timer (victim, TIMER_PKILLED) > 0) { set_char_color (AT_GREEN, ch); ch->SendText ("That character has died within the last 5 minutes.\n\r"); return TRUE; } if (get_timer (ch, TIMER_PKILLED) > 0) { set_char_color (AT_GREEN, ch); ch->SendText ("You have been killed within the last 5 minutes.\n\r"); return TRUE; } return FALSE; } // just verify that a corpse looting is legal BOOL legal_loot (CCharacter *ch, CCharacter *victim) { // anyone can loot mobs if (victim->IsNpc ()) return TRUE; // non-charmed mobs can loot anything if (ch->IsNpc () && ! ch->GetMaster ()) return TRUE; // members of different clans can loot too! -Thoric if (!ch->IsNpc () && !victim->IsNpc () && ch->IsPkiller () && victim->IsPkiller ()) return TRUE; return FALSE; } // See if an attack justifies a KILLER flag. void check_killer (CCharacter *ch, CCharacter *victim) { // NPC's are fair game. if (victim->IsNpc ()) { if (! ch->IsNpc ()) { if (ch->GetPcData ()->GetClan ()) ch->GetPcData ()->GetClan ()->mkills++; ch->GetPcData ()->mkills++; ch->GetInRoom ()->GetArea ()->mkills++; int level_ratio = URANGE (1, ch->GetLevel () / victim->GetLevel (), 50); if (ch->GetPcData ()->deity) { if (victim->GetRace () == ch->GetPcData ()->deity->npcrace) adjust_favor (ch, 3, level_ratio); else if (victim->GetRace () == ch->GetPcData ()->deity->npcfoe) adjust_favor (ch, 17, level_ratio); else adjust_favor (ch, 2, level_ratio); } } return; } // If you kill yourself nothing happens. if (ch == victim || ch->GetLevel () >= LEVEL_IMMORTAL) return; // Any character in the arena is ok to kill. // Added pdeath and pkills here if (in_arena (ch)) { if (! ch->IsNpc () && ! victim->IsNpc ()) { ch->GetPcData ()->pkills++; victim->GetPcData ()->pdeaths++; } return; } // So are killers and thieves. if (victim->IsKiller () || victim->IsThief ()) { if (! ch->IsNpc ()) { if (ch->GetPcData ()->GetClan ()) ch->GetPcData ()->GetClan ()->pkills++; ch->GetPcData ()->pkills++; ch->GetInRoom ()->GetArea ()->pkills++; } return; } // clan checks -Thoric if (! ch->IsNpc () && ! victim->IsNpc () && ch->IsPkiller () && victim->IsPkiller ()) { // not of same clan? Go ahead and kill!!! if (! ch->GetPcData ()->GetClan () || !victim->GetPcData ()->GetClan () || (ch->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL && victim->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL && ch->GetPcData ()->GetClan () != victim->GetPcData ()->GetClan ())) { if (ch->GetPcData ()->GetClan ()) ch->GetPcData ()->GetClan ()->pkills++; ch->GetPcData ()->pkills++; ch->SetHp (ch->GetMaxHp ()); ch->SetMana (ch->GetMaxMana ()); ch->SetMove (ch->GetMaxMove ()); if (ch->GetPcData ()) ch->GetPcData ()->condition [COND_BLOODTHIRST] = 10 + ch->GetLevel (); update_pos (victim); if (victim != ch) { act (AT_MAGIC, "Bolts of blue energy rise from the corpse, " "seeping into $n.", ch, victim->GetName (), NULL, TO_ROOM); act (AT_MAGIC, "Bolts of blue energy rise from the corpse, " "seeping into you.", ch, victim->GetName (), NULL, TO_CHAR); } if (victim->GetPcData ()->GetClan ()) victim->GetPcData ()->GetClan ()->pdeaths++; victim->GetPcData ()->pdeaths++; adjust_favor (victim, 11, 1); adjust_favor (ch, 2, 1); add_timer (victim, TIMER_PKILLED, 115, NULL, 0); WAIT_STATE (victim, 3 * PULSE_VIOLENCE); // SET_BIT (victim->act, PLR_PK); return; } } // Charm-o-rama. if (ch->IsCharmed ()) { if (! ch->GetMaster ()) { bug ("Check_killer: %s bad AFF_CHARM", ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()); affect_strip (ch, gsn_charm_person); ch->ClrCharmed (); return; } // stop_follower (ch); if (ch->GetMaster ()) check_killer (ch->GetMaster (), victim); return; } // NPC's are cool of course (as long as not charmed). // Hitting yourself is cool too (bleeding). // So is being immortal (Alander's idea). // And current killers stay as they are. if (ch->IsNpc ()) { if (! victim->IsNpc ()) { if (victim->GetPcData ()->GetClan ()) victim->GetPcData ()->GetClan ()->mdeaths++; victim->GetPcData ()->mdeaths++; victim->GetInRoom ()->GetArea ()->mdeaths++; int level_ratio = URANGE (1, ch->GetLevel () / victim->GetLevel (), 50); if (victim->GetPcData ()->deity) { if (ch->GetRace () == victim->GetPcData ()->deity->npcrace) adjust_favor (victim, 12, level_ratio); else if (ch->GetRace () == victim->GetPcData ()->deity->npcfoe) adjust_favor (victim, 15, level_ratio); else adjust_favor (victim, 11, level_ratio); } } return; } if (! ch->IsNpc ()) { if (ch->GetPcData ()->GetClan ()) ch->GetPcData ()->GetClan ()->illegal_pk++; ch->GetPcData ()->illegal_pk++; ch->GetInRoom ()->GetArea ()->illegal_pk++; } if (! victim->IsNpc ()) { if (victim->GetPcData ()->GetClan ()) victim->GetPcData ()->GetClan ()->pdeaths++; victim->GetPcData ()->pdeaths++; victim->GetInRoom ()->GetArea ()->pdeaths++; } if (ch->IsKiller ()) return; set_char_color (AT_WHITE, ch); ch->SendText ("A strange feeling grows deep inside you, and a tingle " "goes up your spine...\n\r"); set_char_color (AT_IMMORT, ch); ch->SendText ("A deep voice booms inside your head, 'Thou shall now " "be known as a deadly murderer!!!'\n\r"); set_char_color (AT_WHITE, ch); ch->SendText ("You feel as if your soul has been revealed for all to " "see.\n\r"); ch->SetActBit (PLR_KILLER); if (ch->IsAttacker ()) ch->ClrAttacker (); save_char_obj (ch); } // See if an attack justifies a ATTACKER flag. void check_attacker (CCharacter *ch, CCharacter *victim) { // Made some changes to this function Apr 6/96 to reduce the prolifiration // of attacker flags in the realms. -Narn // NPC's are fair game. // So are killers and thieves. if (victim->IsNpc () || victim->IsKiller () || victim->IsThief ()) return; // deadly char check if (! ch->IsNpc () && ! victim->IsNpc () && ch->CanPkill () && victim->CanPkill ()) return; // Charm-o-rama. if (ch->IsCharmed ()) { if (! ch->GetMaster ()) { bug ("Check_attacker: %s bad AFF_CHARM", ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()); affect_strip (ch, gsn_charm_person); ch->ClrCharmed (); return; } // Won't have charmed mobs fighting give the master an attacker // flag. The killer flag stays in, and I'll put something in // do_murder. -Narn // SET_BIT (ch->GetMaster ()->act, PLR_ATTACKER); // stop_follower (ch); return; } // NPC's are cool of course (as long as not charmed). // Hitting yourself is cool too (bleeding). // So is being immortal (Alander's idea). // And current killers stay as they are. if (ch->IsNpc () || ch == victim || ch->IsImmortal () || ch->IsAttacker () || ch->IsKiller ()) return; ch->SetAttacker (); save_char_obj (ch); } // Set position of a victim. void update_pos (CCharacter *victim) { if (! victim) { bug ("update_pos: null victim"); return; } if (victim->GetHp () > 0) { if (victim->GetPosition () <= POS_STUNNED) victim->SetPosition (POS_STANDING); if (victim->IsParalysed ()) victim->SetPosition (POS_STUNNED); return; } if (victim->IsNpc () || victim->GetHp () <= -11) { if (victim->mount) { act (AT_ACTION, "$n falls from $N.", victim, NULL, victim->mount, TO_ROOM); victim->mount->ClrActBit (ACT_MOUNTED); victim->mount = NULL; } victim->SetPosition (POS_DEAD); return; } if (victim->GetHp () <= -6) victim->SetPosition (POS_MORTAL); else if (victim->GetHp () <= -3) victim->SetPosition (POS_INCAP); else victim->SetPosition (POS_STUNNED); if (victim->GetPosition () > POS_STUNNED && victim->IsParalysed ()) victim->SetPosition (POS_STUNNED); if (victim->mount) { act (AT_ACTION, "$n falls unconscious from $N.", victim, NULL, victim->mount, TO_ROOM); victim->mount->ClrActBit (ACT_MOUNTED); victim->mount = NULL; } } // Start fights. void set_fighting (CCharacter *ch, CCharacter *victim) { CFightData *fight; if (ch->GetFightData ()) { bug ("Set_fighting: %s -> %s (already fighting %s)", ch->GetName (), victim->GetName (), ch->GetFightData ()->who->GetName ()); return; } if (ch->IsSleeping ()) affect_strip (ch, gsn_sleep); // Limit attackers -Thoric if (victim->num_fighting > max_fight (victim)) { ch->SendText ("There are too many people fighting for you to join in.\n\r"); return; } fight = new CFightData; fight->who = victim; fight->xp = (int) (xp_compute (ch, victim) * 0.85); fight->align = align_compute (ch, victim); if (! ch->IsNpc () && victim->IsNpc ()) fight->timeskilled = times_killed (ch, victim); ch->num_fighting = 1; ch->SetFightData (fight); victim->num_fighting++; if (ch->IsNpc ()) ch->SetPosition (POS_FIGHTING); else switch (ch->GetStyle ()) { case STYLE_EVASIVE: ch->SetPosition (POS_EVASIVE); break; case STYLE_DEFENSIVE: ch->SetPosition (POS_DEFENSIVE); break; case STYLE_AGGRESSIVE: ch->SetPosition (POS_AGGRESSIVE); break; case STYLE_BERSERK: ch->SetPosition (POS_BERSERK); break; default: ch->SetPosition (POS_FIGHTING); } if (victim->switched && victim->switched->IsPossessed ()) { victim->switched->SendText ("You are disturbed!\n\r"); do_return (victim->switched, ""); } } void free_fight (CCharacter *ch) { if (! ch) { bug ("Free_fight: null ch!"); return; } if (ch->GetFightData ()) { if (! char_died (ch->GetFightData ()->who)) --ch->GetFightData ()->who->num_fighting; delete ch->GetFightData (); } ch->SetFightData (NULL); if (ch->mount) ch->SetPosition (POS_MOUNTED); else ch->SetPosition (POS_STANDING); // Berserk wears off after combat. -- Altrag if (ch->IsBeserk ()) { affect_strip (ch, gsn_berserk); set_char_color (AT_WEAROFF, ch); ch->SendText (SkillTable.GetSkill (gsn_berserk)->GetOffMsg ()); ch->SendText ("\n\r"); } } // Stop fights. void stop_fighting (CCharacter *ch, BOOL fBoth) { CCharacter *fch; free_fight (ch); update_pos (ch); if (!fBoth) // major short cut here by Thoric return; for (fch = first_char; fch; fch = fch->GetNext ()) { if (fch->GetFightWho () == ch) { free_fight (fch); update_pos (fch); } } } // Improved Death_cry contributed by Diavolo. // Additional improvement by Thoric (and removal of turds... sheesh!) void death_cry (CCharacter *ch) { CRoomIndexData *was_in_room; char *msg; CExitData *pexit; int vnum; if (! ch) { bug ("DEATH_CRY: null ch!"); return; } vnum = 0; switch (number_bits (4)) { default: msg = "You hear $n's death cry."; break; case 0: msg = "$n hits the ground ... DEAD."; break; case 1: msg = "$n splatters blood on your armor."; break; case 2: if (ch->HasBodypart (PART_GUTS)) { msg = "$n's guts spill all over the ground."; vnum = OBJ_VNUM_SPILLED_GUTS; } else msg = "$n collapses lifeless to the ground."; break; case 3: if (ch->HasBodypart (PART_HEAD)) { msg = "$n's severed head plops on the ground."; vnum = OBJ_VNUM_SEVERED_HEAD; } else msg = "You hear $n's death cry."; break; case 4: if (ch->HasBodypart (PART_HEART)) { msg = "$n's heart is torn from $s chest."; vnum = OBJ_VNUM_TORN_HEART; } else msg = "$n collapses lifeless to the ground."; break; case 5: if (ch->HasBodypart (PART_ARMS)) { msg = "$n's arm is sliced from $s dead body."; vnum = OBJ_VNUM_SLICED_ARM; } else msg = "You hear $n's death cry."; break; case 6: if (ch->HasBodypart (PART_LEGS)) { msg = "$n's leg is sliced from $s dead body."; vnum = OBJ_VNUM_SLICED_LEG; } else msg = "$n collapses lifeless to the ground."; break; } act (AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM); if (vnum) { char buf [MAX_STRING_LENGTH]; CObjData *obj; const char *name; name = ch->IsNpc () ? ch->GetShortDescr () : ch->GetName (); obj = create_object (OIdxTable.GetObj (vnum), 0); obj->timer = number_range (4, 7); sprintf (buf, obj->GetShortDescr (), name); obj->SetShortDescr (buf); sprintf (buf, obj->GetDescription (), name); obj->SetDescription (buf); obj = obj_to_room (obj, ch->GetInRoom ()); } if (ch->IsNpc ()) msg = "You hear something's death cry."; else msg = "You hear someone's death cry."; was_in_room = ch->GetInRoom (); for (pexit = was_in_room->first_exit; pexit; pexit = pexit->GetNext ()) { if (pexit->GetToRoom () && pexit->GetToRoom () != was_in_room) { ch->SetInRoom (pexit->GetToRoom ()); act (AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM); } } ch->SetInRoom (was_in_room); } void raw_kill (CCharacter *ch, CCharacter *victim) { CCharacter *victmp; if (! victim) { bug ("raw_kill: null victim!"); return; } // backup in case hp goes below 1 if (!victim->IsAuthed ()) { bug ("raw_kill: killing unauthed"); return; } stop_fighting (victim, TRUE); // Take care of polymorphed chars if (victim->IsNpc () && victim->IsPolymorphed ()) { victim->GetDesc ()->m_pOriginal->RemoveFromRoom (); victim->GetDesc ()->m_pOriginal->SendToRoom (victim->GetInRoom ()); victmp = victim->GetDesc ()->m_pOriginal; do_revert (victim, ""); raw_kill (ch, victmp); return; } mprog_death_trigger (ch, victim); if (char_died (victim)) return; rprog_death_trigger (ch, victim); if (char_died (victim)) return; make_corpse (victim, ch); if (victim->GetSectorType () == SECT_OCEANFLOOR || victim->GetSectorType () == SECT_UNDERWATER || victim->GetSectorType () == SECT_WATER_SWIM || victim->GetSectorType () == SECT_WATER_NOSWIM) act (AT_BLOOD, "$n's blood slowly clouds the surrounding water.", victim, NULL, NULL, TO_ROOM); else if (victim->GetSectorType () == SECT_AIR) act( AT_BLOOD, "$n's blood sprays wildly through the air.", victim, NULL, NULL, TO_ROOM); else make_blood (victim); if (victim->IsNpc ()) { victim->GetMobIndex ()->killed++; extract_char (victim, TRUE); victim = NULL; return; } set_char_color (AT_DIEMSG, victim); if (victim->GetPcData ()->mdeaths + victim->GetPcData ()->pdeaths < 3) do_help (victim, "new_death"); else do_help (victim, "_DIEMSG_"); extract_char (victim, FALSE); if (! victim) { bug ("oops! raw_kill: extract_char destroyed pc char"); return; } while (! victim->m_AffectList.IsEmpty ()) affect_remove (victim, victim->m_AffectList.RemoveTail ()); CRaceData &Ra = *RaceTable.GetRaceData (victim->GetRace ()); victim->SetAffectFlags (Ra.GetAffectFlags ()); victim->SetAttackFlags (Ra.GetAttackFlags ()); victim->SetDefenseFlags (Ra.GetDefenseFlags ()); victim->SetResistFlags (0); victim->SetSusceptFlags (0); victim->SetImmuneFlags (0); victim->SetCarryWeight (0); victim->SetArmor (100 + Ra.GetAcPlus ()); victim->mod_str = 0; victim->mod_dex = 0; victim->mod_wis = 0; victim->mod_int = 0; victim->mod_con = 0; victim->mod_cha = 0; victim->mod_lck = 0; victim->SetDamroll (0); victim->SetHitroll (0); victim->SetMentalState (-10); victim->SetAlignment (URANGE (-1000, victim->GetAlignment (), 1000)); victim->saving_poison_death = Ra.GetSavingPoisonDeath (); victim->saving_wand = Ra.GetSavingWand (); victim->saving_para_petri = Ra.GetSavingParaPetri (); victim->saving_breath = Ra.GetSavingBreath (); victim->saving_spell_staff = Ra.GetSavingSpellstaff (); victim->SetPosition (POS_RESTING); victim->SetHp (UMAX (1, victim->GetHp ())); victim->SetMana (UMAX (1, victim->GetMana ())); victim->SetMove (UMAX (1, victim->GetMove ())); // Pardon crimes... -Thoric if (victim->IsAction (PLR_KILLER)) { victim->ClrActBit (PLR_KILLER); victim->SendText ("The gods have pardoned you for your murderous acts.\n\r"); } if (victim->IsAction (PLR_THIEF)) { victim->ClrActBit (PLR_THIEF); victim->SendText ("The gods have pardoned you for your thievery.\n\r"); } victim->GetPcData ()->condition [COND_FULL] = 12; victim->GetPcData ()->condition [COND_THIRST] = 12; if (victim->IsVampire ()) victim->GetPcData ()->condition [COND_BLOODTHIRST] = victim->GetLevel () / 2; if (SysData.IsSaveOnDeath ()) save_char_obj (victim); } void group_gain (CCharacter *ch, CCharacter *victim) { int xp; int members; // Monsters don't get kill xp's or alignment changes. // Dying of mortal wounds or poison doesn't give xp to anyone! if (ch->IsNpc () || victim == ch) return; members = 0; CCharacter *gch = ch->GetInRoom ()->first_person; for (; gch; gch = gch->GetNextInRoom ()) { if (is_same_group (gch, ch)) members++; } if (members == 0) { bug ("Group_gain: members = 0."); members = 1; } CCharacter *lch = ch->GetLeader () ? ch->GetLeader () : ch; gch = ch->GetInRoom ()->first_person; for (; gch; gch = gch->GetNextInRoom ()) { CObjData *obj; if (! is_same_group (gch, ch)) continue; if (gch->GetLevel () - lch->GetLevel () > 8) { gch->SendText ("You are too high for this group.\n\r"); continue; } if (gch->GetLevel () - lch->GetLevel () < -8) { gch->SendText ("You are too low for this group.\n\r"); continue; } xp = (int) (xp_compute (gch, victim) * 0.1765) / members; if (! gch->GetFightData ()) xp /= 2; gch->SetAlignment (align_compute (gch, victim)); gch->SendTextf ("You receive %d experience points.\n\r", xp); gain_exp (gch, xp); POSITION Cpos = ch->GetHeadCarryPos (); while (obj = ch->GetNextCarrying (Cpos)) { if (obj->wear_loc == WEAR_NONE) continue; if ((obj->IsAntiEvil () && ch->IsEvil ()) || (obj->IsAntiGood () && ch->IsGood ()) || (obj->IsAntiNeutral () && ch->IsNeutral ())) { act (AT_MAGIC, "You are zapped by $p.", ch, obj, NULL, TO_CHAR); act (AT_MAGIC, "$n is zapped by $p.", ch, obj, NULL, TO_ROOM); obj_from_char (obj); obj = obj_to_room (obj, ch->GetInRoom ()); oprog_zap_trigger (ch, obj); // mudprogs if (char_died (ch)) return; } } } } int align_compute (CCharacter *gch, CCharacter *victim) { int CharAlign = gch->GetAlignment (); int VictAlign = victim->GetAlignment (); // 6/12/98 by RCP // This provides a sliding scale for alignment adjustment. For victims // with low absolute alignment values, approx 33% of the victims align // is applied to the killer. As victim alignment increases to 1000, the // percentage is decreased to 12.5%. // Note: All numbers except 200 are * 10 to avoid integer rounding errors // Basic formula is -va / (3 + va/200). int AlignChange = -10 * VictAlign / (30 + abs (10 * VictAlign)/200); return URANGE (-1000, CharAlign + AlignChange, 1000); } //int align_compute (CCharacter *gch, CCharacter *victim) //{ // int align, newalign; // // align = gch->GetAlignment () - victim->GetAlignment (); // // if (align > 500) // newalign = UMIN (gch->GetAlignment () + (align-500)/4, 1000); // else if (align < -500) // newalign = UMAX (gch->GetAlignment () + (align+500)/4, -1000); // else // newalign = gch->GetAlignment () - (int) (gch->GetAlignment () / 4); // // return newalign; //} // Calculate how much XP gch should gain for killing victim // Lots of redesigning for new exp system by Thoric int xp_compute (CCharacter *gch, CCharacter *victim) { int align; int xp; int xp_ratio; int gchlev = gch->GetLevel (); xp = (get_exp_worth (victim) * URANGE (0, (victim->GetLevel () - gchlev) + 10, 13)) / 10; align = gch->GetAlignment () - victim->GetAlignment (); // bonus for attacking opposite alignment if (align > 990 || align < -990) xp = (xp*5) >> 2; else // penalty for good attacking same alignment if (gch->GetAlignment () > 300 && align < 250) xp = (xp*3) >> 2; xp = number_range ((xp*3) >> 2, (xp*5) >> 2); // get 1/4 exp for players -Thoric if (! victim->IsNpc ()) xp /= 4; // reduce exp for killing the same mob repeatedly -Thoric else if (! gch->IsNpc ()) { int times = times_killed (gch, victim); if (times >= 20) xp = 0; else if (times) { xp = (xp * (20-times)) / 20; if (times > 15) xp /= 3; else if (times > 10) xp >>= 1; } } // semi-intelligent experienced player vs. novice player xp gain // "bell curve"ish xp mod by Thoric // based on time played vs. level if (! gch->IsNpc () && gchlev > 5) { xp_ratio = (int) gch->GetPlayed () / gchlev; if (xp_ratio > 20000) // 5/4 xp = (xp*5) >> 2; else if (xp_ratio < 16000) // 3/4 xp = (xp*3) >> 2; else if (xp_ratio < 10000) // 1/2 xp >>= 1; else if (xp_ratio < 5000) // 1/4 xp >>= 2; else if (xp_ratio < 3500) // 1/8 xp >>= 3; else if (xp_ratio < 2000) // 1/16 xp >>= 4; } // Level based experience gain cap. Cannot get more experience for // a kill than the amount for your current experience level -Thoric // This does not seem realistic to me (RCP) // return URANGE (0, xp, exp_level (gch, gchlev+1) - // exp_level (gch, gchlev)); return max (0, xp); } char * s_blade_messages [24] = { "miss", "barely scratch", "scratch", "nick", "cut", "hit", "tear", "rip", "gash", "lacerate", "hack", "maul", "rend", "decimate", "_mangle_", "_devastate_", "_cleave_", "_butcher_", "DISEMBOWEL", "DISFIGURE", "GUT", "EVISCERATE", "* SLAUGHTER *", "*** ANNIHILATE ***" }; char * p_blade_messages [24] = { "misses", "barely scratches", "scratches", "nicks", "cuts", "hits", "tears", "rips", "gashes", "lacerates", "hacks", "mauls", "rends", "decimates", "_mangles_", "_devastates_", "_cleaves_", "_butchers_", "DISEMBOWELS", "DISFIGURES", "GUTS", "EVISCERATES", "* SLAUGHTERS *", "*** ANNIHILATES ***" }; char * s_blunt_messages [24] = { "miss", "barely scuff", "scuff", "pelt", "bruise", "strike", "thrash", "batter", "flog", "pummel", "smash", "maul", "bludgeon", "decimate", "_shatter_", "_devastate_", "_maim_", "_cripple_", "MUTILATE", "DISFIGURE", "MASSACRE", "PULVERIZE", "* OBLITERATE *", "*** ANNIHILATE ***" }; char * p_blunt_messages [24] = { "misses", "barely scuffs", "scuffs", "pelts", "bruises", "strikes", "thrashes", "batters", "flogs", "pummels", "smashes", "mauls", "bludgeons", "decimates", "_shatters_", "_devastates_", "_maims_", "_cripples_", "MUTILATES", "DISFIGURES", "MASSACRES", "PULVERIZES", "* OBLITERATES *", "*** ANNIHILATES ***" }; char * s_generic_messages [24] = { "miss", "brush", "scratch", "graze", "nick", "jolt", "wound", "injure", "hit", "jar", "thrash", "maul", "decimate", "_traumatize_", "_devastate_", "_maim_", "_demolish_", "MUTILATE", "MASSACRE", "PULVERIZE", "DESTROY", "* OBLITERATE *", "*** ANNIHILATE ***", "**** SMITE ****" }; char * p_generic_messages [24] = { "misses", "brushes", "scratches", "grazes", "nicks", "jolts", "wounds", "injures", "hits", "jars", "thrashes", "mauls", "decimates", "_traumatizes_", "_devastates_", "_maims_", "_demolishes_", "MUTILATES", "MASSACRES", "PULVERIZES", "DESTROYS", "* OBLITERATES *", "*** ANNIHILATES ***", "**** SMITES ****" }; char ** const s_message_table [18] = { s_generic_messages, // hit s_blade_messages, // slice s_blade_messages, // stab s_blade_messages, // slash s_blunt_messages, // whip s_blade_messages, // claw s_generic_messages, // blast s_blunt_messages, // pound s_blunt_messages, // crush s_generic_messages, // grep s_blade_messages, // bite s_blade_messages, // pierce s_blunt_messages, // suction s_generic_messages, // bolt s_generic_messages, // arrow s_generic_messages, // dart s_generic_messages, // stone s_generic_messages // pea }; char ** const p_message_table [18] = { p_generic_messages, // hit p_blade_messages, // slice p_blade_messages, // stab p_blade_messages, // slash p_blunt_messages, // whip p_blade_messages, // claw p_generic_messages, // blast p_blunt_messages, // pound p_blunt_messages, // crush p_generic_messages, // grep p_blade_messages, // bite p_blade_messages, // pierce p_blunt_messages, // suction p_generic_messages, // bolt p_generic_messages, // arrow p_generic_messages, // dart p_generic_messages, // stone p_generic_messages // pea }; // Revamped by Thoric to be more realistic void dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt, CObjData* pObj /* = NULL */) { char buf1 [256], buf2 [256], buf3 [256]; const char *vs; const char *vp; const char *attack; char punct; short dampc; CSkill *skill = NULL; BOOL gcflag = FALSE; BOOL gvflag = FALSE; int d_index, w_index; if (! dam) dampc = 0; else dampc = ((dam * 1000) / victim->GetMaxHp ()) + (50 - ((victim->GetHp () * 50) / victim->GetMaxHp ())); CRoomIndexData *pWasInRoom = NULL; if (ch->GetInRoom () != victim->GetInRoom ()) { pWasInRoom = ch->GetInRoom (); ch->RemoveFromRoom (); ch->SendToRoom (victim->GetInRoom ()); } // Get the weapon index if (dt > 0 && dt < SkillTable.GetCount ()) w_index = 0; else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table)) w_index = dt - TYPE_HIT; else { bug ("Dam_message: bad dt %d from %s in %d.", dt, ch->GetName (), ch->GetInRoom ()->vnum); dt = TYPE_HIT; w_index = 0; } // get the damage index if (dam == 0) d_index = 0; else if (dampc < 0) d_index = 1; else if (dampc <= 100) d_index = 1 + dampc/10; else if (dampc <= 200) d_index = 11 + (dampc - 100) / 20; else if (dampc <= 900) d_index = 16 + (dampc - 200) / 100; else d_index = 23; // Lookup the damage message vs = s_message_table [w_index][d_index]; vp = p_message_table [w_index][d_index]; punct = (dampc <= 30) ? '.' : '!'; if (dam == 0 && (! ch->IsNpc () && ch->IsGagged ())) gcflag = TRUE; if (dam == 0 && (! victim->IsNpc () && victim->IsGagged ())) gvflag = TRUE; if (dt >=0 && dt < SkillTable.GetCount ()) skill = SkillTable.GetSkill (dt); if (dt == TYPE_HIT) { sprintf (buf1, "$n %s $N%c", vp, punct); sprintf (buf2, "You %s $N%c", vs, punct); sprintf (buf3, "$n %s you%c", vp, punct); } else if (dt > TYPE_HIT && is_wielding_poisoned (ch)) { if (dt < TYPE_HIT + DIM (attack_table)) attack = attack_table [dt - TYPE_HIT]; else { bug ("Dam_message: bad dt %d from %s in %d.", dt, ch->GetName (), ch->GetInRoom ()->vnum); dt = TYPE_HIT; attack = attack_table [0]; } sprintf (buf1, "$n's poisoned %s %s $N%c", attack, vp, punct); sprintf (buf2, "Your poisoned %s %s $N%c", attack, vp, punct); sprintf (buf3, "$n's poisoned %s %s you%c", attack, vp, punct); } else { if (skill) { attack = skill->GetDamageMsg (); if (dam == 0) { BOOL found = FALSE; if (skill->ValidMissCharMsg ()) { act (AT_HIT, skill->GetMissCharMsg (), ch, NULL, victim, TO_CHAR); found = TRUE; } if (skill->ValidMissVictMsg ()) { act (AT_HITME, skill->GetMissVictMsg (), ch, NULL, victim, TO_VICT); found = TRUE; } if (skill->ValidMissRoomMsg ()) { if (strcmp (skill->GetMissRoomMsg (), "supress")) act (AT_ACTION, skill->GetMissRoomMsg (), ch, NULL, victim, TO_NOTVICT); found = TRUE; } if (found) { // miss message already sent if (pWasInRoom) { ch->RemoveFromRoom (); ch->SendToRoom (pWasInRoom); } return; } } else { if (skill->ValidHitCharMsg ()) act (AT_HIT, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR); if (skill->ValidHitVictMsg ()) act (AT_HITME, skill->GetHitVictMsg (), ch, NULL, victim, TO_VICT); if (skill->ValidHitRoomMsg ()) act (AT_ACTION, skill->GetHitRoomMsg (), ch, NULL, victim, TO_NOTVICT); } } else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table)) { if (pObj) attack = pObj->GetShortDescr (); else attack = attack_table [dt - TYPE_HIT]; } else { bug ("Dam_message: bad dt %d from %s in %d.", dt, ch->GetName (), ch->GetInRoom ()->vnum); dt = TYPE_HIT; attack = attack_table [0]; } sprintf (buf1, "$n's %s %s $N%c", attack, vp, punct); sprintf (buf2, "Your %s %s $N%c", attack, vp, punct); sprintf (buf3, "$n's %s %s you%c", attack, vp, punct); } act (AT_ACTION, buf1, ch, NULL, victim, TO_NOTVICT); if (!gcflag) act (AT_HIT, buf2, ch, NULL, victim, TO_CHAR); if (!gvflag) act (AT_HITME, buf3, ch, NULL, victim, TO_VICT); if (pWasInRoom) { ch->RemoveFromRoom (); ch->SendToRoom (pWasInRoom); } } void do_kill (CCharacter *ch, char *argument) { char arg [MAX_INPUT_LENGTH]; CCharacter *victim; one_argument (argument, arg); if (arg [0] == '\0') { ch->SendText ("Kill whom?\n\r"); return; } if ((victim = get_char_room (ch, arg)) == NULL) { ch->SendText ("They aren't here.\n\r"); return; } if (! victim->IsNpc ()) { if (! victim->IsKiller () && ! victim->IsThief ()) { ch->SendText ("You must MURDER a player.\n\r"); return; } } if (victim == ch) { ch->SendText ("You hit yourself. Ouch!\n\r"); multi_hit (ch, ch, TYPE_UNDEFINED); return; } if (is_safe (ch, victim)) return; if (ch->IsCharmed () && ch->GetMaster () == victim) { act (AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR); return; } if (ch->IsFightPosition ()) { ch->SendText ("You do the best you can!\n\r"); return; } WAIT_STATE (ch, 1 * PULSE_VIOLENCE); check_attacker (ch, victim); multi_hit (ch, victim, TYPE_UNDEFINED); } void do_murde (CCharacter *ch, char *argument) { ch->SendText ("If you want to MURDER, spell it out.\n\r"); return; } void do_murder (CCharacter *ch, char *argument) { char buf [MAX_STRING_LENGTH]; char arg [MAX_INPUT_LENGTH]; CCharacter *victim; one_argument (argument, arg); if (arg [0] == '\0') { ch->SendText ("Murder whom?\n\r"); return; } if ((victim = get_char_room (ch, arg)) == NULL) { ch->SendText ("They aren't here.\n\r"); return; } if (victim == ch) { ch->SendText ("Suicide is a mortal sin.\n\r"); return; } if (is_safe (ch, victim)) return; if (ch->IsCharmed ()) { if (ch->GetMaster () == victim) { act (AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR); return; } else { if (ch->GetMaster ()) ch->GetMaster ()->SetAttacker (); } } if (ch->IsFightPosition ()) { ch->SendText ("You do the best you can!\n\r"); return; } if (! victim->IsNpc () && ch->IsNice ()) { ch->SendText ("You feel too nice to do that!\n\r"); return; } // if (! victim->IsNpc () && IS_SET (victim->act, PLR_PK)) if (! victim->IsNpc ()) { sprintf (log_buf, "%s: murder %s.", ch->GetName (), victim->GetName ()); gpDoc->LogString (log_buf, LOG_NORMAL, ch->GetLevel ()); } WAIT_STATE (ch, 1 * PULSE_VIOLENCE); sprintf (buf, "Help! I am being attacked by %s!", ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()); if (victim->IsPkiller ()) do_wartalk (victim, buf); else do_yell (victim, buf); check_illegal_pk (ch, victim); check_attacker (ch, victim); multi_hit (ch, victim, TYPE_UNDEFINED); } BOOL in_arena (CCharacter *ch) { // if (IS_SET (ch->in_room->room_flags, ROOM_ARENA)) // return TRUE; // if (IS_SET (ch->in_room->area->flags, AFLAG_FREEKILL)) // return TRUE; if (ch->GetInRoom ()->GetArea ()->m_Filename == "arena.are") return TRUE; if (ch->GetInRoom ()->vnum < 29 || ch->GetInRoom ()->vnum > 43) return FALSE; return TRUE; } BOOL check_illegal_pk (CCharacter *ch, CCharacter *victim) { if (! victim->IsNpc () && ! ch->IsNpc ()) { if ((! victim->IsPkiller () || ch->GetLevel () - victim->GetLevel () > 10 || ! ch->IsPkiller ()) && !in_arena (ch) && ch != victim && ! (ch->IsImmortal () && victim->IsImmortal ())) { sprintf (log_buf, "%s performing illegal pkill on %s at %d", (ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()), victim->GetName (), victim->GetInRoom ()->vnum); gpDoc->LogString (log_buf, LOG_PLAYER); to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL); return TRUE; } } return FALSE; } void do_flee (CCharacter *ch, char *argument) { CRoomIndexData *was_in; CRoomIndexData *now_in; char buf[MAX_STRING_LENGTH]; int attempt, los; short door; CExitData *pexit; if (! ch->GetFightWho ()) { if (ch->IsFightPosition ()) { if (ch->mount) ch->SetPosition (POS_MOUNTED); else ch->SetPosition (POS_STANDING); } ch->SendText ("You aren't fighting anyone.\n\r"); return; } if (ch->IsBeserk ()) { ch->SendText ("You aren't thinking very clearly...\n\r"); return; } if (ch->GetMove () <= 0) { ch->SendText ("You're too exhausted to flee from combat!\n\r"); return; } // No fleeing while more aggressive than standard or hurt. - Haus if (ch->IsNpc () && ch->GetPosition () < POS_FIGHTING) { ch->SendText ("Not right now ...\n\r"); return; } if (ch->IsNpc () && ch->GetPosition () <= POS_SLEEPING) return; was_in = ch->GetInRoom (); for (attempt = 0; attempt < 8; attempt++) { door = number_door (); if ((pexit = get_exit (was_in, door)) == NULL || !pexit->GetToRoom () // || IS_SET (pexit->exit_info, EX_NOFLEE) || (pexit->IsClosed () && ! ch->CanPass ()) || (ch->IsNpc () && pexit->GetToRoom ()->IsNoMob ())) continue; affect_strip (ch, gsn_sneak); ch->ClrSneak (); if (ch->mount && ch->mount->GetFightData ()) stop_fighting (ch->mount, TRUE); move_char (ch, pexit, 0); if ((now_in = ch->GetInRoom ()) == was_in) continue; ch->SetInRoom (was_in); act (AT_FLEE, "$n flees head over heels!", ch, NULL, NULL, TO_ROOM); ch->SetInRoom (now_in); act (AT_FLEE, "$n glances around for signs of pursuit.", ch, NULL, NULL, TO_ROOM); if (! ch->IsNpc ()) { CCharacter *wf = ch->GetFightWho (); los = (int) ((exp_level (ch, ch->GetLevel ()+1) - exp_level (ch, ch->GetLevel ())) * 0.03); if (! ch->IsImmortal ()) { sprintf (buf, "You flee head over heels from combat, " "losing %d experience.", los); act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR); } else act (AT_FLEE, "You flee head over heels from combat!", ch, NULL, NULL, TO_CHAR); gain_exp (ch, 0 - los); if (wf && ch->GetPcData ()->deity) { int level_ratio = URANGE (1, wf->GetLevel () / ch->GetLevel (), 50); if (wf && wf->GetRace () == ch->GetPcData ()->deity->npcrace) adjust_favor (ch, 1, level_ratio); else if (wf && wf->GetRace () == ch->GetPcData ()->deity->npcfoe) adjust_favor (ch, 16, level_ratio); else adjust_favor (ch, 0, level_ratio); } } stop_fighting (ch, TRUE); return; } los = (int) ((exp_level (ch, ch->GetLevel ()+1) - exp_level (ch, ch->GetLevel ())) * 0.01); if (! ch->IsImmortal ()) { sprintf (buf, "You attempt to flee from combat, losing %d " "experience.\n\r", los); act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR); } else act (AT_FLEE, "You attempt to flee from combat, but can't escape!", ch, NULL, NULL, TO_CHAR); gain_exp (ch, 0 - los); } void do_sla (CCharacter *ch, char *argument) { ch->SendText ("If you want to SLAY, spell it out.\n\r"); return; } void do_slay (CCharacter *ch, char *argument) { CCharacter *victim; char arg [MAX_INPUT_LENGTH]; char arg2 [MAX_INPUT_LENGTH]; argument = one_argument (argument, arg); one_argument (argument, arg2); if (arg [0] == '\0') { ch->SendText ("Slay whom?\n\r"); return; } if ((victim = get_char_room (ch, arg)) == NULL) { ch->SendText ("They aren't here.\n\r"); return; } if (ch == victim) { ch->SendText ("Suicide is a mortal sin.\n\r"); return; } if (! victim->IsNpc () && victim->GetTrustLevel () >= ch->GetTrustLevel ()) { ch->SendText ("You failed.\n\r"); return; } if (! str_cmp (arg2, "immolate")) { act (AT_FIRE, "Your fireball turns $N into a blazing inferno.", ch, NULL, victim, TO_CHAR ); act (AT_FIRE, "$n releases a searing fireball in your direction.", ch, NULL, victim, TO_VICT ); act (AT_FIRE, "$n points at $N, who bursts into a flaming inferno.", ch, NULL, victim, TO_NOTVICT); } else if (! str_cmp (arg2, "shatter")) { act (AT_LBLUE, "You freeze $N with a glance and shatter the " "frozen corpse into tiny shards.", ch, NULL, victim, TO_CHAR); act (AT_LBLUE, "$n freezes you with a glance and shatters your " "frozen body into tiny shards.", ch, NULL, victim, TO_VICT); act (AT_LBLUE, "$n freezes $N with a glance and shatters the " "frozen body into tiny shards.", ch, NULL, victim, TO_NOTVICT); } else if (!str_cmp (arg2, "demon")) { act (AT_IMMORT, "You gesture, and a slavering demon appears. With " "a horrible grin, the", ch, NULL, victim, TO_CHAR); act (AT_IMMORT, "foul creature turns on $N, who screams in panic " "before being eaten alive.", ch, NULL, victim, TO_CHAR); act (AT_IMMORT, "$n gestures, and a slavering demon appears. The " "foul creature turns on", ch, NULL, victim, TO_VICT); act (AT_IMMORT, "you with a horrible grin. You scream in panic " "before being eaten alive.", ch, NULL, victim, TO_VICT); act (AT_IMMORT, "$n gestures, and a slavering demon appears. With " "a horrible grin, the", ch, NULL, victim, TO_NOTVICT); act (AT_IMMORT, "foul creature turns on $N, who screams in panic " "before being eaten alive.", ch, NULL, victim, TO_NOTVICT); } else if (! str_cmp (arg2, "pounce") && ch->GetTrustLevel () >= LEVEL_ASCENDANT) { act (AT_BLOOD, "Leaping upon $N with bared fangs, you tear open $S " "throat and toss the corpse to the ground...", ch, NULL, victim, TO_CHAR); act (AT_BLOOD, "In a heartbeat, $n rips $s fangs through your " "throat! Your blood sprays and pours to the ground as your " "life ends...", ch, NULL, victim, TO_VICT); act (AT_BLOOD, "Leaping suddenly, $n sinks $s fangs into $N's " "throat. As blood sprays and gushes to the ground, $n tosses" " $N's dying body away.", ch, NULL, victim, TO_NOTVICT); } else if (! str_cmp (arg2, "slit") && ch->GetTrustLevel () >= LEVEL_ASCENDANT) { act (AT_BLOOD, "You calmly slit $N's throat.", ch, NULL, victim, TO_CHAR); act (AT_BLOOD, "$n reaches out with a clawed finger and calmly " "slits your throat.", ch, NULL, victim, TO_VICT); act (AT_BLOOD, "$n calmly slits $N's throat.", ch, NULL, victim, TO_NOTVICT); } else if (! str_cmp (arg2, "dog")) { act( AT_BLOOD, "You order your dogs to rip $N to shreds.", ch, NULL, victim, TO_CHAR ); act( AT_BLOOD, "$n orders $s dogs to rip you apart.", ch, NULL, victim, TO_VICT ); act( AT_BLOOD, "$n orders $s dogs to rip $N to shreds.", ch, NULL, victim, TO_NOTVICT ); } else { act (AT_IMMORT, "You slay $N in cold blood!", ch, NULL, victim, TO_CHAR ); act (AT_IMMORT, "$n slays you in cold blood!", ch, NULL, victim, TO_VICT ); act (AT_IMMORT, "$n slays $N in cold blood!", ch, NULL, victim, TO_NOTVICT); } set_cur_char (victim); raw_kill (ch, victim); }