/// <summary> /// Player track command. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void TrackCommand(CharData ch, string[] str) { if( ch == null ) return; CharData victim; if (ch.IsAffected(Affect.AFFECT_TRACK)) { ch.SendText("You stop tracking.\r\n"); Combat.StopHunting(ch); ch.RemoveAffect(Affect.AFFECT_TRACK); return; } if (!ch.HasSkill("track")) { ch.SendText("You couldn't track an &+Lelephant&n in your own bedroom.\r\n"); return; } if (str.Length == 0) { ch.SendText("Whom are you trying to track?\r\n"); return; } if (ch.Riding) { ch.SendText("You can't sniff a trail mounted.\r\n"); return; } if (ch.FlightLevel != 0) { ch.SendText("You find tracks on the _ground_!\r\n"); return; } if (ch.InRoom.IsWater()) { ch.SendText("You can't track through water.\r\n"); return; } if (ch.CurrentPosition != Position.standing) { if (ch.CurrentPosition == Position.fighting) ch.SendText("You're too busy fighting .\r\n"); else ch.SendText("You must be standing to track!\r\n"); return; } /* only imps can hunt to different areas */ bool area = (ch.GetTrust() < Limits.LEVEL_OVERLORD); if (area) { victim = ch.GetCharInArea(str[0]); } else { victim = ch.GetCharWorld(str[0]); } if (!victim || (!victim.IsNPC() && (ch.IsRacewar(victim)) && !ch.IsImmortal())) { ch.SendText("You can't find a trail of anyone like that.\r\n"); return; } if (ch.InRoom == victim.InRoom) { SocketConnection.Act("You're already in $N&n's room!", ch, null, victim, SocketConnection.MessageTarget.character); return; } /* * Deduct some movement. */ if (ch.CurrentMoves > 2) { ch.CurrentMoves -= 3; } else { ch.SendText("You're too exhausted to hunt anyone!\r\n"); return; } SocketConnection.Act("$n carefully sniffs the air.", ch, null, null, SocketConnection.MessageTarget.room); ch.WaitState(Skill.SkillList["track"].Delay); Exit.Direction direction = Track.FindPath(ch.InRoom.IndexNumber, victim.InRoom.IndexNumber, ch, -40000, area); if (direction == Exit.Direction.invalid) { SocketConnection.Act("You can't sense $N&n's trail from here.", ch, null, victim, SocketConnection.MessageTarget.character); return; } /* * Give a random direction if the player misses the die roll. */ if ((ch.IsNPC() && MUDMath.NumberPercent() > 75) /* NPC @ 25% */ || (!ch.IsNPC() && MUDMath.NumberPercent() > /* PC @ norm */ ((PC)ch).SkillAptitude["track"])) { do { direction = Database.RandomDoor(); } while (!(ch.InRoom.ExitData[(int)direction]) || !(ch.InRoom.ExitData[(int)direction].TargetRoom)); } ch.PracticeSkill("track"); /* * Display the results of the search. */ ch.SetAffectBit(Affect.AFFECT_TRACK); string buf = String.Format("You sense $N&n's trail {0} from here...", direction.ToString()); SocketConnection.Act(buf, ch, null, victim, SocketConnection.MessageTarget.character); if (ch.CurrentPosition == Position.standing) { ch.Move(direction); } Combat.StartHunting(ch, victim); return; }
/// <summary> /// Lets a player go visible if they want to. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Visible(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsAffected(Affect.AFFECT_INVISIBLE) && !ch.IsAffected(Affect.AFFECT_MINOR_INVIS) && !ch.IsAffected(Affect.AFFECT_DUERGAR_HIDE)) { ch.SendText("You already stick out like a &+Rsore thumb&n.\r\n" ); return; } ch.RemoveAffect(Affect.AFFECT_INVISIBLE); ch.RemoveAffect(Affect.AFFECT_MINOR_INVIS); ch.RemoveAffect(Affect.AFFECT_DUERGAR_HIDE); ch.SendText("You make yourself visible.\r\n"); SocketConnection.Act("$n&n slowly fades into existence.\r\n", ch, null, null, SocketConnection.MessageTarget.room ); return; }
/// <summary> /// Move silently. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Sneak(CharData ch, string[] str) { if( ch == null ) return; /* Don't allow charmed mobs to do this, check player's skill */ if ((!ch.HasSkill("sneak"))) { ch.SendText("You're about as sneaky as a buffalo in tap shoes.\r\n"); return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (str.Length != 0 && !MUDString.StringsNotEqual(str[0], "off")) { if (!ch.IsAffected(Affect.AFFECT_SNEAK)) { ch.SendText("You're not sneaking.\r\n"); } else { ch.SendText("You stop sneaking around.\r\n"); ch.RemoveAffect(Affect.AFFECT_SNEAK); } return; } ch.SendText("You attempt to move silently.\r\n"); ch.RemoveAffect( Affect.AFFECT_SNEAK ); /* Check skill knowledge when moving only. */ Affect af = new Affect(Affect.AffectType.skill, "sneak", -1, Affect.Apply.none, 0, Affect.AFFECT_SNEAK); ch.AddAffect(af); ch.PracticeSkill("sneak"); ch.WaitState(10); return; }
/// <summary> /// Steal an object or some coins from a victim. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Steal(CharData ch, string[] str) { if( ch == null ) return; Object obj = null; CharData victim; bool sleeping = false; string arg1 = String.Empty; string arg2 = String.Empty; string arg = String.Empty; int percent; if (!ch.HasSkill("steal") && !ch.IsAffected(Affect.AFFECT_CHARM)) { ch.SendText("Who are you trying to kid? You couldn't steal shoes from a &n&+mbl&+Mo&n&+ma&+Mte&n&+md&n corpse.\r\n"); return; } if (ch.Riding != null) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (String.IsNullOrEmpty(arg1) || String.IsNullOrEmpty(arg2)) { ch.SendText("Steal what from whom?\r\n"); return; } if ((victim = ch.GetCharRoom(arg2)) == null) { ch.SendText("They aren't here.\r\n"); return; } if (victim == ch) { ch.SendText("That's pointless.\r\n"); return; } if (Combat.IsSafe(ch, victim)) return; if (!ch.IsImmortal()) { ch.WaitState(Skill.SkillList["steal"].Delay); } // Justice stuff Crime.CheckThief(ch, victim); if (ch.IsNPC()) { percent = ch.Level * 2; } else { percent = ((PC)ch).SkillAptitude["steal"]; } percent += ch.GetCurrLuck() / 20; /* Luck */ percent -= victim.Level; /* Character level vs victim's */ if (ch.GetRace() == Race.RACE_HALFLING) { // Halflings get a racial bonus percent += 10; } if (victim.IsAffected(Affect.AFFECT_CURSE)) percent += 15; if (ch.IsAffected(Affect.AFFECT_CURSE)) percent -= 15; if (!victim.IsAwake()) percent += 25; /* Sleeping characters are easier */ if (ch.CheckSneak()) percent += 10; /* Quiet characters steal better */ if (!CharData.CanSee(ch, victim)) percent += 10; /* Unseen characters steal better */ if (!MUDString.IsPrefixOf(arg1, "coins")) { percent = (int)(percent * 1.2); /* Cash is fairly easy to steal */ } else { int number = MUDString.NumberArgument(arg1, ref arg); int count = 0; foreach (Object iobj in victim.Carrying) { if (CharData.CanSeeObj(ch, iobj) && MUDString.NameContainedIn(arg, iobj.Name)) { if (++count == number) { obj = iobj; break; } } } if (!obj) { ch.SendText("You can't find it.\r\n"); return; } if (ch.Level < victim.Level) { // stealing from higher level is possible, but harder percent -= 5 * (victim.Level - ch.Level); } else { // slight bonus for mobs lower level percent += (ch.Level - victim.Level); } if (obj.WearLocation == ObjTemplate.WearLocation.none) /* Items worn are harder */ percent = (int)(percent * .8); else percent = (int)(percent * .4); } ch.PracticeSkill("steal"); if (percent > 85) percent = 85; if (percent < 2) percent = 2; if (percent < MUDMath.NumberPercent()) { /* * Failure. */ //strip sneak ch.RemoveAffect(Affect.AFFECT_SNEAK); // chance of removing invis if (ch.IsAffected(Affect.AFFECT_INVISIBLE) && MUDMath.NumberPercent() > percent) { ch.SendText("You really bungled that attempt.\r\n"); ch.RemoveAffect(Affect.AFFECT_INVISIBLE); } else { ch.SendText("Oops.\r\n"); } SocketConnection.Act("$n&n tried to steal from $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); if (victim.IsAwake()) { SocketConnection.Act("$n&n tried to steal from you!", ch, null, victim, SocketConnection.MessageTarget.victim); } else { sleeping = true; } // Thief flag for justice. // Added so blind mobs dont hit who ever failed steal from em. if (victim.IsNPC()) { if (!sleeping && !victim.IsBlind()) { CommandType.Interpret(victim, "kill " + ch.Name); } } else { if (!victim.IsBlind() && !sleeping && victim.IsAffected(Affect.AFFECT_BERZERK)) { victim.SendText("In your &+Rblood rage&n, you lash out in anger!\r\n"); CommandType.Interpret(victim, "kill " + ch.Name); } } /* if ( !Macros.IS_SET( ch.actflags, PC.PLAYER_THIEF ) ) { Macros.SET_BIT( ref ch.actflags, PC.PLAYER_THIEF ); buf = String.Format( "{0} became a THIEF by stealing from {1}", ch._name, victim._name ); Immtalk.Immtalk( ch, Immtalk.IMMTALK_CRIME, ch.GetTrust(), buf ); CharData.SavePlayer( ch ); } */ // } if (sleeping) { if (MUDMath.NumberPercent() < victim.GetCurrLuck()) { CommandType.Interpret(victim, "wake"); } } return; } //end failure if (!MUDString.IsPrefixOf(arg1, "coins")) { int amount = victim.GetGold() * MUDMath.NumberRange(1, 20) / 100; int amount2 = victim.GetSilver() * MUDMath.NumberRange(1, 20) / 100; int amount3 = victim.GetCopper() * MUDMath.NumberRange(1, 20) / 100; int amount4 = victim.GetPlatinum() * MUDMath.NumberRange(1, 20) / 100; if ((amount + amount2 + amount3 + amount4) <= 0) { ch.SendText("You couldn't get any &n&+wcoins&n.\r\n"); return; } ch.ReceiveGold(amount); ch.ReceiveSilver(amount2); ch.ReceiveCopper(amount3); ch.ReceivePlatinum(amount4); victim.SpendGold(amount); victim.SpendSilver(amount2); victim.SpendCopper(amount3); victim.SpendPlatinum(amount4); string text = String.Format("Success! You got {0} &+Wplatinum&n, {1} &+Ygold&n, {2} silver, and {3} &+ycopper&n.\r\n", amount2, amount3, amount, amount4); ch.SendText(text); return; } if (!ch.CanDropObject(obj) || obj.HasFlag(ObjTemplate.ITEM_INVENTORY)) { ch.SendText("You can't pry it away.\r\n"); return; } if (ch.CarryNumber + 1 > Limits.MAX_CARRY) { ch.SendText("You have your hands full.\r\n"); return; } if (ch.CarryWeight + obj.GetWeight() > ch.MaxCarryWeight()) { ch.SendText("You cannot carry that much weight.\r\n"); return; } if (obj.WearLocation != ObjTemplate.WearLocation.none) { ch.SendText("Very daring, and you got it!\r\n"); victim.UnequipObject(obj); } obj.RemoveFromChar(); obj.ObjToChar(ch); ch.SendText("Nice work.\r\n"); if (obj.Trap != null && obj.Trap.CheckTrigger( Trap.TriggerType.steal)) { ch.SetOffTrap(obj); if (ch.CurrentPosition == Position.dead) { return; } } return; }
/// <summary> /// Tracking mob has found the person its after. Attack or react accordingly. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> public static void FoundPrey( CharData ch, CharData victim ) { string victname = String.Empty; string text = String.Empty; string lbuf = String.Empty; if (!victim) { Log.Error("FoundPrey: null victim", 0); return; } if (!victim.InRoom) { Log.Error("FoundPrey: null victim._inRoom", 0); return; } ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_HUNTING, 0, string.Format("{0}&n has found {1}.", ch.ShortDescription, victim.Name)); if (ch.IsAffected(Affect.AFFECT_TRACK)) { ch.RemoveAffect(Affect.AFFECT_TRACK); Combat.StopHunting(ch); return; } if (ch.IsAffected(Affect.AFFECT_JUSTICE_TRACKER)) { /* Give Justice the ability to ground flying culprits */ if (victim.FlightLevel != 0) { SocketConnection.Act("$n&n forces you to land!", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n forces $N&n to land!", ch, null, victim, SocketConnection.MessageTarget.room_vict); victim.FlightLevel = 0; } SocketConnection.Act("$n&n says, 'Stop, $N&n, you're under ARREST!'", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n says, 'Stop, $N&n, you're under ARREST!'", ch, null, victim, SocketConnection.MessageTarget.room); SocketConnection.Act("$n&n chains you up.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n binds $N&n so $E can't move.", ch, null, victim, SocketConnection.MessageTarget.room); victim.SetAffectBit(Affect.AFFECT_BOUND); victim.RemoveFromRoom(); if (ch.InRoom.Area.JailRoom != 0) { victim.AddToRoom(Room.GetRoom(victim.InRoom.Area.JailRoom)); } else { victim.SendText("Justice is broken in this town - there is no jail and you're screwed.\r\n"); } Combat.StopHunting(ch); return; } victname = victim.IsNPC() ? victim.ShortDescription : victim.Name; if (ch.FlightLevel != victim.FlightLevel) { if (ch.CanFly()) { if (ch.FlightLevel < victim.FlightLevel && ch.FlightLevel < CharData.FlyLevel.high) { Command.Fly(ch, new string[] { "up" }); } else { Command.Fly(ch, new string[] { "down" }); } } else { SocketConnection.Act("$n peers around looking for something.", ch, null, null, SocketConnection.MessageTarget.room); } return; } if (!CharData.CanSee(ch, victim)) { if (MUDMath.NumberPercent() < 90) return; switch (MUDMath.NumberBits(5)) { case 0: text = String.Format("You can't hide forever, {0}!", victname); Command.Say(ch, new string[]{text}); break; case 1: SocketConnection.Act("$n&n sniffs around the room.", ch, null, victim, SocketConnection.MessageTarget.room); text = "I can smell your blood!"; Command.Say(ch, new string[]{text}); break; case 2: text = String.Format("I'm going to tear {0} apart!", victname); Command.Yell(ch, new string[]{text}); break; case 3: Command.Say(ch, new string[]{"Just wait until I find you..."}); break; default: SocketConnection.Act("$p peers about looking for something.", ch, null, null, SocketConnection.MessageTarget.room); break; } return; } if (ch.InRoom.HasFlag(RoomTemplate.ROOM_SAFE) && ch.IsNPC()) { text = String.Format("Hunting mob {0} found a safe room {1}.", ch.MobileTemplate.IndexNumber, ch.InRoom.IndexNumber); Log.Trace(text); return; } if (ch.CurrentPosition > Position.kneeling) { switch (MUDMath.NumberBits(5)) { case 0: text = String.Format("I will eat your heart, {0}!", victname); Command.Say(ch, new string[]{text}); break; case 1: text = String.Format("You want a piece of me, {0}?", victname); Command.Say(ch, new string[]{text}); break; case 2: text = String.Format("How does your flesh taste {0}, like chicken?", victname); Command.Say(ch, new string[]{text}); break; case 3: SocketConnection.Act("$n&n howls gleefully and lunges at $N&n!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n howls gleefully and lunges at you!", ch, null, victim, SocketConnection.MessageTarget.victim); break; case 4: SocketConnection.Act("$n&n charges headlong into $N&n!", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); SocketConnection.Act("$n&n charges headlong into you!", ch, null, victim, SocketConnection.MessageTarget.victim); break; default: break; } Combat.StopHunting(ch); Combat.CheckAggressive(victim, ch); if (ch.Fighting) return; // Backstab if able, otherwise just kill them. // Kill if they don't have the skill or if they don't have a stabber. if (!ch.HasSkill("backstab")) { Combat.CombatRound(ch, victim, String.Empty); } else if (!Combat.Backstab(ch, victim)) { Combat.CombatRound(ch, victim, String.Empty); } } return; }
/// <summary> /// The main entry point for executing commands. /// Can be recursively called from 'at', 'order', 'force'. /// </summary> /// <param name="ch"></param> /// <param name="argument"></param> public static void Interpret(CharData ch, string argument) { // Get rid of leading and trailing spaces. argument = argument.Trim(); // Strip leading spaces. argument.Trim(); if (argument.Length == 0) { return; } // Remove AFK if (!ch.IsNPC()) { ch.RemoveActionBit(PC.PLAYER_AFK); } // Implement freeze command. if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_FREEZE)) { ch.SendText("You're totally frozen!\r\n"); return; } // Grab the command word. Special parsing so ' can be a command, // also no spaces needed after punctuation. string command; Object obj; Room room; int cmd; string logline = argument; int argptr = 0; if (!Char.IsLetter(argument[0]) && !Char.IsDigit(argument[0])) { command = argument.Substring(0, 1); argptr++; while (argument.Length > argptr && Char.IsWhiteSpace(argument[argptr])) { argument = argument.Remove(0, 1); } } else { command = MUDString.OneArgument(argument, ref argument); argument.Trim(); // Clean up the remainder of the command. } // Nothing to do if command is empty. Just send them a newline and bail. if (string.IsNullOrEmpty(command) && string.IsNullOrEmpty(argument)) { ch.SendText("\r\n"); return; } // Look for an item with a teleport trigger in the room. // and check to see if the command is a teleport trigger if (ch.InRoom && (obj = ch.GetObjHere(argument))) { if (obj.ItemType == ObjTemplate.ObjectType.teleport) { if (CheckCommandTrigger(command, obj.Values[1]) && obj.Values[2] != 0) { if (obj.Values[2] != -1) { obj.Values[2]--; } room = Room.GetRoom(obj.Values[0]); if (room) { SocketConnection.Act("$n&n vanishes suddenly.", ch, null, null, SocketConnection.MessageTarget.room); string text = String.Format("You {0} $p&n.\r\n", command); SocketConnection.Act(text, ch, obj, null, SocketConnection.MessageTarget.character); Log.Trace(String.Format("{0} activated keyword and was teleported by object.", ch.Name)); ch.RemoveFromRoom(); ch.AddToRoom(room); Interpret(ch, "look auto"); SocketConnection.Act("$n&n arrives suddenly.", ch, null, null, SocketConnection.MessageTarget.room); } else { ch.SendText("BUG: The target room for this teleporter does not exist.\r\n"); Log.Error("Target room for object {0} does not exist.", obj.ObjIndexData.IndexNumber); } return; } } else if (obj.ItemType == ObjTemplate.ObjectType.switch_trigger) { Exit exit; string cbuf = String.Format("Checking {0} against command no. {1} for {2}.", command, obj.Values[0], obj.Name); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_SPAM, 0, cbuf); if (CheckCommandTrigger(command, obj.Values[0])) { ch.SendText("Click.\r\n"); room = Room.GetRoom(obj.Values[1]); if (!room) { Log.Error("Target room for switch object {0} does not exist.", obj.ObjIndexData.IndexNumber); return; } exit = room.ExitData[obj.Values[2]]; if (exit == null) { Log.Error("Target exit for switch object {0} does not exist.", obj.ObjIndexData.IndexNumber); return; } if (exit.HasFlag(Exit.ExitFlag.blocked)) { exit.RemoveFlag(Exit.ExitFlag.blocked); } return; } } } // Look for command in command table. bool found = false; int trust = ch.GetTrust(); for (cmd = 0; cmd < CommandTable.Length; cmd++) { if (CommandTable[cmd].Name.StartsWith(command, StringComparison.CurrentCultureIgnoreCase) && (CommandTable[cmd].MinLevel <= trust)) { found = true; break; } } // Command was found, respond accordingly. if (found) { // Logging and snooping. if (CommandTable[cmd].LoggingType == LogType.never) { logline = "---- Nothing to see here ----"; } if ((!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_LOG)) || fLogAll || CommandTable[cmd].LoggingType == LogType.always) { string logBuf = String.Format("Log {0}: {1}", ch.Name, logline); Log.Trace(logBuf); ImmortalChat.SendImmortalChat(ch, ImmortalChat.IMMTALK_SECURE, ch.GetTrust(), logBuf); } if (ch.Socket && ch.Socket.SnoopBy) { ch.Socket.SnoopBy.WriteToBuffer("% "); ch.Socket.SnoopBy.WriteToBuffer(logline); ch.Socket.SnoopBy.WriteToBuffer("\r\n"); } // Break meditate if (CommandTable[cmd].BreakMeditate) { if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEDITATING)) { ch.RemoveActionBit(PC.PLAYER_MEDITATING); ch.SendText("You stop meditating.\r\n"); } } // Break sneak, hide, and invis // Anything that will break hide OR invis will break concealment // This is DUMB! Breaks invis w/backstab on a target that's not // there: i.e. "backstab trolll"/"backstab humann" . vis and no // attack! - (Should be handled with make_vis function). if (CommandTable[cmd].BreakInvisibility) { if (ch.IsAffected(Affect.AFFECT_INVISIBLE)) { ch.RemoveAffect(Affect.AFFECT_INVISIBLE); ch.RemoveAffect(Affect.AFFECT_HIDE); ch.RemoveAffect(Affect.AFFECT_MINOR_INVIS); SocketConnection.Act("$n&n snaps into visibility.", ch, null, null, SocketConnection.MessageTarget.room); ch.SendText("You snap into visibility.\r\n"); } else if (ch.IsAffected(Affect.AFFECT_MINOR_INVIS)) { ch.RemoveAffect(Affect.AFFECT_INVISIBLE); ch.RemoveAffect(Affect.AFFECT_HIDE); ch.RemoveAffect(Affect.AFFECT_MINOR_INVIS); ch.SendText("You appear.\r\n"); } } if (CommandTable[cmd].BreakHide) { if (ch.IsAffected(Affect.AFFECT_MINOR_INVIS)) { ch.SendText("You appear.\r\n"); } ch.AffectStrip( Affect.AffectType.skill, "shadow form"); ch.RemoveAffect( Affect.AFFECT_HIDE ); ch.RemoveAffect(Affect.AFFECT_MINOR_INVIS); } } // Command was not found, respond accordingly. else { // Look for command in socials table. if (!Database.SocialList.CheckSocial(ch, command, argument)) { if (!ch.IsNPC() && !MUDString.IsPrefixOf(command, "petition")) { string logBuf = String.Format("Log {0}: {1}", ch.Name, logline); Log.Trace(logBuf); ImmortalChat.SendImmortalChat(ch, ImmortalChat.IMMTALK_SECURE, ch.GetTrust(), logBuf); Command.Petition(ch, argument.Split(' ')); return; } Log.Trace("Failed to match command."); ch.SendText("Huh?\r\n"); } return; } // Character not in position for command? if (ch.CurrentPosition < CommandTable[cmd].MinPosition) { switch (ch.CurrentPosition) { case Position.dead: ch.SendText("Lie still; you are &+rDEAD&n!\r\n"); break; case Position.mortally_wounded: case Position.incapacitated: ch.SendText("You are hurt far too bad for that.\r\n"); break; case Position.stunned: ch.SendText("You are too stunned to do that.\r\n"); break; case Position.sleeping: ch.SendText("In your dreams, or what?\r\n"); break; case Position.reclining: ch.SendText("You can't do that while lying around.\r\n"); break; case Position.sitting: ch.SendText("You can't do this sitting!\r\n"); break; case Position.kneeling: ch.SendText("Get off your knees!\r\n"); break; case Position.resting: ch.SendText("Nah... You feel too relaxed...\r\n"); break; case Position.fighting: ch.SendText("No way! You are still fighting!\r\n"); break; } if (!ch.IsImmortal()) { return; } if (ch.CurrentPosition == Position.dead) ch.CurrentPosition = Position.sleeping; ch.SendText("You're not in the right position, but..\r\n"); } if (ch.IsAffected(Affect.AFFECT_MINOR_PARA) && CommandTable[cmd].Function != Command.LookCommand && CommandTable[cmd].Function != Command.Score && CommandTable[cmd].Function != Command.Attributes) { if (!ch.IsImmortal()) { ch.SendText("&+YYour mind moves, but your body doesn't.&n\r\n"); return; } ch.SendText("&+YYour immortality allows you to move!&n\r\n"); } string[] str = argument.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); // Dispatch the command. Catch any exceptions, since most exceptions will probably happen // based on user commands. try { (CommandTable[cmd].Function)(ch, str); } catch (Exception ex) { Log.Error("Exception in CommandType.Interpret: " + ex); } return; }
/// <summary> /// Deliver a killing blow to the victim. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> public static void KillingBlow( CharData ch, CharData victim ) { Event eventdata; Room room; bool noCorpse = false; StopFighting( victim, true ); if( victim.GroupLeader || victim.NextInGroup ) { victim.RemoveFromGroup( victim ); } if( ch != victim ) { if( victim.IsNPC() && victim.MobileTemplate.DeathFun.Count > 0 ) { victim.MobileTemplate.DeathFun[0].SpecFunction( victim, MobFun.PROC_NORMAL ); } // prog_death_trigger( victim ); } if( victim.IsNPC() && victim.DeathFunction != null ) { noCorpse = victim.DeathFunction.SpecFunction( victim, MobFun.PROC_DEATH ); } if( !noCorpse ) { MakeCorpse( victim ); } /* Strip all event-spells from victim! */ for( int i = (Database.EventList.Count - 1); i >= 0; i-- ) { eventdata = Database.EventList[i]; if( eventdata.Type == Event.EventType.immolate || eventdata.Type == Event.EventType.acid_arrow ) { if( (CharData)eventdata.Target2 == victim ) { Database.EventList.Remove( eventdata ); } } } if( victim.Rider ) { SocketConnection.Act( "$n&n dies suddenly, and you topple to the &n&+yground&n.", victim, null, victim.Rider, SocketConnection.MessageTarget.victim ); victim.Rider.Riding = null; victim.Rider.CurrentPosition = Position.resting; victim.Rider = null; } if( victim.Riding ) { SocketConnection.Act( "$n&n topples from you, &+Ldead&n.", victim, null, victim.Riding, SocketConnection.MessageTarget.victim ); victim.Riding.Rider = null; victim.Riding = null; } if (!victim.IsNPC() && victim.IsAffected(Affect.AFFECT_VAMP_BITE)) { victim.SetPermRace( Race.RACE_VAMPIRE ); } for (int i = (victim.Affected.Count - 1); i >= 0; i--) { /* Keep the ghoul affect */ if (!victim.IsNPC() && victim.IsAffected(Affect.AFFECT_WRAITHFORM)) { continue; } victim.RemoveAffect(victim.Affected[i]); } if( victim.IsNPC() ) { victim.MobileTemplate.NumberKilled++; // This may invalidate the char list. CharData.ExtractChar( victim, true ); return; } CharData.ExtractChar( victim, false ); //save corpses, don't wait til next save_corpse event Database.CorpseList.Save(); // Character has died in combat, extract them to repop point and put // them at the menu. /* * Pardon crimes once justice system is complete */ // This is where we send them to the menu. victim.DieFollower( victim.Name ); if( victim.InRoom ) { room = victim.InRoom; } else { List<RepopulationPoint> repoplist = victim.GetAvailableRepops(); if( repoplist.Count < 1 ) { victim.SendText( "There is no RepopPoint entry for your race and class. Sending you to limbo.\r\n" ); room = Room.GetRoom( StaticRooms.GetRoomNumber("ROOM_NUMBER_START") ); } else { // Drop them at the first repop point in the list. We may want to be fancier about this later, such as dropping them // at the repop for class none if their particular class isn't found. room = Room.GetRoom(repoplist[0].Room); if( !room ) { victim.SendText( "The repop point for your race/class does not exist. Please bug this. Sending you to limbo.\r\n" ); room = Room.GetRoom( StaticRooms.GetRoomNumber("ROOM_NUMBER_START") ); } if( !victim.IsNPC() && Room.GetRoom( ( (PC)victim ).CurrentHome ) ) { room = Room.GetRoom( ( (PC)victim ).CurrentHome ); } } } victim.RemoveFromRoom(); if( room ) { victim.InRoom = room; } // Put them in the correct body if( victim.Socket && victim.Socket.Original ) { CommandType.Interpret(victim, "return"); } // Reset reply pointers - handled by CharData.ExtractChar. CharData.SavePlayer( victim ); // Remove from char list: handled by CharData.ExtractChar. victim.Socket.ShowScreen(ModernMUD.Screen.MainMenuScreen); if( victim.Socket != null ) { victim.Socket.ConnectionStatus = SocketConnection.ConnectionState.menu; } // Just died flag used for safe time after re-login. victim.SetActionBit( PC.PLAYER_JUST_DIED ); return; }
/// <summary> /// Flee: Attempt to run away from combat. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Flee(CharData ch, string[] str) { if( ch == null ) return; int attempt; int chances; if (ch.CurrentPosition < Position.reclining || ch.Wait > 0) { return; } // Remove memorization and meditation bits - Xangis if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEDITATING)) { SocketConnection.Act("$N&n is disrupted from meditation.", ch, null, null, SocketConnection.MessageTarget.room); SocketConnection.Act("Your meditation is disrupted.", ch, null, null, SocketConnection.MessageTarget.character); ch.RemoveActionBit(PC.PLAYER_MEDITATING); } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEMORIZING)) { SocketConnection.Act("$N&n abandons $S studies.", ch, null, null, SocketConnection.MessageTarget.room); SocketConnection.Act("You abandon your studies.", ch, null, null, SocketConnection.MessageTarget.character); ch.RemoveActionBit(PC.PLAYER_MEMORIZING); } if (ch.CurrentPosition < Position.fighting) { ch.SendText("You scramble madly to your feet!\r\n"); SocketConnection.Act("$n&n scrambles madly to $s feet!", ch, null, null, SocketConnection.MessageTarget.room); ch.CurrentPosition = Position.standing; return; } if (!ch.InRoom) { ch.SendText("You give up when you realize there's nowhere to flee to.\r\n"); } // Panicked people can flee when not fighting. CharData victim = ch.Fighting; if (!victim) { if (ch.CurrentPosition == Position.fighting) { ch.CurrentPosition = Position.standing; } } if (ch.IsAffected(Affect.AFFECT_BERZERK)) { ch.SendText("You can't flee, you're in a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!\r\n"); return; } if (ch.IsAffected( Affect.AFFECT_BOUND)) { ch.SendText("You are bound! You can't move!\r\n"); SocketConnection.Act("$n&n tries to flee, but is tied up!", ch, null, null, SocketConnection.MessageTarget.room); return; } if (ch.IsAffected( Affect.AFFECT_HOLD) || ch.IsAffected( Affect.AFFECT_MINOR_PARA)) { ch.SendText("You can't move!\r\n"); SocketConnection.Act("$n&n tries to flee, but $e can't move!", ch, null, null, SocketConnection.MessageTarget.room); return; } // You should almost always be able to flee when not fighting. if (ch.CurrentPosition == Position.standing) { chances = 30; } else { chances = 6; } Room wasIn = ch.InRoom; for (attempt = 0; attempt < chances; attempt++) { Exit exit; Exit.Direction door = Database.RandomDoor(); if ((exit = wasIn.GetExit(door)) == null || !exit.TargetRoom || exit.TargetRoom == wasIn || exit.HasFlag(Exit.ExitFlag.closed) || (ch.IsNPC() && (Room.GetRoom(exit.IndexNumber).HasFlag(RoomTemplate.ROOM_NO_MOB) || (ch.HasActionBit(MobTemplate.ACT_STAY_AREA) && exit.TargetRoom.Area != ch.InRoom.Area)))) { continue; } if (ch.Riding && ch.Riding.Fighting) { Combat.StopFighting(ch.Riding, true); } // Just to keep the damned messages from being wacky... ch.SetAffectBit(Affect.AFFECT_IS_FLEEING); ch.Move(door); ch.RemoveAffect(Affect.AFFECT_IS_FLEEING); if (ch.InRoom == wasIn) { break; } Room nowIn = ch.InRoom; ch.InRoom = wasIn; SocketConnection.Act("$n&n panics and attempts to flee...", ch, null, null, SocketConnection.MessageTarget.room, true); string text; if (ch.CheckSneak()) { SocketConnection.Act("$n&n has fled!", ch, null, null, SocketConnection.MessageTarget.room); } else { text = String.Format("$n&n flees {0}ward.", door.ToString()); SocketConnection.Act(text, ch, null, null, SocketConnection.MessageTarget.room, true); } ch.InRoom = nowIn; text = String.Format("You flee {0}ward!\r\n", door.ToString()); ch.SendText(text); Combat.StopFighting(ch, true); return; } SocketConnection.Act("$n&n tries to flee but can't make it out of here!", ch, null, null, SocketConnection.MessageTarget.room, true); ch.SendText("&+WYour escape is blocked!\r\n"); return; }
/// <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; }
/// <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; }
public static void StopFollower( CharData ch ) { if( !ch ) { Log.Error("StopFollower called with no CH argument.\r\n", 0); return; } if( !ch.Master ) { Log.Error("StopFollower: null master.", 0); return; } if( ch.IsAffected( Affect.AFFECT_CHARM ) ) { ch.RemoveAffect(Affect.AFFECT_CHARM); ch.AffectStrip( Affect.AffectType.spell, "domination"); } if( ch.Master != ch && CanSee( ch.Master, ch ) && ch.Master.InRoom ) SocketConnection.Act( "$n&n stops following you.", ch, null, ch.Master, SocketConnection.MessageTarget.victim ); if( ch.InRoom ) SocketConnection.Act( "You stop following $N&n.", ch, null, ch.Master, SocketConnection.MessageTarget.character ); // Remove the follower from the list of followers foreach( CharData follower in ch.Master.Followers ) { if( follower == ch ) { ch.Master.Followers.Remove( follower ); } } ch.Master = null; return; }
/// <summary> /// Extracts a character from the world. If delete is true, it then deletes that character. /// </summary> /// <param name="ch"></param> /// <param name="delete"></param> public static void ExtractChar( CharData ch, bool delete ) { if( ch == null ) { Log.Error( "ExtractChar: null ch.", 0 ); return; } try { if (ch.Fighting) { Combat.StopFighting(ch, true); } Magic.ForgetAllSpells(ch); Event.DeleteAttachedEvents(ch); // Remove any affects we want to be gone next time they log in. ch.RemoveAffect(Affect.AFFECT_CASTING); ch.RemoveAffect(Affect.AFFECT_SINGING); // Meaning they're dead for good or have left the game. if (delete) { string name; if (ch.IsNPC()) { name = ch.ShortDescription; } else { name = ch.Name; } ch.DieFollower(name); if (ch.GroupLeader || ch.NextInGroup) { ch.RemoveFromGroup(ch); } /* Get rid of weapons _first_ */ { Object obj3 = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one); Object obj2 = Object.GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two); if (obj3 != null) { obj3.RemoveFromWorld(); } /* Now kill obj2 if it exists no matter if on body or floor */ if (obj2) { obj2.RemoveFromWorld(); } } for (int i = ch.Carrying.Count - 1; i >= 0; --i) { ch.Carrying[i].RemoveFromWorld(); } } CharData worldChar; for (int xx = Database.CharList.Count - 1; xx >= 0; xx--) { worldChar = Database.CharList[xx]; if (worldChar.ReplyTo == ch) { worldChar.ReplyTo = null; } if (worldChar.IsConsenting(ch)) { worldChar.StopConsenting(ch); SocketConnection.Act("You stop consenting $N&n.", worldChar, null, ch, SocketConnection.MessageTarget.character); } if (worldChar.IsIgnoring(ch)) { worldChar.StopIgnoring(ch); SocketConnection.Act("You stop ignoring $N&n.", worldChar, null, ch, SocketConnection.MessageTarget.character); } if (!worldChar.IsNPC() && ((PC)worldChar).Guarding == ch) { ((PC)worldChar).Guarding = null; SocketConnection.Act("You stop guarding $N&n.", worldChar, null, ch, SocketConnection.MessageTarget.character); } if (worldChar.IsHating(ch)) worldChar.StopHating(ch); if (worldChar.Hunting && worldChar.Hunting.Who == ch) Combat.StopHunting(worldChar); if (worldChar.Fearing && worldChar.Fearing.Who == ch) Combat.StopFearing(worldChar); // Remove from the active character list. // BUG: TODO: FIXME: This invalidates the list for anyone iterating through // a list that may kill characters, such as violence_update. // it = CharList.erase( it ); } if (ch.InRoom) { // This was placed *after* the act strings to be safe. for (int iwch = ch.InRoom.People.Count - 1; iwch >= 0; iwch--) { if (ch.InRoom.People[iwch] == ch) { ch.RemoveFromRoom(); break; } } } // They're not being yanked from game, probably just dead and going to the menu. if (!delete) { Room location; if (ch.Level < 5 || Macros.IsSet((int)Database.SystemData.ActFlags, (int)Sysdata.MudFlags.alwaysequip)) ch.ReceiveNewbieEquipment(); if (!ch.IsNPC() && (ch.GetOrigRace() < Limits.MAX_PC_RACE) && ((int)ch.CharacterClass.ClassNumber < CharClass.ClassList.Length)) { // Get the default respawn location based on currhome, then race/class default. location = Room.GetRoom(((PC)ch).CurrentHome); if (location == null) { int place; List<RepopulationPoint> repoplist = ch.GetAvailableRepops(); if (repoplist.Count < 1) { place = StaticRooms.GetRoomNumber("ROOM_NUMBER_START"); } else { place = repoplist[0].RoomIndexNumber; } location = Room.GetRoom(place); if (location == null) { Log.Error("Starting room does not exist for class {0} of player's race! Calling ch.AddToRoom() for altar.", (int)ch.CharacterClass.ClassNumber); ch.AddToRoom(Room.GetRoom(StaticRooms.GetRoomNumber("ROOM_NUMBER_ALTAR"))); } } else { ch.AddToRoom(location); } } else { location = Room.GetRoom(StaticRooms.GetRoomNumber("ROOM_NUMBER_START")); if (location == null) { Log.Error("Starting room {0} does not exist! Calling char_to_room for altar.", StaticRooms.GetRoomNumber("ROOM_NUMBER_START")); ch.AddToRoom(Room.GetRoom(StaticRooms.GetRoomNumber("ROOM_NUMBER_ALTAR"))); } else { ch.AddToRoom(Room.GetRoom(StaticRooms.GetRoomNumber("ROOM_NUMBER_START"))); } } return; } // Clear modifiers. if (ch.IsNPC()) { --ch.MobileTemplate.NumActive; } else { ((PC)ch).Hunger = 48; ((PC)ch).Thirst = 48; ((PC)ch).Drunk = 0; ((PC)ch).LastRentLocation = 0; ch.ArmorPoints = 100; ch.CurrentPosition = Position.standing; ch.Hitpoints = Math.Max(1, ch.Hitpoints); ch.CurrentMana = Math.Max(1, ch.CurrentMana); ch.CurrentMoves = Math.Max(1, ch.CurrentMoves); ((PC)ch).HitpointModifier = 0; ch.Hitroll = 0; ch.Damroll = 0; ch.SavingThrows[0] = 0; ch.SavingThrows[1] = 0; ch.SavingThrows[2] = 0; ch.SavingThrows[3] = 0; ch.SavingThrows[4] = 0; ch.ModifiedStrength = 0; ch.ModifiedIntelligence = 0; ch.ModifiedWisdom = 0; ch.ModifiedDexterity = 0; ch.ModifiedConstitution = 0; ch.ModifiedAgility = 0; ch.ModifiedCharisma = 0; ch.ModifiedPower = 0; ch.ModifiedLuck = 0; ((PC)ch).MaxStrMod = 0; ((PC)ch).MaxIntMod = 0; ((PC)ch).MaxWisMod = 0; ((PC)ch).MaxDexMod = 0; ((PC)ch).MaxConMod = 0; ((PC)ch).MaxAgiMod = 0; ((PC)ch).MaxChaMod = 0; ((PC)ch).MaxPowMod = 0; ((PC)ch).MaxLukMod = 0; } if (ch.Socket && ch.Socket.Original) { CommandType.Interpret(ch, "return"); } ch.DeleteMe = true; } catch (Exception ex) { Log.Error("Exception in ExtractChar: " + ex.ToString()); } return; }
/// <summary> /// When a spell event terminates, we need something to happen. /// /// By this point we should have terminated the spell/song event data /// and should only need the info about the character and the spell /// and the argument(s). /// /// Passing of the correct function parameters should be handled by the /// event system. /// </summary> /// <param name="ch">The caster</param> /// <param name="spell">The spell number</param> /// <param name="target">The _targetType</param> public static void FinishSpell( CharData ch, Spell spell, Target target ) { Object obj; int chance = 0; bool found = false; string lbuf = String.Format("Magic.FinishSpell: {0} by {1}", spell.Name, ch.Name); Log.Trace( lbuf ); for( int i = Database.CastList.Count - 1; i >= 0; i--) { if (Database.CastList[i].Who && Database.CastList[i].Who == ch) { Database.CastList.RemoveAt( i ); } } // If they're not casting at the end of the song or spell // they certainly can't finish it. if( ch.IsAffected( Affect.AFFECT_CASTING ) ) ch.RemoveAffect( Affect.AFFECT_CASTING ); else { return; } if( !ch.CheckConcentration(spell) ) return; if( ( ch.IsAffected( Affect.AFFECT_MUTE ) || ch.HasInnate( Race.RACE_MUTE ) ) && !ch.IsClass( CharClass.Names.psionicist ) ) { ch.SendText( "Your lips move but no sound comes out.\r\n" ); return; } // Make sure the room is still castable. if( !ch.InRoom.CheckCastable( ch, false, false ) ) return; if( ch.InRoom.CheckStarshell( ch ) ) return; MemorizeData memorized = null; if (!ch.IsNPC() && !ch.IsImmortal() && !ch.IsClass(CharClass.Names.psionicist)) { foreach( MemorizeData mem in ((PC)ch).Memorized ) { if( !mem.Memmed ) continue; if( mem.Name == spell.Name ) { found = true; memorized = mem; break; } } if (!found && !ch.IsNPC() && !ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You do not have that spell memorized!\r\n" ); if( spell.ValidTargets == TargetType.objectOrCharacter ) target = null; else if( spell.ValidTargets == TargetType.none ) target = null; return; } } if( ch.IsAffected( Affect.AFFECT_FEEBLEMIND ) ) { ch.SendText( "You are just too stupid to cast that spell!\r\n" ); SocketConnection.Act( "$n&n screws up $s face in concentration.", ch, null, null, SocketConnection.MessageTarget.room ); SocketConnection.Act( "$n&n tries really, really hard to complete a spell, but fails.", ch, null, null, SocketConnection.MessageTarget.room ); return; } // Locate targets. CharData victim = null; switch( spell.ValidTargets ) { default: Log.Trace( "FinishSpell: bad TargetType for spell {1}.", spell ); return; case TargetType.objectOrCharacter: if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } break; case TargetType.none: break; case TargetType.trap: ch.SendText( "You cannot cast a trap!\r\n" ); return; case TargetType.singleCharacterOffensive: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { //allow casting if in combat and no _targetType specified if( !( ch.Fighting && victim == ch.Fighting ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } } if( !victim ) { ch.SendText( "They aren't here.\r\n" ); return; } if( !victim.InRoom || victim.InRoom != ch.InRoom ) { ch.SendText( "They are not here.\r\n" ); return; } if( Combat.IsSafe( ch, victim ) ) return; // Command.is_safe could wipe out victim, as it calls procs if a boss // check and see that victim is still valid if( !victim ) return; Crime.CheckAttemptedMurder( ch, victim ); /* Check for globes. This will stop any spells of type TargetType.singleCharacterOffensive * but area effect spells with type TargetType.none will get through, since we * don't know whether they will be offensive or not. The only thing we can * really do is add this same thing in the Command.SpellDamage function to prevent * those from getting through. However, we must treat cases of things like * an area effect sleep spell as a special case within the SpellWhatever * function in Spells.cs. However, by the nature of the spell, anything * that is not either offensive and not direct damage, it should get through * just so that these spells have some weaknesses for the strategic to get * around. */ /* * TODO: Find out why this globe code was commented out and either uncomment or delete. if( CharData.IsAffected( victim, Affect.AFFECT_MAJOR_GLOBE ) && Spell.Table[spell].spell_circle[ch.cclass] <= 6 ) { Descriptor._actFlags( "&+RThe globe around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_GREATER_SPIRIT_WARD ) && Spell.Table[spell].spell_circle[ch.cclass] <= 5 ) { Descriptor._actFlags( "&+WThe aura around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_MINOR_GLOBE ) && Spell.Table[spell].spell_circle[ch.cclass] <= 4 ) { Descriptor._actFlags( "&+RThe globe around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+RYour globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+R$N&+R's globe deflects $n&+R's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } if( CharData.IsAffected( victim, Affect.AFFECT_SPIRIT_WARD ) && Spell.Table[spell].spell_circle[ch.cclass] <= 3 ) { Descriptor._actFlags( "&+WThe aura around $N&n's body bears the brunt of your assault!&n", ch, null, victim, Descriptor.MessageTarget.character ); Descriptor._actFlags( "&+WYour globe absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.victim ); Descriptor._actFlags( "&+W$N&+W's aura absorbs $n&+W's attack!&n", ch, null, victim, Descriptor.MessageTarget.room ); return; } */ break; case TargetType.singleCharacterWorld: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) && victim != ch ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } break; case TargetType.singleCharacterDefensive: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) && victim != ch ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !victim || victim.InRoom != ch.InRoom ) { ch.SendText( "They aren't here.\r\n" ); return; } break; case TargetType.self: break; case TargetType.objectInInventory: obj = (Object)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !obj || obj.CarriedBy != ch ) { ch.SendText( "You are not carrying that.\r\n" ); return; } break; case TargetType.objectInRoom: obj = (Object)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !obj || ( obj.CarriedBy != ch && obj.InRoom != ch.InRoom ) ) { ch.SendText( "You do not see that here.\r\n" ); return; } break; case TargetType.objectCorpse: break; case TargetType.singleCharacterRanged: victim = (CharData)target; if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { ch.SendText( "You cannot see to cast that spell.\r\n" ); return; } if( !victim || victim.FlightLevel != ch.FlightLevel || !CharData.CanSee( ch, victim ) ) { ch.SendText( "Your prey has disappeared.\r\n" ); return; } //check that _targetType is still within the spell range if( ch.InRoom == victim.InRoom ) { break; } bool targetInRange = false; int dir; for( dir = 0; dir < Limits.MAX_DIRECTION; dir++ ) { if( !ch.InRoom.ExitData[ dir ] || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.secret ) || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.closed ) || ch.InRoom.ExitData[ dir ].HasFlag( Exit.ExitFlag.blocked ) || ch.InRoom.ExitData[dir].HasFlag(Exit.ExitFlag.walled)) continue; if( ch.InRoom.ExitData[ dir ].TargetRoom == victim.InRoom ) { targetInRange = true; break; } // for fireball we check two rooms away if( ch.InRoom.ExitData[ dir ].TargetRoom && ch.InRoom.ExitData[ dir ].TargetRoom.ExitData[ dir ] && ch.InRoom.ExitData[ dir ].TargetRoom.ExitData[ dir ].TargetRoom == victim.InRoom ) { targetInRange = true; break; } } if( !targetInRange ) { ch.SendText( "They are no longer in range!\r\n" ); return; } break; } // No wait state - we already made them wait. ch.PracticeSpell( spell ); if (ch.IsNPC()) { chance = 85; } else if (ch.HasSpell(spell.Name)) { chance = ((PC)ch).SpellAptitude[spell.Name]; } if( !ch.IsImmortal() && ( MUDMath.NumberPercent() > chance ) ) { ch.SendText( "You lost your concentration.\r\n" ); SocketConnection.Act( "&+r$n&n&+r stops chanting abruptly.&n", ch, null, null, SocketConnection.MessageTarget.room ); } else { // TODO: Figure out whether this should be re-enabled. //if( song ) //{ // ch.SendText( "You complete a verse of the song...\r\n" ); // ch.GainExperience( 1 ); // SaySong( ch, spell ); //} //else { if (!ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "You complete your spell...\r\n" ); if( MUDString.StringsNotEqual( spell.Name, "ventriloquate" ) ) SaySpell( ch, spell ); } ch.GainExperience( 1 ); } if( !ch.IsNPC() ) { string buf = String.Format( "Spell ({0}) being cast by {1}", spell.Name, ch.Name ); Log.Trace( buf ); } int level = Macros.Range(1, ch.Level, Limits.LEVEL_HERO); spell.Invoke(ch, level, target); if( memorized && !ch.IsNPC() && !ch.IsImmortal() ) { memorized.Memmed = false; memorized.Memtime = memorized.FullMemtime; } } if( ( spell.ValidTargets == TargetType.singleCharacterOffensive || spell.ValidTargets == TargetType.singleCharacterRanged ) && victim && victim.Master != ch && victim != ch && victim.IsAwake() ) { if( ch.InRoom == victim.InRoom ) { if( !victim.Fighting && CharData.CanSee( victim, ch ) ) victim.AttackCharacter( ch ); } else { // Range spell presumably, since different rooms Combat.StartGrudge( victim, ch, true ); foreach( CharData roomChar in ch.InRoom.People ) { if( roomChar == victim ) continue; if( roomChar.FlightLevel != ch.FlightLevel ) continue; //protectors will be aggro'd if (roomChar.HasActionBit(MobTemplate.ACT_PROTECTOR) && (roomChar.GetRace() == victim.GetRace())) { Combat.StartGrudge( roomChar, ch, true ); } // all aggro mobs will hunt down caster if (roomChar.HasActionBit(MobTemplate.ACT_AGGRESSIVE)) { Combat.StartGrudge(roomChar, ch, true); } } } } return; }
public static void Chameleon(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsNPC() && !ch.HasSkill("chameleon power")) { ch.SendText("You don't know how to act like a chameleon.\r\n"); return; } ch.SendText("You attempt to blend in with your surroundings.\r\n"); if (ch.IsAffected(Affect.AFFECT_HIDE)) { ch.RemoveAffect(Affect.AFFECT_HIDE); } if (ch.CheckSkill("chameleon power")) { ch.SetAffectBit(Affect.AFFECT_HIDE); } return; }
/// <summary> /// Set ch as fighting victim. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> public static void SetFighting( CharData ch, CharData victim ) { if( ch == victim ) return; if( ch.Fighting ) { Log.Error( "Set_fighting: already fighting", 0 ); string buf = String.Format( "...{0} attacking {1} at {2}", ( ch.IsNPC() ? ch.ShortDescription : ch.Name ), ( victim.IsNPC() ? victim.ShortDescription : victim.Name ), victim.InRoom.IndexNumber ); Log.Error( buf, 0 ); return; } if (ch.IsAffected(Affect.AFFECT_SLEEP)) { ch.RemoveAffect(Affect.AFFECT_SLEEP); } if( ch.FlightLevel != victim.FlightLevel ) { StartGrudge( ch, victim, false ); return; } ch.Fighting = victim; if( ch.CurrentPosition == Position.standing ) ch.CurrentPosition = Position.fighting; return; }
/// <summary> /// Command to climb something. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Climb(CharData ch, string[] str) { if( ch == null ) return; int chance; if (!ch.HasSkill("climb")) { ch.SendText("You lack the skills to climb anything.\r\n"); return; } if (str.Length == 0) { ch.SendText("Climb what?\r\n"); return; } Object obj = ch.GetObjHere(str[0]); if (!obj) { ch.SendText("Uhh... what exactly did you want to climb!?\r\n"); return; } if (obj.ItemType != ObjTemplate.ObjectType.wall) { ch.SendText("That wasn't exactly designed for climbing.\r\n"); return; } if (ch.IsNPC()) { chance = ch.Level * 3 / 2 + 20; } else { chance = ((PC)ch).SkillAptitude["climb"]; } // Agility helps. chance += ch.GetCurrAgi() / 10; switch (obj.ObjIndexData.IndexNumber) { case StaticObjects.OBJECT_NUMBER_WALL_STONE: chance += 5; break; case StaticObjects.OBJECT_NUMBER_WALL_IRON: chance -= 15; break; default: ch.SendText("That wasn't exactly designed for climbing.\r\n"); return; } // Maximum chance of 98% if (chance > 98) { chance = 98; } if (MUDMath.NumberPercent() >= chance) { ch.SendText("You try to climb it, but you fall on your ass!\r\n"); ch.CurrentPosition = Position.sitting; ch.WaitState(5); return; } ch.SendText("With great skill, you scale the wall!\r\n"); // Value 0 of a wall object is the direction that has been walled... // This means that they should move in that direction. We leave it up to // move_char to make sure that there is actually an exit in that direction. // we use the climbing bit to allow them to pass the walls in move_char. ch.SetAffectBit(Affect.AFFECT_CLIMBING); ch.Move((Exit.Direction)obj.Values[0]); ch.RemoveAffect(Affect.AFFECT_CLIMBING); return; }
public static void Berzerk(CharData ch, string[] str) { if( ch == null ) return; Affect af = new Affect(); /* Don't allow charmed mobs to do this, check player's level */ if ((ch.IsNPC() && ch.IsAffected( Affect.AFFECT_CHARM)) || (!ch.IsNPC() && !ch.HasSkill("berzerk"))) { ch.SendText("You're not enough of a warrior to enter a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n.\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_BERZERK)) { if (MUDMath.NumberPercent() + 10 > ((PC)ch).SkillAptitude["berzerk"]) { ch.SendText("You failed to calm yourself down!\r\n"); ch.WaitState(Skill.SkillList["berzerk"].Delay); return; } ch.SendText("You no longer see targets everywhere.\r\n"); ch.RemoveAffect(Affect.AFFECT_BERZERK); ch.WaitState(Skill.SkillList["berzerk"].Delay); return; } ch.SendText("Your slam your weapon into yourself and &+Rbl&n&+ro&+Ro&n&+rd&n splatters all over!\r\n"); ch.SendText("The sight of &+Rbl&n&+ro&+Ro&n&+rd&n begins to drive you crazy!\r\n"); if (ch.CheckSkill("berzerk")) { af.Value = "berzerk"; af.Type = Affect.AffectType.skill; af.Duration = MUDMath.Dice(1, 2); af.AddModifier( Affect.Apply.hitroll, Math.Max(ch.Level / 6, 2)); af.AddModifier( Affect.Apply.damroll, Math.Max(ch.Level / 6, 2)); af.AddModifier( Affect.Apply.ac, (ch.Level / 2)); af.AddModifier( Affect.Apply.max_constitution, MUDMath.Dice(5, 9)); af.AddModifier( Affect.Apply.agility, 0 - MUDMath.Dice(5, 9)); af.AddModifier( Affect.Apply.max_strength, MUDMath.Dice(5, 9)); af.SetBitvector(Affect.AFFECT_BERZERK); ch.AddAffect(af); ch.SendText("You are overcome by &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!\r\n"); SocketConnection.Act("$n has slipped into a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!!", ch, null, null, SocketConnection.MessageTarget.room); return; } ch.SendText("You get a little angry, but fail to call up a &+Rblood rage&n.\r\n"); return; }
/// <summary> /// Hide yourself or an object. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Hide(CharData ch, string[] str) { if( ch == null ) return; /* Check player's skill */ if (!ch.HasSkill("hide")) { if(str.Length != 0) { HideItem(ch, new[] { str[0] }); return; } ch.SendText("You're far too obvious to hide anywhere.\r\n"); return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (ch.CurrentPosition <= Position.sleeping) { return; } ch.SendText("You attempt to hide.\r\n"); if (ch.IsAffected(Affect.AFFECT_HIDE)) { ch.RemoveAffect(Affect.AFFECT_HIDE); } if (ch.CheckSkill("hide")) { ch.SetAffectBit(Affect.AFFECT_HIDE); } ch.WaitState(12); return; }
/// <summary> /// Sing -- using a bard song without an instrument. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Sing(CharData ch, string[] str) { if( ch == null ) return; if (!ch.IsClass(CharClass.Names.bard) && !ch.IsImmortal()) { ch.SendText("You don't know how to sing.\r\n"); return; } if (ch.IsAffected(Affect.AFFECT_MINOR_PARA) || ch.IsAffected(Affect.AFFECT_HOLD)) { ch.SendText("You can't Sing when you're paralyzed!\r\n"); return; } if (ch.HasActionBit(Affect.AFFECT_MUTE)) { ch.SendText("You have no voice!\r\n"); return; } if (str.Length == 0) { if (ch.IsAffected(Affect.AFFECT_SINGING)) { ch.RemoveAffect(Affect.AFFECT_SINGING); ch.SendText("You stop singing.\r\n"); } else { ch.SendText("Sing what?\r\n"); } return; } Magic.Cast(ch, String.Join(" ", str)); }
/// <summary> /// Tracking code. /// </summary> /// <param name="ch"></param> public static void HuntVictim( CharData ch ) { if (!ch || !ch.Hunting || !ch.IsAffected(Affect.AFFECT_TRACK)) { return; } if( ch.CurrentPosition != Position.standing ) { if( ch.IsAffected( Affect.AFFECT_TRACK ) ) { ch.SendText( "You abort your tracking effort.\r\n" ); ch.RemoveAffect(Affect.AFFECT_TRACK); Combat.StopHunting( ch ); } return; } CharData tmp = null; try { /* * Make sure the victim still exists. */ bool found = false; foreach (CharData it in Database.CharList) { ch = it; if (ch.Hunting != null && ch.Hunting.Who == tmp) found = true; } if (!found || !CharData.CanSee(ch, ch.Hunting.Who)) { if (!ch.IsAffected(Affect.AFFECT_TRACK)) CommandType.Interpret(ch, "say Damn! My prey is gone!"); else { ch.SendText("The trail seems to disappear.\r\n"); ch.RemoveAffect(Affect.AFFECT_TRACK); } Combat.StopHunting(ch); return; } if (ch.InRoom == ch.Hunting.Who.InRoom) { if (ch.Fighting) { return; } FoundPrey(ch, ch.Hunting.Who); return; } ch.WaitState(Skill.SkillList["track"].Delay); Exit.Direction dir = FindPath(ch.InRoom.IndexNumber, ch.Hunting.Who.InRoom.IndexNumber, ch, -40000, true); if (dir == Exit.Direction.invalid) { if (!ch.IsAffected(Affect.AFFECT_TRACK)) { SocketConnection.Act("$n&n says 'Damn! Lost $M!'", ch, null, ch.Hunting.Who, SocketConnection.MessageTarget.room); } else { ch.SendText("You lose the trail.\r\n"); ch.RemoveAffect(Affect.AFFECT_TRACK); Combat.StopHunting(ch); } return; } /* * Give a random direction if the mob misses the die roll. */ if (MUDMath.NumberPercent() > 75) /* @ 25% */ { do { dir = Database.RandomDoor(); } while (!(ch.InRoom.ExitData[(int)dir]) || !(ch.InRoom.ExitData[(int)dir].TargetRoom)); } if (ch.InRoom.ExitData[(int)dir].HasFlag(Exit.ExitFlag.closed)) { CommandType.Interpret(ch, "open " + dir.ToString()); return; } ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_HUNTING, 0, String.Format("{0}&n leaves room {1} to the {2}.", ch.ShortDescription, ch.InRoom.IndexNumber, dir.ToString())); if (ch.IsAffected(Affect.AFFECT_TRACK)) { SocketConnection.Act(String.Format("You sense $N&n's trail {0} from here...", dir.ToString()), ch, null, ch.Hunting.Who, SocketConnection.MessageTarget.character); } ch.Move(dir); if (ch.IsAffected(Affect.AFFECT_TRACK)) SocketConnection.Act("$n&n peers around looking for tracks.", ch, null, null, SocketConnection.MessageTarget.room); if (!ch.Hunting) { if (!ch.InRoom) { string text = String.Empty; text = String.Format("Hunt_victim: no ch.in_room! Mob #{0}, _name: {1}. Placing mob in limbo (ch.AddToRoom()).", ch.MobileTemplate.IndexNumber, ch.Name); Log.Error(text, 0); ch.AddToRoom(Room.GetRoom(StaticRooms.GetRoomNumber("ROOM_NUMBER_LIMBO"))); text = String.Format("{0}&n has gone to limbo while hunting {1}.", ch.ShortDescription, ch.Hunting.Name); ImmortalChat.SendImmortalChat(null, ImmortalChat.IMMTALK_HUNTING, 0, text); return; } CommandType.Interpret(ch, "say Damn! Lost my prey!"); return; } if (ch.InRoom == ch.Hunting.Who.InRoom) { FoundPrey(ch, ch.Hunting.Who); } return; } catch (Exception ex) { Log.Error("Exception in HuntVictim: " + ex.ToString()); } }
/// <summary> /// The main social action processing routine. Sends the social strings and any associated sounds. /// </summary> /// <param name="ch">Character acting out the social.</param> /// <param name="command">Command entered by the character.</param> /// <param name="argument">Additional modifiers to the command entered.</param> /// <returns></returns> public bool CheckSocial(CharData ch, string command, string argument) { string arg = String.Empty; Social soc = FindSocial(command); if (soc == null) { return false; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_NO_EMOTE)) { ch.SendText("You are anti-social!\r\n"); return true; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_MEDITATING)) { ch.RemoveActionBit(PC.PLAYER_MEDITATING); ch.SendText("You stop meditating.\r\n"); } // Performing a social action removes hide and conceal. if (ch.IsAffected(Affect.AFFECT_MINOR_INVIS)) { ch.SendText("You appear.\r\n"); } ch.AffectStrip(Affect.AffectType.skill, "shadow form"); ch.AffectStrip(Affect.AffectType.spell, "concealment"); ch.RemoveAffect(Affect.AFFECT_MINOR_INVIS); ch.RemoveAffect(Affect.AFFECT_HIDE); switch (ch.CurrentPosition) { case Position.dead: ch.SendText("Lie still; you are DEAD.\r\n"); return true; case Position.incapacitated: case Position.mortally_wounded: ch.SendText("You are hurt far too badly for that.\r\n"); return true; case Position.stunned: ch.SendText("You are too stunned to do that.\r\n"); return true; case Position.sleeping: // Special exception - only social when you're using when asleep is "snore". if (!"snore".StartsWith(soc.Name, StringComparison.CurrentCultureIgnoreCase)) { break; } ch.SendText("In your dreams, or what?\r\n"); return true; } MUDString.OneArgument(argument, ref arg); CharData victim = null; if (arg.Length == 0) { SocketConnection.Act(soc.CharNoArgument, ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act(soc.OthersNoArgument, ch, null, victim, SocketConnection.MessageTarget.room); if (!String.IsNullOrEmpty(soc.AudioFile) && ch.InRoom != null) { foreach (CharData cd in ch.InRoom.People) { cd.SendSound(soc.AudioFile); } } return true; } victim = ch.GetCharWorld(arg); if (!victim || (ch.IsRacewar(victim) && ch.InRoom != victim.InRoom)) { ch.SendText("They aren't here.\r\n"); } else if (victim == ch) { SocketConnection.Act(soc.CharSelf, ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act(soc.OthersSelf, ch, null, victim, SocketConnection.MessageTarget.room); if (!String.IsNullOrEmpty(soc.AudioFile) && ch.InRoom != null) { foreach (CharData cd in ch.InRoom.People) { cd.SendSound(soc.AudioFile); } } } else if (!ch.GetCharRoom(arg) && CharData.CanSee(ch, victim) && soc.CharFound.Length > 0 && soc.VictimFound.Length > 0) { if (!ch.IsImmortal()) { ch.SendText("You don't see them here.\r\n"); return true; } if (!victim.IsNPC()) { const string ldbase = "From far away, "; if (victim.IsIgnoring(ch)) { ch.SendText("They are ignoring you.\r\n"); return false; } Room original = ch.InRoom; ch.RemoveFromRoom(); ch.AddToRoom(victim.InRoom); string ldmsg = ldbase; ldmsg += soc.CharFound; SocketConnection.Act(ldmsg, ch, null, victim, SocketConnection.MessageTarget.character); ldmsg = ldbase; ldmsg += soc.VictimFound; SocketConnection.Act(ldmsg, ch, null, victim, SocketConnection.MessageTarget.victim); if (!String.IsNullOrEmpty(soc.AudioFile) && ch.InRoom != null) { foreach (CharData cd in ch.InRoom.People) { cd.SendSound(soc.AudioFile); } } ch.RemoveFromRoom(); ch.AddToRoom(original); } else { ch.SendText("They aren't here.\r\n"); } } else { SocketConnection.Act(soc.CharFound, ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act(soc.VictimFound, ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act(soc.OthersFound, ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); if (!String.IsNullOrEmpty(soc.AudioFile) && ch.InRoom != null) { foreach (CharData cd in ch.InRoom.People) { cd.SendSound(soc.AudioFile); } } // If mobs are to respond to socials, it should be inserted here. // This might be useful for some quests, mob functions, or other things. } return true; }