/// <summary> /// Reports current condition verbally. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Report(CharData ch, string[] str) { string arg = String.Empty; if (ch == null) { Log.Error("Commandreport: null ch.", 0); return; } if (!ch.CanSpeak()) { ch.SendText("Your lips move but no sound comes out.\r\n"); return; } string buf = String.Empty; if (str.Length == 0) { buf += "&nYou report: " + ch.Hitpoints + "/" + ch.GetMaxHit() + " hp " + ch.CurrentMana + "/" + ch.MaxMana + " mana " + ch.CurrentMoves + "/" + ch.MaxMoves + " mv.\r\n"; ch.SendText(buf); buf += "$n&n reports: " + ch.Hitpoints + "/" + ch.GetMaxHit() + " hp " + ch.CurrentMana + "/" + ch.MaxMana + " mana " + ch.CurrentMoves + "/" + ch.MaxMoves + " mv.\r\n"; SocketConnection.Act(buf, ch, null, null, SocketConnection.MessageTarget.room); return; } //report is essentially a tell, why not use that code? buf += arg + " reporting: " + ch.Hitpoints + "/" + ch.GetMaxHit() + " hp " + ch.CurrentMana + "/" + ch.MaxMana + " mana " + ch.CurrentMoves + "/" + ch.MaxMoves + " mv.\r\n"; CommandType.Interpret(ch, "tell " + buf); return; }
/// <summary> /// Shows a character's score screen. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Score(CharData ch, string[] str) { if( ch == null ) return; string text = String.Empty; if (ch == null) { Log.Error("Command.Score(): null ch.", 0); return; } Affect prev; text += "&+WName: &+G" + ch.Name + "&n" + (ch.IsNPC() ? String.Empty : ((PC)ch).Title) + "\r\n"; text += "&+WRace:&n " + MUDString.PadStr(Race.RaceList[ch.GetRace()].ColorName, 16); text += "&+WClass:&n " + MUDString.PadStr(ch.CharacterClass.WholistName, 32) + "&n\r\n"; text += "&+WLevel: " + MUDString.PadInt(ch.Level, 5) + " Played: " + (ch.TimePlayed.Hours) + " hours &+WSex: "; text += System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(ch.GetSexString()) + "\r\n"; if (ch.Fighting == null) { text += "&+WExperience: &+B" + StringConversion.ExperienceString(ch) + "&n\r\n\r\n"; } text += "&+WCurrent/Max Health: [&n&+g" + MUDString.PadInt(ch.Hitpoints, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.GetMaxHit(), 5); text += "&+W] Coins: Carried In Bank\r\n"; text += "&+WCurrent/Max Moves: [&n&+g" + MUDString.PadInt(ch.CurrentMoves, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.MaxMoves, 5); text += "&+W] &+WPlatinum " + MUDString.PadInt(ch.GetPlatinum(), 5) + " "; text += (ch.IsNPC() ? 0 : ((PC)ch).Bank.Platinum) + "\r\n"; text += "Current/Max Mana: [&n&+g" + MUDString.PadInt(ch.CurrentMana, 5) + "&+W / &n&+g" + MUDString.PadInt(ch.MaxMana, 5); text += "&+W] &+YGold " + MUDString.PadInt(ch.GetGold(), 5) + " " + (ch.IsNPC() ? 0 : ((PC)ch).Bank.Gold) + "\r\n"; text += " &n&+wSilver " + MUDString.PadInt(ch.GetSilver(), 5) + " "; text += (ch.IsNPC() ? 0 : ((PC)ch).Bank.Silver) + "\r\n"; text += "&+WFrags: &+W" + MUDString.PadInt((ch.IsNPC() ? 0 : ((PC)ch).Frags), 3) + "&n &n&+yCopper "; text += MUDString.PadInt(ch.GetCopper(), 5) + " " + (ch.IsNPC() ? 0 : ((PC)ch).Bank.Copper) + "\r\n"; if (!ch.IsNPC()) { text += "&+WTotal Deaths: &+W" + MUDString.PadInt(((PC)ch).MobDeaths + ((PC)ch).PlayerDeaths, 5) + "&n &+WMobs Killed: &+W"; text += MUDString.PadInt(((PC)ch).MobKills, 5) + "&n\r\n&+WPlayers Killed: &+W" + MUDString.PadInt(((PC)ch).PlayerKills, 5); text += "&n &+WPlayer Deaths: &+W" + MUDString.PadInt(((PC)ch).PlayerDeaths, 5) + "&n\r\n"; } if (!ch.IsNPC()) { int divisor = ((PC)ch).Created.Quantity; if (divisor == 0) divisor = 1; text += String.Format("&+WItems Created: &n{0} &+WTotal Value: &n{1} &+WBest: &n{2} &+WAvg: &n{3}\r\n", MUDString.PadInt(((PC)ch).Created.Quantity, 5), MUDString.PadInt(((PC)ch).Created.TotalCost, 5), MUDString.PadInt(((PC)ch).Created.MaxCost, 5), MUDString.PadInt((((PC)ch).Created.TotalCost / divisor), 5)); divisor = ((PC)ch).Destroyed.Quantity; if (divisor == 0) divisor = 1; text += String.Format("&+WItems Destroyed: &n{0} &+WTotal Value: &n{1} &+WBest: &n{2} &+WAvg: &n{3}\r\n", MUDString.PadInt(((PC)ch).Destroyed.Quantity, 5), MUDString.PadInt(((PC)ch).Destroyed.TotalCost, 5), MUDString.PadInt(((PC)ch).Destroyed.MaxCost, 5), MUDString.PadInt((((PC)ch).Destroyed.TotalCost / divisor), 5)); } if (!ch.IsNPC()) { text += "&+WTotal Score: &+W" + ((PC)ch).Score + "&n\r\n"; } if (ch.IsClass(CharClass.Names.monk) || ch.IsClass(CharClass.Names.mystic)) { text += "&+WTradition: &+B" + TraditionData.Names[((PC)ch).Tradition] + "&n\r\n"; text += "&+WTraining Points: &+B" + (ch.IsNPC() ? 0 : ((PC)ch).SkillPoints) + "&n\r\n"; } if (ch.Followers != null && ch.Followers.Count > 0) { text += "&+BFollowers:&n\r\n"; foreach (CharData follower in ch.Followers) { if (follower == null) { continue; } text += follower.ShowNameTo(ch, true) + " &n\r\n"; } text += "\r\n"; } if (ch.IsAffected( Affect.AFFECT_POISON)) { text += "&+GYou are poisoned.&n\r\n"; } if ((ch.IsAffected( Affect.AFFECT_DETECT_MAGIC) || ch.IsImmortal()) && MUDString.StringsNotEqual(BitvectorFlagType.AffectString(ch.AffectedBy, true), "none")) { text += "&+BEnchantments: &+W" + BitvectorFlagType.AffectString(ch.AffectedBy, true) + "&n\r\n\r\n"; } if (ch.Affected != null) { bool printed = false; prev = null; foreach (Affect affect in ch.Affected) { if (!printed) { text += "&+BActive Spells:&+W\r\n"; printed = true; } /* Show only new affects to mortals. */ if (prev != null && prev.Value == affect.Value && prev.Type == affect.Type && !ch.IsImmortal()) { prev = affect; continue; } prev = affect; if (affect.Type == Affect.AffectType.skill && !String.IsNullOrEmpty(affect.Value) && ch.IsImmortal()) { text += MUDString.CapitalizeANSIString( Skill.SkillList[affect.Value].Name ); } else if (affect.Type == Affect.AffectType.skill && !String.IsNullOrEmpty(affect.Value)) { continue; } else if (affect.Type == Affect.AffectType.spell && !String.IsNullOrEmpty(affect.Value)) { text += MUDString.CapitalizeANSIString(Spell.SpellList[affect.Value].Name); } else if (affect.Type == Affect.AffectType.song && !String.IsNullOrEmpty(affect.Value)) { text += MUDString.CapitalizeANSIString(affect.Value); } else { text += "Something"; } if (ch.IsImmortal()) { foreach (AffectApplyType apply in affect.Modifiers) { text += " modifies " + StringConversion.AffectApplyString(apply.Location) + " by " + apply.Amount; } text += " for " + affect.Duration + " hours with bits " + affect.AffectString(false) + ".\r\n"; } else { if (affect.Duration == 0 && ch.IsAffected( Affect.AFFECT_DETECT_MAGIC)) { text += " (fading rapidly)\r\n"; } else if (affect.Duration == 1 && ch.IsAffected( Affect.AFFECT_DETECT_MAGIC)) { text += " (fading)\r\n"; } else { text += "\r\n"; } } } } text += "&n"; ch.SendText(text); return; }
/// <summary> /// Builds a condition string for a player. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static string ConditionString( CharData ch ) { int percent; if( ch.GetMaxHit() > 0 ) percent = ( 100 * ch.Hitpoints ) / ch.GetMaxHit(); else percent = -1; if( percent >= 100 ) return "&+gexcellent&n"; if( percent >= 90 ) return "&+ybarely &n&+gscratched&n"; if( percent >= 79 ) return "&+yslightly&+L &n&+gbruised&n"; if( percent >= 69 ) return "&+ylightly wounded&n"; if( percent >= 58 ) return "&+ymoderately &n&+mwou&+rnded&n"; if( percent >= 48 ) return "&+mseverely &n&+rwo&n&+yu&+rn&+yde&n&+rd&n"; if( percent >= 37 ) return "&n&+rbleeding&+y copiously&n"; if( percent >= 27 ) return "&+rbadly &n&+rwo&+Ru&n&+rn&+Rded&n"; if( percent >= 16 ) return "&+yin &n&+R awful shape&n"; if( ch.Hitpoints > -3 ) return "&+Rnearly&+L &n&+rdead&n"; if( ch.Hitpoints > -5 ) return "&+Lincapacitated, and &+Rbl&n&+re&+Re&n&+rdi&+Rn&n&+rg&+L to death&n"; if( ch.Hitpoints >= -10 ) return "&+rmortally &+Rw&n&+rou&+Rnd&N&+re&+Rd&n"; return "&+Wdead&n"; }
/// <summary> /// Command to set your level of aggressiveness. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Aggressive(CharData ch, string[] str) { if( ch == null ) return; string arg1 = String.Empty; if (ch.IsNPC()) return; if (ch.IsClass(CharClass.Names.paladin)) { ch.SendText("Your beliefs prevent you from acting in such a manner.\r\n"); return; } if (arg1.Length > 0) { if (MUDString.NameContainedIn("off", arg1) || MUDString.NameContainedIn("none", arg1)) { ((PC)ch).AggressiveLevel = -1; } else { int aggr; Int32.TryParse(arg1, out aggr); if (aggr < -1 || aggr > ch.GetMaxHit()) { ch.SendText("Value out of range.\r\n"); return; } ((PC)ch).AggressiveLevel = aggr; } } string buf; if (((PC)ch).AggressiveLevel == -1) { buf = "You are not aggressive.\r\n"; } else { buf = "You are aggressive to creatures if hit points above " + ((PC)ch).AggressiveLevel + ".\r\n"; } ch.SendText(buf); return; }
/// <summary> /// Ingest a liquid. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Drink(CharData ch, string[] str) { if( ch == null ) return; Object obj = null; if (ch.IsBlind()) { return; } if (ch.Fighting || ch.CurrentPosition == Position.fighting) { ch.SendText("You can't drink while you're fighting!\r\n"); return; } if (str.Length == 0 && ch.InRoom != null) { foreach (Object iobj in ch.InRoom.Contents) { if (iobj.ItemType == ObjTemplate.ObjectType.drink_container) { obj = iobj; break; } } if (!obj) { ch.SendText("Drink what?\r\n"); return; } } else { if (!(obj = ch.GetObjHere(str[0]))) { ch.SendText("You can't find it.\r\n"); return; } } // Allow bards to get twice as drunk as other classes - Xangis if (!ch.IsNPC() && !ch.IsImmortal() && ((PC)ch).Drunk > 15 && ch.IsClass(CharClass.Names.bard) && MUDMath.NumberPercent() < ch.GetCurrAgi() - ((PC)ch).Drunk) { ch.SendText("You fail to reach your mouth. *Hic*\r\n"); return; } if (!ch.IsNPC() && !ch.IsImmortal() && ((PC)ch).Drunk > 25 && ch.IsClass(CharClass.Names.bard) && MUDMath.NumberPercent() < ch.GetCurrAgi() - ((PC)ch).Drunk) { ch.SendText("You fail to reach your mouth. *Hic*\r\n"); return; } switch (obj.ItemType) { default: ch.SendText("You can't drink from that.\r\n"); break; case ObjTemplate.ObjectType.drink_container: // -1 Means a container never goes empty. if (obj.Values[1] <= 0 && obj.Values[1] != -1) { ch.SendText("It is already &+Lempty&n.\r\n"); return; } /* No drinking if you're full */ if ((!ch.IsImmortal()) && ( (!ch.IsNPC() && ((PC)ch).Thirst > 40) || (!ch.IsNPC() && ((PC)ch).Hunger > 50))) { ch.SendText("You couldn't possibly drink any more.\r\n"); return; } int liquid; if ((liquid = obj.Values[2]) >= Liquid.Table.Length) { Log.Error("Drink: bad liquid number {0}.", liquid); liquid = obj.Values[2] = 0; } SocketConnection.Act("You drink $T from $p&n.", ch, obj, Liquid.Table[liquid].Name, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n drinks $T from $p&n.", ch, obj, Liquid.Table[liquid].Name, SocketConnection.MessageTarget.room); int amount = MUDMath.NumberRange(3, 10); if (obj.Values[0] != -1) { amount = Math.Min(amount, obj.Values[1]); } ch.AdjustDrunk(amount * Liquid.Table[liquid].DrunkValue); if (!ch.IsUndead()) { ch.AdjustHunger(amount * Liquid.Table[liquid].HungerValue); if (ch.IsAffected(Affect.AFFECT_THIRST)) { ch.AdjustThirst((amount * Liquid.Table[liquid].ThirstValue) / 12); ch.SendText("That doesn't taste as &+bwet&n as it used to.\r\n"); } else { ch.AdjustThirst(amount * Liquid.Table[liquid].ThirstValue); } } else { /* If blood */ if (Liquid.Table[liquid].Name == "blood") { ch.AdjustHunger(amount * 2); ch.AdjustThirst(amount); } } if (!ch.IsNPC() && ((PC)ch).Drunk > 10) { ch.SendText("You feel &n&+gdrunk&n.\r\n"); } if (!ch.IsNPC() && ((PC)ch).Hunger > 20) { ch.SendText("You are &n&+yfull&n.\r\n"); } if (!ch.IsNPC() && ((PC)ch).Thirst > 20) { ch.SendText("You do not feel &n&+cth&+Ci&n&+cr&+Cst&n&+cy&n.\r\n"); } if (obj.Values[3] != 0 && !CharData.CheckImmune(ch, Race.DamageType.poison)) { /* The shit was poisoned ! */ Affect af = new Affect(); ch.SendText("You choke and gag.\r\n"); SocketConnection.Act("$n chokes and gags.", ch, null, null, SocketConnection.MessageTarget.room); af.Type = Affect.AffectType.spell; af.Value = "poison"; af.Duration = 3 * amount; af.AddModifier(Affect.Apply.strength, -(obj.Level / 7 + 1)); af.SetBitvector(Affect.AFFECT_POISON); ch.CombineAffect(af); } /* HOLY_WATER and UNHOLY_WATER effects */ if ((ch.IsGood() && obj.Values[2] == 27) || (ch.IsEvil() && obj.Values[2] == 28)) { int heal = MUDMath.Dice(1, 8); if (ch.Hitpoints < ch.GetMaxHit()) { ch.Hitpoints = Math.Min(ch.Hitpoints + heal, ch.GetMaxHit()); ch.UpdatePosition(); ch.SendText("You feel a little better!\r\n"); } } if ((ch.IsEvil() && obj.Values[2] == 27) || (ch.IsGood() && obj.Values[2] == 28)) { int harm = MUDMath.Dice(1, 10); ch.Hitpoints = Math.Max(ch.Hitpoints - harm, -10); ch.SendText("You choke and feel as if you'd swallowed boiling oil!\r\n"); ch.UpdatePosition(); } /* End (UN)HOLY_WATER effects */ // -1 Means a container never goes empty. if (obj.Values[1] != -1) { obj.Values[1] -= amount; if (obj.Values[1] <= 0) { ch.SendText("The container is now &+Lempty&n.\r\n"); obj.Values[1] = 0; } } break; } return; }
/// <summary> /// Creates a character based on an object. Used for animate-object-type spells. /// </summary> /// <param name="obj"></param> /// <returns></returns> public CharData CreateCharacterFromObject( ref Object obj ) { int count; MobTemplate mobTemplate = Database.GetMobTemplate( StaticMobs.MOB_NUMBER_OBJECT ); if( !mobTemplate ) { Log.Error( "CreateCharacterFromObject: null object template.", 0 ); return null; } CharData mob = new CharData(); mob.MobileTemplate = mobTemplate; mob.Name = obj._name; mob.ShortDescription = obj._shortDescription; mob.FullDescription = obj._fullDescription; mob.Description = obj._fullDescription; mob.CharacterClass = mobTemplate.CharacterClass; mob.Level = Math.Max( obj._level, 1 ); mob.ActionFlags = mobTemplate.ActionFlags; mob.CurrentPosition = mobTemplate.DefaultPosition; for( count = 0; count < Limits.NUM_AFFECT_VECTORS; ++count ) { mob.AffectedBy[ count ] = mobTemplate.AffectedBy[ count ]; } mob.Alignment = mobTemplate.Alignment; mob.Gender = mobTemplate.Gender; mob.SetPermRace( mobTemplate.Race ); mob.CurrentSize = Race.RaceList[ mob.GetRace() ].DefaultSize; if (mob.HasActionBit(MobTemplate.ACT_SIZEMINUS)) mob.CurrentSize--; if (mob.HasActionBit(MobTemplate.ACT_SIZEPLUS)) mob.CurrentSize++; mob.CastingSpell = 0; mob.CastingTime = 0; mob.PermStrength = 55; mob.PermIntelligence = 55; mob.PermWisdom = 55; mob.PermDexterity = 55; mob.PermConstitution = 55; mob.PermAgility = 55; mob.PermCharisma = 55; mob.PermPower = 55; mob.PermLuck = 55; mob.ModifiedStrength = 0; mob.ModifiedIntelligence = 0; mob.ModifiedWisdom = 0; mob.ModifiedDexterity = 0; mob.ModifiedConstitution = 0; mob.ModifiedAgility = 0; mob.ModifiedCharisma = 0; mob.ModifiedPower = 0; mob.ModifiedLuck = 0; mob.Resistant = mobTemplate.Resistant; mob.Immune = mobTemplate.Immune; mob.Susceptible = mobTemplate.Susceptible; mob.Vulnerable = mobTemplate.Vulnerable; mob.SetCoins( 0, 0, 0, 0 ); mob.ArmorPoints = MUDMath.Interpolate( mob.Level, 100, -100 ); // * MOB HITPOINTS * // // Was level d 8, upped it to level d 13 // considering mobs *still* won't have as many hitpoints as some players until // at least lvl 10, this shouldn't be too big an upgrade. // // Mob hitpoints are not based on constitution *unless* they have a // constitution modifier from an item, spell, or other affect mob.MaxHitpoints = mob.Level * 100; mob.Hitpoints = mob.GetMaxHit(); /* * Insert in list. */ Database.CharList.Add( mob ); // Increment in-game count of mob. mobTemplate.NumActive++; mob.AddToRoom( obj._inRoom ); return mob; }
/// <summary> /// Builds condition meter of style #1 for a player. /// </summary> /// <param name="ch"></param> /// <returns></returns> static string ConditionMeter( CharData ch ) { int percent; if( ch.GetMaxHit() > 0 ) percent = ( 100 * ch.Hitpoints ) / ch.GetMaxHit(); else percent = -1; if( percent >= 100 ) return "&+L(&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&+L)&n"; if( percent >= 90 ) return "&+L(&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&+R=&+L)&n"; if( percent >= 79 ) return "&+L(&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&+R=&n&+r-&+L)&n"; if( percent >= 69 ) return "&+L(&+C-&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&+R=&n&+r-&+R=&+L)&n"; if( percent >= 58 ) return "&+L(&n&+c=&+C-&n&+c=&+C-&n&+c=&+C-&+R=&n&+r-&+R=&n&+r-&+L)&n"; if( percent >= 48 ) return "&+L(&+C-&n&+c=&+C-&n&+c=&+C-&+R=&n&+r-&+R=&n&+r-&+R=&+L)&n"; if( percent >= 37 ) return "&+L(&n&+c=&+C-&n&+c=&+C-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+L)&n"; if( percent >= 27 ) return "&+L(&+C-&n&+c=&+C-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&+L)&n"; if( percent >= 16 ) return "&+L(&n&+c=&+C-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+L)&n"; if( percent >= 6 ) return "&+L(&+C-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&+L)&n"; return "&+L(&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+R=&n&+r-&+L)&n"; }
public static void Wimpy(CharData ch, string[] str) { if( ch == null ) return; int wimpy; if (str.Length == 0) { wimpy = ch.Wimpy; } else { Int32.TryParse(str[0], out wimpy); } if (wimpy < 0) { ch.SendText("&nYour courage exceeds your wisdom.\r\n"); return; } if (wimpy > ch.GetMaxHit()) { ch.SendText("&nSuch cowardice ill becomes you.\r\n"); return; } ch.Wimpy = wimpy; string text = String.Format("&nWimpy set to {0} hit points.\r\n", wimpy); ch.SendText(text); return; }
/// <summary> /// Inflicts damage from a spell, based on the weapon damage() function, but customized for spells. /// /// Needs to be cleaned up because it's just too big (600+ lines). /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <param name="dam"></param> /// <param name="spell"></param> /// <param name="damType"></param> /// <returns></returns> public static bool InflictSpellDamage( CharData ch, CharData victim, int dam, Spell spell, AttackType.DamageType damType ) { if( ch == null || victim == null || victim.CurrentPosition == Position.dead ) return true; // Remove memorization and meditation bits. // And invis. ch.BreakInvisibility(); victim.BreakMeditate(); victim.BreakMemorization(); if( CheckShrug( ch, victim ) ) return false; if( victim.CurrentPosition == Position.sleeping && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) { SocketConnection.Act( "$n&n has a rude awakening!", victim, null, null, SocketConnection.MessageTarget.room ); victim.CurrentPosition = Position.resting; if( ch.InRoom == victim.InRoom && ch.FlightLevel == victim.FlightLevel ) SetFighting( victim, ch ); } // Check for globe spells. See also FinishSpell under TargetType.singleCharacterOffensive // This check here is just to prevent area effect spells from // doing damage if too low level. The check for direct spells is in // Magic.cs if (victim.IsAffected( Affect.AFFECT_MAJOR_GLOBE) && (spell.SpellCircle[(int)ch.CharacterClass.ClassNumber] <= 6 || spell.Name == "fireshield" || spell.Name == "shockshield" || spell.Name == "soulshield" || spell.Name == "coldshield" ) ) { SocketConnection.Act( "&+RThe globe around $N&+R's body bears the brunt of your assault!&n", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return false; } if (victim.IsAffected( Affect.AFFECT_GREATER_SPIRIT_WARD) && spell.SpellCircle[(int)ch.CharacterClass.ClassNumber] <= 5) { SocketConnection.Act( "&+WThe aura around $N&+W's body bears the brunt of your assault!&n", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return false; } if (victim.IsAffected( Affect.AFFECT_MINOR_GLOBE) && spell.SpellCircle[(int)ch.CharacterClass.ClassNumber] <= 4) { SocketConnection.Act( "&+RThe globe around $N&+R's body bears the brunt of your assault!&n", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return false; } if (victim.IsAffected( Affect.AFFECT_SPIRIT_WARD) && spell.SpellCircle[(int)ch.CharacterClass.ClassNumber] <= 3) { SocketConnection.Act( "&+WThe aura around $N&+W's body bears the brunt of your assault!&n", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, SocketConnection.MessageTarget.victim ); return false; } /* * Stop up any residual loopholes. */ // 1275 is average damage from Akiaurn's Power Word // I changed this to reflect that. if( ( dam > 1275 ) && ch.Level < Limits.LEVEL_AVATAR && ch.GetRace() != Race.RACE_DRAGON ) { string buf3; if( ch.IsNPC() && ch.Socket ) buf3 = String.Format( "Spell_Damage: {0} from {1} by {2}: > 1275 points with {3} spell!", dam, ch.Name, ch.Socket.Original.Name, spell.Name ); else buf3 = String.Format( "Spell_Damage: {0} from {1}: > 1275 points with {2} spell!", dam, ch.IsNPC() ? ch.ShortDescription : ch.Name, spell.Name ); Log.Error( buf3, 0 ); dam = 1275; } if (victim.IsAffected( Affect.AFFECT_MINOR_PARA) && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) { SocketConnection.Act( "$n&n disrupts the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); SocketConnection.Act( "You disrupt the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+YYou can move again.&n", ch, null, victim, SocketConnection.MessageTarget.victim ); victim.RemoveAffect( Affect.AFFECT_MINOR_PARA ); victim.AffectStrip( Affect.AffectType.spell, "earthen grasp" ); victim.AffectStrip( Affect.AffectType.spell, "greater earthen grasp"); } bool immune = false; if( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ if( IsSafe( ch, victim ) ) return false; // is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( !victim ) return true; Crime.CheckAttemptedMurder( ch, victim ); if( victim.CurrentPosition > Position.stunned && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) { // Offensive spells engage victim if not fighting, and // caster only if neither are fighting. if( !victim.Fighting && victim.InRoom == ch.InRoom && victim.FlightLevel == ch.FlightLevel ) { SetFighting( victim, ch ); if( !ch.Fighting ) SetFighting( ch, victim ); // Can't have prone people automaticaly stand. if( victim.CurrentPosition == Position.standing ) victim.CurrentPosition = Position.fighting; } /* * If NPC victim is following, ch might attack victim's master. * No charm check here because charm would be dispelled from * tanking mobile when combat ensues thus ensuring PC charmer is * not harmed. * Check for is_same_group wont work as following mobile is not * always grouped with PC charmer */ if( ch.IsNPC() && victim.IsNPC() && victim.Master && victim.Master.InRoom == ch.InRoom && MUDMath.NumberBits( 2 ) == 0 ) { StopFighting( ch, false ); SetFighting( ch, victim.Master ); return false; } } /* * More charm stuff. */ if( victim.Master == ch && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) StopFighting( victim, true ); /* * Hunting stuff... */ if( dam != 0 && victim.IsNPC() && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) { StartGrudge( victim, ch, false ); } /* * Damage modifiers. */ if (victim.IsAffected( Affect.AFFECT_SANCTUARY)) dam /= 2; if ((victim.IsAffected( Affect.AFFECT_PROTECT_EVIL)) && ch.IsEvil()) dam -= dam / 8; else if ((victim.IsAffected( Affect.AFFECT_PROTECT_GOOD)) && ch.IsGood()) dam -= dam / 8; if( dam < 0 ) dam = 0; } switch( victim.CheckRIS( damType ) ) { case Race.ResistanceType.resistant: dam -= dam / 3; break; case Race.ResistanceType.immune: immune = true; dam = 0; break; case Race.ResistanceType.susceptible: dam += dam / 2; break; case Race.ResistanceType.vulnerable: dam *= 2; break; } if( ( damType == AttackType.DamageType.wind || damType == AttackType.DamageType.gas || damType == AttackType.DamageType.asphyxiation ) && victim.IsAffected( Affect.AFFECT_DENY_AIR)) { if( MUDMath.NumberPercent() < 50 ) { victim.SendText( "&+CYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_DENY_FIRE)) { if( MUDMath.NumberPercent() < 50 ) { victim.SendText( "&+rYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.earth || damType == AttackType.DamageType.crushing ) && victim.IsAffected(Affect.AFFECT_DENY_EARTH)) { if( MUDMath.NumberPercent() < 50 ) { victim.SendText( "&+yYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.water || damType == AttackType.DamageType.acid || damType == AttackType.DamageType.drowning ) && victim.IsAffected(Affect.AFFECT_DENY_WATER)) { if( MUDMath.NumberPercent() < 50 ) { victim.SendText( "&+bYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } // Check for protection spells that give 25% damage reduction - Xangis if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_PROTECT_FIRE)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.cold && victim.IsAffected( Affect.AFFECT_PROTECT_COLD)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.acid && victim.IsAffected( Affect.AFFECT_PROTECT_ACID)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.gas && victim.IsAffected( Affect.AFFECT_PROTECT_GAS)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.electricity && victim.IsAffected( Affect.AFFECT_PROTECT_LIGHTNING)) dam = ( dam * 3 ) / 4; /* * We moved DamageMessage out of the victim != ch if above * so self damage would show. Other valid type_undefined * damage is ok to avoid like mortally wounded damage */ if( spell != Spell.SpellList["reserved"] && !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) SendSpellDamageMessage( ch, victim, dam, spell, immune ); /* PC to PC damage quartered. * NPC to PC damage divided by 3. */ if( dam > 0 && !victim.IsNPC() && victim != ch ) { if( !ch.IsNPC() ) dam /= 4; else dam /= 3; } /* * Hurt the victim. * Inform the victim of his new state. */ if( !( victim.GetRace() == Race.RACE_FIRE_ELE && damType == AttackType.DamageType.fire ) && !( victim.GetRace() == Race.RACE_WATER_ELE && damType == AttackType.DamageType.water ) && !( victim.GetRace() == Race.RACE_EARTH_ELE && damType == AttackType.DamageType.earth ) && !( victim.GetRace() == Race.RACE_AIR_ELE && damType == AttackType.DamageType.wind ) ) { /* Added damage exp! */ // chance added because people level faster and faster as they get higher level... // you can now only get damage exp on mobs that con easy or better // and there's only a 25% chance per hit of you evern being eligible for damage exp. if( MUDMath.NumberPercent() < 25 && victim.Level >= ( ch.Level - 3 ) ) ch.GainExperience( Math.Max( 1, dam / 20 ) ); victim.Hitpoints -= dam; } else { string attack; if( spell != null && spell != Spell.SpellList["none"] ) attack = spell.Name; else attack = "it"; SocketConnection.Act( "$N&n absorbs your $t!", ch, attack, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You absorb $n&n's $t!", ch, attack, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n absorbs $n&n's $t", ch, attack, victim, SocketConnection.MessageTarget.room_vict ); if( ch.IsImmortal() ) { string buf4 = String.Format( "You healed {0} damage.", victim.GetMaxHit() >= dam + victim.Hitpoints ? dam : victim.GetMaxHit() - victim.Hitpoints ); ch.SendText( buf4 ); } victim.Hitpoints = Math.Min( victim.GetMaxHit(), victim.Hitpoints + dam ); return false; } if( !victim.IsNPC() && victim.Level >= Limits.LEVEL_AVATAR && victim.Hitpoints < 1 ) victim.Hitpoints = 1; if (victim.IsAffected(Affect.AFFECT_BERZERK) && victim.CurrentPosition <= Position.stunned ) victim.RemoveAffect(Affect.AFFECT_BERZERK); victim.UpdatePosition(); switch( victim.CurrentPosition ) { case Position.mortally_wounded: victim.SendText( "&+LYou are &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n\r\n" ); SocketConnection.Act( "$n&+L is &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.incapacitated: victim.SendText( "&+LYou are incapacitated and will slowly &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.\r\n" ); SocketConnection.Act( "$n&+L is incapacitated and will slowly &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.stunned: victim.SendText( "&+LYou are stunned, but will probably recover.&n\r\n" ); SocketConnection.Act( "$n&+L is stunned, but will probably recover.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; case Position.dead: SocketConnection.Act( spell.MessageKill, ch, null, victim, SocketConnection.MessageTarget.room_vict ); SocketConnection.Act( spell.MessageKill, ch, null, victim, SocketConnection.MessageTarget.character ); if( victim == ch ) { victim.SendText( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L!&n\r\n\r\n" ); } else { string buf = String.Format( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L by&n {0}&+L!&n\r\n\r\n", ch.ShowNameTo( victim, false ) ); victim.SendText( buf ); } StopFighting( victim, true ); SocketConnection.Act( "$n&+L is &n&+rdead&+L!&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; default: if( dam > victim.GetMaxHit() / 5 ) victim.SendText( "That really did &+RHURT&n!\r\n" ); if( victim.Hitpoints < victim.GetMaxHit() / 10 ) victim.SendText( "You sure are &n&+rBL&+RE&n&+rE&+RDI&n&+rN&+RG&n!\r\n" ); break; } /* * Sleep spells and extremely wounded folks. */ if( !victim.IsAwake() ) /* lets make NPC's not slaughter PC's */ { if( victim.Fighting && victim.Fighting.Hunting && victim.Fighting.Hunting.Who == victim ) StopHunting( victim.Fighting ); if( victim.Fighting && !victim.IsNPC() && ch.IsNPC() ) StopFighting( victim, true ); else StopFighting( victim, false ); } /* * Payoff for killing things. */ if( victim.CurrentPosition == Position.dead ) { StopFighting( ch, false ); if( !victim.HasActionBit(MobTemplate.ACT_NOEXP ) || !victim.IsNPC() ) GroupExperienceGain( ch, victim ); if( !victim.IsNPC() ) { if( ch.IsNPC() ) { ( (PC)victim ).MobDeaths++; if( victim.IsGuild() ) { ( (PC)victim ).GuildMembership.MonsterDeaths++; ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); } else { ( (PC)ch ).PlayerKills++; ( (PC)victim ).PlayerDeaths++; ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); ( (PC)ch ).Score += CalculateKillScore( ch, victim ); if( ch.IsGuild() && victim.IsGuild() && ( (PC)ch ).GuildMembership != ( (PC)victim ).GuildMembership ) { ( (PC)ch ).GuildMembership.PlayerKills++; ( (PC)victim ).GuildMembership.PlayerDeaths++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } } string logBuf = String.Format( "{0}&n killed by {1}&n at {2}", victim.Name, ( ch.IsNPC() ? ch.ShortDescription : ch.Name ), victim.InRoom.IndexNumber ); Log.Trace( logBuf ); ImmortalChat.SendImmortalChat( ch, ImmortalChat.IMMTALK_DEATHS, Limits.LEVEL_AVATAR, logBuf ); /* * Dying penalty: * 1/2 way back to previous 2 levels. */ // Newbies do not lose exp from death. if( ch.Level > 5 ) victim.GainExperience( ( 0 - ( ( ( 50 + victim.Level ) * ExperienceTable.Table[ victim.Level ].LevelExperience ) / 400 ) ) ); if( victim.Level < 2 && victim.ExperiencePoints < 1 ) victim.ExperiencePoints = 1; } else { if( !ch.IsNPC() ) { ( (PC)ch ).MobKills++; if( ch.IsGuild() ) { ( (PC)ch ).GuildMembership.MonsterKills++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); } ( (PC)ch ).Score += CalculateKillScore( ch, victim ); } } KillingBlow( ch, victim ); // Keep in mind after this point the character is not in the // CharList, not in any room, and is at the menu. Don't do // anything that would cause a segmentation fault. if( ch.IsGuild() && victim.IsGuild() && ( (PC)ch ).GuildMembership != ( (PC)victim ).GuildMembership ) { ( (PC)ch ).GuildMembership.Score += 20; } return true; } if( victim == ch ) { return false; } /* * Wimp out? */ if( victim.IsNPC() && dam > 0 ) { if( ( victim.HasActionBit(MobTemplate.ACT_WIMPY ) && MUDMath.NumberBits( 1 ) == 0 && victim.Hitpoints < victim.GetMaxHit() / 5 ) || (victim.IsAffected(Affect.AFFECT_CHARM) && victim.Master && victim.Master.InRoom != victim.InRoom ) ) { StartFearing( victim, ch ); StopHunting( victim ); CommandType.Interpret(victim, "flee"); } } if( !victim.IsNPC() && victim.Hitpoints > 0 && victim.Hitpoints <= victim.Wimpy ) { CommandType.Interpret(victim, "flee"); } return false; }
/// <summary> /// Create an instance of a mobile from the provided template. /// </summary> /// <param name="mobTemplate"></param> /// <returns></returns> public static CharData CreateMobile( MobTemplate mobTemplate ) { int count; if( !mobTemplate ) { Log.Error("CreateMobile: null MobTemplate.", 0); throw new NullReferenceException(); } CharData mob = new CharData(); mob.MobileTemplate = mobTemplate; mob.Followers = null; mob.Name = mobTemplate.PlayerName; mob.ShortDescription = mobTemplate.ShortDescription; mob.FullDescription = mobTemplate.FullDescription; mob.Description = mobTemplate.Description; mob.SpecialFunction = mobTemplate.SpecFun; mob.SpecialFunctionNames = mobTemplate.SpecFunNames; mob.CharacterClass = mobTemplate.CharacterClass; mob.Level = MUDMath.FuzzyNumber( mobTemplate.Level ); mob.ActionFlags = mobTemplate.ActionFlags; mob.CurrentPosition = mobTemplate.DefaultPosition; mob.ChatterBotName = mobTemplate.ChatterBotName; // TODO: Look up the chatter bot name and load a runtime bot into the variable. mob.ChatBot = null; for( count = 0; count < Limits.NUM_AFFECT_VECTORS; ++count ) { mob.AffectedBy[ count ] = mobTemplate.AffectedBy[ count ]; } mob.Alignment = mobTemplate.Alignment; mob.Gender = mobTemplate.Gender; mob.SetPermRace( mobTemplate.Race ); mob.CurrentSize = Race.RaceList[ mob.GetRace() ].DefaultSize; if (mob.HasActionBit(MobTemplate.ACT_SIZEMINUS)) mob.CurrentSize--; if (mob.HasActionBit(MobTemplate.ACT_SIZEPLUS)) mob.CurrentSize++; mob.CastingSpell = 0; mob.CastingTime = 0; mob.PermStrength = MUDMath.Dice( 2, 46 ) + 8; mob.PermIntelligence = MUDMath.Dice( 2, 46 ) + 8; mob.PermWisdom = MUDMath.Dice( 2, 46 ) + 8; mob.PermDexterity = MUDMath.Dice( 2, 46 ) + 8; mob.PermConstitution = MUDMath.Dice( 2, 46 ) + 7; mob.PermAgility = MUDMath.Dice( 2, 46 ) + 8; mob.PermCharisma = MUDMath.Dice( 2, 46 ) + 8; mob.PermPower = MUDMath.Dice( 2, 46 ) + 8; mob.PermLuck = MUDMath.Dice( 2, 46 ) + 8; mob.ModifiedStrength = 0; mob.ModifiedIntelligence = 0; mob.ModifiedWisdom = 0; mob.ModifiedDexterity = 0; mob.ModifiedConstitution = 0; mob.ModifiedAgility = 0; mob.ModifiedCharisma = 0; mob.ModifiedPower = 0; mob.ModifiedLuck = 0; mob.Resistant = mobTemplate.Resistant; mob.Immune = mobTemplate.Immune; mob.Susceptible = mobTemplate.Susceptible; mob.Vulnerable = mobTemplate.Vulnerable; mob.MaxMana = mob.Level * 10; if( Race.RaceList[mobTemplate.Race].Coins ) { int level = mobTemplate.Level; mob.ReceiveCopper( MUDMath.Dice( 12, level ) / 32 ); mob.ReceiveSilver( MUDMath.Dice( 9, level ) / 32 ); mob.ReceiveGold( MUDMath.Dice( 5, level ) / 32 ); mob.ReceivePlatinum( MUDMath.Dice( 2, level ) / 32 ); } else { mob.SetCoins( 0, 0, 0, 0 ); } mob.ArmorPoints = MUDMath.Interpolate( mob.Level, 100, -100 ); // * MOB HITPOINTS * // // Was level d 8, upped it to level d 13 // considering mobs *still* won't have as many hitpoints as some players until // at least level 10, this shouldn't be too big an upgrade. // // Mob hitpoints are not based on constitution *unless* they have a // constitution modifier from an item, spell, or other affect // In light of recent player dissatisfaction with the // mob hitpoints, I'm implementing a log curve, using // hp = exp( 2.15135 + level*0.151231) // This will will result in the following hp matrix: // Level Hitpoints // 20 175 // 30 803 // 40 3643 // 50 16528 // 55 35207 // 60 75000 mob.MaxHitpoints = MUDMath.Dice( mob.Level, 13 ) + 1; // Mob hps are non-linear above level 10. if( mob.Level > 20 ) { int upper = (int)Math.Exp( 1.85 + mob.Level * 0.151231 ); int lower = (int)Math.Exp( 1.80 + mob.Level * 0.151231 ); mob.MaxHitpoints += MUDMath.NumberRange( lower, upper ); } else if (mob.Level > 10) { mob.MaxHitpoints += MUDMath.NumberRange(mob.Level * 2, ((mob.Level - 8) ^ 2 * mob.Level) / 2); } // Demons/devils/dragons gain an extra 30 hitpoints per level (+1500 at lvl 50). if (mob.GetRace() == Race.RACE_DEMON || mob.GetRace() == Race.RACE_DEVIL || mob.GetRace() == Race.RACE_DRAGON) { mob.MaxHitpoints += (mob.Level * 30); } mob.Hitpoints = mob.GetMaxHit(); // Horses get more moves, necessary for mounts. if(Race.RaceList[ mob.GetRace() ].Name.Equals( "Horse", StringComparison.CurrentCultureIgnoreCase )) { mob.MaxMoves = 290 + MUDMath.Dice( 4, 5 ); mob.CurrentMoves = mob.MaxMoves; } mob.LoadRoomIndexNumber = 0; // Insert in list. CharList.Add( mob ); // Increment count of in-game instances of mob. mobTemplate.NumActive++; return mob; }
/// <summary> /// Inflict damage from a single hit. This could use some cleanup since it's way too unwieldy at more than 600 lines. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <param name="dam"></param> /// <param name="skill"></param> /// <param name="weapon"></param> /// <param name="damType"></param> /// <returns></returns> public static bool InflictDamage(CharData ch, CharData victim, int dam, string skill, ObjTemplate.WearLocation weapon, AttackType.DamageType damType) { if (ch == null || victim == null) { return false; } Object obj; bool critical = false; if( victim.CurrentPosition == Position.dead || victim.Hitpoints < -10 ) { return true; } /* * Stop up any residual loopholes. */ if( ( dam > 1276 ) && ch.Level < Limits.LEVEL_AVATAR ) { string text; if (ch.IsNPC() && ch.Socket) { text = String.Format("Damage: {0} from {1} by {2}: > 1276 points with {3} damage type!", dam, ch.Name, ch.Socket.Original.Name, skill); } else { text = String.Format("Damage: {0} from {1}: > 1276 points with {2} damage type!", dam, ch.IsNPC() ? ch.ShortDescription : ch.Name, skill); } Log.Error( text, 0 ); dam = 1276; } // Remove memorization and meditation bits - Xangis victim.BreakMeditate(); victim.BreakMemorization(); if (victim.IsAffected( Affect.AFFECT_MINOR_PARA)) { SocketConnection.Act( "$n&n disrupts the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); SocketConnection.Act( "You disrupt the magic preventing $N&n from moving.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+YYou can move again.&n", ch, null, victim, SocketConnection.MessageTarget.victim ); victim.RemoveAffect( Affect.AFFECT_MINOR_PARA ); } bool immune = false; if( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ victim = CheckGuarding( ch, victim ); if( IsSafe( ch, victim ) ) return false; // is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( victim == null ) return true; Crime.CheckAttemptedMurder( ch, victim ); if( victim.CurrentPosition > Position.stunned ) { if( !victim.Fighting ) SetFighting( victim, ch ); // Can't have prone people automatically stand if( victim.CurrentPosition == Position.standing ) victim.CurrentPosition = Position.fighting; if( !ch.Fighting ) SetFighting( ch, victim ); /* * If NPC victim is following, ch might attack victim's master. * No charm check here because charm would be dispelled from * tanking mobile when combat ensues thus ensuring PC charmer is * not harmed. * Check for IsSameGroup wont work as following mobile is not * always grouped with PC charmer * * Added a check for whether ch has switch skill. If not, * much lower chancing of retargetting */ if( ch.IsNPC() && victim.IsNPC() && victim.Master && victim.Master.InRoom == ch.InRoom && MUDMath.NumberBits( 2 ) == 0 ) { StartGrudge( ch, victim.Master, false ); } } /* * More charm stuff. */ if( victim.Master == ch ) { StopFighting( victim, true ); } ch.BreakInvisibility(); /* * Hunting stuff... */ if( dam != 0 && victim.IsNPC() ) { /* StartGrudge is combined StartHating and StartHunting */ StartGrudge( victim, ch, false ); } /* * Damage modifiers. */ // Critical hits for double damage // Average of 5% for those that have average luck // Gnomes could concievably have 10% if( MUDMath.NumberPercent() < ( 2 + ( ch.GetCurrLuck() / 18 ) ) && dam > 0 ) { ch.SendText( "&+WYou score a CRITICAL HIT!&n\r\n" ); dam *= 2; critical = true; } if( victim.IsAffected( Affect.AFFECT_SANCTUARY ) ) dam /= 2; if( victim.IsAffected( Affect.AFFECT_PROTECT_EVIL ) && ch.IsEvil() ) dam -= dam / 8; else if( victim.IsAffected( Affect.AFFECT_PROTECT_GOOD ) && ch.IsGood() ) dam -= dam / 8; // Check stoneskin. People not affected by a stoneskin affect // cannot lose their stoneskin for any reason. This should mean // that mobs will keep their stoneskin and players should always // have a chance to lose it, since no player should ever be // setbit stoneskin. // // The bool value of found is used so that we can have them // take full damage when their stoneskin shatters, but get the // damage reduction if they are either a mob or their stoneskin // wears off that round. // /* Yeah, yeah.. so maybe backstabs shouldn't be aff'd. */ // Actually they should be affected, but they should have a much // higher chance of getting through (say 30-70%). // // Critical hits will now go through stoneskin // automatically if (!critical && victim.IsAffected( Affect.AFFECT_STONESKIN) && ( skill != "backstab" || MUDMath.NumberPercent() < ( 25 + ch.Level ) ) ) { bool found = false; for (int i = (victim.Affected.Count - 1); i >= 0; i--) { if( victim.Affected[i].HasBitvector( Affect.AFFECT_STONESKIN ) ) { // Small chance of shattering the stoneskin on a good hit. // Reduced chance by about 20% if( dam >= 25 && MUDMath.NumberPercent() <= ( dam / 12 ) ) { victim.SendText( "&+LYour stoneskin is shattered by the massive blow!&n\r\n" ); SocketConnection.Act( "$n&n's massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "Your massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.character ); victim.RemoveAffect(victim.Affected[i]); found = true; } else if( dam > 0 ) // Added check for actual damage { for( int j = 0; j < victim.Affected[i].Modifiers.Count; j++ ) { victim.Affected[i].Modifiers[j].Amount--; if (victim.Affected[i].Modifiers[j].Amount < 1) { victim.RemoveAffect(victim.Affected[i]); victim.SendText("&+LYou feel your skin soften and return to normal.&n\r\n"); } dam /= 15; found = true; } } } } // This means they're Affect.AFFECT_STONESKIN as an innate/permenant. // We will still allow it to shatter, but it will refresh itself // upon a mob update. Because of this, we make it easier to shatter. // No damage reduction when it shatters. if( !found ) { if( dam >= 8 && MUDMath.NumberPercent() <= ( dam / 8 ) ) { victim.SendText( "&+LYour stoneskin is shattered by the massive blow!&n\r\n" ); SocketConnection.Act( "$n&n's massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim ); SocketConnection.Act( "Your massive blow shatters $N&n's stoneskin!", ch, null, victim, SocketConnection.MessageTarget.character ); victim.RemoveAffect( Affect.AFFECT_STONESKIN ); } else { dam = dam / 15 != 0 ? dam / 15 : 1; } } } if( dam < 0 ) dam = 0; /* * Check for disarm, trip, parry, dodge and shield block. */ if (skill != "barehanded fighting" || skill == "kick") { // Trip and disarm removed because those should be handled // by each individual mob's special function. if( ch.IsNPC() && ch.HasInnate( Race.RACE_WEAPON_WIELD ) && MUDMath.NumberPercent() < Math.Min( 25, Math.Max( 10, ch.Level ) ) && !victim.IsNPC() ) UseMagicalItem( ch ); } } switch( victim.CheckRIS( damType ) ) { case Race.ResistanceType.resistant: dam -= dam / 3; break; case Race.ResistanceType.immune: immune = true; dam = 0; break; case Race.ResistanceType.susceptible: dam += dam / 2; break; case Race.ResistanceType.vulnerable: dam *= 2; break; default: break; } if( ( damType == AttackType.DamageType.wind || damType == AttackType.DamageType.gas || damType == AttackType.DamageType.asphyxiation ) && victim.IsAffected(Affect.AFFECT_DENY_AIR)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+CYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_DENY_FIRE)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+rYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.earth || damType == AttackType.DamageType.crushing ) && victim.IsAffected( Affect.AFFECT_DENY_EARTH)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+yYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } if( ( damType == AttackType.DamageType.water || damType == AttackType.DamageType.acid || damType == AttackType.DamageType.drowning ) && victim.IsAffected( Affect.AFFECT_DENY_WATER)) { if( MUDMath.NumberPercent() < 50 ) { ch.SendText( "&+bYou deny the damage.&n\r\n" ); immune = true; dam = 0; } else dam -= dam / 5; } // Check for protection spells that give 25% damage reduction - Xangis if (damType == AttackType.DamageType.fire && victim.IsAffected( Affect.AFFECT_PROTECT_FIRE)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.cold && victim.IsAffected( Affect.AFFECT_PROTECT_COLD)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.acid && victim.IsAffected( Affect.AFFECT_PROTECT_ACID)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.gas && victim.IsAffected( Affect.AFFECT_PROTECT_GAS)) dam = ( dam * 3 ) / 4; else if (damType == AttackType.DamageType.electricity && victim.IsAffected( Affect.AFFECT_PROTECT_LIGHTNING)) dam = ( dam * 3 ) / 4; // Barkskin protects from 8% of slash and 12% of pierce damage. if (victim.IsAffected( Affect.AFFECT_BARKSKIN)) { if (skill == "1h slashing" || skill == "2h slashing") dam = dam * 11 / 12; else if (skill == "1h piercing" || skill == "2h piercing") dam = dam * 7 / 8; } // Check for vampiric touch for anti-paladins and vampires if( weapon == ObjTemplate.WearLocation.hand_one || weapon == ObjTemplate.WearLocation.hand_two || weapon == ObjTemplate.WearLocation.hand_three || weapon == ObjTemplate.WearLocation.hand_four ) { if( ( ( ch.IsClass(CharClass.Names.antipaladin) || ch.GetRace() == Race.RACE_VAMPIRE ) && skill == "barehanded fighting" && !Object.GetEquipmentOnCharacter(ch, weapon)) || (ch.IsAffected( Affect.AFFECT_VAMP_TOUCH) && ( !( obj = Object.GetEquipmentOnCharacter( ch, weapon ) ) || obj.HasAffect( Affect.AFFECT_VAMP_TOUCH ) ) ) ) { ch.Hitpoints += dam / 3; if( ch.Hitpoints > ( ch.GetMaxHit() + 50 + ch.Level * 5 ) ) { ch.Hitpoints = ch.GetMaxHit() + 50 + ch.Level * 5; } } } /* PC to PC damage quartered. * NPC to PC damage divided by 3. */ if( dam > 0 && !victim.IsNPC() && victim != ch ) { if( !ch.IsNPC() ) dam /= 4; else dam /= 3; } /* * Just a check for anything that is excessive damage * Send a log message, keeping the imms on their toes * Changed this from 300 to 250 'cause hitters get more than one * attack/round and w/haste that's 1000 possible in the time one fist * goes off. That's more than the fist might do and it has to be * memmed. */ if (dam > 250 && skill != "backstab" ) { string buf4; if (!string.IsNullOrEmpty(skill)) { buf4 = String.Format("Excessive damage: {0} attacking {1} for {2}, skill = {3}({4}).", ch.Name, victim.Name, dam, Skill.SkillList[skill].DamageText, skill); } else { buf4 = String.Format("Excessive damage: {0} attacking {1} for {2}, unknown damage type.", ch.Name, victim.Name, dam); } Log.Trace( buf4 ); } /* * We moved DamageMessage out of the victim != ch if above * so self damage would show. Other valid type_undefined * damage is ok to avoid like mortally wounded damage */ if (!String.IsNullOrEmpty(skill)) { SendDamageMessage(ch, victim, dam, skill, weapon, immune); } victim.Hitpoints -= dam; /* Check for HOLY_SACRFICE and BATTLE_ECSTASY */ if( dam > 0 && victim != ch ) { CharData groupChar; if (victim.IsAffected( Affect.AFFECT_HOLY_SACRIFICE) && victim.GroupLeader) { for( groupChar = victim.GroupLeader; groupChar; groupChar = groupChar.NextInGroup ) { if( groupChar == victim || groupChar.InRoom != ch.InRoom ) continue; groupChar.Hitpoints += dam / 5; if (groupChar.Hitpoints > groupChar.GetMaxHit() + 50 + groupChar.Level * 5) { groupChar.Hitpoints = groupChar.GetMaxHit() + 50 + groupChar.Level * 5; } } //end for loop } //end if holy sac if( ch.GroupLeader != null ) { for( groupChar = ch.GroupLeader; groupChar != null; groupChar = groupChar.NextInGroup ) { if( groupChar == victim || groupChar.InRoom != ch.InRoom ) continue; if( groupChar.IsAffected( Affect.AFFECT_BATTLE_ECSTASY ) ) { groupChar.Hitpoints += dam / 20; if( groupChar.Hitpoints > groupChar.GetMaxHit() + 50 + groupChar.Level * 5 ) groupChar.Hitpoints = groupChar.GetMaxHit() + 50 + groupChar.Level * 5; } // end if battle ecstasy } //end for loop } //end if grouped } //end if // Make sure if they got an instant kill roll that the victim dies. if (skill == "instant kill") { if( victim.GetRace() != Race.RACE_DEVIL && victim.GetRace() != Race.RACE_DEMON && victim.GetRace() != Race.RACE_GOD ) victim.Hitpoints = -20; } /* Added damage exp! */ // chance added because people level faster and faster as they get higher level... // to be worked out when exp is redone. // you can now only get damage exp on mobs that con easy or better // and there's only a 25% chance per hit of you evern being eligible for damage exp. if( MUDMath.NumberPercent() < 25 && victim.Level >= ( ch.Level - 3 ) ) ch.GainExperience( Math.Max( 1, dam / 20 ) ); if( !victim.IsNPC() && victim.Level >= Limits.LEVEL_AVATAR && victim.Hitpoints < 1 ) victim.Hitpoints = 1; /* * Magic shields that retaliate * * Apparently two people with the same sort of shield do not * take damage from each other */ if( ( dam > 1 ) && victim != ch ) { if( victim.IsAffected( Affect.AFFECT_FIRESHIELD ) && !ch.IsAffected( Affect.AFFECT_FIRESHIELD ) ) InflictSpellDamage( victim, ch, dam / 2, "fireshield", AttackType.DamageType.fire ); if (victim.IsAffected( Affect.AFFECT_COLDSHIELD) && !ch.IsAffected(Affect.AFFECT_COLDSHIELD)) InflictSpellDamage( victim, ch, dam / 2, "coldshield", AttackType.DamageType.cold ); if (victim.IsAffected(Affect.AFFECT_SHOCK_SHIELD) && !ch.IsAffected(Affect.AFFECT_SHOCK_SHIELD)) InflictSpellDamage( victim, ch, dam / 2, "shockshield", AttackType.DamageType.electricity ); /* Soulshield is a complex one. If the attacker and victim are of * opposite alignment, the shield retaliates with 1/2 damage just like * any other shield. If the victim is neutral and the attacker is * not, the shield retaliates with 1/4 damage. If the victim is good * or evil and the attacker is neutral, the shield retaliates with * 1/8 damage. If the attacker and victim are of same alignment, * the shield does nothing. */ if (victim.IsAffected(Affect.AFFECT_SOULSHIELD) && !ch.IsAffected(Affect.AFFECT_SOULSHIELD)) { if( victim.IsEvil() && ch.IsGood() ) InflictSpellDamage(victim, ch, dam / 2, "soulshield", AttackType.DamageType.harm); else if( victim.IsGood() && ch.IsEvil() ) InflictSpellDamage(victim, ch, dam / 2, "soulshield", AttackType.DamageType.harm); else if( victim.IsNeutral() && ( ch.IsEvil() || ch.IsGood() ) ) InflictSpellDamage(victim, ch, dam / 4, "soulshield", AttackType.DamageType.harm); else if( victim.IsGood() && ch.IsNeutral() ) InflictSpellDamage(victim, ch, dam / 8, "soulshield", AttackType.DamageType.harm); else if( victim.IsEvil() && ch.IsNeutral() ) InflictSpellDamage(victim, ch, dam / 8, "soulshield", AttackType.DamageType.harm); } } if (victim.IsAffected( Affect.AFFECT_BERZERK ) && victim.CurrentPosition <= Position.stunned ) victim.RemoveAffect(Affect.AFFECT_BERZERK); if (dam > 0 && skill != "barehanded fighting" && IsWieldingPoisoned( ch, weapon ) && !Magic.SpellSavingThrow( ch.Level, victim, AttackType.DamageType.poison ) ) { InflictPoison( "poison_weapon", ch.Level, IsWieldingPoisoned( ch, weapon ), ch, victim ); SocketConnection.Act( "$n&n suffers from the &+Gpoison&n inflicted upon $m.", victim, null, null, SocketConnection.MessageTarget.room, true ); Object.StripAffect( Object.GetEquipmentOnCharacter( ch, weapon ), Affect.AffectType.skill, "poison weapon" ); } victim.UpdatePosition(); switch( victim.CurrentPosition ) { case Position.mortally_wounded: victim.SendText( "&+LYou are &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n\r\n" ); SocketConnection.Act( "$n&+L is &+Rmo&n&+rr&+Rt&n&+ral&+Rl&n&+ry&+L wounded, and will die soon, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.incapacitated: victim.SendText( "&+LYou are incapacitated and will &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.&n\r\n" ); SocketConnection.Act( "$n&+L is incapacitated and will slowly &n&+rbl&+Re&n&+re&+Rd&+L to death, if not aided.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); StopNotVicious( victim ); break; case Position.stunned: victim.SendText( "&+LYou are stunned, but will probably recover.&n\r\n" ); SocketConnection.Act( "$n&+L is stunned, but will probably recover.&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; case Position.dead: if( victim == ch ) { victim.SendText( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L!&n\r\n\r\n" ); } else { string buf = String.Format( "&+LYou have been &+Rsl&n&+ra&+Ri&n&+rn&+L by&n {0}&+L!&n\r\n\r\n", ch.ShowNameTo( victim, false ) ); victim.SendText( buf ); } /* Added this to stop a bug. */ Combat.StopFighting( victim, true ); SocketConnection.Act( "$n&+L is &n&+rdead&+L!&n", victim, null, null, SocketConnection.MessageTarget.room, true ); break; default: if( dam > victim.GetMaxHit() / 5 ) victim.SendText( "That really did &+RHURT&n!\r\n" ); if( victim.Hitpoints < victim.GetMaxHit() / 10 ) victim.SendText( "You sure are &n&+rBL&+RE&n&+rE&+RDI&n&+rN&+RG&n!\r\n" ); break; } // Check for weapon procs if( ( obj = Object.GetEquipmentOnCharacter( ch, weapon ) ) && Position.dead != victim.CurrentPosition ) { if( obj.SpecFun.Count > 0 ) obj.CheckSpecialFunction(true); } /* * Sleep spells and extremely wounded folks. */ if( !victim.IsAwake() ) /* lets make NPC's not slaughter PC's */ { if( victim.Fighting && victim.Fighting.Hunting && victim.Fighting.Hunting.Who == victim ) StopHunting( victim.Fighting ); if( victim.Fighting && !victim.IsNPC() && ch.IsNPC() ) StopFighting( victim, true ); else StopFighting( victim, false ); } /* * Payoff for killing things. */ if( victim.CurrentPosition == Position.dead ) { // Done in attempt to squelch the combat continuation bug StopFighting( victim, true ); if( !victim.HasActionBit(MobTemplate.ACT_NOEXP ) || !victim.IsNPC() ) GroupExperienceGain( ch, victim ); if( ch.IsNPC() ) { if( ch.Hunting ) { if( ch.Hunting.Who == victim ) StopHunting( ch ); } if( ch.IsHating(victim) ) { ch.StopHating( victim ); } } if( !victim.IsNPC() ) { if( ch.IsNPC() ) { ( (PC)victim ).MobDeaths++; if( victim.IsGuild() ) { ( (PC)victim ).GuildMembership.MonsterDeaths++; ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); } else { ( (PC)ch ).PlayerKills++; ( (PC)victim ).PlayerDeaths++; ( (PC)victim ).Score += CalculateDeathScore( ch, victim ); ( (PC)ch ).Score += CalculateKillScore( ch, victim ); if( ch.IsGuild() && victim.IsGuild() && ( (PC)ch ).GuildMembership != ( (PC)victim ).GuildMembership ) { ( (PC)ch ).GuildMembership.PlayerKills++; ( (PC)victim ).GuildMembership.PlayerDeaths++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); ( (PC)victim ).GuildMembership.Score += CalculateDeathScore( ch, victim ); } } string logBuf = String.Format( "{0}&n killed by {1}&n at {2}", victim.Name, ( ch.IsNPC() ? ch.ShortDescription : ch.Name ), victim.InRoom.IndexNumber ); Log.Trace( logBuf ); ImmortalChat.SendImmortalChat( ch, ImmortalChat.IMMTALK_DEATHS, Limits.LEVEL_AVATAR, logBuf ); /* * Dying penalty: * * At level 1 you lose 12.5% of a level. * At level 50 you lose 25% of a level. */ // Made it so people level 5 and under lose no exp from death. if( ch.Level > 5 ) victim.GainExperience( ( 0 - ( ( ( 50 + victim.Level ) * ExperienceTable.Table[ victim.Level ].LevelExperience ) / 400 ) ) ); if( victim.Level < 2 && victim.ExperiencePoints < 1 ) victim.ExperiencePoints = 1; } else { if( !ch.IsNPC() ) { ( (PC)ch ).MobKills++; if( ch.IsGuild() ) { ( (PC)ch ).GuildMembership.MonsterKills++; ( (PC)ch ).GuildMembership.Score += CalculateKillScore( ch, victim ); } ( (PC)ch ).Score += CalculateKillScore( ch, victim ); } } KillingBlow( ch, victim ); return true; } if( victim == ch ) { return false; } /* * Wimp out? */ if( victim.IsNPC() && dam > 0 ) { if( ( victim.HasActionBit(MobTemplate.ACT_WIMPY ) && MUDMath.NumberBits( 1 ) == 0 && victim.Hitpoints < victim.GetMaxHit() / 5 ) || (victim.IsAffected( Affect.AFFECT_CHARM) && victim.Master && victim.Master.InRoom != victim.InRoom ) ) { StartFearing( victim, ch ); StopHunting( victim ); CommandType.Interpret(victim, "flee"); } } if( !victim.IsNPC() && victim.Hitpoints > 0 && victim.Hitpoints <= victim.Wimpy ) { CommandType.Interpret(victim, "flee"); } return false; }
public void Restore(CharData victim) { if (victim == null) return; if (victim.Hitpoints < victim.GetMaxHit()) victim.Hitpoints = victim.GetMaxHit(); victim.CurrentMana = victim.MaxMana; victim.CurrentMoves = victim.MaxMoves; if (!victim.IsNPC()) { ((PC)victim).Hunger = 48; ((PC)victim).Thirst = 48; ((PC)victim).Drunk = 0; victim.PurgeInnateTimers(); } if (victim.IsAffected(Affect.AFFECT_BLIND)) victim.RemoveBlindness(); if (victim.IsAffected(Affect.AFFECT_POISON)) victim.RemovePoison(); victim.UpdatePosition(); SocketConnection.Act("$n has restored you.", this, null, victim, SocketConnection.MessageTarget.victim); return; }
/// <summary> /// Regeneration and natural healing. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static int HitGain( CharData ch ) { int gain; int percent = 0; if( ch == null ) { Log.Error( "HitGain(): null ch", 0 ); return 0; } // They aren't going to gain hits in a no heal room... if (ch.InRoom.HasFlag(RoomTemplate.ROOM_NO_HEAL)) { return 0; } gain = 1; percent = 100; if (ch.CheckSkill("fast healing", PracticeType.none)) { gain += 1; } if (MUDMath.NumberPercent() < 3) { ch.PracticeSkill("fast healing"); } switch( ch.CurrentPosition ) { case Position.sleeping: gain += 3; break; case Position.reclining: gain += 2; break; case Position.resting: gain += 1; break; case Position.fighting: gain = 0; break; } if( ch.HasInnate( Race.RACE_REGENERATE ) ) { // Automatically one extra hp, two at level 30. gain += 1 + (ch.Level / 30); // One percent chance of gaining another per level. percent += (ch.Level); } // Hunger and thirst for PCs. if (!ch.IsNPC()) { if (((PC)ch).Hunger == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou double over from &+Rhunger pains&n!\r\n"); } if (((PC)ch).Thirst == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou suffer from severe &+cth&+Ci&n&+cr&+Cst&n!\r\n"); } } if( ch.IsAffected( Affect.AFFECT_POISON ) ) { gain /= 4; percent /= 2; } if( gain == 0 ) if( MUDMath.NumberPercent() < percent ) gain = 1; // Heal rooms heal you a little quicker if (ch.InRoom.HasFlag(RoomTemplate.ROOM_HEAL)) { gain += Math.Max(1, gain / 2); } if( ( ch.InRoom.TerrainType != TerrainType.underwater_has_ground && ch.InRoom.TerrainType != TerrainType.unswimmable_water && ch.InRoom.TerrainType != TerrainType.swimmable_water && ch.HasInnate( Race.RACE_WATERBREATH ) && MUDString.StringsNotEqual( Race.RaceList[ ch.GetRace() ].Name, "Object" ) && ch.GetRace() != Race.RACE_GOD ) || ( ch.InRoom.TerrainType == TerrainType.underwater_has_ground && ( !ch.IsImmortal() && !ch.IsAffected( Affect.AFFECT_BREATHE_UNDERWATER ) && !ch.HasInnate( Race.RACE_WATERBREATH ) ) ) ) gain = 0; return Math.Min( gain, ch.GetMaxHit() - ch.Hitpoints ); }
public static void ScribeScroll(CharData ch, string[] str) { if( ch == null ) return; Object scroll; Spell spell; if (String.IsNullOrEmpty(str[0])) { ch.SendText("Scribe what spell?\r\n"); return; } if (!(scroll = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one))) { ch.SendText("You hold nothing in your hand.\r\n"); return; } if (scroll.ItemType != ObjTemplate.ObjectType.scroll) { ch.SendText("You are not holding a &+Wparchment&n.\r\n"); return; } if ((spell = StringLookup.SpellLookup(str[0])) == null) { ch.SendText("You don't know any spells by that _name.\r\n"); return; } SocketConnection.Act("$n begins writing a &+Wscroll&n.", ch, scroll, null, SocketConnection.MessageTarget.room); ch.WaitState(Skill.SkillList["Scribe"].Delay); ch.PracticeSkill("Scribe"); if (!ch.IsNPC() && (MUDMath.NumberPercent() > ((PC)ch).SkillAptitude["brew"] || MUDMath.NumberPercent() > ((ch.GetCurrInt() - 13) * 5 + (ch.GetCurrWis() - 13) * 3))) { SocketConnection.Act("$p&n bursts in &n&+rflames&n!", ch, scroll, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p&n bursts in &+Rflames&n!", ch, scroll, null, SocketConnection.MessageTarget.room); scroll.RemoveFromWorld(); Combat.InflictDamage(ch, ch, ch.GetMaxHit(), "Scribe", ObjTemplate.WearLocation.none, AttackType.DamageType.fire); return; } scroll.Level = ch.Level * 2 / 3; scroll.Values[0] = ch.Level / 3; ch.ImprintSpell(spell, ch.Level, scroll); return; }
/// <summary> /// Builds a condition meter of style #2 for a player. /// </summary> /// <param name="ch"></param> /// <returns></returns> static string ConditionMeter2( CharData ch ) { if (ch == null) return String.Empty; int percent; if( ch.GetMaxHit() > 0 ) percent = ( 100 * ch.Hitpoints ) / ch.GetMaxHit(); else percent = -1; if( percent >= 100 ) return "&+L(&+r+++&+y++&+Y++&n&+g+++&+L)&n"; if( percent >= 90 ) return "&+L(&+r+++&+y++&+Y++&n&+g++&+L-&+L)&n"; if( percent >= 79 ) return "&+L(&+r+++&+y++&+Y++&n&+g+&+L--&+L)&n"; if( percent >= 69 ) return "&+L(&+r+++&+y++&+Y++&+L---&+L)&n"; if( percent >= 58 ) return "&+L(&+r+++&+y++&+Y+&+L----&+L)&n"; if( percent >= 48 ) return "&+L(&+r+++&+y++&+L-----&+L)&n"; if( percent >= 37 ) return "&+L(&+r+++&+y+&+L------&+L)&n"; if( percent >= 27 ) return "&+L(&+r+++&+L-------&+L)&n"; if( percent >= 16 ) return "&+L(&+r++&+L--------&+L)&n"; if( percent >= 6 ) return "&+L(&+r+&+L---------&+L)&n"; return "&+L(&+L----------&+L)&n"; }
public static void Brew(CharData ch, string[] str) { if( ch == null ) return; Object potion; Spell spell; if (str.Length == 0) { ch.SendText("Which spell do you want to brew into a &+Lpotion&n?\r\n"); return; } if (!(potion = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one))) { ch.SendText("You hold nothing in your hand.\r\n"); return; } if (potion.ItemType != ObjTemplate.ObjectType.potion) { ch.SendText("You are not holding a vial.\r\n"); return; } if ((spell = StringLookup.SpellLookup(str[0])) == null) { ch.SendText("You don't know any spells by that _name.\r\n"); return; } if (spell.ValidTargets != TargetType.singleCharacterDefensive && spell.ValidTargets != TargetType.self) { ch.SendText("You cannot brew that spell.\r\n"); return; } SocketConnection.Act("$n begins preparing a &+Lpotion&n.", ch, potion, null, SocketConnection.MessageTarget.room); ch.WaitState(Skill.SkillList["brew"].Delay); ch.PracticeSkill("brew"); if (!ch.IsNPC() && (MUDMath.NumberPercent() > ((PC)ch).SkillAptitude["brew"] || MUDMath.NumberPercent() > ((ch.GetCurrInt() - 13) * 5 + (ch.GetCurrWis() - 13) * 3))) { SocketConnection.Act("$p&n explodes violently!", ch, potion, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$p&n explodes violently!", ch, potion, null, SocketConnection.MessageTarget.room); potion.RemoveFromWorld(); Combat.InflictDamage(ch, ch, ch.GetMaxHit() / 16, "brew", ObjTemplate.WearLocation.none, AttackType.DamageType.energy); return; } potion.Level = ch.Level / 2; potion.Values[0] = ch.Level / 4; ch.ImprintSpell(spell, ch.Level, potion); return; }
/// <summary> /// Builds a condition string of style #2 for a player. /// </summary> /// <param name="ch"></param> /// <returns></returns> static string ConditionString2( CharData ch ) { int percent; if( ch.GetMaxHit() > 0 ) percent = ( 100 * ch.Hitpoints ) / ch.GetMaxHit(); else percent = -1; if( percent >= 100 ) return "&+CExcellent&n"; if( percent >= 90 ) return "&+LBarely &n&+cScratched&n"; if( percent >= 79 ) return "&+CBa&n&+ct&+Ct&n&+cer&+Ce&n&+cd&+L and &n&+mBr&+Mu&n&+mi&+Mse&n&+md&n"; if( percent >= 69 ) return "&+LLightly &+MWo&n&+mu&+Mn&n&+mde&+Md&n"; if( percent >= 58 ) return "&+LModerately &+MWo&+Ru&+Mn&+Rde&+Md&n"; if( percent >= 48 ) return "&+LSeverely &n&+rWo&n&+mu&+rn&+mde&n&+rd&n"; if( percent >= 37 ) return "&n&+rBl&+Re&n&+re&+Rdi&n&+rn&+Rg&+L Freely&n"; if( percent >= 27 ) return "&+LBathed in &n&+rBl&+Ro&n&+ro&+Rd&n"; if( percent >= 16 ) return "&+LLeaking&n&+r Guts&n"; if( percent >= 6 ) return "&+RWr&n&+ri&+Rt&n&+rhi&+Rn&n&+rg&+L in &n&+rA&+Rgo&n&+rn&+Ry&n"; return "&+LAt &n&+wDeath&+L's Door&n"; }
/// <summary> /// Knock a door from its hinges with brute force. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void DoorBash(CharData ch, string[] str) { if( ch == null ) return; Exit.Direction door; Room toRoom; if (ch.IsNPC() || (!ch.HasSkill("doorbash") && !ch.HasInnate(Race.RACE_DOORBASH))) { ch.SendText("You don't feel massive enough!\r\n"); return; } if (str.Length == 0) { ch.SendText("Doorbash what?\r\n"); return; } if (ch.Fighting) { ch.SendText("You can't break off your fight.\r\n"); return; } if ((door = Movement.FindDoor(ch, str[0])) >= 0) { Exit reverseExit; int chance; Exit exit = ch.InRoom.GetExit(door); if (!exit.HasFlag(Exit.ExitFlag.closed)) { ch.SendText("Calm down. It is already open.\r\n"); return; } ch.WaitState(Skill.SkillList["doorbash"].Delay); if (ch.IsNPC()) { chance = 0; } else if (!ch.HasSkill("doorbash")) { chance = 20; } else { chance = ((PC)ch).SkillAptitude["doorbash"] / 2; } if (exit.HasFlag(Exit.ExitFlag.locked)) { chance /= 2; } if (exit.HasFlag(Exit.ExitFlag.bashproof) && !ch.IsImmortal()) { SocketConnection.Act("WHAAAAM!!! You bash against the $d, but it doesn't budge.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("WHAAAAM!!! $n&n bashes against the $d, but it holds strong.", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 20), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); if (exit.HasFlag(Exit.ExitFlag.spiked)) { SocketConnection.Act("You are impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n is impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 5), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.pierce); } return; } if (exit.HasFlag(Exit.ExitFlag.spiked)) { SocketConnection.Act("You are impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n is impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 5), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.pierce); } if ((ch.GetCurrStr() >= 20) && MUDMath.NumberPercent() < (chance + 4 * (ch.GetCurrStr() - 20))) { /* Success */ exit.RemoveFlag(Exit.ExitFlag.closed); if (exit.HasFlag(Exit.ExitFlag.locked)) { exit.RemoveFlag(Exit.ExitFlag.locked); } exit.AddFlag(Exit.ExitFlag.bashed); SocketConnection.Act("Crash! You bashed open the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n bashes open the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 30), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); /* Bash through the other side */ if ((toRoom = Room.GetRoom(exit.IndexNumber)) && (reverseExit = toRoom.GetExit(Exit.ReverseDirection(door))) && reverseExit.TargetRoom == ch.InRoom) { reverseExit.RemoveFlag(Exit.ExitFlag.closed); if (reverseExit.HasFlag(Exit.ExitFlag.locked)) { reverseExit.RemoveFlag(Exit.ExitFlag.locked); } reverseExit.AddFlag(Exit.ExitFlag.bashed); foreach (CharData irch in toRoom.People) { SocketConnection.Act("The $d crashes open!", irch, null, reverseExit.Keyword, SocketConnection.MessageTarget.character); } // Have any aggro mobs on the other side come after the player. foreach (CharData charData in toRoom.People) { if (charData != ch && (charData.IsNPC() && !charData.IsAffected( Affect.AFFECT_CHARM)) && charData.IsAggressive(ch) && charData.IsAwake() && CharData.CanSee(charData, ch) && !charData.Fighting) { Combat.StartHating(charData, ch); Combat.StartHunting(charData, ch); } } } } else { /* Failure */ SocketConnection.Act("OW! You bash against the $d, but it doesn't budge.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n bashes against the $d, but it holds strong.", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 10), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } } /* * Check for "guards"... anyone bashing a door is considered as * a potential aggressor, and there's a 25% chance that mobs * will do unto before being done unto. * But first...let's make sure ch ain't dead? That'd be a pain. */ if (ch.Hitpoints <= 1) return; ch.PracticeSkill("doorbash"); foreach (CharData guardChar in ch.InRoom.People) { if (guardChar != ch && guardChar.HasActionBit(MobTemplate.ACT_PROTECTOR) && (guardChar.IsNPC() && !guardChar.IsAffected( Affect.AFFECT_CHARM)) && guardChar.IsAwake() && CharData.CanSee(guardChar, ch) && !guardChar.Fighting && MUDMath.NumberBits(2) == 0) { SocketConnection.Act("$n&n is very unhappy about you trying to destroy the door.", guardChar, null, ch, SocketConnection.MessageTarget.victim); guardChar.AttackCharacter(ch); } } return; }
/// <summary> /// Shows the graphical client-friendly version of the prompt. /// </summary> private void ShowEnhancedPrompt(CharData ch) { StringBuilder output = new StringBuilder(); output.Append("<prompt>"); // H = Hits. output.Append("H:"); output.Append(ch.Hitpoints); // I = Max Hits. output.Append(" I:"); output.Append(ch.GetMaxHit()); // M = Moves. output.Append(" M:"); output.Append(ch.CurrentMoves); // N = Max Moves. output.Append(" N:"); output.Append(ch.MaxMoves); // A = Mana. output.Append(" A:"); output.Append(ch.CurrentMana); // B = Max Mana output.Append(" B:"); output.Append(ch.MaxMana); // P = Player Name. output.Append(" P:"); output.Append((ch.ShowNameTo(ch, true)).Replace(' ', '_')); // Q = Player Condition. output.Append(" Q:"); output.Append((ch.Hitpoints * 100) / ch.MaxHitpoints); output.Append(" R:"); output.Append(Position.PositionString(ch.CurrentPosition)); // T = Tank Name. output.Append(" T:"); if (ch.Fighting && ch.Fighting.Fighting) { output.Append((ch.Fighting.Fighting.ShowNameTo(ch, true)).Replace(' ', '_')); // U = Enemy Condition. output.Append(" U:"); output.Append((ch.Fighting.Fighting.Hitpoints * 100) / ch.Fighting.Fighting.GetMaxHit()); output.Append(" V:"); output.Append(Position.PositionString(ch.Fighting.Fighting.CurrentPosition)); } else { output.Append("0 U:0 V:0"); } // E = Enemy Name. output.Append(" E:"); if (ch.Fighting) { output.Append((ch.Fighting.ShowNameTo(ch, true)).Replace(' ', '_')); // F = Enemy Condition. output.Append(" F:"); output.Append((ch.Fighting.Hitpoints * 100) / ch.Fighting.GetMaxHit()); output.Append(" G:"); output.Append(Position.PositionString(ch.Fighting.CurrentPosition)); output.Append(" "); } else { output.Append("0 F:0 G:0"); } output.Append("</prompt>"); ch.SendText(output.ToString()); }
/// <summary> /// A basic bandage self command, usable once per day. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void FirstAid(CharData ch, string[] str) { if( ch == null ) return; if (ch.IsNPC()) return; if (ch.Fighting || ch.CurrentPosition == Position.fighting) { ch.SendText("You can't do that while fighting!\r\n"); return; } if (((PC)ch).FirstaidTimer > 0) { ch.SendText("You can only first aid once per day.\r\n"); return; } if (ch.Hitpoints < ch.GetMaxHit()) { ch.Hitpoints += MUDMath.Dice(3, (ch.Level / 2)) + 1; if (((PC)ch).SkillAptitude["bandage"] > 9) { ch.Hitpoints += ((PC)ch).SkillAptitude["bandage"] / 8; ch.PracticeSkill("bandage"); } if (ch.Hitpoints > ch.GetMaxHit()) { ch.Hitpoints = ch.GetMaxHit(); } } ((PC)ch).FirstaidTimer = 24; ch.SendText("You attempt to render first aid unto yourself.\r\n"); return; }
/// <summary> /// Show a character to another character. /// </summary> /// <param name="victim"></param> /// <param name="ch"></param> public static void ShowCharacterToCharacterFull(CharData victim, CharData ch) { Object obj; string text = String.Empty; int percent; if (CharData.CanSee(victim, ch)) { SocketConnection.Act("$n&n looks at you.", ch, null, victim, SocketConnection.MessageTarget.victim); if (victim != ch) { SocketConnection.Act("$n&n looks at $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } else { SocketConnection.Act("$n&n looks at $mself.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } } if (victim.Riding != null) { text += String.Format("&nMounted on {0}, ", victim.Riding.ShowNameTo(ch, false)); } else if (victim.Rider != null) { text += String.Format("&nRidden by {0}, ", victim.Rider.ShowNameTo(ch, false)); } if (!victim.IsNPC() && victim.IsGuild()) { text += String.Format("&n{0} of {1}.\r\n", ((PC)victim).GuildRank.ToString().ToUpper(), ((PC)victim).GuildMembership.Name); } SocketConnection.Act(text, ch, null, victim, SocketConnection.MessageTarget.character); if (!String.IsNullOrEmpty(victim.Description)) { ch.SendText(victim.Description); } else { SocketConnection.Act("&nYou see nothing special about $M.", ch, null, victim, SocketConnection.MessageTarget.character); } if (victim.GetMaxHit() > 0) { percent = (100 * victim.Hitpoints) / victim.GetMaxHit(); } else { percent = -1; } text = victim.ShowNameTo(ch, true); if (percent >= 100) text += " &nis in perfect &n&+ghealth&n. "; else if (percent >= 90) text += " &nis slightly &n&+yscratched&n. "; else if (percent >= 80) text += " &nhas a &+yfew bruises&n. "; else if (percent >= 70) text += " &nhas &+Ysome cuts&n. "; else if (percent >= 60) text += " &nhas &+Mseveral wounds&n. "; else if (percent >= 50) text += " &nhas &+mmany nasty wounds&n. "; else if (percent >= 40) text += " &nis &+Rbleeding freely&n. "; else if (percent >= 30) text += " &nis &+Rcovered in blood&n. "; else if (percent >= 20) text += " &nis &+rleaking guts&n. "; else if (percent >= 10) text += " &nis &+ralmost dead&n. "; else text += " &nis &+rDYING&n. "; ch.SendText(text); // Show size on look at someone. text = MUDString.CapitalizeANSIString(String.Format("{0}&n is a {1} of {2} size.\r\n", victim.GetSexPronoun(), Race.RaceList[victim.GetRace()].ColorName, Race.SizeString(victim.CurrentSize))); ch.SendText(text); ShowAffectLines(ch, victim); bool found = false; foreach (ObjTemplate.WearLocation location in ObjTemplate.TopDownEquipment) { obj = Object.GetEquipmentOnCharacter(victim, location); if (obj && CharData.CanSeeObj(ch, obj)) { if (!found) { ch.SendText("\r\n"); SocketConnection.Act("&n$E is using:", ch, null, victim, SocketConnection.MessageTarget.character); found = true; } if (obj.ItemType == ObjTemplate.ObjectType.weapon && (location == ObjTemplate.WearLocation.hand_one || location == ObjTemplate.WearLocation.hand_three || location == ObjTemplate.WearLocation.hand_four || location == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_WIELD)) { if (obj.HasFlag(ObjTemplate.ITEM_TWOHANDED) && !ch.HasInnate(Race.RACE_EXTRA_STRONG_WIELD)) { ch.SendText("&+y(wielding twohanded) &n"); } else { ch.SendText("&+y(wielding) &n"); } } else { if (obj.ItemType == ObjTemplate.ObjectType.shield && (location == ObjTemplate.WearLocation.hand_one || location == ObjTemplate.WearLocation.hand_three || location == ObjTemplate.WearLocation.hand_four || location == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_SHIELD)) { ch.SendText("&+y(worn as shield) &n"); } else { ch.SendText(StringConversion.EquipmentLocationDisplay[(int)location]); } } ch.SendText(FormatObjectToCharacter(obj, ch, true)); ch.SendText("\r\n"); } } // Keep in mind that players can spam looking at someone in order // to increase their skill in peek - this will need to be fixed. if ((victim != ch && !ch.IsNPC() && ((((PC)ch).SkillAptitude.ContainsKey("peek") && MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["peek"]) || ch.Level >= Limits.LEVEL_AVATAR)) || ch.Riding == victim || ch.Rider == victim) { ch.SendText("\r\n&nYou peek at the inventory:\r\n"); ch.PracticeSkill("peek"); ShowListToCharacter(victim.Carrying, ch, true, true); } return; }