/// <summary> /// Bodyslam an opponent. Can only be used to initiate combat. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Bodyslam(CharData ch, string[] str) { if( ch == null ) return; CharData victim; /* Check player's level and class, mobs can use this skill */ if (!ch.HasInnate(Race.RACE_BODYSLAM)) { ch.SendText("You don't feel massive enough to manhandle that.\r\n"); return; } if (ch.IsBlind()) { return; } if (ch.Riding) { ch.SendText("You can't do that while mounted.\r\n"); return; } if (str.Length != 0) { if (!(victim = ch.GetCharRoom(str[0])) || victim.CurrentPosition == Position.dead) { ch.SendText("They aren't here.\r\n"); return; } } else { ch.SendText("Bodyslam who?\r\n"); return; } // Added size restrictions -- Xangis if (victim.CurrentSize > ch.CurrentSize) { if (ch.HasInnate(Race.RACE_SLAM_LARGER)) { // allowing centaurs to slam one size up if it's an ogre if (victim.CurrentSize > (ch.CurrentSize + 1)) { ch.SendText("You can't bodyslam something that much bigger than you.\r\n"); return; } } else { ch.SendText("You can't bodyslam something bigger than you.\r\n"); return; } } if ((ch.CurrentSize > victim.CurrentSize) && ((ch.CurrentSize - victim.CurrentSize) > 3)) { ch.SendText("They're too small to slam.\r\n"); return; } /* Bodyslam self? Ok! */ if (ch == victim) { ch.SendText("You slam yourself to the ground!\r\n"); SocketConnection.Act("$n&n throws $mself to the ground in a fit of clumsiness.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.WaitState((Skill.SkillList["bodyslam"].Delay / 2)); ch.CurrentPosition = Position.reclining; Combat.InflictDamage(ch, ch, MUDMath.NumberRange(1, 6), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); return; } ch.WaitState(Skill.SkillList["bodyslam"].Delay); ch.PracticeSkill("bodyslam"); int chance = (ch.Level * 3) / 2 + 15; chance += ch.GetCurrAgi() - victim.GetCurrAgi(); chance -= (victim.Level - ch.Level); switch (victim.CurrentPosition) { case Position.dead: return; case Position.mortally_wounded: chance += 15; break; case Position.incapacitated: chance += 10; break; case Position.unconscious: chance += 5; break; case Position.stunned: chance += 3; break; case Position.sleeping: chance += 2; break; case Position.reclining: chance -= 45; break; case Position.resting: chance -= 30; break; case Position.sitting: chance -= 20; break; case Position.kneeling: chance -= 15; break; case Position.fighting: case Position.standing: default: break; } // Small penalty for the small buggers -- Xangis if (victim.CurrentSize < (ch.CurrentSize - 1)) { chance -= 15; } if (chance > 90) { chance = 90; } // Shaman bodyslam penalty. if (ch.IsClass(CharClass.Names.shaman) || ch.IsClass(CharClass.Names.druid)) { chance = (chance * 2) / 3; } if (victim.IsAffected(Affect.AFFECT_AWARE)) { chance -= 15; } else if (victim.IsAffected( Affect.AFFECT_SKL_AWARE)) { if (ch.HasSkill("springleap")) { if (ch.IsNPC()) { if (MUDMath.NumberPercent() < ((ch.Level * 3) / 2 + 15)) { chance -= 15; } } else if (MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["awareness"]) { ch.PracticeSkill("awareness"); chance -= 15; } else { ch.PracticeSkill("awareness"); } } } if (!ch.Fighting) { Combat.SetFighting(ch, victim); } if (victim.Fighting == null) { Combat.SetFighting(victim, ch); } if (ch.IsNPC() || MUDMath.NumberPercent() < chance) { if (victim.IsAffected( Affect.AFFECT_SINGING)) { victim.RemoveAffect(Affect.AFFECT_SINGING); SocketConnection.Act("$n&n suddenly loses track of the key $e was singing in.", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("You get the wind knocked out of you!\r\n"); } if (victim.IsAffected( Affect.AFFECT_CASTING)) { victim.RemoveAffect(Affect.AFFECT_CASTING); SocketConnection.Act("$n&n's thoughts of casting are scattered about as $e is slammed into the ground.", victim, null, null, SocketConnection.MessageTarget.room); victim.SendText("Your brain slamming against your skull disrupts your concentration.\r\n"); } // Moved damage to bottom because it would crash when a person died, because // it still tried to access them as a valid character. Also added tumble check for // thieves. if (!Combat.CheckTumble(victim)) { victim.WaitState(Skill.SkillList["bodyslam"].Delay); victim.CurrentPosition = Position.reclining; Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, ch.Level), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } else { if (!Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, (ch.Level / 3)), "bodyslam", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon)) { ch.SendText("You roll with the blow, finally landing on your feet.\r\n"); SocketConnection.Act("$n&n rolls with the blow, finally landing on $s feet.", ch, null, null, SocketConnection.MessageTarget.room); } } } else { ch.Hitpoints -= MUDMath.NumberRange(1, 5); SocketConnection.Act("As $N&n avoids your slam, you smack headfirst into the &n&+yground&n.", ch, null, victim, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n throws $mself to the &n&+yground&n in a fit of clumsiness.", ch, null, victim, SocketConnection.MessageTarget.victim); SocketConnection.Act("$n&n misses a bodyslam on $N&n and slams $s head into the &n&+yground&n.", ch, null, victim, SocketConnection.MessageTarget.room_vict); ch.CurrentPosition = Position.reclining; } return; }
/// <summary> /// Innate command. Shows and activates innate abilitiies. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Innate(CharData ch, string[] str) { if( ch == null ) return; string text; Affect af = new Affect(); // Use innates here if (str.Length > 0 && !String.IsNullOrEmpty(str[0])) { if (!MUDString.IsPrefixOf(str[0], "strength")) { if (ch.HasInnate(Race.RACE_STRENGTH)) { if (ch.IsAffected(Affect.AFFECT_STRENGTH_INCREASED)) { ch.SendText("You are already affected by strength."); return; } if (ch.HasInnateTimer(InnateTimerData.Type.strength)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "strength"; af.Duration = MUDMath.Dice(8, 5); af.AddModifier(Affect.Apply.strength, MUDMath.Dice(2, 4) + 15); af.SetBitvector(Affect.AFFECT_STRENGTH_INCREASED); ch.AddAffect(af); ch.SendText("You feel stronger.\r\n"); ch.AddInnateTimer(InnateTimerData.Type.strength, 24); ch.WaitState(14); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "levitate")) { if (ch.HasInnate(Race.RACE_LEVITATE)) { if (ch.IsAffected( Affect.AFFECT_LEVITATE)) { ch.SendText("You are already levitating."); return; } if (ch.HasInnateTimer(InnateTimerData.Type.levitate)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "levitation"; af.Duration = MUDMath.Dice(8, 5); af.SetBitvector(Affect.AFFECT_LEVITATE); ch.AddAffect(af); ch.SendText("Your feet rise off the ground.\r\n"); ch.AddInnateTimer(InnateTimerData.Type.levitate, 24); ch.WaitState(6); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "faerie")) { if (ch.HasInnate(Race.RACE_FAERIE_FIRE)) { CharData victim = ch.GetCharRoom(str[0]); if (victim == null) ch.SendText("You do not see them here."); else { Spell spl = Spell.SpellList["faerie fire"]; if (spl != null) { spl.Invoke(ch, ch.Level, victim); } } } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "invisibility")) { if (ch.HasInnate(Race.RACE_INVIS)) { if (ch.IsAffected( Affect.AFFECT_INVISIBLE)) { ch.SendText("You are already invisible!\r\n"); return; } if (ch.HasInnateTimer(InnateTimerData.Type.invisibility)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "invisibility"; af.Duration = MUDMath.Dice(2, 8) + 4; af.SetBitvector(Affect.AFFECT_INVISIBLE); SocketConnection.Act("$n&n fades out of existence.", ch, null, null, SocketConnection.MessageTarget.room); ch.SendText("You vanish.\r\n"); ch.AddAffect(af); ch.AddInnateTimer(InnateTimerData.Type.invisibility, 24); ch.WaitState(24); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "enlarge")) { if (ch.HasInnate(Race.RACE_ENLARGE)) { if (ch.IsAffected(Affect.AFFECT_ENLARGED)) { ch.SendText("You are already enlarged.\r\n"); return; } if (ch.HasInnateTimer(InnateTimerData.Type.enlarge)) { ch.SendText("You need to rest a bit first.\r\n"); return; } af.Type = Affect.AffectType.spell; af.Value = "enlarge"; af.Duration = MUDMath.Dice(8, 5); af.AddModifier(Affect.Apply.size, 1); af.SetBitvector(Affect.AFFECT_NONE); ch.AddAffect(af); ch.SendText("&nYou grow to almost twice your normal size!\r\n"); SocketConnection.Act("$n&n grows to almost twice $s normal size!&n", ch, null, null, SocketConnection.MessageTarget.room); ch.AddInnateTimer(InnateTimerData.Type.enlarge, 24); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (!MUDString.IsPrefixOf(str[0], "shift")) { if (str.Length > 1 && !MUDString.IsPrefixOf(str[1], "astral")) { if (ch.HasInnate(Race.RACE_SHIFT_ASTRAL)) { if (ch.HasInnateTimer(InnateTimerData.Type.shift_astral)) { ch.SendText("You need to rest a bit first.\r\n"); return; } Spell spell = StringLookup.SpellLookup("plane shift"); if (!spell) { ch.SendText("Something seems to be blocking your ability to shift."); Log.Error("Innate Shift: 'plane shift' spell not found. Check the spells file."); return; } spell.Invoke(ch, ch.Level, new Target(str[1])); ch.AddInnateTimer(InnateTimerData.Type.shift_astral, 8); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } else if (str.Length > 1 && !MUDString.IsPrefixOf(str[1], "prime")) { if (ch.HasInnate(Race.RACE_SHIFT_PRIME)) { if (ch.HasInnateTimer(InnateTimerData.Type.shift_prime)) { ch.SendText("You need to rest a bit first.\r\n"); return; } Spell spell = StringLookup.SpellLookup("plane shift"); if (!spell) { ch.SendText("Something seems to be blocking your ability to shift."); Log.Error("Innate Shift: 'plane shift' spell not found. Check the spells file."); return; } spell.Invoke(ch, ch.Level, new Target(str[1])); ch.AddInnateTimer(InnateTimerData.Type.shift_prime, 8); ch.WaitState(10); } else { ch.SendText("You don't know how to do that.\r\n"); return; } } } else { ch.SendText("That's not implemented yet. :(\r\n"); } return; } text = String.Format("&+BInnate abilities available to your race and class:&n\r\n"); if (ch.HasInnate(Race.RACE_BODYSLAM)) text += "&n bodyslam\r\n"; if (ch.HasInnate(Race.RACE_CHARGE)) text += "&n charge\r\n"; if (ch.HasInnate(Race.RACE_FLY)) text += "&n fly\r\n"; if (ch.HasInnate(Race.RACE_PASSDOOR)) text += "&n pass door\r\n"; if (ch.HasInnate(Race.RACE_SWIM)) text += "&n swim\r\n"; if (ch.HasInnate(Race.RACE_WATERBREATH)) text += "&n water breathing\r\n"; if (ch.HasInnate(Race.RACE_INFRAVISION)) text += "&n infravision\r\n"; if (ch.HasInnate(Race.RACE_ULTRAVISION)) text += "&n ultravision\r\n"; if (ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.antipaladin) || ch.IsClass(CharClass.Names.paladin)) text += "&n detect align\r\n"; if (ch.HasInnate(Race.RACE_DETECT_INVIS)) text += "&n detect invis\r\n"; if (ch.HasInnate(Race.RACE_DETECT_HIDDEN)) text += "&n detect hidden\r\n"; if (ch.HasInnate(Race.RACE_MUTE)) text += "&n mute\r\n"; if (ch.HasInnate(Race.RACE_DOORBASH)) text += "&n doorbash\r\n"; if (ch.HasInnate(Race.RACE_SHRUG)) text += "&n shrug\r\n"; if (ch.HasInnate(Race.RACE_ODSNEAK)) text += "&n outdoor sneak\r\n"; if (ch.HasInnate(Race.RACE_UDSNEAK)) text += "&n underdark sneak\r\n"; if (ch.HasInnate(Race.RACE_STRENGTH)) text += "&n strength\r\n"; if (ch.HasInnate(Race.RACE_FAERIE_FIRE)) text += "&n faerie fire\r\n"; // if( ch.HasInnate( Race.RACE_STEAL )) // buf += "&n steal\r\n" ); if (ch.HasInnate(Race.RACE_ENLARGE)) text += "&n enlarge\r\n"; if (ch.HasInnate(Race.RACE_INVIS)) text += "&n invisibility\r\n"; // if( ch.HasInnate( Race.RACE_SUMMON_HOARDE )) // buf += "&n summon hoarde\r\n" ); if (ch.HasInnate(Race.RACE_SHIFT_PRIME)) text += "&n shift prime\r\n"; if (ch.HasInnate(Race.RACE_SHIFT_ASTRAL)) text += "&n shift astral\r\n"; if (ch.HasInnate(Race.RACE_LEVITATE)) text += "&n levitate\r\n"; if (ch.HasInnate(Race.RACE_BITE)) text += "&n bite\r\n"; ch.SendText(text); return; }
/// <summary> /// Looks at an object, mobile, or room. /// </summary> /// <param name="ch">The acting character.</param> /// <param name="str">Command arguments.</param> public static void LookCommand(CharData ch, string[] str) { if( ch == null ) return; // Build argument list, stripping articles. bool inside = false; List<String> args = new List<string>(str); for (int i = (args.Count - 1); i >= 0; i-- ) { if (args[i].Equals("in", StringComparison.CurrentCultureIgnoreCase) || args[i].Equals("i", StringComparison.CurrentCultureIgnoreCase)) { args.RemoveAt(i); inside = true; } else if (args[i].Equals("at", StringComparison.CurrentCultureIgnoreCase)) { args.RemoveAt(i); } } // If it's a mob that isn't switched, bail. if (ch.Socket == null) return; if (ch.CurrentPosition < Position.sleeping) { ch.SendText("&nYou can't see anything but &+Ystars&n! See how pretty!\r\n"); return; } if (ch.CurrentPosition == Position.sleeping) { ch.SendText("&nYou can't see anything, you're &+Lsleeping&n! Zzz.\r\n"); return; } if (ch.IsBlind()) return; // Look panel for ships. if ( args.Count > 0 && args[0].Equals("panel", StringComparison.CurrentCultureIgnoreCase)) { CommandType.Interpret(ch, "Lookpanel"); return; } // Look out for ships. if (args.Count > 0 && args[0].Equals("out", StringComparison.CurrentCultureIgnoreCase)) { CommandType.Interpret(ch, "Lookout"); return; } Object obj; Exit exit; string pdesc; int number = 0; string output = String.Empty; // 'look' or 'look auto' or 'look room' if (args.Count == 0 || args[0].Equals( "auto", StringComparison.CurrentCultureIgnoreCase) || args[0].Equals("room", StringComparison.CurrentCultureIgnoreCase)) { if (ch.InRoom == null) { ch.SendText("You are not in a room. You are just floating in empty space. This should never happen. You should <petition> someone for help.\r\n"); Log.Error("Character executing Commandlook command from null room: " + ch.Name); return; } if (ch.FlightLevel > 0) { switch (ch.FlightLevel) { case CharData.FlyLevel.low: ch.SendText("Hovering above "); break; case CharData.FlyLevel.medium: ch.SendText("Flying above "); break; case CharData.FlyLevel.high: ch.SendText("Flying high above "); break; } } if (!ch.HasActionBit(PC.PLAYER_GODMODE) && ch.InRoom.IsDark() && !ch.HasInnate(Race.RACE_ULTRAVISION) && !ch.IsAffected( Affect.AFFECT_ULTRAVISION)) { ch.SendText("&+lSomewhere\r\n"); } else if (!ch.HasActionBit(PC.PLAYER_GODMODE)) { String roomOpen = String.Empty; String roomClose = String.Empty; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { roomOpen = "<zone>" + ch.InRoom.Area.Name + "</zone><roomTitle>"; roomClose = "</roomTitle>"; } else { roomClose = "&n\r\n"; } // Added support for both manual and automatic descriptions on the worldmap. if (!ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP) || ch.InRoom.Title.Length > 1) { output += roomOpen + ch.InRoom.Title + roomClose; } else { output += roomOpen + "No room title." + roomClose; } } else { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText("<zone>" + ch.InRoom.Area.Name + "</zone>"); } Look.ShowRoomInfo(ch, ch.InRoom); } if (!ch.IsNPC() && !ch.HasActionBit(PC.PLAYER_GODMODE) && ch.InRoom.IsDark() && !ch.HasInnate(Race.RACE_ULTRAVISION) && !ch.IsAffected( Affect.AFFECT_ULTRAVISION)) { ch.SendText("&+LIt is pitch black...&n \r\n"); Look.ShowCharacterToCharacter(ch.InRoom.People, ch); return; } if (!ch.IsNPC() && (args.Count > 0 && (args[0].Equals("room", StringComparison.CurrentCultureIgnoreCase) || args[0].Equals("auto", StringComparison.CurrentCultureIgnoreCase)))) { String roomDescOpen = String.Empty; String roomDescClose = String.Empty; String mapSpace = String.Empty; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { roomDescOpen = "<roomDescription>"; roomDescClose = "</roomDescription>"; } else { roomDescClose = "&n\r\n"; mapSpace = " "; } if( !ch.HasActionBit(PC.PLAYER_BRIEF) && !ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP)) { // Added support for both manual and automatic descriptions on the worldmap. if (ch.InRoom.Description.Length > 0) { output += roomDescOpen + " " + (ch.InRoom.Description.Trim()) + roomDescClose; } //else if (ch._inRoom.WorldmapTerrainType < Database.SystemData.MapInfo.Length) // output += roomDescOpen + mapSpace + Database.SystemData.MapInfo[ch._inRoom.WorldmapTerrainType].RoomDescription + roomDescClose; else { output += roomDescOpen + " No room description." + roomDescClose; } } } if (!String.IsNullOrEmpty(output)) { ch.SendText(output); } if (ch.InRoom.Area.HasFlag(Area.AREA_WORLDMAP)) { if (ch.HasActionBit(PC.PLAYER_MAP)) { Command.Worldmap(ch, null); } else if (!ch.IsNPC() && ch.Socket.Terminal != SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText("\r\n"); } } if (ch.InRoom.HasFlag(RoomTemplate.ROOM_SILENT)) { ch.SendText("&nIt seems preternaturally quiet.\r\n"); } CommandType.Interpret(ch, "exits auto"); Look.ShowRoomAffects(ch, ch.InRoom); Look.ShowListToCharacter(ch.InRoom.Contents, ch, false, false); Look.ShowCharacterToCharacter(ch.InRoom.People, ch); return; } // 'look direction' int door = -1; if (args.Count > 0) { if ("north".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 0; else if ("east".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 1; else if ("south".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 2; else if ("west".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 3; else if ("up".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 4; else if ("down".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 5; else if ("northwest".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 6; else if ("southwest".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 7; else if ("northeast".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 8; else if ("southeast".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 9; else if ("nw".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 6; else if ("sw".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 7; else if ("ne".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 8; else if ("se".StartsWith(args[0], StringComparison.CurrentCultureIgnoreCase)) door = 9; } if (door != -1) { // If no exit data, then return. exit = ch.InRoom.ExitData[door]; if (!exit) { ch.SendText("There's nothing to see in that direction.\r\n"); return; } if (exit.HasFlag(Exit.ExitFlag.walled)) { ch.SendText("There's a wall in the way.\r\n"); return; } // Check for farsee if ((ch.IsAffected( Affect.AFFECT_FARSEE) || ch.HasActionBit(PC.PLAYER_GODMODE)) && !exit.HasFlag(Exit.ExitFlag.closed)) { if (exit.TargetRoom) { Room room = ch.InRoom; ch.RemoveFromRoom(); ch.AddToRoom(Room.GetRoom(exit.IndexNumber)); CommandType.Interpret(ch, "look"); ch.RemoveFromRoom(); ch.AddToRoom(room); return; } ch.SendText("Nothing special there.\r\n"); } if (exit.Description.Length != 0) { ch.SendText(exit.Description); } else { ch.SendText("Nothing special there.\r\n"); } if (exit.Keyword.Length != 0) { if (exit.HasFlag(Exit.ExitFlag.bashed)) SocketConnection.Act("The $d has been bashed from its &n&+whinges&n.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.closed)) SocketConnection.Act("The $d is closed.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.secret)) SocketConnection.Act("The $d is secret.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.blocked)) SocketConnection.Act("The $d is blocked.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); } else { if (exit.HasFlag(Exit.ExitFlag.bashed)) { SocketConnection.Act("The door has been bashed from its &n&+whinges&n.", ch, null, null, SocketConnection.MessageTarget.character); } else if (exit.HasFlag(Exit.ExitFlag.closed)) SocketConnection.Act("The door is closed.", ch, null, null, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.is_door)) SocketConnection.Act("The door is open.", ch, null, null, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.secret)) SocketConnection.Act("The door is secret.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); else if (exit.HasFlag(Exit.ExitFlag.blocked)) SocketConnection.Act("The $d is blocked.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); } // If exit found, don't keep looking. return; } // Look inside something ('look in'). if (inside) { // 'look in' if (args.Count < 1) { ch.SendText("Look in what?\r\n"); return; } obj = ch.GetObjHere(args[0]); if (!obj) { ch.SendText("You do not see that here.\r\n"); return; } switch (obj.ItemType) { default: ch.SendText("That is not a container.\r\n"); break; case ObjTemplate.ObjectType.drink_container: if (obj.Values[1] == -1) { ch.SendText("It is full.\r\n"); break; } if (obj.Values[1] <= 0) { ch.SendText("It is empty.\r\n"); break; } output += "It's "; if (obj.Values[1] < obj.Values[0] / 4) output += "less than half"; else if (obj.Values[1] < (3 * obj.Values[0] / 4)) output += "about half"; else if (obj.Values[1] < obj.Values[0]) output += "more than half"; else output += "completely"; output += " full of a " + Liquid.Table[obj.Values[2]].Color + "liquid.\r\n"; ch.SendText(output); break; case ObjTemplate.ObjectType.quiver: case ObjTemplate.ObjectType.container: case ObjTemplate.ObjectType.npc_corpse: case ObjTemplate.ObjectType.pc_corpse: if (Macros.IsSet(obj.Values[1], ObjTemplate.CONTAINER_CLOSED.Vector)) { ch.SendText("It is closed.\r\n"); break; } SocketConnection.Act("$p&n contains:", ch, obj, null, SocketConnection.MessageTarget.character, true); Look.ShowListToCharacter(obj.Contains, ch, true, true); break; case ObjTemplate.ObjectType.portal: SocketConnection.Act("A $p&n leads to:", ch, obj, null, SocketConnection.MessageTarget.character); output += Room.GetRoom(obj.Values[0]).Title + "\r\n"; output += Room.GetRoom(obj.Values[0]).Description; output += "\r\n"; ch.SendText(output); break; } return; } // Look at another char. if (args.Count > 0) { CharData victim = ch.GetCharRoom(args[0]); if (victim != null) { Look.ShowCharacterToCharacterFull(victim, ch); return; } } // Look at an object. if (args.Count > 0) { // Check inventory. obj = ch.GetObjCarrying(args[0]); // If not in inventory, check eq. if (obj == null) obj = ch.GetObjWear(args[0]); // If not on character, check room. if (obj == null) obj = Object.GetObjFromList(ch.InRoom.Contents, ch, args[0]); // If object found, show it to the char. if (obj != null) { pdesc = (Database.GetExtraDescription(args[0], obj.ExtraDescription)); if (pdesc.Length != 0) { ch.SendText(pdesc); } else if ((pdesc = (Database.GetExtraDescription(args[0], obj.ObjIndexData.ExtraDescriptions))).Length > 0) { ch.SendText(pdesc); } else if (obj.FullDescription.Length > 0) { ch.SendText(obj.FullDescription); ch.SendText("\r\n"); } if (obj.HasAffect(Affect.AffectType.skill, "poison weapon")) { if (ch.IsClass(CharClass.Names.thief) || ch.IsClass(CharClass.Names.assassin) || MUDMath.NumberPercent() < ch.GetCurrInt() / 2) ch.SendText("It has a &+Gsickly &+Lcolored&n hue.\r\n"); } return; } } // Look at an object in the room if (args.Count > 0) { int count = 0; foreach (Object iobj in ch.InRoom.Contents) { if (CharData.CanSeeObj(ch, iobj)) { pdesc = (Database.GetExtraDescription(args[0], iobj.ExtraDescription)); if (pdesc.Length != 0) { if (++count == number) { ch.SendText(pdesc); return; } continue; } pdesc = (Database.GetExtraDescription(args[0], iobj.ObjIndexData.ExtraDescriptions)); if (pdesc.Length != 0) { if (++count == number) { ch.SendText(pdesc); return; } continue; } if (MUDString.NameContainedIn(args[0], iobj.Name)) { if (++count == number) { ch.SendText(iobj.FullDescription); ch.SendText("\r\n"); return; } continue; } } } } // Check for room extra descriptions if (args.Count > 0) { pdesc = (Database.GetExtraDescription(args[0], ch.InRoom.ExtraDescriptions)); if (!String.IsNullOrEmpty(pdesc)) { ch.SendText(pdesc); return; } } ch.SendText("You do not see that here.\r\n"); return; }
/// <summary> /// Shows available room exits. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Exits(CharData ch, string[] str) { if( ch == null ) return; Exit exit; int door; bool auto = (str.Length != 0 && !MUDString.StringsNotEqual(str[0], "auto")); if (ch.IsBlind() || !ch.InRoom) { return; } string text; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED && auto) { text = "<exits>"; } else { text = auto ? "&+cExits: &n" : "&+cVisible exits:&n\r\n"; } bool found = false; // Show the direction with an asterisk if it // has a closed door, and in exits command it shows up as a // closed <whatever>. for (door = 0; door < Limits.MAX_DIRECTION; ++door) { exit = ch.InRoom.ExitData[door]; if (exit && exit.TargetRoom) { if (exit.HasFlag(Exit.ExitFlag.secret)) { /* Mortals do not see secret exits. */ if (ch.Level < Limits.LEVEL_AVATAR) continue; /* Mark secret exits for immortals. */ text += "&+LS&n&+c"; } if (exit.HasFlag(Exit.ExitFlag.blocked)) { /* Mortals do not see secret exits. */ if (ch.Level < Limits.LEVEL_AVATAR) continue; /* Mark secret exits for immortals. */ text += "&+yB&n&+c"; } found = true; if (auto) { if (exit.HasFlag(Exit.ExitFlag.walled) && exit.HasFlag(Exit.ExitFlag.illusion)) continue; if (exit.HasFlag(Exit.ExitFlag.closed)) text += "&n&+y#&n&+c"; // Don't show exits to impassable rooms. // Show a yellow exclamation point to those with holylight on though. if (exit.TargetRoom && exit.TargetRoom.TerrainType == TerrainType.underground_impassable) { if (!ch.HasActionBit(PC.PLAYER_GODMODE)) { continue; } text += "&+Y!&n"; } text += door.ToString(); text += " "; } else { if (exit.HasFlag(Exit.ExitFlag.walled)) { text += String.Format("&n{0} - (Walled)&n\r\n", MUDString.PadStr(door.ToString().ToUpper(), 5)); } else if (!exit.HasFlag(Exit.ExitFlag.closed)) { // gods with holylight on can go to useless exits. if (exit.TargetRoom && exit.TargetRoom.TerrainType == TerrainType.underground_impassable) { if (!ch.HasActionBit(PC.PLAYER_GODMODE)) { continue; } text += "&+Y!&n"; } text += String.Format("&n {0} - {1}&n\r\n", MUDString.PadStr(door.ToString().ToUpper(), 5), (!ch.HasInnate(Race.RACE_ULTRAVISION) && Room.GetRoom(exit.IndexNumber).IsDark()) ? "&nToo dark to tell" : exit.TargetRoom.Title); } else { text += String.Format("&+y#&n{0} - Closed {1}\r\n", MUDString.PadStr(door.ToString().ToUpper(), 5), exit.Keyword); } } } } if (!found) { text += auto ? " none" : "&nNone.\r\n"; } if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED && auto) { text += "&n</exits>"; } else if (auto) { text += "&n\r\n"; } ch.SendText(text); return; }
/// <summary> /// Formats an object for display to a character. /// </summary> /// <param name="obj"></param> /// <param name="ch"></param> /// <param name="shortDisplay"></param> /// <returns></returns> public static string FormatObjectToCharacter(Object obj, CharData ch, bool shortDisplay) { string text = String.Empty; if (obj == null || ch == null) { Log.Error("format_obj_to_char: null ch or obj.", 0); return null; } if (shortDisplay) { if (obj.ShortDescription.Length > 0) { text += obj.ShortDescription; } text += " "; } if (obj.HasFlag(ObjTemplate.ITEM_NOSHOW) && ch.IsImmortal()) { text += "&n(&+LNoshow&n) "; } else if (obj.HasFlag(ObjTemplate.ITEM_NOSHOW)) { return text; } if (obj.HasFlag(ObjTemplate.ITEM_INVIS)) { text += "&n(&+LInvis&n) "; } if ((ch.IsAffected( Affect.AFFECT_DETECT_EVIL) || ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.antipaladin) || ch.IsClass(CharClass.Names.paladin)) && obj.HasFlag(ObjTemplate.ITEM_EVIL)) { text += "&n(&+LDark Aura&n) "; } if ((ch.IsAffected(Affect.AFFECT_DETECT_GOOD) || ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.antipaladin) || ch.IsClass(CharClass.Names.paladin)) && obj.HasFlag(ObjTemplate.ITEM_BLESS)) { text += "&n(&+WLight Aura&n) "; } if (obj.HasFlag(ObjTemplate.ITEM_MAGIC) && (ch.IsAffected(Affect.AFFECT_DETECT_MAGIC) || (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_GODMODE)))) { text += "&n(&+BMagic&n) "; } if (obj.HasFlag(ObjTemplate.ITEM_GLOW)) { text += "&n(&+CGlowing&n) "; } if (obj.HasFlag(ObjTemplate.ITEM_LIT)) { text += "&n(&+WIlluminating&n) "; } if (obj.HasFlag(ObjTemplate.ITEM_SECRET) && (ch.IsAffected(Affect.AFFECT_DETECT_HIDDEN) || (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_GODMODE)))) { text += "&n(&+yHidden&n) "; } if (!shortDisplay) { if (obj.ItemType == ObjTemplate.ObjectType.pc_corpse && (ch.IsImmortal() || (int)ch.GetRacewarSide() == obj.Values[4])) { text += "The " + obj.ShortDescription + " is lying here."; } else if (obj.FullDescription.Length > 0) { text += obj.FullDescription; } } text += "&n"; return text; }
/// <summary> /// Charge into someone. Helps to have horns on your head. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Charge(CharData ch, string[] str) { if( ch == null ) return; CharData victim; /* Check player's level and class, mobs can use this skill */ if (!ch.HasInnate(Race.RACE_CHARGE)) { ch.SendText("You can't do that.\r\n"); return; } if (ch.IsBlind()) { return; } if (str.Length != 0) { if (!(victim = ch.GetCharRoom(str[0])) || victim.CurrentPosition == Position.dead) { ch.SendText("They aren't here.\r\n"); return; } } else { ch.SendText("Charge who?\r\n"); return; } /* charge me trap */ if (ch == victim) { ch.SendText("You chase your tail for a while.\r\n"); return; } ch.WaitState(MUDMath.FuzzyNumber(Skill.SkillList["charge"].Delay)); if (!ch.Fighting) { Combat.SetFighting(ch, victim); } if (!victim.Fighting) { Combat.SetFighting(victim, ch); } // Chance is based on level of charger and victim. int chance = (ch.Level * 2) - victim.Level + 25; if (chance > 95) { chance = 95; } if (ch.IsNPC() || MUDMath.NumberPercent() < chance) { Combat.InflictDamage(ch, victim, MUDMath.NumberRange(1, ch.Level), "charge", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } else { Combat.InflictDamage(ch, victim, 0, "charge", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } return; }
/// <summary> /// Knock a door from its hinges with brute force. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void DoorBash(CharData ch, string[] str) { if( ch == null ) return; Exit.Direction door; Room toRoom; if (ch.IsNPC() || (!ch.HasSkill("doorbash") && !ch.HasInnate(Race.RACE_DOORBASH))) { ch.SendText("You don't feel massive enough!\r\n"); return; } if (str.Length == 0) { ch.SendText("Doorbash what?\r\n"); return; } if (ch.Fighting) { ch.SendText("You can't break off your fight.\r\n"); return; } if ((door = Movement.FindDoor(ch, str[0])) >= 0) { Exit reverseExit; int chance; Exit exit = ch.InRoom.GetExit(door); if (!exit.HasFlag(Exit.ExitFlag.closed)) { ch.SendText("Calm down. It is already open.\r\n"); return; } ch.WaitState(Skill.SkillList["doorbash"].Delay); if (ch.IsNPC()) { chance = 0; } else if (!ch.HasSkill("doorbash")) { chance = 20; } else { chance = ((PC)ch).SkillAptitude["doorbash"] / 2; } if (exit.HasFlag(Exit.ExitFlag.locked)) { chance /= 2; } if (exit.HasFlag(Exit.ExitFlag.bashproof) && !ch.IsImmortal()) { SocketConnection.Act("WHAAAAM!!! You bash against the $d, but it doesn't budge.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("WHAAAAM!!! $n&n bashes against the $d, but it holds strong.", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 20), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); if (exit.HasFlag(Exit.ExitFlag.spiked)) { SocketConnection.Act("You are impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n is impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 5), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.pierce); } return; } if (exit.HasFlag(Exit.ExitFlag.spiked)) { SocketConnection.Act("You are impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n is impaled by spikes protruding from the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 5), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.pierce); } if ((ch.GetCurrStr() >= 20) && MUDMath.NumberPercent() < (chance + 4 * (ch.GetCurrStr() - 20))) { /* Success */ exit.RemoveFlag(Exit.ExitFlag.closed); if (exit.HasFlag(Exit.ExitFlag.locked)) { exit.RemoveFlag(Exit.ExitFlag.locked); } exit.AddFlag(Exit.ExitFlag.bashed); SocketConnection.Act("Crash! You bashed open the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n bashes open the $d!", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 30), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); /* Bash through the other side */ if ((toRoom = Room.GetRoom(exit.IndexNumber)) && (reverseExit = toRoom.GetExit(Exit.ReverseDirection(door))) && reverseExit.TargetRoom == ch.InRoom) { reverseExit.RemoveFlag(Exit.ExitFlag.closed); if (reverseExit.HasFlag(Exit.ExitFlag.locked)) { reverseExit.RemoveFlag(Exit.ExitFlag.locked); } reverseExit.AddFlag(Exit.ExitFlag.bashed); foreach (CharData irch in toRoom.People) { SocketConnection.Act("The $d crashes open!", irch, null, reverseExit.Keyword, SocketConnection.MessageTarget.character); } // Have any aggro mobs on the other side come after the player. foreach (CharData charData in toRoom.People) { if (charData != ch && (charData.IsNPC() && !charData.IsAffected( Affect.AFFECT_CHARM)) && charData.IsAggressive(ch) && charData.IsAwake() && CharData.CanSee(charData, ch) && !charData.Fighting) { Combat.StartHating(charData, ch); Combat.StartHunting(charData, ch); } } } } else { /* Failure */ SocketConnection.Act("OW! You bash against the $d, but it doesn't budge.", ch, null, exit.Keyword, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n bashes against the $d, but it holds strong.", ch, null, exit.Keyword, SocketConnection.MessageTarget.room); Combat.InflictDamage(ch, ch, (ch.GetMaxHit() / 10), "doorbash", ObjTemplate.WearLocation.none, AttackType.DamageType.bludgeon); } } /* * Check for "guards"... anyone bashing a door is considered as * a potential aggressor, and there's a 25% chance that mobs * will do unto before being done unto. * But first...let's make sure ch ain't dead? That'd be a pain. */ if (ch.Hitpoints <= 1) return; ch.PracticeSkill("doorbash"); foreach (CharData guardChar in ch.InRoom.People) { if (guardChar != ch && guardChar.HasActionBit(MobTemplate.ACT_PROTECTOR) && (guardChar.IsNPC() && !guardChar.IsAffected( Affect.AFFECT_CHARM)) && guardChar.IsAwake() && CharData.CanSee(guardChar, ch) && !guardChar.Fighting && MUDMath.NumberBits(2) == 0) { SocketConnection.Act("$n&n is very unhappy about you trying to destroy the door.", guardChar, null, ch, SocketConnection.MessageTarget.victim); guardChar.AttackCharacter(ch); } } return; }
/// <summary> /// Regeneration and natural healing. /// </summary> /// <param name="ch"></param> /// <returns></returns> public static int HitGain( CharData ch ) { int gain; int percent = 0; if( ch == null ) { Log.Error( "HitGain(): null ch", 0 ); return 0; } // They aren't going to gain hits in a no heal room... if (ch.InRoom.HasFlag(RoomTemplate.ROOM_NO_HEAL)) { return 0; } gain = 1; percent = 100; if (ch.CheckSkill("fast healing", PracticeType.none)) { gain += 1; } if (MUDMath.NumberPercent() < 3) { ch.PracticeSkill("fast healing"); } switch( ch.CurrentPosition ) { case Position.sleeping: gain += 3; break; case Position.reclining: gain += 2; break; case Position.resting: gain += 1; break; case Position.fighting: gain = 0; break; } if( ch.HasInnate( Race.RACE_REGENERATE ) ) { // Automatically one extra hp, two at level 30. gain += 1 + (ch.Level / 30); // One percent chance of gaining another per level. percent += (ch.Level); } // Hunger and thirst for PCs. if (!ch.IsNPC()) { if (((PC)ch).Hunger == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou double over from &+Rhunger pains&n!\r\n"); } if (((PC)ch).Thirst == 0) { gain /= 2; gain -= 1; percent -= 25; ch.SendText("&nYou suffer from severe &+cth&+Ci&n&+cr&+Cst&n!\r\n"); } } if( ch.IsAffected( Affect.AFFECT_POISON ) ) { gain /= 4; percent /= 2; } if( gain == 0 ) if( MUDMath.NumberPercent() < percent ) gain = 1; // Heal rooms heal you a little quicker if (ch.InRoom.HasFlag(RoomTemplate.ROOM_HEAL)) { gain += Math.Max(1, gain / 2); } if( ( ch.InRoom.TerrainType != TerrainType.underwater_has_ground && ch.InRoom.TerrainType != TerrainType.unswimmable_water && ch.InRoom.TerrainType != TerrainType.swimmable_water && ch.HasInnate( Race.RACE_WATERBREATH ) && MUDString.StringsNotEqual( Race.RaceList[ ch.GetRace() ].Name, "Object" ) && ch.GetRace() != Race.RACE_GOD ) || ( ch.InRoom.TerrainType == TerrainType.underwater_has_ground && ( !ch.IsImmortal() && !ch.IsAffected( Affect.AFFECT_BREATHE_UNDERWATER ) && !ch.HasInnate( Race.RACE_WATERBREATH ) ) ) ) gain = 0; return Math.Min( gain, ch.GetMaxHit() - ch.Hitpoints ); }
/// <summary> /// True if char can see victim. /// /// This is only a straightford all-or-none vision checker. /// /// If you need more granularity, use Command.HowSee which returns an enum /// based on the level of visibility but otherwise functions similarly. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool CanSee( CharData ch, CharData victim ) { if( ch == null ) { Log.Error( "CharData.CanSee: called with null ch.", 0 ); return false; } if( victim == null ) { Log.Error( "CharData.CanSee: called with null victim.", 0 ); return false; } if( ch == victim ) { return true; } /* All mobiles cannot see wizinvised immorts */ if (ch.IsNPC() && !ch.IsNPC() && ch.HasActionBit(PC.PLAYER_WIZINVIS)) { return false; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_WIZINVIS) && ch.GetTrust() < ch.Level) { return false; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_GODMODE)) { return true; } if( ch.IsAffected( Affect.AFFECT_BLIND ) ) { return false; } if (ch.InRoom == null) { Log.Error("CanSee called by player " + ch.Name + " with null room."); return false; } if( ch.InRoom.IsDark() && !ch.HasInnate( Race.RACE_ULTRAVISION ) && !ch.IsAffected( Affect.AFFECT_ULTRAVISION ) && !ch.HasInnate( Race.RACE_INFRAVISION ) && !ch.IsAffected(Affect.AFFECT_INFRAVISION ) ) { return false; } if (ch.CurrentPosition == Position.dead) { return true; } if ((victim.IsAffected(Affect.AFFECT_INVISIBLE) || victim.IsAffected(Affect.AFFECT_MINOR_INVIS)) && !ch.HasInnate( Race.RACE_DETECT_INVIS ) && !ch.IsAffected(Affect.AFFECT_DETECT_INVIS ) && !(ch.IsAffected(Affect.AFFECT_ELEM_SIGHT) && ( ch.GetRace() == Race.RACE_AIR_ELE || ch.GetRace() == Race.RACE_WATER_ELE || ch.GetRace() == Race.RACE_FIRE_ELE || ch.GetRace() == Race.RACE_EARTH_ELE ) ) ) { return false; } if( victim.IsAffected( Affect.AFFECT_HIDE ) && !ch.HasInnate( Race.RACE_DETECT_HIDDEN ) && !ch.IsAffected( Affect.AFFECT_DETECT_HIDDEN ) && !ch.Fighting ) { return false; } return true; }
/// <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; }
/// <summary> /// This function's main access commands are wear, wield, and hold, which have the /// following flow: /// wear: Command.Wear, wear_obj, equip_hand /// wield: Command.Wield, equip_hand /// hold: Command.Hold, equip_hand /// /// We assume by this point that the character is physically able to use the item and will be able /// to equip it however specified. Those checks are performed by WearObject(), Equip(), and Hold(). /// </summary> /// <param name="ch"></param> /// <param name="obj"></param> /// <param name="type"></param> /// <returns></returns> public static bool EquipInHand(CharData ch, Object obj, int type) { int weight = 0; ObjTemplate.WearLocation firstAvail = ObjTemplate.WearLocation.none; ObjTemplate.WearLocation secondAvail = ObjTemplate.WearLocation.none; ObjTemplate.WearLocation lastAvail = ObjTemplate.WearLocation.none; Object hand1 = GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_one); Object hand2 = GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_two); Object hand3 = GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_three); Object hand4 = GetEquipmentOnCharacter(ch, ObjTemplate.WearLocation.hand_four); if (hand1 && (hand1._itemType == ObjTemplate.ObjectType.weapon || hand1._itemType == ObjTemplate.ObjectType.ranged_weapon)) weight += hand1.GetWeight(); if (hand2 && (hand2._itemType == ObjTemplate.ObjectType.weapon || hand2._itemType == ObjTemplate.ObjectType.ranged_weapon)) weight += hand2.GetWeight(); if (hand3 && (hand3._itemType == ObjTemplate.ObjectType.weapon || hand3._itemType == ObjTemplate.ObjectType.ranged_weapon)) weight += hand3.GetWeight(); if (hand4 && (hand4._itemType == ObjTemplate.ObjectType.weapon || hand4._itemType == ObjTemplate.ObjectType.ranged_weapon)) weight += hand4.GetWeight(); if (ch.GetRace() != Race.RACE_THRIKREEN) { if (hand3) Log.Error("non-thrikreen wielding item in hand3", 0); if (hand4) Log.Error("non-thrikreen wielding item in hand4", 0); } // Find number of hand slots used and first available hand. // Be sure to handle twohanded stuff. if (hand4 && hand4.HasFlag(ObjTemplate.ITEM_TWOHANDED)) { Log.Error("Twohanded weapon in fourth hand -- this is not possible.", 0); } if (hand3 && hand3.HasFlag(ObjTemplate.ITEM_TWOHANDED)) { if (hand4) { Log.Error("Twohanded weapon in third hand with fourth hand holding twohanded weapon -- this is not possible, all twohanded must have a blank hand after it.", 0); } hand4 = hand3; } if (hand2 && hand2.HasFlag(ObjTemplate.ITEM_TWOHANDED)) { if (hand3) { Log.Error("Twohanded weapon in second hand with third hand holding twohanded weapon -- this is not possible, all twohanded must have a blank hand after it.", 0); } hand2 = hand3; } if (!ch.HasInnate(Race.RACE_EXTRA_STRONG_WIELD)) { if (hand1 && hand1.HasFlag(ObjTemplate.ITEM_TWOHANDED)) { if (hand2) { Log.Error("Twohanded weapon in second hand with first hand holding twohanded weapon -- this is not possible, all twohanded must have a blank hand after it.", 0); } hand2 = hand1; } } if (obj.HasFlag(ObjTemplate.ITEM_TWOHANDED) && !ch.HasInnate(Race.RACE_EXTRA_STRONG_WIELD)) { if (ch.GetRace() == Race.RACE_THRIKREEN && !hand4) { firstAvail = ObjTemplate.WearLocation.hand_four; lastAvail = ObjTemplate.WearLocation.hand_four; } if (ch.GetRace() == Race.RACE_THRIKREEN && !hand3) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_three; secondAvail = firstAvail; firstAvail = ObjTemplate.WearLocation.hand_three; } if (!hand2) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_two; secondAvail = firstAvail; firstAvail = ObjTemplate.WearLocation.hand_two; } if (!hand1) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_one; secondAvail = firstAvail; firstAvail = ObjTemplate.WearLocation.hand_one; } if (firstAvail == 0) { ch.SendText("Your hands are full!\r\n"); return false; } if (secondAvail == 0) { ch.SendText("You need two hands free to wield that!\r\n"); return false; } } else if (obj.HasFlag(ObjTemplate.ITEM_TWOHANDED)) { if (ch.GetRace() == Race.RACE_THRIKREEN && !hand4) { firstAvail = ObjTemplate.WearLocation.hand_four; } if (ch.GetRace() == Race.RACE_THRIKREEN && !hand3) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_three; secondAvail = firstAvail; firstAvail = ObjTemplate.WearLocation.hand_three; } if (!hand2) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_two; secondAvail = firstAvail; firstAvail = ObjTemplate.WearLocation.hand_two; } if (!hand1) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_one; secondAvail = ObjTemplate.WearLocation.hand_one; firstAvail = ObjTemplate.WearLocation.hand_one; } if (firstAvail == 0) { ch.SendText("Your hands are full!\r\n"); return false; } if (secondAvail == 0) { ch.SendText("You need two hands free to wield that!\r\n"); return false; } } else { if (ch.GetRace() == Race.RACE_THRIKREEN && !hand4) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_four; firstAvail = ObjTemplate.WearLocation.hand_four; } if (ch.GetRace() == Race.RACE_THRIKREEN && !hand3) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_three; firstAvail = ObjTemplate.WearLocation.hand_three; } if (!hand2) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_two; firstAvail = ObjTemplate.WearLocation.hand_two; } if (hand1 == null) { if (lastAvail == 0) lastAvail = ObjTemplate.WearLocation.hand_one; firstAvail = ObjTemplate.WearLocation.hand_one; } if (firstAvail == 0) { ch.SendText("Your hands are full!\r\n"); return false; } } // Successful hand availability, send message and ready the item. // Twohanded shields, held items, and lights are equipped primary. // This could annoy ogres/thris but twohanded versions of these items // are so rare it's not likely to be an issue. switch (type) { case EQUIP_HOLD: if (!obj.HasFlag(ObjTemplate.ITEM_TWOHANDED)) ch.EquipObject(ref obj, lastAvail); else ch.EquipObject(ref obj, firstAvail); break; case EQUIP_SHIELD: SocketConnection.Act("You strap $p&n to your arm.", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n straps $p&n to $s arm.", ch, obj, null, SocketConnection.MessageTarget.room); if (!obj.HasFlag(ObjTemplate.ITEM_TWOHANDED)) ch.EquipObject(ref obj, lastAvail); else ch.EquipObject(ref obj, firstAvail); break; case EQUIP_LIGHT: if (obj._itemType == ObjTemplate.ObjectType.light && obj._values[2] != 0) { SocketConnection.Act("You &n&+rli&+Rght&n $p&n and hold it before you.", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n &+Rlig&n&+rhts&n $p&n and holds it before $m.", ch, obj, null, SocketConnection.MessageTarget.room); } else { SocketConnection.Act("You hold the &+Lspent&n remains of $p&n.", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n holds the spent husk of $p&n.", ch, obj, null, SocketConnection.MessageTarget.room); } if (!obj.HasFlag(ObjTemplate.ITEM_TWOHANDED)) ch.EquipObject(ref obj, lastAvail); else ch.EquipObject(ref obj, firstAvail); break; case EQUIP_WIELD: // Have to check for dual wield skill, and for total weight of weapons. if (firstAvail != ObjTemplate.WearLocation.hand_one) { // Those without dual wield cannot wield anything in their second hand. Include thrikreen if (!ch.IsNPC() && !ch.HasSkill("dual wield")) { ch.SendText("You lack the skills to wield a weapon in anything but your primary hand.\r\n"); return false; } } if ((weight + obj.GetWeight()) > StrengthModifier.Table[ch.GetCurrStr()].WieldWeight) { SocketConnection.Act("Your meager strength is overwhelmed by $p&n.", ch, obj, null, SocketConnection.MessageTarget.character); return false; } SocketConnection.Act("You wield $p&n.", ch, obj, null, SocketConnection.MessageTarget.character); SocketConnection.Act("$n&n brandishes $p&n.", ch, obj, null, SocketConnection.MessageTarget.room); ch.EquipObject(ref obj, firstAvail); break; } // Objects with a trap activated on wear. if (obj._trap != null && obj._trap.CheckTrigger( Trap.TriggerType.wear)) { ch.SetOffTrap(obj); if (ch.CurrentPosition == Position.dead) return false; } return true; }
/// <summary> /// Handles all spellcasting, whether it be willing, singing, or casting /// If they got here as a bard, they're using the SING command word, /// if they got here as a psionicist, they're using the WILL command word, /// and if they got here as anything else, they're using CAST. /// /// These are just cheesy details handled by CommandType.cs... we don't care. /// What we do care about is that we *know* it's safe to base all our /// messages/decisions on the character's class. /// /// This function is also *mob-safe*, meaning that mobs can cast spells /// too. However, this is not the preferred method (as far as I can tell) /// /// Shaman totems are checked for in this function. /// </summary> /// <param name="ch"></param> /// <param name="argument"></param> public static void Cast( CharData ch, string argument ) { // No casting while berzerked... Nor singing! Hah! if (ch.IsAffected(Affect.AFFECT_BERZERK)) { ch.SendText( "Not while you're in a &+RBl&n&+ro&+Ro&n&+rd&+L Rage&n!\r\n" ); return; } // No casting while thirsty... Nor singing! Hah! if (ch.IsAffected(Affect.AFFECT_THIRST) && ( ch.IsNPC() || ( (PC)ch ).Thirst < 10 ) ) { ch.SendText( "&+BNo&+Ct w&+chi&+ble &+cyo&+Bu'&+cre &+Bso p&+carc&+Bhed&n!\r\n" ); return; } String[] pieces = argument.Split( new Char[] {'\''}, StringSplitOptions.RemoveEmptyEntries); if (pieces.Length < 1) { ch.SendText("Spell names must always be in single quotes, such as: cast 'magic missile' troll.\r\n"); return; } if (pieces.Length > 1) { pieces[1] = pieces[1].Trim(); } Spell spell; if (((spell = StringLookup.SpellLookup(pieces[0])) == null) || ((!ch.HasSpell(pieces[0])) && !ch.IsImmortal())) { ch.SendText( "You can't do that.\r\n" ); return; } if( !CheckTotem( ch, spell )) return; if( !ch.CheckConcentration( spell ) ) return; if (!ch.CheckMemorized(spell)) return; if( ( !ch.CanSpeak() || ch.HasInnate(Race.RACE_MUTE)) && !ch.IsClass(CharClass.Names.psionicist)) { ch.SendText( "Your lips move but no sound comes out.\r\n" ); return; } if( !ch.InRoom.CheckCastable( ch, ch.IsClass( CharClass.Names.bard ), true) ) return; if( ch.InRoom.CheckStarshell( ch ) ) return; int manaUsed = 0; // TODO: Rather than hard-code psionicist as a mana class, let that be set in the class files. if (ch.IsClass(CharClass.Names.psionicist)) { manaUsed = Macros.ManaCost(ch, spell); } else if (ch.IsClass(CharClass.Names.bard)) { manaUsed = spell.MinimumMana; } // Locate targets. if( ch.IsNPC() ) { ImmortalChat.SendImmortalChat( null, ImmortalChat.IMMTALK_SPAM, 0, "Magic.Cast: Attempting to find _targetType for " + ch.ShortDescription + "&n." ); } if (pieces.Length > 1) { ProcessSpellTargets(ch, spell, pieces[1]); } else { ProcessSpellTargets(ch, spell, null); } }
/// <summary> /// Show a character to another character. /// </summary> /// <param name="victim"></param> /// <param name="ch"></param> public static void ShowCharacterToCharacterFull(CharData victim, CharData ch) { Object obj; string text = String.Empty; int percent; if (CharData.CanSee(victim, ch)) { SocketConnection.Act("$n&n looks at you.", ch, null, victim, SocketConnection.MessageTarget.victim); if (victim != ch) { SocketConnection.Act("$n&n looks at $N&n.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } else { SocketConnection.Act("$n&n looks at $mself.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); } } if (victim.Riding != null) { text += String.Format("&nMounted on {0}, ", victim.Riding.ShowNameTo(ch, false)); } else if (victim.Rider != null) { text += String.Format("&nRidden by {0}, ", victim.Rider.ShowNameTo(ch, false)); } if (!victim.IsNPC() && victim.IsGuild()) { text += String.Format("&n{0} of {1}.\r\n", ((PC)victim).GuildRank.ToString().ToUpper(), ((PC)victim).GuildMembership.Name); } SocketConnection.Act(text, ch, null, victim, SocketConnection.MessageTarget.character); if (!String.IsNullOrEmpty(victim.Description)) { ch.SendText(victim.Description); } else { SocketConnection.Act("&nYou see nothing special about $M.", ch, null, victim, SocketConnection.MessageTarget.character); } if (victim.GetMaxHit() > 0) { percent = (100 * victim.Hitpoints) / victim.GetMaxHit(); } else { percent = -1; } text = victim.ShowNameTo(ch, true); if (percent >= 100) text += " &nis in perfect &n&+ghealth&n. "; else if (percent >= 90) text += " &nis slightly &n&+yscratched&n. "; else if (percent >= 80) text += " &nhas a &+yfew bruises&n. "; else if (percent >= 70) text += " &nhas &+Ysome cuts&n. "; else if (percent >= 60) text += " &nhas &+Mseveral wounds&n. "; else if (percent >= 50) text += " &nhas &+mmany nasty wounds&n. "; else if (percent >= 40) text += " &nis &+Rbleeding freely&n. "; else if (percent >= 30) text += " &nis &+Rcovered in blood&n. "; else if (percent >= 20) text += " &nis &+rleaking guts&n. "; else if (percent >= 10) text += " &nis &+ralmost dead&n. "; else text += " &nis &+rDYING&n. "; ch.SendText(text); // Show size on look at someone. text = MUDString.CapitalizeANSIString(String.Format("{0}&n is a {1} of {2} size.\r\n", victim.GetSexPronoun(), Race.RaceList[victim.GetRace()].ColorName, Race.SizeString(victim.CurrentSize))); ch.SendText(text); ShowAffectLines(ch, victim); bool found = false; foreach (ObjTemplate.WearLocation location in ObjTemplate.TopDownEquipment) { obj = Object.GetEquipmentOnCharacter(victim, location); if (obj && CharData.CanSeeObj(ch, obj)) { if (!found) { ch.SendText("\r\n"); SocketConnection.Act("&n$E is using:", ch, null, victim, SocketConnection.MessageTarget.character); found = true; } if (obj.ItemType == ObjTemplate.ObjectType.weapon && (location == ObjTemplate.WearLocation.hand_one || location == ObjTemplate.WearLocation.hand_three || location == ObjTemplate.WearLocation.hand_four || location == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_WIELD)) { if (obj.HasFlag(ObjTemplate.ITEM_TWOHANDED) && !ch.HasInnate(Race.RACE_EXTRA_STRONG_WIELD)) { ch.SendText("&+y(wielding twohanded) &n"); } else { ch.SendText("&+y(wielding) &n"); } } else { if (obj.ItemType == ObjTemplate.ObjectType.shield && (location == ObjTemplate.WearLocation.hand_one || location == ObjTemplate.WearLocation.hand_three || location == ObjTemplate.WearLocation.hand_four || location == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_SHIELD)) { ch.SendText("&+y(worn as shield) &n"); } else { ch.SendText(StringConversion.EquipmentLocationDisplay[(int)location]); } } ch.SendText(FormatObjectToCharacter(obj, ch, true)); ch.SendText("\r\n"); } } // Keep in mind that players can spam looking at someone in order // to increase their skill in peek - this will need to be fixed. if ((victim != ch && !ch.IsNPC() && ((((PC)ch).SkillAptitude.ContainsKey("peek") && MUDMath.NumberPercent() < ((PC)ch).SkillAptitude["peek"]) || ch.Level >= Limits.LEVEL_AVATAR)) || ch.Riding == victim || ch.Rider == victim) { ch.SendText("\r\n&nYou peek at the inventory:\r\n"); ch.PracticeSkill("peek"); ShowListToCharacter(victim.Carrying, ch, true, true); } return; }
/// <summary> /// Show a character to another character. This is the abbreviated version used in "look room" /// </summary> /// <param name="victim"></param> /// <param name="ch"></param> public static void ShowCharacterToCharacterAbbreviated(CharData victim, CharData ch) { string text = String.Empty; if (!victim || !ch) { Log.Error("ShowCharacterToCharacter0(): null ch or victim.", 0); return; } if (victim.Rider && victim.Rider.InRoom == ch.InRoom) { return; } // If invis, show char invis symbol first. if (victim.IsAffected(Affect.AFFECT_INVISIBLE)) { text += "&+L*&n "; } // Show the player's description. if (((!ch.IsNPC() && victim.CurrentPosition == Position.standing) || (victim.IsNPC() && victim.MobileTemplate != null && victim.CurrentPosition == victim.MobileTemplate.DefaultPosition)) && (!String.IsNullOrEmpty(victim.FullDescription)) && !victim.Riding) { // Added long description does not have \r\n removed. We may want to. text += victim.Description + "&n"; } else { // Show the player's name. text += victim.ShowNameTo(ch, true) + "&n"; // Show the player's title. // Show the player's race, only if PC, and on the same side of the racewar or a god. if (!victim.IsNPC() && ((PC)victim).Title.Length > 0) { if (MUDString.StringsNotEqual(((PC)victim).Title, " &n") && (ch.IsNPC())) { text += ((PC)victim).Title; } if (victim.IsGuild() && (ch.IsNPC())) { text += " " + ((PC)victim).GuildMembership.WhoName; } if (!ch.IsRacewar(victim) || victim.IsImmortal() || ch.IsImmortal()) { text += " (" + Race.RaceList[victim.GetRace()].ColorName + ")"; } } // Show the player's condition. text += " is "; if (victim.CurrentPosition == Position.standing && victim.CanFly()) { text += "flying"; } else { text += Position.PositionString(victim.CurrentPosition); } text += " here"; if (victim.Fighting != null) { text += "&n fighting "; if (victim.Fighting == ch) { text += "&nyou!"; } else if (victim.InRoom == victim.Fighting.InRoom) { text += victim.Fighting.ShowNameTo(ch, false); } else { text += "&nsomeone who left??"; } } if (victim.Riding && victim.Riding.InRoom == victim.InRoom) { text += "&n, mounted on " + victim.Riding.ShowNameTo(ch, false); } text += "&n."; } if (victim.IsAffected(Affect.AFFECT_CASTING)) { text += "&n&+y (casting)&n"; } if (victim.IsAffected(Affect.AFFECT_MINOR_PARA)) { text += "&n (&+Yparalyzed)&n"; } if (!victim.IsNPC() && victim.HasActionBit(PC.PLAYER_WIZINVIS) && victim.GetTrust() <= ch.GetTrust()) { text += " &n&+g*&n"; } if (victim.IsAffected(Affect.AFFECT_HIDE) && (ch.IsAffected(Affect.AFFECT_DETECT_HIDDEN) || ch.HasInnate(Race.RACE_DETECT_HIDDEN))) { text += " &n(&+LHiding&n)"; } if (victim.IsAffected(Affect.AFFECT_CHARM) && ch.HasActionBit(PC.PLAYER_GODMODE)) { text += " &n(&n&+mCharmed&n)"; } if ((victim.IsAffected(Affect.AFFECT_PASS_DOOR) || victim.HasInnate(Race.RACE_PASSDOOR)) && ch.HasActionBit(PC.PLAYER_GODMODE)) { text += " &n(&+WTranslucent&n)"; } if ((victim.GetRace() == Race.RACE_UNDEAD || victim.GetRace() == Race.RACE_VAMPIRE) && (ch.IsAffected( Affect.AFFECT_DETECT_UNDEAD) || ch.HasActionBit(PC.PLAYER_GODMODE))) { text += " &n(&+WPale&n)"; } if (victim.IsAffected(Affect.AFFECT_FAERIE_FIRE)) { text += " &n(&n&+mFa&+Me&n&+mr&+Mie&+L Aura&n)"; } if (victim.IsEvil() && (ch.IsAffected(Affect.AFFECT_DETECT_EVIL) || ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin))) { text += " &n(&+rBlood&+L Aura&n)"; } if (victim.IsGood() && (ch.IsAffected(Affect.AFFECT_DETECT_GOOD) || ch.HasInnate(Race.RACE_DETECT_ALIGN) || ch.IsClass(CharClass.Names.paladin) || ch.IsClass(CharClass.Names.antipaladin))) { text += " &n(&+CLight&+L Aura&n)"; } if (victim.IsAffected(Affect.AFFECT_SANCTUARY)) { text += " &n(&+WWhite&+L Aura&n)"; } if (!victim.IsNPC() && victim.HasActionBit(PC.PLAYER_AFK)) { text += " &n&+b(&+RAFK&n&+b)&n"; } if (!victim.IsNPC() && victim.HasActionBit(PC.PLAYER_BOTTING)) { text += " &n&+b(&+YBot&n&+b)&n"; } text += "\r\n"; ch.SendText(text); return; }
/// <summary> /// Put on a piece of equipment. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Wear(CharData ch, string[] str) { if( ch == null ) return; Object obj; if (ch.IsAffected(Affect.AFFECT_HOLD) || ch.IsAffected(Affect.AFFECT_MINOR_PARA)) { ch.SendText("Your body refuses the call to movement.\r\n"); return; } if (!ch.IsNPC() && ch.IsAffected( Affect.AFFECT_WRAITHFORM)) { ch.SendText("You try, but your &n&+wghoul&n form resists your attempts.\r\n"); return; } if (ch.Fighting || ch.CurrentPosition == Position.fighting) { ch.SendText("You can't wear stuff while you're fighting!\r\n"); return; } if (str.Length == 0) { ch.SendText("Wear, wield, or hold what?\r\n"); return; } if (str[0] == "all") { foreach (Object iobj in ch.Carrying) { if (iobj.WearLocation != ObjTemplate.WearLocation.none || !CharData.CanSeeObj(ch, iobj)) { continue; } if (iobj.HasWearFlag(ObjTemplate.WEARABLE_WIELD) && !ch.HasInnate(Race.RACE_WEAPON_WIELD)) { continue; } Object.WearObject(ch, iobj, false); if (iobj.Trap != null && iobj.Trap.CheckTrigger( Trap.TriggerType.wear)) { ch.SetOffTrap(iobj); if (ch.CurrentPosition == Position.dead) { return; } } } return; } if (!(obj = ch.GetObjCarrying(str[0]))) { ch.SendText("You do not have that item.\r\n"); return; } if (obj.HasWearFlag(ObjTemplate.WEARABLE_WIELD) && !ch.HasInnate(Race.RACE_WEAPON_WIELD)) { ch.SendText("You are not able to wield a weapon.\r\n"); return; } Object.WearObject(ch, obj, true); if (obj.Trap != null && obj.Trap.CheckTrigger( Trap.TriggerType.wear)) { ch.SetOffTrap(obj); if (ch.CurrentPosition == Position.dead) { return; } } return; }
/// <summary> /// Checks whether a character can see an objects. Returns true if yes, false if no. /// </summary> /// <param name="ch"></param> /// <param name="obj"></param> /// <returns></returns> public static bool CanSeeObj( CharData ch, Object obj ) { if( obj == null ) { Log.Error("Calling CanSeeObj with no obj data!", 0); return false; } if( ch == null ) { Log.Error("Calling CanSeeObj with no CharData!", 0); return false; } if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_GODMODE)) return true; if (ch.IsAffected(Affect.AFFECT_BLIND) || ch.CurrentPosition <= Position.sleeping) return false; if( obj.ItemType == ObjTemplate.ObjectType.light && obj.Values[ 2 ] != 0 ) return true; if( obj.HasFlag( ObjTemplate.ITEM_SECRET ) && !( ch.HasInnate( Race.RACE_DETECT_HIDDEN ) || ch.IsAffected( Affect.AFFECT_DETECT_HIDDEN ) ) ) return false; if( obj.HasFlag( ObjTemplate.ITEM_INVIS ) && !ch.HasInnate( Race.RACE_DETECT_INVIS ) && !ch.IsAffected( Affect.AFFECT_DETECT_INVIS ) ) return false; if( obj.HasFlag( ObjTemplate.ITEM_LIT ) ) return true; if( ch.InRoom && ch.InRoom.IsDark() && !( ch.HasInnate( Race.RACE_ULTRAVISION ) || ch.IsAffected( Affect.AFFECT_ULTRAVISION ) ) ) return false; return true; }
/// <summary> /// Equip a weapon. Now calls equip_hand to resolve the actual equipping /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Wield(CharData ch, string[] str) { if( ch == null ) return; Object obj; if (ch.IsAffected(Affect.AFFECT_HOLD) || ch.IsAffected( Affect.AFFECT_MINOR_PARA)) { ch.SendText("Your body refuses the call to movement.\r\n"); return; } if (!ch.IsNPC() && ch.IsAffected( Affect.AFFECT_WRAITHFORM)) { ch.SendText("You try, but your &n&+wghoul&n form resists your attempts.\r\n"); return; } if (str.Length == 0) { ch.SendText("Wield what?\r\n"); return; } if (!(obj = ch.GetObjCarrying(str[0]))) { ch.SendText("You do not have that item.\r\n"); return; } if (!obj.HasWearFlag(ObjTemplate.WEARABLE_WIELD)) { if (obj.ItemType == ObjTemplate.ObjectType.weapon) { ch.SendText("That object is not usable as a weapon.\r\n"); return; } if (obj.ItemType == ObjTemplate.ObjectType.ranged_weapon && !obj.HasWearFlag(ObjTemplate.WEARABLE_HOLD)) { ch.SendText("That object is not usable as a missile weapon.\r\n"); return; } if (obj.ItemType == ObjTemplate.ObjectType.ranged_weapon) { // Ranged weapons flagged wither wield or hold are fine to use -- Xangis } else { ch.SendText("That object is not a weapon.\r\n"); return; } } if (!ch.HasInnate(Race.RACE_WEAPON_WIELD)) { ch.SendText("You are not able to wield a weapon.\r\n"); return; } if (!obj.IsWearableBy(ch)) return; Object.EquipInHand(ch, obj, Object.EQUIP_WIELD); 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> /// Ask: Primarily used for quest files, but can be used in regular conversation too. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Ask(CharData ch, string[] str) { if( ch == null ) return; QuestTemplate quest; if (str.Length < 2) { ch.SendText("Ask who what?\r\n"); return; } CharData victim = ch.GetCharRoom(str[0]); if (!victim) { ch.SendText("They're not here.\r\n"); return; } if (ch.IsAffected( Affect.AFFECT_MUTE) || ch.HasInnate(Race.RACE_MUTE) || ch.InRoom.HasFlag(RoomTemplate.ROOM_SILENT)) { ch.SendText("Your lips move but nothing escapes their bounds.\r\n"); return; } string text = String.Join(" ", str, 1, (str.Length - 1)); text = DrunkSpeech.MakeDrunk(text, ch); SocketConnection.Act("$n&n asks $N&n a question.", ch, null, victim, SocketConnection.MessageTarget.everyone_but_victim); string buf = String.Format("&n&+c$n&n&+c asks you '&+c{0}&n&+c'&n", text); SocketConnection.Act(buf, ch, null, victim, SocketConnection.MessageTarget.victim); buf = String.Format("&n&+cYou ask $N&n&+c '{0}&n&+c'&n", text); SocketConnection.Act(buf, ch, null, victim, SocketConnection.MessageTarget.character); // players can't have talk files -- go home! if (!victim.IsNPC()) return; bool questfound = false; foreach (QuestTemplate it in QuestTemplate.QuestList) { quest = it; if (quest.Messages == null || (quest.IndexNumber != victim.MobileTemplate.IndexNumber)) continue; foreach (TalkData message in quest.Messages) { if (MUDString.NameContainedIn(text, message.Keywords)) { ch.SendText("\r\n"); ch.SendText(message.Message); questfound = true; } } } // Only check chatterbot responses when there isn't a quest with the same keywords. if (!questfound && victim.ChatBot != null) { victim.ChatBot.CheckConversation(victim, ch, text); } return; }
/// <summary> /// Checks whether the victim is able to dodge the attacker's swing. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool CheckDodge( CharData ch, CharData victim ) { if( !victim.IsAwake() || victim.CurrentPosition < Position.reclining ) return false; if (ch.IsAffected(Affect.AFFECT_DAZZLE)) return false; if( !victim.HasSkill( "dodge" ) ) return false; int chance = victim.GetSkillChance("dodge"); // Size difference bonus for dodge for halflings - they get 2% dodge // bonus per size difference between them and the attacker. -- Xangis // Drow get a flat 15% bonus. if( victim.GetRace() == Race.RACE_HALFLING ) { if( ch.CurrentSize > victim.CurrentSize ) { chance += 3 * ( ch.CurrentSize - victim.CurrentSize ); } } else if( victim.HasInnate( Race.RACE_GOOD_DODGE ) ) { chance += 8; } else if( victim.HasInnate( Race.RACE_BAD_DODGE ) ) { chance -= 3; } // Bashed mobs/creatures have a hard time dodging if( victim.CurrentPosition < Position.fighting ) { chance -= 25; } // Leap is 16% max at level 50. Considering crappy thri hitpoints it's necessary. if( victim.GetRace() == Race.RACE_THRIKREEN && MUDMath.NumberPercent() <= ( victim.Level / 3 ) ) { SocketConnection.Act( "$N&n leaps over your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You leap over $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n leaps over $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); return true; } victim.PracticeSkill( "dodge" ); if( MUDMath.NumberPercent() >= chance - ch.Level ) return false; switch( MUDMath.NumberRange( 1, 2 ) ) { case 1: SocketConnection.Act( "$N&n dodges your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You dodge $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n dodges $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; case 2: SocketConnection.Act( "$N&n sidesteps your attack.", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "You narrowly dodge $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.victim ); SocketConnection.Act( "$N&n avoids $n&n's attack.", ch, null, victim, SocketConnection.MessageTarget.room_vict ); break; default: break; } if( ch.Fighting == null ) SetFighting( ch, victim ); if( victim.Fighting == null ) SetFighting( victim, ch ); return true; }
/// <summary> /// Shows items currently worn. /// </summary> /// <param name="ch"></param> /// <param name="str"></param> public static void Equipment(CharData ch, string[] str) { if (ch == null) return; Object obj = null; String equipmentOpen = String.Empty; String equipmentClose = String.Empty; String separator = String.Empty; if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { equipmentOpen = "<equipment>"; equipmentClose = "</equipment>"; separator = ":"; } else { equipmentOpen = "&nYou are using:\r\n"; separator = "\r\n"; } ch.SendText(equipmentOpen); bool found = false; foreach (ObjTemplate.WearLocation iWear in ObjTemplate.TopDownEquipment) { obj = Object.GetEquipmentOnCharacter(ch, iWear); if (!obj) continue; // Added wielding if (obj.ItemType == ObjTemplate.ObjectType.weapon && (iWear == ObjTemplate.WearLocation.hand_one || iWear == ObjTemplate.WearLocation.hand_four || iWear == ObjTemplate.WearLocation.hand_three || iWear == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_WIELD)) { if (obj.HasFlag(ObjTemplate.ITEM_TWOHANDED) && !ch.HasInnate(Race.RACE_EXTRA_STRONG_WIELD)) { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText(((int)iWear).ToString() + ","); } else { ch.SendText("&+y(wielding twohanded) &n"); } } else { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText(((int)iWear).ToString() + ","); } else { ch.SendText("&+y(wielding) &n"); } } } else { if (obj.ItemType == ObjTemplate.ObjectType.shield && (iWear == ObjTemplate.WearLocation.hand_one || iWear == ObjTemplate.WearLocation.hand_four || iWear == ObjTemplate.WearLocation.hand_three || iWear == ObjTemplate.WearLocation.hand_two) && obj.HasWearFlag(ObjTemplate.WEARABLE_SHIELD)) { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText(((int)iWear).ToString() + ","); } else { ch.SendText("&+y(worn as shield) &n"); } } else { if (!ch.IsNPC() && ch.Socket.Terminal == SocketConnection.TerminalType.TERMINAL_ENHANCED) { ch.SendText(((int)iWear).ToString() + ","); } else { ch.SendText(StringConversion.EquipmentLocationDisplay[(int)iWear]); } } } if (CharData.CanSeeObj(ch, obj)) { ch.SendText(Look.FormatObjectToCharacter(obj, ch, true)); ch.SendText(separator); } else { ch.SendText("&nsomething.\r\n"); } found = true; } if (!found) ch.SendText("&nNothing.\r\n"); ch.SendText(equipmentClose); return; }
/// <summary> /// Checks for magic resistance, also known as "shrug" -- as in "shrugging off the effects". /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static bool CheckShrug( CharData ch, CharData victim ) { int chance; if( !victim.HasInnate( Race.RACE_SHRUG ) ) { return false; } switch( victim.GetRace() ) { // Default at 99 to show races not listed here. case Race.RACE_GREYELF: chance = 25; break; case Race.RACE_GITHZERAI: chance = 10; break; case Race.RACE_DROW: chance = 40; break; case Race.RACE_RAKSHASA: chance = 15; break; case Race.RACE_GITHYANKI: chance = 20; break; // Demons, devils, etc. default: chance = 25 + ( victim.Level ) / 2; break; } if( MUDMath.NumberPercent() < chance ) { SocketConnection.Act( "&+MYour spell flows around &n$N&+M, leaving $M unharmed!&n", ch, null, victim, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+M$N&+M's spell flows around you, leaving you unharmed!&n", victim, null, ch, SocketConnection.MessageTarget.character ); SocketConnection.Act( "&+M$N&+M's spell flows around $n&+M, leaving $m unharmed!&n", victim, null, ch, SocketConnection.MessageTarget.room_vict ); return true; } return false; }
public static void ReturnToLoad( CharData ch ) { if( !ch || !ch.InRoom ) return; if( ch.InRoom.Area != Room.GetRoom( ch.LoadRoomIndexNumber ).Area ) return; Exit.Direction dir = FindPath( ch.InRoom.IndexNumber, ch.LoadRoomIndexNumber, ch, -40000, true ); if( dir == Exit.Direction.invalid ) { return; } if( ch.InRoom.ExitData[ (int)dir ].HasFlag( Exit.ExitFlag.closed ) && !ch.IsAffected( Affect.AFFECT_PASS_DOOR ) && !ch.HasInnate( Race.RACE_PASSDOOR ) ) { CommandType.Interpret(ch, "unlock " + dir.ToString()); CommandType.Interpret(ch, "open " + dir.ToString()); return; } ch.Move( dir ); if( !ch.InRoom ) { string text = "Return_to_load: no ch._inRoom! Mob #" + ch.MobileTemplate.IndexNumber + ", _name: " + ch.Name + ". Placing mob in limbo (mob.AddToRoom())."; Log.Error( text, 0 ); ch.AddToRoom( Room.GetRoom( StaticRooms.GetRoomNumber("ROOM_NUMBER_LIMBO") ) ); ImmortalChat.SendImmortalChat(ch, ImmortalChat.IMMTALK_SPAM, 0, text); return; } return; }
/// <summary> /// Returns a visibility value based on how well the looker can see the target. /// </summary> /// <param name="ch"></param> /// <param name="victim"></param> /// <returns></returns> public static Visibility HowSee(CharData ch, CharData victim) { if (ch == null) { Log.Error("how_see called with null ch.", 0); return Visibility.invisible; } if (victim == null) { Log.Error("how_see called with null victim.", 0); return Visibility.invisible; } // Char almost dead, or asleep. if (ch.CurrentPosition <= Position.sleeping) { return Visibility.invisible; } // All mobiles cannot see wizinvised immortals. if (ch.IsNPC() && !victim.IsNPC() && victim.HasActionBit(PC.PLAYER_WIZINVIS)) { return Visibility.invisible; } // Handles Immortal Invis. if (!victim.IsNPC() && victim.HasActionBit(PC.PLAYER_WIZINVIS) && ch.GetTrust() < victim.Level) { return Visibility.invisible; } // Handles Immmortal sight. if (!ch.IsNPC() && ch.HasActionBit(PC.PLAYER_GODMODE)) { return Visibility.visible; } // Handles blindness. if (ch.IsAffected(Affect.AFFECT_BLIND)) { return Visibility.invisible; } // Handles regular invisibility. if ((victim.IsAffected(Affect.AFFECT_INVISIBLE) || victim.IsAffected(Affect.AFFECT_MINOR_INVIS))) { if (ch.HasInnate(Race.RACE_DETECT_INVIS) || ch.IsAffected(Affect.AFFECT_DETECT_INVIS) || (ch.IsAffected(Affect.AFFECT_ELEM_SIGHT) && (victim.GetRace() == Race.RACE_AIR_ELE || victim.GetRace() == Race.RACE_WATER_ELE || victim.GetRace() == Race.RACE_FIRE_ELE || victim.GetRace() == Race.RACE_EARTH_ELE))) { if (victim.IsAffected(Affect.AFFECT_HIDE)) { if (ch.IsAffected(Affect.AFFECT_DETECT_HIDDEN)) { return Visibility.visible; } if (ch.HasInnate(Race.RACE_DETECT_HIDDEN) || ch.IsAffected(Affect.AFFECT_SENSE_LIFE)) { return Visibility.sense_hidden; } return Visibility.invisible; } return Visibility.visible; } } // Handles dark rooms. Added ultracheck. if (victim.InRoom.IsDark()) { if (ch.HasInnate(Race.RACE_ULTRAVISION) || ch.IsAffected(Affect.AFFECT_ULTRAVISION)) { return Visibility.visible; } if ((ch.HasInnate(Race.RACE_INFRAVISION) || ch.IsAffected(Affect.AFFECT_INFRAVISION)) && !victim.InRoom.HasFlag(RoomTemplate.ROOM_UNDERWATER)) { return Visibility.sense_infravision; } if (!(ch.HasInnate(Race.RACE_ULTRAVISION) || ch.IsAffected(Affect.AFFECT_ULTRAVISION))) { return Visibility.too_dark; } } // Handles hidden people. if (victim.IsAffected(Affect.AFFECT_HIDE)) { if (ch.IsAffected(Affect.AFFECT_DETECT_HIDDEN)) { return Visibility.visible; } if (ch.HasInnate(Race.RACE_DETECT_HIDDEN) || ch.IsAffected(Affect.AFFECT_SENSE_LIFE)) { return Visibility.sense_hidden; } return Visibility.invisible; } return Visibility.visible; }