private void Move() { // Normal Move Mode if (Monster == null || !IsAlive) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path.Any(s => s != null)) // move back to initial position after following target { int timetowalk = 2000 / Monster.Speed; if (time > timetowalk) { int mapX = Path.ElementAt(0).X; int mapY = Path.ElementAt(0).Y; Path.RemoveAt(0); Observable.Timer(TimeSpan.FromMilliseconds(timetowalk)) .Subscribe( x => { MapX = (short)mapX; MapY = (short)mapY; }); LastMove = DateTime.Now; MapInstance.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); return; } } else if (time > _movetime) { short mapX = FirstX, mapY = FirstY; if (MapInstance.Map?.GetFreePosition(ref mapX, ref mapY, (byte)ServerManager.Instance.RandomNumber(0, 2), (byte)_random.Next(0, 2)) ?? false) { int distance = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }); double value = 1000d * distance / (2 * Monster.Speed); Observable.Timer(TimeSpan.FromMilliseconds(value)) .Subscribe( x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddMilliseconds(value); MapInstance.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.All)); } } } HostilityTarget(); }
internal IEnumerable <Character> GetCharactersInRange(short mapX, short mapY, byte distance, bool attackGreaterDistance = false) { List <Character> characters = new List <Character>(); IEnumerable <ClientSession> cl = Sessions.Where(s => s.HasSelectedCharacter && s.Character.Hp > 0); IEnumerable <ClientSession> clientSessions = cl as IList <ClientSession> ?? cl.ToList(); for (int i = clientSessions.Count() - 1; i >= 0; i--) { if (!attackGreaterDistance) { if (Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = clientSessions.ElementAt(i).Character.PositionX, Y = clientSessions.ElementAt(i).Character.PositionY }) <= distance + 1) { characters.Add(clientSessions.ElementAt(i).Character); } } else { if (Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = clientSessions.ElementAt(i).Character.PositionX, Y = clientSessions.ElementAt(i).Character.PositionY }) > distance) { characters.Add(clientSessions.ElementAt(i).Character); } } } return(characters); }
public long OrderedCharacterId(Character Character) { order++; IEnumerable <ClientSession> lst = Characters.Where(s => Map.GetDistance(s.Character, Character) < 50); if (order > lst.Count() - 1) { order = 0; } return(lst.ElementAt(order).Character.CharacterId); }
/// <summary> /// Check if the Monster is in the given Range. /// </summary> /// <param name="mapX">The X coordinate on the Map of the object to check.</param> /// <param name="mapY">The Y coordinate on the Map of the object to check.</param> /// <param name="distance">The maximum distance of the object to check.</param> /// <returns>True if the Monster is in range, False if not.</returns> public bool IsInRange(short mapX, short mapY, byte distance) { return(Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) <= distance); }
/// <summary> /// Follow the Monsters target to it's position. /// </summary> /// <param name="targetSession">The TargetSession to follow</param> private void FollowTarget(ClientSession targetSession) { if (IsMoving) { short maxDistance = 22; int distance = 0; if (!Path.Any() && targetSession != null) { short xoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); short yoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); try { List <Node> list = BestFirstSearch.TracePath(new Node() { X = MapX, Y = MapY }, targetSession.Character.BrushFire, targetSession.Character.MapInstance.Map.Grid); Path = list; } catch (Exception ex) { Logger.Log.Error($"Pathfinding using Pathfinder failed. Map: {MapId} StartX: {MapX} StartY: {MapY} TargetX: {(short)(targetSession.Character.PositionX + xoffset)} TargetY: {(short)(targetSession.Character.PositionY + yoffset)}", ex); RemoveTarget(); } } if (Monster != null && DateTime.Now > LastMove && Monster.Speed > 0 && Path.Any()) { int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; short mapX = Path.ElementAt(maxindex - 1).X; short mapY = Path.ElementAt(maxindex - 1).Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Monster.Speed; MapInstance.Broadcast(new BroadcastPacket(null, $"mv 3 {MapMonsterId} {mapX} {mapY} {Monster.Speed}", ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))).Subscribe(x => { MapX = mapX; MapY = mapY; }); distance = (int)Path.First().F; Path.RemoveRange(0, maxindex); } if (targetSession == null || MapId != targetSession.Character.MapInstance.Map.MapId || distance > maxDistance) { RemoveTarget(); } } }
public long?GetNextOrderedCharacterId(Character character) { _order++; List <ClientSession> sessions = Characters.Where(s => Map.GetDistance(s.Character, character) < 50).ToList(); if (_order > sessions.Count() - 1) // if order wents out of amount of ppl, reset it -> zero based index { _order = 0; } if (!sessions.Any()) // group seems to be empty { return(null); } return(sessions[_order].Character.CharacterId); }
internal void GetNearestOponent() { if (Target == -1) { const int maxDistance = 100; int distance = 100; List <ClientSession> sess = new List <ClientSession>(); DamageList.Keys.ToList().ForEach(s => sess.Add(MapInstance.GetSessionByCharacterId(s))); ClientSession session = sess.OrderBy(s => distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY })).FirstOrDefault(); if (distance < maxDistance) { if (session != null) { Target = session.Character.CharacterId; } } } }
internal void GetNearestOponent() { if (Target == -1) { const int maxDistance = 22; int distance = 100; List <ClientSession> sess = new List <ClientSession>(); DamageList.Keys.ToList().ForEach(s => sess.Add(MapInstance.GetSessionByCharacterId(s))); //ClientSession session = sess.OrderBy(s => distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY })).FirstOrDefault(); Character character = sess.Where(s => s?.Character != null && (ServerManager.Instance.ChannelId != 51 || MonsterVNum - (byte)s.Character.Faction != 678) && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapInstance == MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Monster.NoticeRange).OrderBy(s => distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY })).FirstOrDefault()?.Character; if (distance < maxDistance && character != null) { Target = character.CharacterId; } } }
/// <summary> /// Remove the current Target from Monster. /// </summary> internal void RemoveTarget() { Path = MapInstance.Map.StraightPath(new GridPos { x = MapX, y = MapY }, new GridPos { x = FirstX, y = FirstY }); if (!Path.Any()) { Path = Map.JPSPlus(JumpPointParameters, new GridPos { x = MapX, y = MapY }, new GridPos { x = FirstX, y = FirstY }); } Target = -1; int nearestDistance = 100; foreach (KeyValuePair <long, long> kvp in DamageList) { ClientSession session = MapInstance.GetSessionByCharacterId(kvp.Key); if (session != null) { int distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = session.Character.PositionX, Y = session.Character.PositionY }); if (distance < nearestDistance) { nearestDistance = distance; Target = session.Character.CharacterId; } } } }
internal void NpcLife() { double time = (DateTime.Now - LastEffect).TotalMilliseconds; if (Effect > 0 && time > EffectDelay) { Map.Broadcast(GenerateEff(), MapX, MapY); LastEffect = DateTime.Now; } time = (DateTime.Now - LastMove).TotalMilliseconds; if (IsMoving && Npc.Speed > 0 && time > _movetime) { _movetime = _random.Next(500, 3000); byte point = (byte)_random.Next(2, 4); byte fpoint = (byte)_random.Next(0, 2); byte xpoint = (byte)_random.Next(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = FirstX; short mapY = FirstY; if (Map.GetFreePosition(ref mapX, ref mapY, xpoint, ypoint)) { Observable.Timer(TimeSpan.FromMilliseconds(1000 * (xpoint + ypoint) / (2 * Npc.Speed))) .Subscribe( x => { this.MapX = mapX; this.MapY = mapY; }); LastMove = DateTime.Now.AddSeconds((xpoint + ypoint) / (2 * Npc.Speed)); Map.Broadcast(new BroadcastPacket(null, GenerateMv2(), ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); } } if (Target == -1) { if (this.Npc.IsHostile && this.Shop == null) { MapMonster monster = this.Map.Monsters.FirstOrDefault(s => MapId == s.MapId && Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.MapX, Y = s.MapY }) < (Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange)); ClientSession session = this.Map.Sessions.FirstOrDefault(s => MapId == s.Character.MapId && Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.Character.MapX, Y = s.Character.MapY }) < Npc.NoticeRange); if (monster != null && session != null) { Target = monster.MapMonsterId; } } } else if (Target != -1) { MapMonster monster = this.Map.Monsters.FirstOrDefault(s => s.MapMonsterId == Target); if (monster == null || monster.CurrentHp < 1) { Target = -1; return; } NpcMonsterSkill npcMonsterSkill = null; if (_random.Next(10) > 8) { npcMonsterSkill = Npc.Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } short damage = 100; int distance = Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = monster.MapX, Y = monster.MapY }); if (monster != null && monster.CurrentHp > 0 && ((npcMonsterSkill != null && distance < npcMonsterSkill.Skill.Range) || (distance <= Npc.BasicRange))) { if (((DateTime.Now - LastEffect).TotalMilliseconds >= 1000 + Npc.BasicCooldown * 200 && !Npc.Skills.Any()) || npcMonsterSkill != null) { if (npcMonsterSkill != null) { npcMonsterSkill.LastSkillUse = DateTime.Now; Map.Broadcast($"ct 2 {MapNpcId} 3 {Target} {npcMonsterSkill.Skill.CastAnimation} {npcMonsterSkill.Skill.CastEffect} {npcMonsterSkill.Skill.SkillVNum}"); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { Map.Broadcast(GenerateEff()); } monster.CurrentHp -= damage; if (npcMonsterSkill != null) { Map.Broadcast($"su 2 {MapNpcId} 3 {Target} {npcMonsterSkill.SkillVNum} {npcMonsterSkill.Skill.Cooldown} {npcMonsterSkill.Skill.AttackAnimation} {npcMonsterSkill.Skill.Effect} 0 0 {(monster.CurrentHp > 0 ? 1 : 0)} { (int)(monster.CurrentHp / monster.Monster.MaxHP * 100) } {damage} 0 0"); } else { Map.Broadcast($"su 2 {MapNpcId} 3 {Target} 0 {Npc.BasicCooldown} 11 {Npc.BasicSkill} 0 0 {(monster.CurrentHp > 0 ? 1 : 0)} { (int)(monster.CurrentHp / monster.Monster.MaxHP * 100) } {damage} 0 0"); } LastEffect = DateTime.Now; if (monster.CurrentHp < 1) { if (IsMoving) { Path = Map.StraightPath(new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = FirstX, y = FirstY }); if (!Path.Any()) { Path = Map.JPSPlus(JumpPointParameters, new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = FirstX, y = FirstY }); } } monster.IsAlive = false; monster.LastMove = DateTime.Now; monster.CurrentHp = 0; monster.CurrentMp = 0; monster.Death = DateTime.Now; Target = -1; } } } else { int maxdistance = (Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange); if (IsMoving) { short maxDistance = 5; if (Path.Count() == 0 && monster != null && distance > 1 && distance < maxDistance) { short xoffset = (short)_random.Next(-1, 1); short yoffset = (short)_random.Next(-1, 1); Path = Map.StraightPath(new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = (short)(monster.MapX + xoffset), y = (short)(monster.MapY + yoffset) }); if (!Path.Any()) { Path = Map.JPSPlus(JumpPointParameters, new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = (short)(monster.MapX + xoffset), y = (short)(monster.MapY + yoffset) }); } } if (DateTime.Now > LastMove && Npc.Speed > 0 && Path.Any()) { short mapX; short mapY; int maxindex = (Path.Count > Npc.Speed / 2 && Npc.Speed > 1) ? Npc.Speed / 2 : Path.Count; mapX = (short)Path.ElementAt(maxindex - 1).x; mapY = (short)Path.ElementAt(maxindex - 1).y; double waitingtime = (double)(Map.GetDistance(new MapCell() { X = mapX, Y = mapY, MapId = MapId }, new MapCell() { X = MapX, Y = MapY, MapId = MapId })) / (double)(Npc.Speed); Map.Broadcast(new BroadcastPacket(null, $"mv 2 {this.MapNpcId} {mapX} {mapY} {Npc.Speed}", ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds((waitingtime > 1 ? 1 : waitingtime)); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))) .Subscribe( x => { this.MapX = mapX; this.MapY = mapY; }); for (int j = maxindex; j > 0; j--) { Path.RemoveAt(0); } } if (Path.Count() == 0 && (monster == null || MapId != monster.MapId || distance > maxDistance)) { Path = Map.StraightPath(new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = FirstX, y = FirstY }); if (!Path.Any()) { Path = Map.JPSPlus(JumpPointParameters, new GridPos() { x = this.MapX, y = this.MapY }, new GridPos() { x = FirstX, y = FirstY }); } Target = -1; } } else { if (distance > maxdistance) { Target = -1; } } } } }
private void Move() { // Normal Move Mode if (Monster == null || !IsAlive) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path.Where(s => s != null).Any()) { int timetowalk = 2000 / (Monster.Speed); if (time > timetowalk) { int mapX = Path.ElementAt(0).x; int mapY = Path.ElementAt(0).y; Path.RemoveAt(0); Observable.Timer(TimeSpan.FromMilliseconds(timetowalk)) .Subscribe( x => { MapX = (short)mapX; MapY = (short)mapY; }); LastMove = DateTime.Now; Map.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); return; } } else if (time > _movetime) { _movetime = _random.Next(600, 3000); byte point = (byte)_random.Next(2, 4); byte fpoint = (byte)_random.Next(0, 2); byte xpoint = (byte)_random.Next(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = FirstX; short mapY = FirstY; if (Map?.GetFreePosition(ref mapX, ref mapY, xpoint, ypoint) ?? false) { Observable.Timer(TimeSpan.FromMilliseconds(1000 * (xpoint + ypoint) / (2 * Monster.Speed))) .Subscribe( x => { this.MapX = mapX; this.MapY = mapY; }); LastMove = DateTime.Now.AddSeconds((xpoint + ypoint) / (2 * Monster.Speed)); Map.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); } } } if (Monster.IsHostile) { Character character = ServerManager.Instance.Sessions.FirstOrDefault(s => s != null && s.Character != null && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapId == MapId && Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.Character.MapX, Y = s.Character.MapY }) < Monster.NoticeRange)?.Character; if (character != null) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon) { character.Session.SendPacket(GenerateEff(5000)); } } } }
private void npcLife() { // Respawn if (CurrentHp <= 0 && ShouldRespawn != null && !ShouldRespawn.Value) { MapInstance.RemoveNpc(this); MapInstance.Broadcast(GenerateOut()); } if (!IsAlive && ShouldRespawn != null && ShouldRespawn.Value) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Npc.RespawnTime / 10d) { Respawn(); } } if (LastProtectedEffect.AddMilliseconds(6000) <= DateTime.Now) { LastProtectedEffect = DateTime.Now; if (IsMate || IsProtected) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, 825), MapX, MapY); } } double time = (DateTime.Now - LastEffect).TotalMilliseconds; if (EffectDelay > 0) { if (time > EffectDelay) { if (Effect > 0 && EffectActivated) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, Effect), MapX, MapY); } LastEffect = DateTime.Now; } } time = (DateTime.Now - LastMove).TotalMilliseconds; if (Target == -1 && IsMoving && Npc.Speed > 0 && time > _movetime && !HasBuff(CardType.Move, (byte)AdditionalTypes.Move.MovementImpossible)) { _movetime = ServerManager.RandomNumber(500, 3000); int maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; if (maxindex < 1) { maxindex = 1; } if (Path.Count == 0 || Path.Count >= maxindex && maxindex > 0 && Path[maxindex - 1] == null) { short xoffset = (short)ServerManager.RandomNumber(-1, 1); short yoffset = (short)ServerManager.RandomNumber(-1, 1); MapCell moveToPosition = new MapCell { X = FirstX, Y = FirstY }; if (RunToX != 0 || RunToY != 0) { moveToPosition = new MapCell { X = RunToX, Y = RunToY }; _movetime = ServerManager.RandomNumber(300, 1200); } Path = BestFirstSearch.FindPathJagged(new GridPos { X = MapX, Y = MapY }, new GridPos { X = (short)ServerManager.RandomNumber(moveToPosition.X - 3, moveToPosition.X + 3), Y = (short)ServerManager.RandomNumber(moveToPosition.Y - 3, moveToPosition.Y + 3) }, MapInstance.Map.JaggedGrid); maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; } if (DateTime.Now > LastMove && Npc.Speed > 0 && Path.Count > 0) { byte speedIndex = (byte)(Npc.Speed / 2.5 < 1 ? 1 : Npc.Speed / 2.5); maxindex = Path.Count > speedIndex ? speedIndex : Path.Count; short mapX = (short)ServerManager.RandomNumber(Path[maxindex - 1].X - 1, Path[maxindex - 1].X + 1); short mapY = (short)_random.Next(Path[maxindex - 1].Y - 1, Path[maxindex - 1].Y + 1); //short mapX = Path[maxindex - 1].X; //short mapY = Path[maxindex - 1].Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Npc.Speed; MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Npc, MapNpcId, mapX, mapY, Npc.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))).Subscribe(x => { MapX = mapX; MapY = mapY; }); Path.RemoveRange(0, maxindex); } } if (Target == -1) { if (IsHostile && Shop == null) { MapMonster monster = MapInstance.GetMonsterInRangeList(MapX, MapY, (byte)(Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange)).Where(s => BattleEntity.CanAttackEntity(s.BattleEntity)).FirstOrDefault(); ClientSession session = MapInstance.Sessions.FirstOrDefault(s => BattleEntity.CanAttackEntity(s.Character.BattleEntity) && MapInstance == s.Character.MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Npc.NoticeRange); if (monster != null) { Target = monster.MapMonsterId; } if (session?.Character != null) { Target = session.Character.CharacterId; } } } else if (Target != -1) { MapMonster monster = MapInstance.Monsters.Find(s => s.MapMonsterId == Target); if (monster == null || monster.CurrentHp < 1) { Target = -1; return; } NpcMonsterSkill npcMonsterSkill = null; if (ServerManager.RandomNumber(0, 10) > 8) { npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } int hitmode = 0; bool onyxWings = false; int damage = DamageHelper.Instance.CalculateDamage(new BattleEntity(this), new BattleEntity(monster), npcMonsterSkill?.Skill, ref hitmode, ref onyxWings); if (monster.Monster.BCards.Find(s => s.Type == (byte)CardType.LightAndShadow && s.SubType == (byte)AdditionalTypes.LightAndShadow.InflictDamageToMP) is BCard card) { int reduce = damage / 100 * card.FirstData; if (monster.CurrentMp < reduce) { reduce = (int)monster.CurrentMp; monster.CurrentMp = 0; } else { monster.DecreaseMp(reduce); } damage -= reduce; } int distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = monster.MapX, Y = monster.MapY }); if (monster.CurrentHp > 0 && ((npcMonsterSkill != null && distance < npcMonsterSkill.Skill.Range) || distance <= Npc.BasicRange) && !HasBuff(CardType.SpecialAttack, (byte)AdditionalTypes.SpecialAttack.NoAttack)) { if (((DateTime.Now - LastSkill).TotalMilliseconds >= 1000 + (Npc.BasicCooldown * 200) /* && Skills.Count == 0*/) || npcMonsterSkill != null) { if (npcMonsterSkill != null) { npcMonsterSkill.LastSkillUse = DateTime.Now; MapInstance.Broadcast(StaticPacketHelper.CastOnTarget(UserType.Npc, MapNpcId, UserType.Monster, Target, npcMonsterSkill.Skill.CastAnimation, npcMonsterSkill.Skill.CastEffect, npcMonsterSkill.Skill.SkillVNum)); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, Effect)); } monster.BattleEntity.GetDamage(damage, BattleEntity); lock (monster.DamageList) { if (!monster.DamageList.Any(s => s.Key.MapEntityId == MapNpcId)) { monster.AddToAggroList(BattleEntity); } } MapInstance.Broadcast(npcMonsterSkill != null ? StaticPacketHelper.SkillUsed(UserType.Npc, MapNpcId, 3, Target, npcMonsterSkill.SkillVNum, npcMonsterSkill.Skill.Cooldown, npcMonsterSkill.Skill.AttackAnimation, npcMonsterSkill.Skill.Effect, 0, 0, monster.CurrentHp > 0, (int)((float)monster.CurrentHp / (float)monster.MaxHp * 100), damage, hitmode, 0) : StaticPacketHelper.SkillUsed(UserType.Npc, MapNpcId, 3, Target, 0, Npc.BasicCooldown, 11, Npc.BasicSkill, 0, 0, monster.CurrentHp > 0, (int)((float)monster.CurrentHp / (float)monster.MaxHp * 100), damage, hitmode, 0)); LastSkill = DateTime.Now; if (npcMonsterSkill?.Skill.TargetType == 1 && npcMonsterSkill?.Skill.HitType == 2) { IEnumerable <ClientSession> clientSessions = MapInstance.Sessions?.Where(s => s.Character.IsInRange(MapX, MapY, npcMonsterSkill.Skill.TargetRange)); IEnumerable <Mate> mates = MapInstance.GetListMateInRange(MapX, MapY, npcMonsterSkill.Skill.TargetRange); foreach (BCard skillBcard in npcMonsterSkill.Skill.BCards) { if (skillBcard.Type == 25 && skillBcard.SubType == 1 && new Buff((short)skillBcard.SecondData, Npc.Level)?.Card?.BuffType == BuffType.Good) { if (clientSessions != null) { foreach (ClientSession clientSession in clientSessions) { if (clientSession.Character != null) { if (!BattleEntity.CanAttackEntity(clientSession.Character.BattleEntity)) { skillBcard.ApplyBCards(clientSession.Character.BattleEntity, BattleEntity); } } } } if (mates != null) { foreach (Mate mate in mates) { if (!BattleEntity.CanAttackEntity(mate.BattleEntity)) { skillBcard.ApplyBCards(mate.BattleEntity, BattleEntity); } } } } } } if (monster.CurrentHp < 1 && monster.SetDeathStatement()) { monster.RunDeathEvent(); RemoveTarget(); } } } else { int maxdistance = Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange; if (IsMoving && !HasBuff(CardType.Move, (byte)AdditionalTypes.Move.MovementImpossible)) { const short maxDistance = 5; int maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; if (maxindex < 1) { maxindex = 1; } if ((Path.Count == 0 && distance >= 1 && distance < maxDistance) || (Path.Count >= maxindex && maxindex > 0 && Path[maxindex - 1] == null)) { short xoffset = (short)ServerManager.RandomNumber(-1, 1); short yoffset = (short)ServerManager.RandomNumber(-1, 1); //go to monster Path = BestFirstSearch.FindPathJagged(new GridPos { X = MapX, Y = MapY }, new GridPos { X = (short)(monster.MapX + xoffset), Y = (short)(monster.MapY + yoffset) }, MapInstance.Map.JaggedGrid); maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; } if (DateTime.Now > LastMove && Npc.Speed > 0 && Path.Count > 0) { byte speedIndex = (byte)(Npc.Speed / 2.5 < 1 ? 1 : Npc.Speed / 2.5); maxindex = Path.Count > speedIndex ? speedIndex : Path.Count; //short mapX = (short)ServerManager.RandomNumber(Path[maxindex - 1].X - 1, Path[maxindex - 1].X + 1); //short mapY = (short)_random.Next(Path[maxindex - 1].Y - 1, Path[maxindex - 1].Y + 1); short mapX = Path[maxindex - 1].X; short mapY = Path[maxindex - 1].Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Npc.Speed; MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Npc, MapNpcId, mapX, mapY, Npc.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))).Subscribe(x => { MapX = mapX; MapY = mapY; }); Path.RemoveRange(0, maxindex); } if (Target != -1 && (MapId != monster.MapId || distance > maxDistance)) { RemoveTarget(); } } } } }
internal void MonsterLife() { //Respawn if (!Alive && Respawn) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10) { DamageList = new Dictionary <long, long>(); Alive = true; Target = -1; CurrentHp = Monster.MaxHP; CurrentMp = Monster.MaxMP; MapX = firstX; MapY = firstY; Path = new List <MapCell>(); Map.Broadcast(GenerateIn3()); Map.Broadcast(GenerateEff(7)); } return; } else if (Target == -1) { //Normal Move Mode if (Alive == false) { return; } Random random = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); double time = (DateTime.Now - LastMove).TotalSeconds; int MoveFrequent = 5 - (int)Math.Round((double)(Monster.Speed / 5)); if (MoveFrequent < 1) { MoveFrequent = 1; } if (IsMoving) { if (Path.Where(s => s != null).ToList().Count > 0) { if ((DateTime.Now - LastMove).TotalSeconds > 1.0 / Monster.Speed) { short mapX = Path.ElementAt(0).X; short mapY = Path.ElementAt(0).Y; Path.RemoveAt(0); LastMove = DateTime.Now; Map.Broadcast($"mv 3 {this.MapMonsterId} {this.MapX} {this.MapY} {Monster.Speed}"); Task.Factory.StartNew(async() => { await Task.Delay(400); this.MapX = mapX; this.MapY = mapY; }); return; } } else if (time > 0.5 * random.Next(1, 3) * (0.5 + random.NextDouble())) { byte point = (byte)random.Next(2, 4); byte fpoint = (byte)random.Next(0, 2); byte xpoint = (byte)random.Next(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = firstX; short mapY = firstY; if (ServerManager.GetMap(MapId).GetFreePosition(ref mapX, ref mapY, xpoint, ypoint)) { Task.Factory.StartNew(async() => { await Task.Delay(400); this.MapX = mapX; this.MapY = mapY; }); LastMove = DateTime.Now; string movePacket = $"mv 3 {this.MapMonsterId} {mapX} {mapY} {Monster.Speed}"; Map.Broadcast(movePacket); } } } if (Monster.IsHostile) { Character character = ServerManager.Instance.Sessions.Where(s => s.Character != null && s.Character.Hp > 0).OrderBy(s => Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.Character.MapX, Y = s.Character.MapY })).FirstOrDefault(s => s.Character != null && !s.Character.Invisible && s.Character.MapId == MapId)?.Character; if (character != null) { if (Map.GetDistance(new MapCell() { X = character.MapX, Y = character.MapY }, new MapCell() { X = MapX, Y = MapY }) < 10) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon) { character.Session.SendPacket(GenerateEff(5000)); } } } } } else { ClientSession targetSession = Map.Sessions.SingleOrDefault(s => s.Character.CharacterId == Target); if (targetSession == null || targetSession.Character.Invisible) { Target = -1; return; } Random random = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); NpcMonsterSkill npcMonsterSkill = null; if (random.Next(10) > 8 || inWaiting) { inWaiting = false; if ((DateTime.Now - LastEffect).TotalMilliseconds < Monster.BasicCooldown * 200) { inWaiting = true; } npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => random.Next()).FirstOrDefault(); } int damage = 100; if (targetSession != null && targetSession.Character.Hp > 0 && ((npcMonsterSkill != null && CurrentMp - npcMonsterSkill.Skill.MpCost >= 0 && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) < npcMonsterSkill.Skill.Range) || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) <= Monster.BasicRange))) { if ((DateTime.Now - LastEffect).TotalMilliseconds >= Monster.BasicCooldown * 200 && !inWaiting) { if (npcMonsterSkill != null) { npcMonsterSkill.LastUse = DateTime.Now; CurrentMp -= npcMonsterSkill.Skill.MpCost; Map.Broadcast($"ct 3 {MapMonsterId} 1 {Target} {npcMonsterSkill.Skill.CastAnimation} {npcMonsterSkill.Skill.CastEffect} {npcMonsterSkill.Skill.SkillVNum}"); } LastMove = DateTime.Now; // deal 0 damage to GM with GodMode damage = targetSession.Character.HasGodMode ? 0 : 100; if (targetSession.Character.IsSitting) { targetSession.Character.IsSitting = false; Map.Broadcast(null, targetSession.Character.GenerateRest(), ReceiverType.OnlySomeone, "", targetSession.Character.CharacterId); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { Map.Broadcast(GenerateEff(npcMonsterSkill.Skill.CastEffect)); Thread.Sleep(npcMonsterSkill.Skill.CastTime * 100); } Path = new List <MapCell>(); targetSession.Character.LastDefence = DateTime.Now; targetSession.Character.GetDamage(damage); Map.Broadcast(null, ServerManager.Instance.GetUserMethod <string>(Target, "GenerateStat"), ReceiverType.OnlySomeone, "", Target); if (npcMonsterSkill != null) { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} {npcMonsterSkill.SkillVNum} {npcMonsterSkill.Skill.Cooldown} {npcMonsterSkill.Skill.AttackAnimation} {npcMonsterSkill.Skill.Effect} {this.MapX} {this.MapY} {(targetSession.Character.Hp > 0 ? 1 : 0)} { (int)(targetSession.Character.Hp / targetSession.Character.HPLoad() * 100) } {damage} 0 0"); } else { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} 0 {Monster.BasicCooldown} 11 {Monster.BasicSkill} 0 0 {(targetSession.Character.Hp > 0 ? 1 : 0)} { (int)(targetSession.Character.Hp / targetSession.Character.HPLoad() * 100) } {damage} 0 0"); } LastEffect = DateTime.Now; if (targetSession.Character.Hp <= 0) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(targetSession.Character.CharacterId); Target = -1; } if (npcMonsterSkill != null && (npcMonsterSkill.Skill.Range > 0 || npcMonsterSkill.Skill.TargetRange > 0)) { foreach (Character chara in ServerManager.GetMap(MapId).GetListPeopleInRange(npcMonsterSkill.Skill.TargetRange == 0 ? this.MapX : targetSession.Character.MapX, npcMonsterSkill.Skill.TargetRange == 0 ? this.MapY : targetSession.Character.MapY, (byte)(npcMonsterSkill.Skill.TargetRange + npcMonsterSkill.Skill.Range)).Where(s => s.CharacterId != Target && s.Hp > 0)) { if (chara.IsSitting) { chara.IsSitting = false; Map.Broadcast(null, chara.GenerateRest(), ReceiverType.OnlySomeone, "", chara.CharacterId); } damage = chara.HasGodMode ? 0 : 100; bool AlreadyDead2 = chara.Hp <= 0; chara.GetDamage(damage); chara.LastDefence = DateTime.Now; Map.Broadcast(null, chara.GenerateStat(), ReceiverType.OnlySomeone, "", chara.CharacterId); Map.Broadcast($"su 3 {MapMonsterId} 1 {chara.CharacterId} 0 {Monster.BasicCooldown} 11 {Monster.BasicSkill} 0 0 {(chara.Hp > 0 ? 1 : 0)} { (int)(chara.Hp / chara.HPLoad() * 100) } {damage} 0 0"); if (chara.Hp <= 0 && !AlreadyDead2) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(chara.CharacterId); } } } } } else { if (IsMoving == true) { short maxDistance = 22; if (Path.Count() == 0 && targetSession != null && (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) < maxDistance)) { Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY, MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY, MapId = this.MapId }); } } if (Path.Count > 0 && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY, MapId = this.MapId }) > 1) { this.MapX = Path.ElementAt(0).X; this.MapY = Path.ElementAt(0).Y; Path.RemoveAt(0); } if (targetSession == null || MapId != targetSession.Character.MapId || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) > maxDistance)) { Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = firstX, Y = firstY, MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = firstX, Y = firstY, MapId = this.MapId }); } Target = -1; } else { if ((DateTime.Now - LastMove).TotalSeconds > 1.0 / Monster.Speed) { LastMove = DateTime.Now; Map.Broadcast($"mv 3 {this.MapMonsterId} {this.MapX} {this.MapY} {Monster.Speed}"); } } } } } }
public void Initialize() { if (MapInstance.MapInstanceType == MapInstanceType.BaseMapInstance && ServerManager.Instance.MapBossVNums.Contains(NpcVNum)) { MapCell randomCell = MapInstance.Map.GetRandomPosition(); if (randomCell != null) { if (MapInstance.Portals.Any(s => Map.GetDistance(new MapCell { X = s.SourceX, Y = s.SourceY }, new MapCell { X = randomCell.X, Y = randomCell.Y }) < 5)) { randomCell = MapInstance.Map.GetRandomPosition(); } MapX = randomCell.X; MapY = randomCell.Y; } } _random = new Random(MapNpcId); Npc = ServerManager.GetNpcMonster(NpcVNum); MaxHp = Npc.MaxHP; MaxMp = Npc.MaxMP; if (MapInstance?.MapInstanceType == MapInstanceType.TimeSpaceInstance) { if (IsProtected) { MaxHp *= 8; MaxMp *= 8; } } IsAlive = true; CurrentHp = MaxHp; CurrentMp = MaxMp; LastEffect = DateTime.Now; LastProtectedEffect = DateTime.Now; LastMove = DateTime.Now; LastSkill = DateTime.Now; IsHostile = Npc.IsHostile; ShouldRespawn = ShouldRespawn ?? true; FirstX = MapX; FirstY = MapY; EffectActivated = true; _movetime = ServerManager.RandomNumber(500, 3000); Path = new List <Node>(); Recipes = ServerManager.Instance.GetRecipesByMapNpcId(MapNpcId); Target = -1; Teleporters = ServerManager.Instance.GetTeleportersByNpcVNum(MapNpcId); Shop shop = ServerManager.Instance.GetShopByMapNpcId(MapNpcId); if (shop != null) { shop.Initialize(); Shop = shop; } Skills = new List <NpcMonsterSkill>(); foreach (NpcMonsterSkill ski in Npc.Skills) { Skills.Add(new NpcMonsterSkill { SkillVNum = ski.SkillVNum, Rate = ski.Rate }); } BattleEntity = new BattleEntity(this); if (AliveTime > 0) { Thread AliveTimeThread = new Thread(() => AliveTimeCheck()); AliveTimeThread.Start(); } if (NpcVNum == 1408) { OnDeathEvents.Add(new EventContainer(MapInstance, EventActionType.SPAWNMONSTER, new MonsterToSummon(621, new MapCell { X = MapX, Y = MapY }, null, move: true))); } if (NpcVNum == 1409) { OnDeathEvents.Add(new EventContainer(MapInstance, EventActionType.SPAWNMONSTER, new MonsterToSummon(622, new MapCell { X = MapX, Y = MapY }, null, move: true))); } if (NpcVNum == 1410) { OnDeathEvents.Add(new EventContainer(MapInstance, EventActionType.SPAWNMONSTER, new MonsterToSummon(623, new MapCell { X = MapX, Y = MapY }, null, move: true))); } if (OnSpawnEvents.Any()) { OnSpawnEvents.ToList().ForEach(e => { EventHelper.Instance.RunEvent(e, npc: this); }); OnSpawnEvents.Clear(); } }
private void NpcLife() { double time = (DateTime.Now - LastEffect).TotalMilliseconds; if (time > EffectDelay) { if (IsMate || IsProtected) { MapInstance.Broadcast(GenerateEff(825), MapX, MapY); } if (Effect > 0 && EffectActivated) { MapInstance.Broadcast(GenerateEff(Effect), MapX, MapY); } LastEffect = DateTime.Now; } time = (DateTime.Now - LastMove).TotalMilliseconds; if (IsMoving && Npc.Speed > 0 && time > _movetime) { _movetime = ServerManager.Instance.RandomNumber(500, 3000); byte point = (byte)ServerManager.Instance.RandomNumber(2, 4); byte fpoint = (byte)ServerManager.Instance.RandomNumber(0, 2); byte xpoint = (byte)ServerManager.Instance.RandomNumber(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = FirstX; short mapY = FirstY; if (MapInstance.Map.GetFreePosition(ref mapX, ref mapY, xpoint, ypoint)) { double value = (xpoint + ypoint) / (double)(2 * Npc.Speed); Observable.Timer(TimeSpan.FromMilliseconds(1000 * value)) .Subscribe( x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddSeconds(value); MapInstance.Broadcast(new BroadcastPacket(null, GenerateMv2(), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); } } if (Target == -1) { if (IsHostile && Shop == null) { MapMonster monster = MapInstance.Monsters.FirstOrDefault(s => MapInstance == s.MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.MapX, Y = s.MapY }) < (Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange)); ClientSession session = MapInstance.Sessions.FirstOrDefault(s => MapInstance == s.Character.MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Npc.NoticeRange); if (monster != null && session != null) { Target = monster.MapMonsterId; } } } else if (Target != -1) { MapMonster monster = MapInstance.Monsters.FirstOrDefault(s => s.MapMonsterId == Target); if (monster == null || monster.CurrentHp < 1) { Target = -1; return; } NpcMonsterSkill npcMonsterSkill = null; if (ServerManager.Instance.RandomNumber(0, 10) > 8) { npcMonsterSkill = Npc.Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } const short damage = 100; int distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = monster.MapX, Y = monster.MapY }); if (monster.CurrentHp > 0 && (npcMonsterSkill != null && distance < npcMonsterSkill.Skill.Range || distance <= Npc.BasicRange)) { if ((DateTime.Now - LastEffect).TotalMilliseconds >= 1000 + Npc.BasicCooldown * 200 && !Npc.Skills.Any() || npcMonsterSkill != null) { if (npcMonsterSkill != null) { npcMonsterSkill.LastSkillUse = DateTime.Now; MapInstance.Broadcast($"ct 2 {MapNpcId} 3 {Target} {npcMonsterSkill.Skill.CastAnimation} {npcMonsterSkill.Skill.CastEffect} {npcMonsterSkill.Skill.SkillVNum}"); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { MapInstance.Broadcast(GenerateEff(Effect)); } monster.CurrentHp -= damage; MapInstance.Broadcast(npcMonsterSkill != null ? $"su 2 {MapNpcId} 3 {Target} {npcMonsterSkill.SkillVNum} {npcMonsterSkill.Skill.Cooldown} {npcMonsterSkill.Skill.AttackAnimation} {npcMonsterSkill.Skill.Effect} 0 0 {(monster.CurrentHp > 0 ? 1 : 0)} {(int)((double)monster.CurrentHp / monster.Monster.MaxHP * 100)} {damage} 0 0" : $"su 2 {MapNpcId} 3 {Target} 0 {Npc.BasicCooldown} 11 {Npc.BasicSkill} 0 0 {(monster.CurrentHp > 0 ? 1 : 0)} {(int)((double)monster.CurrentHp / monster.Monster.MaxHP * 100)} {damage} 0 0"); LastEffect = DateTime.Now; if (monster.CurrentHp < 1) { RemoveTarget(); monster.IsAlive = false; monster.LastMove = DateTime.Now; monster.CurrentHp = 0; monster.CurrentMp = 0; monster.Death = DateTime.Now; Target = -1; } } } else { int maxdistance = Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange; if (IsMoving) { const short maxDistance = 5; if (!Path.Any() && distance > 1 && distance < maxDistance) { short xoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); short yoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); //go to monster Path = BestFirstSearch.FindPath(new GridPos { X = MapX, Y = MapY }, new GridPos { X = (short)(monster.MapX + xoffset), Y = (short)(monster.MapY + yoffset) }, MapInstance.Map.Grid); } if (DateTime.Now > LastMove && Npc.Speed > 0 && Path.Any()) { int maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; maxindex = maxindex < 1 ? 1 : maxindex; short mapX = (short)Path.ElementAt(maxindex - 1).X; short mapY = (short)Path.ElementAt(maxindex - 1).Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Npc.Speed; MapInstance.Broadcast(new BroadcastPacket(null, $"mv 2 {MapNpcId} {mapX} {mapY} {Npc.Speed}", ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))) .Subscribe( x => { MapX = mapX; MapY = mapY; }); Path.RemoveRange(0, maxindex > Path.Count ? Path.Count : maxindex); } if (Target != -1 && (MapId != monster.MapId || distance > maxDistance)) { RemoveTarget(); } } } } }
private void npcLife() { double time = (DateTime.Now - LastEffect).TotalMilliseconds; if (time > EffectDelay) { if (IsMate || IsProtected) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, 825), MapX, MapY); } if (Effect > 0 && EffectActivated) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, Effect), MapX, MapY); } LastEffect = DateTime.Now; } time = (DateTime.Now - LastMove).TotalMilliseconds; if (IsMoving && Npc.Speed > 0 && time > _movetime) { _movetime = ServerManager.RandomNumber(500, 3000); byte point = (byte)ServerManager.RandomNumber(2, 4); byte fpoint = (byte)ServerManager.RandomNumber(0, 2); byte xpoint = (byte)ServerManager.RandomNumber(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = FirstX; short mapY = FirstY; if (MapInstance.Map.GetFreePosition(ref mapX, ref mapY, xpoint, ypoint)) { double value = (xpoint + ypoint) / (double)(2 * Npc.Speed); Observable.Timer(TimeSpan.FromMilliseconds(1000 * value)).Subscribe(x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddSeconds(value); MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Npc, MapNpcId, MapX, MapY, Npc.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); } } if (Target == -1) { if (IsHostile && Shop == null) { MapMonster monster = MapInstance.Monsters.Find(s => MapInstance == s.MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.MapX, Y = s.MapY }) < (Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange)); ClientSession session = MapInstance.Sessions.FirstOrDefault(s => MapInstance == s.Character.MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Npc.NoticeRange); if (monster != null && session != null) { Target = monster.MapMonsterId; } } } else if (Target != -1) { MapMonster monster = MapInstance.Monsters.Find(s => s.MapMonsterId == Target); if (monster == null || monster.CurrentHp < 1) { Target = -1; return; } NpcMonsterSkill npcMonsterSkill = null; if (ServerManager.RandomNumber(0, 10) > 8) { npcMonsterSkill = Npc.Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } int hitmode = 0; bool onyxWings = false; int damage = DamageHelper.Instance.CalculateDamage(new BattleEntity(this), new BattleEntity(monster), npcMonsterSkill?.Skill, ref hitmode, ref onyxWings); if (monster.Monster.BCards.Find(s => s.Type == (byte)CardType.LightAndShadow && s.SubType == (byte)AdditionalTypes.LightAndShadow.InflictDamageToMP) is BCard card) { int reduce = damage / 100 * card.FirstData; if (monster.CurrentMp < reduce) { monster.CurrentMp = 0; } else { monster.CurrentMp -= reduce; } } int distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = monster.MapX, Y = monster.MapY }); if (monster.CurrentHp > 0 && ((npcMonsterSkill != null && distance < npcMonsterSkill.Skill.Range) || distance <= Npc.BasicRange)) { if (((DateTime.Now - LastEffect).TotalMilliseconds >= 1000 + (Npc.BasicCooldown * 200) && Npc.Skills.Count == 0) || npcMonsterSkill != null) { if (npcMonsterSkill != null) { npcMonsterSkill.LastSkillUse = DateTime.Now; MapInstance.Broadcast(StaticPacketHelper.CastOnTarget(UserType.Npc, MapNpcId, 3, Target, npcMonsterSkill.Skill.CastAnimation, npcMonsterSkill.Skill.CastEffect, npcMonsterSkill.Skill.SkillVNum)); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Npc, MapNpcId, Effect)); } monster.CurrentHp -= damage; MapInstance.Broadcast(npcMonsterSkill != null ? StaticPacketHelper.SkillUsed(UserType.Npc, MapNpcId, 3, Target, npcMonsterSkill.SkillVNum, npcMonsterSkill.Skill.Cooldown, npcMonsterSkill.Skill.AttackAnimation, npcMonsterSkill.Skill.Effect, 0, 0, monster.CurrentHp > 0, (int)((float)monster.CurrentHp / (float)monster.Monster.MaxHP * 100), damage, hitmode, 0) : StaticPacketHelper.SkillUsed(UserType.Npc, MapNpcId, 3, Target, 0, Npc.BasicCooldown, 11, Npc.BasicSkill, 0, 0, monster.CurrentHp > 0, (int)((float)monster.CurrentHp / (float)monster.Monster.MaxHP * 100), damage, hitmode, 0)); LastEffect = DateTime.Now; if (monster.CurrentHp < 1) { RemoveTarget(); monster.IsAlive = false; monster.LastMove = DateTime.Now; monster.CurrentHp = 0; monster.CurrentMp = 0; monster.Death = DateTime.Now; Target = -1; } } } else { int maxdistance = Npc.NoticeRange > 5 ? Npc.NoticeRange / 2 : Npc.NoticeRange; if (IsMoving) { const short maxDistance = 5; int maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; if (maxindex < 1) { maxindex = 1; } if ((Path.Count == 0 && distance >= 1 && distance < maxDistance) || (Path.Count >= maxindex && maxindex > 0 && Path[maxindex - 1] == null)) { short xoffset = (short)ServerManager.RandomNumber(-1, 1); short yoffset = (short)ServerManager.RandomNumber(-1, 1); //go to monster Path = BestFirstSearch.FindPathJagged(new GridPos { X = MapX, Y = MapY }, new GridPos { X = (short)(monster.MapX + xoffset), Y = (short)(monster.MapY + yoffset) }, MapInstance.Map.JaggedGrid); maxindex = Path.Count > Npc.Speed / 2 && Npc.Speed > 1 ? Npc.Speed / 2 : Path.Count; } if (DateTime.Now > LastMove && Npc.Speed > 0 && Path.Count > 0) { short mapX = Path[maxindex - 1].X; short mapY = Path[maxindex - 1].Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Npc.Speed; MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Npc, MapNpcId, MapX, MapY, Npc.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))).Subscribe(x => { MapX = mapX; MapY = mapY; }); Path.RemoveRange(0, maxindex); } if (Target != -1 && (MapId != monster.MapId || distance > maxDistance)) { RemoveTarget(); } } } } }
private void move() { // Normal Move Mode if (Monster == null || !IsAlive || _noMove) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path == null) { Path = new List <Node>(); } if (Path.Count > 0) // move back to initial position after following target { int timetowalk = 2000 / Monster.Speed; if (time > timetowalk) { int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; if (Path[maxindex - 1] == null) { return; } short mapX = Path[maxindex - 1].X; short mapY = Path[maxindex - 1].Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Monster.Speed; LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds(timetowalk)).Subscribe(x => { MapX = mapX; MapY = mapY; MoveEvent?.Events.ForEach(e => EventHelper.Instance.RunEvent(e, monster: this)); }); Path.RemoveRange(0, maxindex > Path.Count ? Path.Count : maxindex); MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Monster, MapMonsterId, MapX, MapY, Monster.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); return; } } else if (time > _movetime) { short mapX = FirstX, mapY = FirstY; if (MapInstance.Map?.GetFreePosition(ref mapX, ref mapY, (byte)ServerManager.Instance.RandomNumber(0, 2), (byte)_random.Next(0, 2)) ?? false) { int distance = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }); double value = 1000d * distance / (2 * Monster.Speed); Observable.Timer(TimeSpan.FromMilliseconds(value)).Subscribe(x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddMilliseconds(value); MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Monster, MapMonsterId, MapX, MapY, Monster.Speed)), ReceiverType.All)); } } } HostilityTarget(); }
internal void HostilityTarget() { if (IsHostile && Target == -1) { Character character = ServerManager.Instance.Sessions.FirstOrDefault(s => s?.Character != null && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapInstance == MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Monster.NoticeRange)?.Character; if (character != null) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon) { character.Session.SendPacket(GenerateEff(5000)); } } } }
/// <summary> /// Calculates the damage attacker inflicts defender /// </summary> /// <param name="attacker">The attacking Entity</param> /// <param name="defender">The defending Entity</param> /// <param name="skill">The used Skill</param> /// <param name="hitMode">reference to HitMode</param> /// <param name="onyxWings"></param> /// <returns>Damage</returns> public int CalculateDamage(BattleEntity attacker, BattleEntity defender, Skill skill, ref int hitMode, ref bool onyxWings) { int[] GetAttackerBenefitingBuffs(CardType type, byte subtype) { int value1 = 0; int value2 = 0; int value3 = 0; int[] tmp; int temp = 0; tmp = getBuff(attacker.Level, attacker.Buffs, attacker.BCards, type, subtype, BuffType.Good, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; tmp = getBuff(attacker.Level, attacker.Buffs, attacker.BCards, type, subtype, BuffType.Neutral, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; tmp = getBuff(defender.Level, defender.Buffs, defender.BCards, type, subtype, BuffType.Bad, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; return(new int[] { value1, value2, value3, temp }); } int[] GetDefenderBenefitingBuffs(CardType type, byte subtype) { int value1 = 0; int value2 = 0; int value3 = 0; int[] tmp; int temp = 0; tmp = getBuff(defender.Level, defender.Buffs, defender.BCards, type, subtype, BuffType.Good, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; tmp = getBuff(defender.Level, defender.Buffs, defender.BCards, type, subtype, BuffType.Neutral, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; tmp = getBuff(attacker.Level, attacker.Buffs, attacker.BCards, type, subtype, BuffType.Bad, ref temp); value1 += tmp[0]; value2 += tmp[1]; value3 += tmp[2]; return(new int[] { value1, value2, value3, temp }); } int GetShellWeaponEffectValue(ShellWeaponEffectType effectType) { return(attacker.ShellWeaponEffects?.Where(s => s.Effect == (byte)effectType)?.FirstOrDefault()?.Value ?? 0); } int GetShellArmorEffectValue(ShellArmorEffectType effectType) { return(defender.ShellArmorEffects?.Where(s => s.Effect == (byte)effectType)?.FirstOrDefault()?.Value ?? 0); } if (skill != null) { attacker.BCards.AddRange(skill.BCards); } #region Basic Buff Initialisation attacker.Morale += GetAttackerBenefitingBuffs(CardType.Morale, (byte)AdditionalTypes.Morale.MoraleIncreased)[0]; attacker.Morale += GetDefenderBenefitingBuffs(CardType.Morale, (byte)AdditionalTypes.Morale.MoraleDecreased)[0]; defender.Morale += GetDefenderBenefitingBuffs(CardType.Morale, (byte)AdditionalTypes.Morale.MoraleIncreased)[0]; defender.Morale += GetAttackerBenefitingBuffs(CardType.Morale, (byte)AdditionalTypes.Morale.MoraleDecreased)[0]; attacker.AttackUpgrade += (short)GetAttackerBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.AttackLevelIncreased)[0]; attacker.AttackUpgrade += (short)GetDefenderBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.AttackLevelDecreased)[0]; defender.DefenseUpgrade += (short)GetDefenderBenefitingBuffs(CardType.Defence, (byte)AdditionalTypes.Defence.DefenceLevelIncreased)[0]; defender.DefenseUpgrade += (short)GetAttackerBenefitingBuffs(CardType.Defence, (byte)AdditionalTypes.Defence.DefenceLevelDecreased)[0]; int[] attackerpercentdamage = GetDefenderBenefitingBuffs(CardType.RecoveryAndDamagePercent, 11); int[] defenderpercentdefense = GetDefenderBenefitingBuffs(CardType.RecoveryAndDamagePercent, 2); if (attackerpercentdamage[3] != 0) { return(defender.HPMax / 100 * attackerpercentdamage[2]); } if (defenderpercentdefense[3] != 0) { return(defender.HPMax / 100 * Math.Abs(defenderpercentdefense[2])); } /* * * Percentage Boost categories: * 1.: Adds to Total Damage * 2.: Adds to Normal Damage * 3.: Adds to Base Damage * 4.: Adds to Defense * 5.: Adds to Element * * Buff Effects get added, whereas * Shell Effects get multiplied afterwards. * * Simplified Example on Defense (Same for Attack): * - 1k Defense * - Costume(+5% Defense) * - Defense Potion(+20% Defense) * - S-Defense Shell with 20% Boost * * Calculation: * 1000 * 1.25 * 1.2 = 1500 * Def Buff Shell Total * * Keep in Mind that after each step, one has * to round the current value down if necessary * * Static Boost categories: * 1.: Adds to Total Damage * 2.: Adds to Normal Damage * 3.: Adds to Base Damage * 4.: Adds to Defense * 5.: Adds to Element * */ #region Definitions double boostCategory1 = 1; double boostCategory2 = 1; double boostCategory3 = 1; double boostCategory4 = 1; double boostCategory5 = 1; double shellBoostCategory1 = 1; double shellBoostCategory2 = 1; double shellBoostCategory3 = 1; double shellBoostCategory4 = 1; double shellBoostCategory5 = 1; int staticBoostCategory1 = 0; int staticBoostCategory2 = 0; int staticBoostCategory3 = 0; int staticBoostCategory4 = 0; int staticBoostCategory5 = 0; #endregion #region Type 1 #region Static // None for now #endregion #region Boost boostCategory1 += GetAttackerBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.DamageIncreased)[0] / 100D; boostCategory1 += GetAttackerBenefitingBuffs(CardType.Item, (byte)AdditionalTypes.Item.AttackIncreased)[0] / 100D; boostCategory1 += GetDefenderBenefitingBuffs(CardType.Item, (byte)AdditionalTypes.Item.DefenceIncreased)[0] / 100D; shellBoostCategory1 += GetShellWeaponEffectValue(ShellWeaponEffectType.PercentageTotalDamage) / 100D; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { boostCategory1 += GetAttackerBenefitingBuffs(CardType.SpecialisationBuffResistance, (byte)AdditionalTypes.SpecialisationBuffResistance.IncreaseDamageInPVP)[0] / 100D; boostCategory1 += GetAttackerBenefitingBuffs(CardType.LeonaPassiveSkill, (byte)AdditionalTypes.LeonaPassiveSkill.AttackIncreasedInPVP)[0] / 100D; shellBoostCategory1 += GetShellWeaponEffectValue(ShellWeaponEffectType.PercentageDamageInPVP) / 100D; } #endregion #endregion #region Type 2 #region Static // None for now #endregion #region Boost boostCategory2 += GetDefenderBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.DamageDecreased)[0] / 100D; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { boostCategory2 += GetDefenderBenefitingBuffs(CardType.SpecialisationBuffResistance, (byte)AdditionalTypes.SpecialisationBuffResistance.DecreaseDamageInPVP)[0] / 100D; boostCategory2 += GetDefenderBenefitingBuffs(CardType.LeonaPassiveSkill, (byte)AdditionalTypes.LeonaPassiveSkill.AttackDecreasedInPVP)[0] / 100D; } #endregion #endregion #region Type 3 #region Static staticBoostCategory3 += GetAttackerBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.AllAttacksIncreased)[0]; staticBoostCategory3 += GetDefenderBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.AllAttacksDecreased)[0]; staticBoostCategory3 += GetShellWeaponEffectValue(ShellWeaponEffectType.DamageImproved); #endregion #region Soft-Damage int[] soft = GetAttackerBenefitingBuffs(CardType.IncreaseDamage, (byte)AdditionalTypes.IncreaseDamage.IncreasingPropability); int[] skin = GetAttackerBenefitingBuffs(CardType.EffectSummon, (byte)AdditionalTypes.EffectSummon.DamageBoostOnHigherLvl); if (attacker.Level < defender.Level) { soft[0] += skin[0]; soft[1] += skin[1]; } if (ServerManager.Instance.RandomNumber() < soft[0]) { boostCategory3 += soft[1] / 100D; if (attacker.EntityType.Equals(EntityType.Player) && attacker.Session != null) { attacker.Session.CurrentMapInstance?.Broadcast(StaticPacketHelper.GenerateEff(UserType.Player, attacker.Session.Character.CharacterId, 15)); } } #endregion #endregion #region Type 4 #region Static staticBoostCategory4 += GetDefenderBenefitingBuffs(CardType.Defence, (byte)AdditionalTypes.Defence.AllIncreased)[0]; staticBoostCategory4 += GetAttackerBenefitingBuffs(CardType.Defence, (byte)AdditionalTypes.Defence.AllDecreased)[0]; #endregion #region Boost boostCategory4 += GetDefenderBenefitingBuffs(CardType.DodgeAndDefencePercent, (byte)AdditionalTypes.DodgeAndDefencePercent.DefenceIncreased)[0] / 100D; boostCategory4 += GetAttackerBenefitingBuffs(CardType.DodgeAndDefencePercent, (byte)AdditionalTypes.DodgeAndDefencePercent.DefenceReduced)[0] / 100D; shellBoostCategory4 += GetShellArmorEffectValue(ShellArmorEffectType.PercentageTotalDefence) / 100D; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { boostCategory4 += GetDefenderBenefitingBuffs(CardType.LeonaPassiveSkill, (byte)AdditionalTypes.LeonaPassiveSkill.DefenceIncreasedInPVP)[0] / 100D; boostCategory4 += GetAttackerBenefitingBuffs(CardType.LeonaPassiveSkill, (byte)AdditionalTypes.LeonaPassiveSkill.DefenceDecreasedInPVP)[0] / 100D; shellBoostCategory4 -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesPercentageEnemyDefenceInPVP) / 100D; shellBoostCategory4 += GetShellArmorEffectValue(ShellArmorEffectType.PercentageAllPVPDefence) / 100D; } int[] def = GetAttackerBenefitingBuffs(CardType.Block, (byte)AdditionalTypes.Block.ChanceAllIncreased); if (ServerManager.Instance.RandomNumber() < def[0]) { boostCategory3 += def[1] / 100D; } #endregion #endregion #region Type 5 #region Static staticBoostCategory5 += GetAttackerBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.AllIncreased)[0]; staticBoostCategory5 += GetDefenderBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.AllDecreased)[0]; staticBoostCategory5 += GetShellWeaponEffectValue(ShellWeaponEffectType.IncreasedElementalProperties); #endregion #region Boost // Nothing for now #endregion #endregion #region All Type Class Dependant int[] def2 = null; switch (attacker.AttackType) { case AttackType.Melee: def2 = GetAttackerBenefitingBuffs(CardType.Block, (byte)AdditionalTypes.Block.ChanceMeleeIncreased); boostCategory1 += GetAttackerBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.MeleeIncreased)[0] / 100D; boostCategory1 += GetDefenderBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.MeleeDecreased)[0] / 100D; staticBoostCategory3 += GetAttackerBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksIncreased)[0]; staticBoostCategory3 += GetDefenderBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksDecreased)[0]; staticBoostCategory4 += GetShellArmorEffectValue(ShellArmorEffectType.CloseDefence); break; case AttackType.Range: def2 = GetAttackerBenefitingBuffs(CardType.Block, (byte)AdditionalTypes.Block.ChanceRangedIncreased); boostCategory1 += GetAttackerBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.RangedIncreased)[0] / 100D; boostCategory1 += GetDefenderBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.RangedDecreased)[0] / 100D; staticBoostCategory3 += GetAttackerBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksIncreased)[0]; staticBoostCategory3 += GetDefenderBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksDecreased)[0]; staticBoostCategory4 += GetShellArmorEffectValue(ShellArmorEffectType.DistanceDefence); break; case AttackType.Magical: def2 = GetAttackerBenefitingBuffs(CardType.Block, (byte)AdditionalTypes.Block.ChanceRangedIncreased); boostCategory1 += GetAttackerBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.MagicalIncreased)[0] / 100D; boostCategory1 += GetDefenderBenefitingBuffs(CardType.Damage, (byte)AdditionalTypes.Damage.MagicalDecreased)[0] / 100D; staticBoostCategory3 += GetAttackerBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksIncreased)[0]; staticBoostCategory3 += GetDefenderBenefitingBuffs(CardType.AttackPower, (byte)AdditionalTypes.AttackPower.MeleeAttacksDecreased)[0]; staticBoostCategory4 += GetShellArmorEffectValue(ShellArmorEffectType.MagicDefence); break; } def[0] += def2[0]; def[1] += def2[1]; #endregion #region Softdef finishing if (ServerManager.Instance.RandomNumber() < def[0]) { boostCategory3 += def[1] / 100D; } #endregion #region Element Dependant switch (attacker.Element) { case 1: defender.FireResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllIncreased)[0]; defender.FireResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllDecreased)[0]; defender.FireResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.FireIncreased)[0]; defender.FireResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.FireDecreased)[0]; defender.FireResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllIncreased)[0]; defender.FireResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllDecreased)[0]; defender.FireResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.FireIncreased)[0]; defender.FireResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.FireDecreased)[0]; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyFireResistanceInPVP); defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyAllResistancesInPVP); } defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedFireResistence); defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedAllResistence); staticBoostCategory5 += GetShellWeaponEffectValue(ShellWeaponEffectType.IncreasedFireProperties); boostCategory5 += GetAttackerBenefitingBuffs(CardType.IncreaseDamage, (byte)AdditionalTypes.IncreaseDamage.FireIncreased)[0] / 100D; staticBoostCategory5 += GetAttackerBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.FireIncreased)[0]; staticBoostCategory5 += GetDefenderBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.FireDecreased)[0]; break; case 2: defender.WaterResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllIncreased)[0]; defender.WaterResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllDecreased)[0]; defender.WaterResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.WaterIncreased)[0]; defender.WaterResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.WaterDecreased)[0]; defender.WaterResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllIncreased)[0]; defender.WaterResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllDecreased)[0]; defender.WaterResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.WaterIncreased)[0]; defender.WaterResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.WaterDecreased)[0]; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyWaterResistanceInPVP); defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyAllResistancesInPVP); } defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedWaterResistence); defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedAllResistence); staticBoostCategory5 += GetShellWeaponEffectValue(ShellWeaponEffectType.IncreasedWaterProperties); boostCategory5 += GetAttackerBenefitingBuffs(CardType.IncreaseDamage, (byte)AdditionalTypes.IncreaseDamage.WaterIncreased)[0] / 100D; staticBoostCategory5 += GetAttackerBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.WaterIncreased)[0]; staticBoostCategory5 += GetDefenderBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.WaterDecreased)[0]; break; case 3: defender.LightResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllIncreased)[0]; defender.LightResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllDecreased)[0]; defender.LightResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.LightIncreased)[0]; defender.LightResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.LightDecreased)[0]; defender.LightResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllIncreased)[0]; defender.LightResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllDecreased)[0]; defender.LightResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.LightIncreased)[0]; defender.LightResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.LightDecreased)[0]; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyLightResistanceInPVP); defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyAllResistancesInPVP); } defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedLightResistence); defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedAllResistence); staticBoostCategory5 += GetShellWeaponEffectValue(ShellWeaponEffectType.IncreasedLightProperties); boostCategory5 += GetAttackerBenefitingBuffs(CardType.IncreaseDamage, (byte)AdditionalTypes.IncreaseDamage.LightIncreased)[0] / 100D; staticBoostCategory5 += GetAttackerBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.LightIncreased)[0]; staticBoostCategory5 += GetDefenderBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.Light5Decreased)[0]; break; case 4: defender.ShadowResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllIncreased)[0]; defender.ShadowResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.AllDecreased)[0]; defender.ShadowResistance += GetDefenderBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.DarkIncreased)[0]; defender.ShadowResistance += GetAttackerBenefitingBuffs(CardType.ElementResistance, (byte)AdditionalTypes.ElementResistance.DarkDecreased)[0]; defender.ShadowResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllIncreased)[0]; defender.ShadowResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.AllDecreased)[0]; defender.ShadowResistance += GetDefenderBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.DarkIncreased)[0]; defender.ShadowResistance += GetAttackerBenefitingBuffs(CardType.EnemyElementResistance, (byte)AdditionalTypes.EnemyElementResistance.DarkDecreased)[0]; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyDarkResistanceInPVP); defender.FireResistance -= GetShellWeaponEffectValue(ShellWeaponEffectType.ReducesEnemyAllResistancesInPVP); } defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedDarkResistence); defender.FireResistance += GetShellArmorEffectValue(ShellArmorEffectType.IncreasedAllResistence); staticBoostCategory5 += GetShellWeaponEffectValue(ShellWeaponEffectType.IncreasedDarkProperties); boostCategory5 += GetAttackerBenefitingBuffs(CardType.IncreaseDamage, (byte)AdditionalTypes.IncreaseDamage.DarkIncreased)[0] / 100D; staticBoostCategory5 += GetAttackerBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.DarkIncreased)[0]; staticBoostCategory5 += GetDefenderBenefitingBuffs(CardType.Element, (byte)AdditionalTypes.Element.DarkDecreased)[0]; break; } #endregion #endregion #region Attack Type Related Variables switch (attacker.AttackType) { case AttackType.Melee: defender.Defense = defender.MeleeDefense; defender.ArmorDefense = defender.ArmorMeleeDefense; defender.Dodge = defender.MeleeDefenseDodge; break; case AttackType.Range: defender.Defense = defender.RangeDefense; defender.ArmorDefense = defender.ArmorRangeDefense; defender.Dodge = defender.RangeDefenseDodge; break; case AttackType.Magical: defender.Defense = defender.MagicalDefense; defender.ArmorDefense = defender.ArmorMagicalDefense; break; } #endregion #region Too Near Range Attack Penalty (boostCategory2) if (attacker.AttackType == AttackType.Range && Map.GetDistance(new MapCell { X = attacker.PositionX, Y = attacker.PositionY }, new MapCell { X = defender.PositionX, Y = defender.PositionY }) < 4) { boostCategory2 -= 0.3; } #endregion #region Morale and Dodge attacker.Morale -= defender.Morale; double chance = 0; if (attacker.AttackType != AttackType.Magical) { int hitrate = attacker.Hitrate + attacker.Morale; double multiplier = defender.Dodge / (hitrate > 1 ? hitrate : 1); if (multiplier > 5) { multiplier = 5; } chance = (-0.25 * Math.Pow(multiplier, 3)) - (0.57 * Math.Pow(multiplier, 2)) + (25.3 * multiplier) - 1.41; if (chance <= 1) { chance = 1; } //if (GetBuff(CardType.Buff, (byte)AdditionalTypes.DodgeAndDefencePercent.)[0] != 0) TODO: Eagle Eyes AND Other Fixed Hitrates //{ // chance = 10; //} } int bonus = 0; if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { switch (attacker.AttackType) { case AttackType.Melee: bonus += GetShellArmorEffectValue(ShellArmorEffectType.CloseDefenceDodgeInPVP); break; case AttackType.Range: bonus += GetShellArmorEffectValue(ShellArmorEffectType.DistanceDefenceDodgeInPVP); break; case AttackType.Magical: bonus += GetShellArmorEffectValue(ShellArmorEffectType.IgnoreMagicDamage); break; } bonus += GetShellArmorEffectValue(ShellArmorEffectType.DodgeAllAttacksInPVP); } if (!defender.Invincible && ServerManager.Instance.RandomNumber() - bonus < chance) { hitMode = 1; return(0); } #endregion #region Base Damage int baseDamage = ServerManager.Instance.RandomNumber(attacker.DamageMinimum, attacker.DamageMaximum + 1); int weaponDamage = ServerManager.Instance.RandomNumber(attacker.WeaponDamageMinimum, attacker.WeaponDamageMaximum + 1); #region Attack Level Calculation int[] atklvlfix = GetDefenderBenefitingBuffs(CardType.CalculatingLevel, (byte)AdditionalTypes.CalculatingLevel.CalculatedAttackLevel); int[] deflvlfix = GetAttackerBenefitingBuffs(CardType.CalculatingLevel, (byte)AdditionalTypes.CalculatingLevel.CalculatedDefenceLevel); if (atklvlfix[3] != 0) { attacker.AttackUpgrade = (short)atklvlfix[0]; } if (deflvlfix[3] != 0) { attacker.DefenseUpgrade = (short)deflvlfix[0]; } attacker.AttackUpgrade -= defender.DefenseUpgrade; if (attacker.AttackUpgrade < -10) { attacker.AttackUpgrade = -10; } else if (attacker.AttackUpgrade > ServerManager.Instance.Configuration.MaxUpgrade) { attacker.AttackUpgrade = ServerManager.Instance.Configuration.MaxUpgrade; } switch (attacker.AttackUpgrade) { case 0: weaponDamage += 0; break; case 1: weaponDamage += (int)(weaponDamage * 0.1); break; case 2: weaponDamage += (int)(weaponDamage * 0.15); break; case 3: weaponDamage += (int)(weaponDamage * 0.22); break; case 4: weaponDamage += (int)(weaponDamage * 0.32); break; case 5: weaponDamage += (int)(weaponDamage * 0.43); break; case 6: weaponDamage += (int)(weaponDamage * 0.54); break; case 7: weaponDamage += (int)(weaponDamage * 0.65); break; case 8: weaponDamage += (int)(weaponDamage * 0.9); break; case 9: weaponDamage += (int)(weaponDamage * 1.2); break; case 10: weaponDamage += (int)(weaponDamage * 2); break; default: if (attacker.AttackUpgrade > 0) { weaponDamage *= attacker.AttackUpgrade / 5; } break; } #endregion baseDamage = (int)((int)((baseDamage + staticBoostCategory3 + weaponDamage + 15) * boostCategory3) * shellBoostCategory3); #endregion #region Defense switch (attacker.AttackUpgrade) { default: if (attacker.AttackUpgrade < 0) { defender.ArmorDefense += defender.ArmorDefense / 5; } break; case -10: defender.ArmorDefense += (int)(defender.ArmorDefense * 2); break; case -9: defender.ArmorDefense += (int)(defender.ArmorDefense * 1.2); break; case -8: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.9); break; case -7: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.65); break; case -6: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.54); break; case -5: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.43); break; case -4: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.32); break; case -3: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.22); break; case -2: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.15); break; case -1: defender.ArmorDefense += (int)(defender.ArmorDefense * 0.1); break; case 0: defender.ArmorDefense += 0; break; } int defense = (int)((int)((defender.Defense + defender.ArmorDefense + staticBoostCategory4) * boostCategory4) * shellBoostCategory4); if (GetAttackerBenefitingBuffs(CardType.SpecialDefence, (byte)AdditionalTypes.SpecialDefence.AllDefenceNullified)[3] != 0 || (GetAttackerBenefitingBuffs(CardType.SpecialDefence, (byte)AdditionalTypes.SpecialDefence.MeleeDefenceNullified)[3] != 0 && attacker.AttackType.Equals(AttackType.Melee)) || (GetAttackerBenefitingBuffs(CardType.SpecialDefence, (byte)AdditionalTypes.SpecialDefence.RangedDefenceNullified)[3] != 0 && attacker.AttackType.Equals(AttackType.Range)) || (GetAttackerBenefitingBuffs(CardType.SpecialDefence, (byte)AdditionalTypes.SpecialDefence.MagicDefenceNullified)[3] != 0 && attacker.AttackType.Equals(AttackType.Magical))) { defense = 0; } #endregion #region Normal Damage int normalDamage = (int)((int)((baseDamage + staticBoostCategory2 - defense) * boostCategory2) * shellBoostCategory2); if (normalDamage < 0) { normalDamage = 0; } #endregion #region Crit Damage attacker.CritChance += GetShellWeaponEffectValue(ShellWeaponEffectType.CriticalChance); attacker.CritChance -= GetShellArmorEffectValue(ShellArmorEffectType.ReducedCritChanceRecive); attacker.CritRate += GetShellWeaponEffectValue(ShellWeaponEffectType.CriticalDamage); if (defender.CellonOptions != null) { attacker.CritRate -= defender.CellonOptions.Where(s => s.Type == CellonOptionType.CritReduce).Sum(s => s.Value); } if (ServerManager.Instance.RandomNumber() < attacker.CritChance && attacker.AttackType != AttackType.Magical) { double multiplier = attacker.CritRate / 100D; if (multiplier > 3) { multiplier = 3; } normalDamage += (int)(normalDamage * multiplier); hitMode = 3; } #endregion #region Fairy Damage int fairyDamage = (int)((baseDamage + 100) * attacker.ElementRate / 100D); #endregion #region Elemental Damage Advantage double elementalBoost = 0; switch (attacker.Element) { case 0: break; case 1: defender.Resistance = defender.FireResistance; switch (defender.Element) { case 0: elementalBoost = 1.3; // Damage vs no element break; case 1: elementalBoost = 1; // Damage vs fire break; case 2: elementalBoost = 2; // Damage vs water break; case 3: elementalBoost = 1; // Damage vs light break; case 4: elementalBoost = 1.5; // Damage vs darkness break; } break; case 2: defender.Resistance = defender.WaterResistance; switch (defender.Element) { case 0: elementalBoost = 1.3; break; case 1: elementalBoost = 2; break; case 2: elementalBoost = 1; break; case 3: elementalBoost = 1.5; break; case 4: elementalBoost = 1; break; } break; case 3: defender.Resistance = defender.LightResistance; switch (defender.Element) { case 0: elementalBoost = 1.3; break; case 1: elementalBoost = 1.5; break; case 2: case 3: elementalBoost = 1; break; case 4: elementalBoost = 3; break; } break; case 4: defender.Resistance = defender.ShadowResistance; switch (defender.Element) { case 0: elementalBoost = 1.3; break; case 1: elementalBoost = 1; break; case 2: elementalBoost = 1.5; break; case 3: elementalBoost = 3; break; case 4: elementalBoost = 1; break; } break; } if (skill?.Element == 0 || (skill?.Element != attacker.Element && attacker.EntityType == EntityType.Player)) { elementalBoost = 0; } #endregion #region Elemental Damage int elementalDamage = (int)((int)((int)((int)((staticBoostCategory5 + fairyDamage) * elementalBoost) * (1 - (defender.Resistance / 100D))) * boostCategory5) * shellBoostCategory5); if (elementalDamage < 0) { elementalDamage = 0; } #endregion #region Total Damage int totalDamage = (int)((int)((normalDamage + elementalDamage + attacker.Morale + staticBoostCategory1) * boostCategory1) * shellBoostCategory1); if ((attacker.EntityType == EntityType.Player || attacker.EntityType == EntityType.Mate) && (defender.EntityType == EntityType.Player || defender.EntityType == EntityType.Mate)) { totalDamage /= 2; } if (defender.EntityType == EntityType.Monster || defender.EntityType == EntityType.NPC) { totalDamage -= getMonsterDamageBonus(defender.Level); } if (totalDamage < 5) { totalDamage = ServerManager.Instance.RandomNumber(1, 6); } if (attacker.EntityType == EntityType.Monster || attacker.EntityType == EntityType.NPC) { totalDamage += getMonsterDamageBonus(attacker.Level); } #endregion #region Onyx Wings int[] onyxBuff = GetAttackerBenefitingBuffs(CardType.StealBuff, (byte)AdditionalTypes.StealBuff.ChanceSummonOnyxDragon); if (onyxBuff[0] > ServerManager.Instance.RandomNumber()) { onyxWings = true; } #endregion return(totalDamage); }
private void Move() { // Normal Move Mode if (Monster == null || !IsAlive) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path.Any(s => s != null)) // move back to initial position after following target { int timetowalk = 2000 / Monster.Speed; if (time > timetowalk) { int mapX = Path.ElementAt(0).x; int mapY = Path.ElementAt(0).y; Path.RemoveAt(0); Observable.Timer(TimeSpan.FromMilliseconds(timetowalk)) .Subscribe( x => { MapX = (short)mapX; MapY = (short)mapY; }); LastMove = DateTime.Now; Map.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); return; } } else if (time > _movetime) { short mapX = FirstX, mapY = FirstY; if (Map?.GetFreePosition(ref mapX, ref mapY, (byte)ServerManager.RandomNumber(0, 2), (byte)_random.Next(0, 2)) ?? false) { int distance = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }); Observable.Timer(TimeSpan.FromMilliseconds(1000 * distance / (2 * Monster.Speed))) .Subscribe( x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddMilliseconds(1000 * distance / (2 * Monster.Speed)); Map.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.All)); } } } if (Monster.IsHostile) { Character character = ServerManager.Instance.Sessions.FirstOrDefault(s => s?.Character != null && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapId == MapId && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.MapX, Y = s.Character.MapY }) < Monster.NoticeRange)?.Character; if (character != null) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon) { character.Session.SendPacket(GenerateEff(5000)); } } } }
/// <summary> /// Handle any kind of Monster interaction /// </summary> internal void MonsterLife() { if (Monster == null) { return; } // handle hit queue HitRequest hitRequest; while (HitQueue.TryDequeue(out hitRequest)) { if (IsAlive) { int hitmode = 0; // calculate damage int damage = hitRequest.Session.Character.GenerateDamage(this, hitRequest.Skill, ref hitmode); switch (hitRequest.TargetHitType) { case TargetHitType.SingleTargetHit: { // Target Hit Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.SingleTargetHitCombo: { // Taget Hit Combo Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.SkillCombo.Animation} {hitRequest.SkillCombo.Effect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.SingleAOETargetHit: { // Target Hit Single AOE switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } if (hitRequest.ShowTargetHitAnimation) { Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} 0 0 {(IsAlive ? 1 : 0)} {((int)((double)hitRequest.Session.Character.Hp / hitRequest.Session.Character.HPLoad()) * 100)} 0 0 {hitRequest.Skill.SkillType - 1}"); } Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.AOETargetHit: { // Target Hit AOE switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.ZoneHit: { // Zone HIT Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.MapX} {hitRequest.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} 5 {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.SpecialZoneHit: { // Special Zone hit Map?.Broadcast($"su 1 {hitRequest.Session.Character.CharacterId} 3 {MapMonsterId} {hitRequest.Skill.SkillVNum} {hitRequest.Skill.Cooldown} {hitRequest.Skill.AttackAnimation} {hitRequest.SkillEffect} {hitRequest.Session.Character.MapX} {hitRequest.Session.Character.MapY} {(IsAlive ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} 0 {hitRequest.Skill.SkillType - 1}"); break; } } // generate the kill bonus hitRequest.Session.Character.GenerateKillBonus(this); } else { // monster already has been killed, send cancel hitRequest.Session.SendPacket($"cancel 2 {MapMonsterId}"); } } // Respawn if (!IsAlive && ShouldRespawn != null && ShouldRespawn.Value) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10) { Respawn(); } } // normal movement else if (Target == -1) { Move(); } // target following else { if (Map != null) { ClientSession targetSession = Map.GetSessionByCharacterId(Target); // remove target in some situations if (targetSession == null || targetSession.Character.Invisible || targetSession.Character.Hp <= 0 || CurrentHp <= 0) { RemoveTarget(); return; } NpcMonsterSkill npcMonsterSkill = null; if (ServerManager.RandomNumber(0, 10) > 8 && Skills != null) { npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } // check if target is in range if (!targetSession.Character.InvisibleGm && !targetSession.Character.Invisible && targetSession.Character.Hp > 0 && ((npcMonsterSkill != null && CurrentMp >= npcMonsterSkill.Skill.MpCost && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) < npcMonsterSkill.Skill.Range) || (Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) <= Monster.BasicRange))) { TargetHit(targetSession, npcMonsterSkill); } else { FollowTarget(targetSession); } } } }
internal void MonsterLife() { // Respawn if (!Alive && Respawn) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10) { DamageList = new Dictionary <long, long>(); Alive = true; Target = -1; CurrentHp = Monster.MaxHP; CurrentMp = Monster.MaxMP; MapX = FirstX; MapY = FirstY; Path = new List <MapCell>(); Map.Broadcast(GenerateIn3()); Map.Broadcast(GenerateEff(7), 10); } return; } else if (Target == -1) { // Normal Move Mode if (!Alive) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path.Where(s => s != null).ToList().Count > 0) { int timetowalk = 1000 / (2 * Monster.Speed); if (time > timetowalk) { short mapX = Path.ElementAt(0).X; short mapY = Path.ElementAt(0).Y; Path.RemoveAt(0); LastMove = DateTime.Now; Map.Broadcast($"mv 3 {this.MapMonsterId} {this.MapX} {this.MapY} {Monster.Speed}"); Task.Factory.StartNew(async() => { await Task.Delay(timetowalk); MapX = mapX; MapY = mapY; }); return; } } else if (time > _movetime) { _movetime = _random.Next(400, 3200); byte point = (byte)_random.Next(2, 4); byte fpoint = (byte)_random.Next(0, 2); byte xpoint = (byte)_random.Next(fpoint, point); byte ypoint = (byte)(point - xpoint); short mapX = FirstX; short mapY = FirstY; if (Map.GetFreePosition(ref mapX, ref mapY, xpoint, ypoint)) { Task.Factory.StartNew(async() => { await Task.Delay(1000 * (xpoint + ypoint) / (2 * Monster.Speed)); this.MapX = mapX; this.MapY = mapY; }); LastMove = DateTime.Now.AddSeconds((xpoint + ypoint) / (2 * Monster.Speed)); string movePacket = $"mv 3 {this.MapMonsterId} {mapX} {mapY} {Monster.Speed}"; Map.Broadcast(movePacket); } } } if (Monster.IsHostile) { Character character = ServerManager.Instance.Sessions.FirstOrDefault(s => s != null && s.Character != null && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapId == MapId && Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.Character.MapX, Y = s.Character.MapY }) < 10)?.Character; if (character != null) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon) { character.Session.SendPacket(GenerateEff(5000)); } } } } else { ClientSession targetSession = Map.GetSessionByCharacterId(Target); if (targetSession == null || targetSession.Character.Invisible) { Target = -1; Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); } } NpcMonsterSkill npcMonsterSkill = null; if (_random.Next(10) > 8) { npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastUse).TotalMilliseconds >= 100 * s.Skill.Cooldown).OrderBy(rnd => _random.Next()).FirstOrDefault(); } int damage = 500; if (targetSession != null && targetSession.Character.Hp > 0 && ((npcMonsterSkill != null && CurrentMp - npcMonsterSkill.Skill.MpCost >= 0 && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) < npcMonsterSkill.Skill.Range) || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }) <= Monster.BasicRange))) { if (((DateTime.Now - LastEffect).TotalMilliseconds >= 1000 + Monster.BasicCooldown * 200 && !Skills.Any()) || npcMonsterSkill != null) { if (npcMonsterSkill != null) { npcMonsterSkill.LastUse = DateTime.Now; CurrentMp -= npcMonsterSkill.Skill.MpCost; Map.Broadcast($"ct 3 {MapMonsterId} 1 {Target} {npcMonsterSkill.Skill.CastAnimation} {npcMonsterSkill.Skill.CastEffect} {npcMonsterSkill.Skill.SkillVNum}"); } LastMove = DateTime.Now; // deal 0 damage to GM with GodMode damage = targetSession.Character.HasGodMode ? 0 : 100; if (targetSession.Character.IsSitting) { targetSession.Character.IsSitting = false; Map.Broadcast(targetSession.Character.GenerateRest()); Thread.Sleep(500); } if (npcMonsterSkill != null && npcMonsterSkill.Skill.CastEffect != 0) { Map.Broadcast(GenerateEff(npcMonsterSkill.Skill.CastEffect)); Thread.Sleep(npcMonsterSkill.Skill.CastTime * 100); } Path = new List <MapCell>(); targetSession.Character.LastDefence = DateTime.Now; targetSession.Character.GetDamage(damage); Map.Broadcast(null, ServerManager.Instance.GetUserMethod <string>(Target, "GenerateStat"), ReceiverType.OnlySomeone, "", Target); if (npcMonsterSkill != null) { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} {npcMonsterSkill.SkillVNum} {npcMonsterSkill.Skill.Cooldown} {npcMonsterSkill.Skill.AttackAnimation} {npcMonsterSkill.Skill.Effect} {this.MapX} {this.MapY} {(targetSession.Character.Hp > 0 ? 1 : 0)} { (int)(targetSession.Character.Hp / targetSession.Character.HPLoad() * 100) } {damage} 0 0"); } else { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} 0 {Monster.BasicCooldown} 11 {Monster.BasicSkill} 0 0 {(targetSession.Character.Hp > 0 ? 1 : 0)} { (int)(targetSession.Character.Hp / targetSession.Character.HPLoad() * 100) } {damage} 0 0"); } LastEffect = DateTime.Now; if (targetSession.Character.Hp <= 0) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(targetSession.Character.CharacterId); Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); } } if (npcMonsterSkill != null && (npcMonsterSkill.Skill.Range > 0 || npcMonsterSkill.Skill.TargetRange > 0)) { foreach (Character chara in ServerManager.GetMap(MapId).GetListPeopleInRange(npcMonsterSkill.Skill.TargetRange == 0 ? this.MapX : targetSession.Character.MapX, npcMonsterSkill.Skill.TargetRange == 0 ? this.MapY : targetSession.Character.MapY, (byte)(npcMonsterSkill.Skill.TargetRange + npcMonsterSkill.Skill.Range)).Where(s => s.CharacterId != Target && s.Hp > 0)) { if (chara.IsSitting) { chara.IsSitting = false; Map.Broadcast(chara.GenerateRest()); Thread.Sleep(500); } damage = chara.HasGodMode ? 0 : 100; bool AlreadyDead2 = chara.Hp <= 0; chara.GetDamage(damage); chara.LastDefence = DateTime.Now; Map.Broadcast(null, chara.GenerateStat(), ReceiverType.OnlySomeone, "", chara.CharacterId); Map.Broadcast($"su 3 {MapMonsterId} 1 {chara.CharacterId} 0 {Monster.BasicCooldown} 11 {Monster.BasicSkill} 0 0 {(chara.Hp > 0 ? 1 : 0)} { (int)(chara.Hp / chara.HPLoad() * 100) } {damage} 0 0"); if (chara.Hp <= 0 && !AlreadyDead2) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(chara.CharacterId); } } } } } else { int distance = 0; if (targetSession != null) { distance = Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }); } if (IsMoving) { short maxDistance = 22; if (Path.Count() == 0 && targetSession != null && distance > 1 && distance < maxDistance) { short xoffset = (short)_random.Next(-1, 1); short yoffset = (short)_random.Next(-1, 1); Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = (short)(targetSession.Character.MapX + xoffset), Y = (short)(targetSession.Character.MapY + yoffset), MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = (short)(targetSession.Character.MapX + xoffset), Y = (short)(targetSession.Character.MapY + yoffset), MapId = this.MapId }); } } if (DateTime.Now > LastMove && Monster.Speed > 0 && Path.Count > 0) { short mapX; short mapY; int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; mapX = Path.ElementAt(maxindex - 1).X; mapY = Path.ElementAt(maxindex - 1).Y; double waitingtime = (double)(Map.GetDistance(new MapCell() { X = mapX, Y = mapY, MapId = MapId }, new MapCell() { X = MapX, Y = MapY, MapId = MapId })) / (double)(Monster.Speed); Map.Broadcast($"mv 3 {this.MapMonsterId} {mapX} {mapY} {Monster.Speed}"); LastMove = DateTime.Now.AddSeconds((waitingtime > 1 ? 1 : waitingtime)); Task.Factory.StartNew(async() => { await Task.Delay((int)((waitingtime > 1 ? 1 : waitingtime) * 1000)); this.MapX = mapX; this.MapY = mapY; }); for (int j = maxindex; j > 0; j--) { Path.RemoveAt(0); } } if (Path.Count() == 0 && (targetSession == null || MapId != targetSession.Character.MapId || distance > maxDistance)) { Path = ServerManager.GetMap(MapId).StraightPath(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); if (!Path.Any()) { Path = ServerManager.GetMap(MapId).JPSPlus(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = FirstX, Y = FirstY, MapId = this.MapId }); } Target = -1; } } } } }
internal void HostilityTarget() { if (IsHostile && Target == -1) { Character character = ServerManager.Instance.Sessions.FirstOrDefault(s => s?.Character != null && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapInstance == MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < (NoticeRange == 0 ? Monster.NoticeRange : NoticeRange))?.Character; if (character != null) { if (!OnNoticeEvents.Any() && MoveEvent == null) { Target = character.CharacterId; if (!Monster.NoAggresiveIcon && LastEffect.AddSeconds(5) < DateTime.Now) { character.Session.SendPacket(GenerateEff(5000)); } } OnNoticeEvents.ForEach(e => { EventHelper.Instance.RunEvent(e, monster: this); }); OnNoticeEvents.RemoveAll(s => s != null); } } }
private void Move() { // Normal Move Mode if (Monster == null || !IsAlive) { return; } if (IsMoving && Monster.Speed > 0) { double time = (DateTime.Now - LastMove).TotalMilliseconds; if (Path.Any()) { int timetowalk = 2000 / Monster.Speed; if (time > timetowalk) { int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; short mapX = Path.ElementAt(maxindex - 1).X; short mapY = Path.ElementAt(maxindex - 1).Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Monster.Speed; LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds(timetowalk)) .Subscribe( x => { MapX = (short)mapX; MapY = (short)mapY; MoveEvent?.Events.ForEach(e => { EventHelper.Instance.RunEvent(e, monster: this); }); if (MoveEvent != null && MoveEvent.InZone(MapX, MapY)) { MoveEvent = null; } }); Path.RemoveRange(0, maxindex); MapInstance.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); return; } } else if (time > _movetime && Target == -1) { short mapX = FirstX, mapY = FirstY; if (MapInstance.Map?.GetFreePosition(ref mapX, ref mapY, (byte)ServerManager.Instance.RandomNumber(0, 2), (byte)_random.Next(0, 2)) ?? false) { int distance = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }); double value = 1000d * distance / (2 * Monster.Speed); Observable.Timer(TimeSpan.FromMilliseconds(value)) .Subscribe( x => { MapX = mapX; MapY = mapY; }); LastMove = DateTime.Now.AddMilliseconds(value); MapInstance.Broadcast(new BroadcastPacket(null, GenerateMv3(), ReceiverType.All)); } } } HostilityTarget(); }
/// <summary> /// Follow the Monsters target to it's position. /// </summary> /// <param name="targetSession">The TargetSession to follow</param> private void FollowTarget(ClientSession targetSession) { int distance = 0; if (targetSession != null) { distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = targetSession.Character.MapX, Y = targetSession.Character.MapY }); } if (IsMoving) { short maxDistance = 22; if (!Path.Any() && targetSession != null && distance > 1 && distance < maxDistance) { short xoffset = (short)ServerManager.RandomNumber(-1, 1); short yoffset = (short)ServerManager.RandomNumber(-1, 1); Path = Map.StraightPath(new GridPos { x = MapX, y = MapY }, new GridPos { x = (short)(targetSession.Character.MapX + xoffset), y = (short)(targetSession.Character.MapY + yoffset) }); if (!Path.Any()) { try { Path = Map.JPSPlus(JumpPointParameters, new GridPos { x = MapX, y = MapY }, new GridPos { x = (short)(targetSession.Character.MapX + xoffset), y = (short)(targetSession.Character.MapY + yoffset) }); } catch (Exception ex) { Logger.Log.Error($"Pathfinding using JPSPlus failed. Map: {MapId} StartX: {MapX} StartY: {MapY} TargetX: {(short)(targetSession.Character.MapX + xoffset)} TargetY: {(short)(targetSession.Character.MapY + yoffset)}", ex); RemoveTarget(); } } } if (Monster != null && DateTime.Now > LastMove && Monster.Speed > 0 && Path.Any()) { int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; short mapX = (short)Path.ElementAt(maxindex - 1).x; short mapY = (short)Path.ElementAt(maxindex - 1).y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY, MapId = MapId }, new MapCell { X = MapX, Y = MapY, MapId = MapId }) / (double)Monster.Speed; Map.Broadcast(new BroadcastPacket(null, $"mv 3 {MapMonsterId} {mapX} {mapY} {Monster.Speed}", ReceiverType.AllInRange, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))) .Subscribe( x => { MapX = mapX; MapY = mapY; }); for (int j = maxindex; j > 0; j--) { Path.RemoveAt(0); } } if (!Path.Any() && (DateTime.Now - LastEffect).Seconds > 20 && (targetSession == null || MapId != targetSession.Character.MapId || distance > maxDistance)) { RemoveTarget(); } } }
internal void HostilityTarget() { if (IsHostile && Target == -1) { Character character = ServerManager.Instance.Sessions.Where(s => s?.Character != null && (ServerManager.Instance.ChannelId != 51 || MonsterVNum - (byte)s.Character.Faction != 678) && s.Character.Hp > 0 && !s.Character.InvisibleGm && !s.Character.Invisible && s.Character.MapInstance == MapInstance && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = s.Character.PositionX, Y = s.Character.PositionY }) < Monster.NoticeRange).OrderBy(s => ServerManager.Instance.RandomNumber(0, int.MaxValue)).FirstOrDefault()?.Character; if (character != null) { if (OnNoticeEvents.Count == 0 && MoveEvent == null) { Target = character.CharacterId; if (!NoAggresiveIcon) { character.Session.SendPacket(StaticPacketHelper.GenerateEff(UserType.Monster, MapMonsterId, 5000)); } } OnNoticeEvents.ForEach(e => EventHelper.Instance.RunEvent(e, monster: this)); OnNoticeEvents.RemoveAll(s => s != null); } } }
internal void MonsterLife() { LifeTaskIsRunning = true; NpcMonster monster = ServerManager.GetNpc(this.MonsterVNum); //Respawn if (!Alive) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= monster.RespawnTime / 10) { Alive = true; Target = -1; CurrentHp = monster.MaxHP; CurrentMp = monster.MaxMP; MapX = firstX; MapY = firstY; Map.Broadcast(GenerateIn3()); Map.Broadcast(GenerateEff(7)); } LifeTaskIsRunning = false; return; } else if (Target == -1) { //Normal Move Mode if (monster == null || Alive == false) { LifeTaskIsRunning = false; return; } Random r = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); double time = (DateTime.Now - LastMove).TotalSeconds; int MoveFrequent = 5 - (int)Math.Round((double)(monster.Speed / 5)); if (MoveFrequent < 1) { MoveFrequent = 1; } if (IsMoving) { if (path.Where(s => s != null).ToList().Count > 0)//fix a path problem { if ((DateTime.Now - LastMove).TotalSeconds > 1.0 / monster.Speed) { short MapX = path.ElementAt(0).X; short MapY = path.ElementAt(0).Y; path.RemoveAt(0); LastMove = DateTime.Now; Map.Broadcast($"mv 3 {this.MapMonsterId} {this.MapX} {this.MapY} {monster.Speed}"); Task.Factory.StartNew(async() => { await Task.Delay(500); this.MapX = MapX; this.MapY = MapY; }); LifeTaskIsRunning = false; return; } } else if (time > r.Next(1, MoveFrequent) + 1) { int MoveDistance = (int)Math.Round((double)monster.Speed / 2); byte xpoint = (byte)(r.Next(1, MoveDistance)); byte ypoint = (byte)(r.Next(1, MoveDistance)); short MapX = firstX; short MapY = firstY; if (ServerManager.GetMap(MapId).GetFreePosition(ref MapX, ref MapY, xpoint, ypoint)) { LastMove = DateTime.Now; string movepacket = $"mv 3 {this.MapMonsterId} {MapX} {MapY} {monster.Speed}"; Map.Broadcast(movepacket); Task.Factory.StartNew(async() => { await Task.Delay(500); this.MapX = MapX; this.MapY = MapY; }); } } } if (monster.IsHostile) { Character character = ServerManager.Instance.Sessions.Where(s => s.Character != null && s.Character.Hp > 0).OrderBy(s => Map.GetDistance(new MapCell() { X = MapX, Y = MapY }, new MapCell() { X = s.Character.MapX, Y = s.Character.MapY })).FirstOrDefault(s => s.Character != null && !s.Character.Invisible && s.Character.MapId == MapId)?.Character; if (character != null) { if (Map.GetDistance(new MapCell() { X = character.MapX, Y = character.MapY }, new MapCell() { X = MapX, Y = MapY }) < 7) { Target = character.CharacterId; if (!monster.NoAggresiveIcon) { ServerManager.Instance.Sessions.FirstOrDefault(s => s != null && s.Client != null && s.Character != null && s.Character.CharacterId.Equals(Target)).Client.SendPacket(GenerateEff(5000)); } } } } } else { short?MapX = ServerManager.Instance.GetProperty <short?>(Target, "MapX"); short?MapY = ServerManager.Instance.GetProperty <short?>(Target, "MapY"); int? Hp = ServerManager.Instance.GetProperty <int?>(Target, "Hp"); short?mapId = ServerManager.Instance.GetProperty <short?>(Target, "MapId"); bool? invisible = ServerManager.Instance.GetProperty <bool?>(Target, "Invisible"); if (MapX == null || MapY == null || Hp <= 0 || invisible != null && (bool)invisible) { Target = -1; LifeTaskIsRunning = false; return; } Random r = new Random((int)DateTime.Now.Ticks & 0x0000FFFF); NpcMonsterSkill ski = monster.Skills.Where(s => !s.Used && (DateTime.Now - s.LastUse).TotalMilliseconds >= 100 * ServerManager.GetSkill(s.SkillVNum).Cooldown).OrderBy(rnd => r.Next()).FirstOrDefault(); Skill sk = null; if (ski != null) { sk = ServerManager.GetSkill(ski.SkillVNum); } ClientSession targetSession = Map.Sessions.SingleOrDefault(s => s.Character.CharacterId == Target); int damage = 100; if (targetSession != null && (sk != null && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = (short)MapX, Y = (short)MapY }) < sk.Range) || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = (short)MapX, Y = (short)MapY }) <= monster.BasicRange)) { if ((sk != null && ((DateTime.Now - LastEffect).TotalMilliseconds >= sk.Cooldown * 100 + 1000)) || ((DateTime.Now - LastEffect).TotalMilliseconds >= (monster.BasicCooldown < 4 ? 4 : monster.BasicCooldown) * 100 + 100)) { if (ski != null) { ski.Used = true; ski.LastUse = DateTime.Now; Map.Broadcast($"ct 3 {MapMonsterId} 1 {Target} {sk.CastAnimation} -1 {sk.SkillVNum}"); } LastMove = DateTime.Now; // deal 0 damage to GM with GodMode if (targetSession == null) { damage = 0; } else { damage = targetSession.Character.HasGodMode ? 0 : 100; } if (sk != null && sk.CastEffect != 0) { Map.Broadcast(GenerateEff(sk.CastEffect)); Thread.Sleep(sk.CastTime * 100); } path = new List <MapCell>(); targetSession.Character.LastDefence = DateTime.Now; targetSession.Character.GetDamage(damage); Map.Broadcast(null, ServerManager.Instance.GetUserMethod <string>(Target, "GenerateStat"), ReceiverType.OnlySomeone, "", Target); if (sk != null) { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} {ski.SkillVNum} {sk.Cooldown} {sk.AttackAnimation} {sk.Effect} {this.MapX} {this.MapY} {(targetSession.Character.Hp > 0 ? 1 : 0)} {(int)((double)targetSession.Character.Hp / ServerManager.Instance.GetUserMethod<double>(Target, "HPLoad"))} {damage} 0 0"); } else { Map.Broadcast($"su 3 {MapMonsterId} 1 {Target} 0 {monster.BasicCooldown} 11 {monster.BasicSkill} 0 0 {(targetSession.Character.Hp > 0 ? 1 : 0)} {(int)((double)targetSession.Character.Hp / ServerManager.Instance.GetUserMethod<double>(Target, "HPLoad"))} {damage} 0 0"); } if (ski != null) { ski.Used = false; } LastEffect = DateTime.Now; if (targetSession.Character.Hp <= 0) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(Target); Target = -1; } if ((sk != null && (sk.Range > 0 || sk.TargetRange > 0))) { foreach (Character chara in ServerManager.GetMap(MapId).GetListPeopleInRange(sk.TargetRange == 0 ? this.MapX : (short)MapX, sk.TargetRange == 0 ? this.MapY : (short)MapY, (byte)(sk.TargetRange + sk.Range)).Where(s => s.CharacterId != Target)) { damage = chara.HasGodMode ? 0 : 100; bool AlreadyDead2 = chara.Hp <= 0; chara.GetDamage(damage); chara.LastDefence = DateTime.Now; Map.Broadcast(null, chara.GenerateStat(), ReceiverType.OnlySomeone, "", chara.CharacterId); Map.Broadcast($"su 3 {MapMonsterId} 1 {chara.CharacterId} 0 {monster.BasicCooldown} 11 {monster.BasicSkill} 0 0 {(chara.Hp > 0 ? 1 : 0)} {(int)((double)chara.Hp / chara.HPLoad())} {damage} 0 0"); if (chara.Hp <= 0 && !AlreadyDead2) { Thread.Sleep(1000); ServerManager.Instance.AskRevive(chara.CharacterId); } } } } } else { if (IsMoving == true) { short maxdistance = 22; if (path.Count() == 0) { path = ServerManager.GetMap(MapId).AStar(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = (short)MapX, Y = (short)MapY, MapId = this.MapId }); } if (path.Count > 0 && Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = (short)MapX, Y = (short)MapY, MapId = this.MapId }) > 1) { this.MapX = path.ElementAt(0).X; this.MapY = path.ElementAt(0).Y; path.RemoveAt(0); } if (MapId != mapId || (Map.GetDistance(new MapCell() { X = this.MapX, Y = this.MapY }, new MapCell() { X = (short)MapX, Y = (short)MapY }) > maxdistance)) { path = ServerManager.GetMap(MapId).AStar(new MapCell() { X = this.MapX, Y = this.MapY, MapId = this.MapId }, new MapCell() { X = firstX, Y = firstY, MapId = this.MapId }); Target = -1; } else { if ((DateTime.Now - LastMove).TotalSeconds > 1.0 / monster.Speed) { LastMove = DateTime.Now; Map.Broadcast($"mv 3 {this.MapMonsterId} {this.MapX} {this.MapY} {monster.Speed}"); } } } } } LifeTaskIsRunning = false; }
/// <summary> /// Follow the Monsters target to it's position. /// </summary> /// <param name="targetSession">The TargetSession to follow</param> private void followTarget(ClientSession targetSession) { if (IsMoving && !_noMove) { const short maxDistance = 22; int distance = Map.GetDistance(new MapCell() { X = targetSession.Character.PositionX, Y = targetSession.Character.PositionY }, new MapCell() { X = MapX, Y = MapY }); if (targetSession != null) { if (targetSession.Character.LastMonsterAggro.AddSeconds(5) < DateTime.Now || targetSession.Character.BrushFire == null) { targetSession.Character.UpdateBushFire(); } targetSession.Character.LastMonsterAggro = DateTime.Now; } if (Path.Count == 0 && targetSession != null) { short xoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); short yoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); try { Path = BestFirstSearch.TracePath(new Node() { X = MapX, Y = MapY }, targetSession.Character.BrushFire, targetSession.Character.MapInstance.Map.Grid); } catch (Exception ex) { Logger.Error($"Pathfinding using Pathfinder failed. Map: {MapId} StartX: {MapX} StartY: {MapY} TargetX: {(short)(targetSession.Character.PositionX + xoffset)} TargetY: {(short)(targetSession.Character.PositionY + yoffset)}", ex); RemoveTarget(); } } if (Monster != null && DateTime.Now > LastMove && Monster.Speed > 0 && Path.Count > 0) { int maxindex = Path.Count > Monster.Speed / 2 ? Monster.Speed / 2 : Path.Count; short mapX = Path[maxindex - 1].X; short mapY = Path[maxindex - 1].Y; double waitingtime = Map.GetDistance(new MapCell { X = mapX, Y = mapY }, new MapCell { X = MapX, Y = MapY }) / (double)Monster.Speed; MapInstance.Broadcast(new BroadcastPacket(null, PacketFactory.Serialize(StaticPacketHelper.Move(UserType.Monster, MapMonsterId, mapX, mapY, Monster.Speed)), ReceiverType.All, xCoordinate: mapX, yCoordinate: mapY)); LastMove = DateTime.Now.AddSeconds(waitingtime > 1 ? 1 : waitingtime); Observable.Timer(TimeSpan.FromMilliseconds((int)((waitingtime > 1 ? 1 : waitingtime) * 1000))).Subscribe(x => { MapX = mapX; MapY = mapY; }); distance = (int)Path[0].F; Path.RemoveRange(0, maxindex > Path.Count ? Path.Count : maxindex); } if (targetSession == null || MapId != targetSession.Character.MapInstance.Map.MapId || distance > (maxDistance) + 3) { RemoveTarget(); } } }
public IEnumerable <BattleEntity> GetBattleEntitiesInRange(MapCell pos, byte distance) { return(BattleEntities.Where(b => Map.GetDistance(b.GetPos(), pos) <= distance)); }
/// <summary> /// Handle any kind of Monster interaction /// </summary> private void monsterLife() { if (Monster == null) { return; } if ((DateTime.Now - LastEffect).TotalSeconds >= 5) { LastEffect = DateTime.Now; if (IsTarget) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Monster, MapMonsterId, 824)); } if (IsBonus) { MapInstance.Broadcast(StaticPacketHelper.GenerateEff(UserType.Monster, MapMonsterId, 826)); } } if (IsBoss && IsAlive) { MapInstance.Broadcast(GenerateBoss()); } // handle hit queue while (HitQueue.TryDequeue(out HitRequest hitRequest)) { if (IsAlive && hitRequest.Session.Character.Hp > 0 && (ServerManager.Instance.ChannelId != 51 || MonsterVNum - (byte)hitRequest.Session.Character.Faction != 678)) { int hitmode = 0; bool isCaptureSkill = hitRequest.Skill.BCards.Any(s => s.Type.Equals((byte)CardType.Capture)); // calculate damage bool onyxWings = false; BattleEntity battleEntity = new BattleEntity(hitRequest.Session.Character, hitRequest.Skill); int damage = DamageHelper.Instance.CalculateDamage(battleEntity, new BattleEntity(this), hitRequest.Skill, ref hitmode, ref onyxWings); if (onyxWings) { short onyxX = (short)(hitRequest.Session.Character.PositionX + 2); short onyxY = (short)(hitRequest.Session.Character.PositionY + 2); int onyxId = MapInstance.GetNextMonsterId(); MapMonster onyx = new MapMonster() { MonsterVNum = 2371, MapX = onyxX, MapY = onyxY, MapMonsterId = onyxId, IsHostile = false, IsMoving = false, ShouldRespawn = false }; MapInstance.Broadcast(UserInterfaceHelper.Instance.GenerateGuri(31, 1, hitRequest.Session.Character.CharacterId, onyxX, onyxY)); onyx.Initialize(MapInstance); MapInstance.AddMonster(onyx); MapInstance.Broadcast(onyx.GenerateIn()); CurrentHp -= damage / 2; Observable.Timer(TimeSpan.FromMilliseconds(350)).Subscribe(o => { MapInstance.Broadcast(StaticPacketHelper.SkillUsed(UserType.Monster, onyxId, 3, MapMonsterId, -1, 0, -1, hitRequest.Skill.Effect, -1, -1, true, 92, damage / 2, 0, 0)); MapInstance.RemoveMonster(onyx); MapInstance.Broadcast(StaticPacketHelper.Out(UserType.Monster, onyx.MapMonsterId)); }); } if (hitmode != 1) { hitRequest.Skill.BCards.Where(s => s.Type.Equals((byte)CardType.Buff)).ToList().ForEach(s => s.ApplyBCards(this, hitRequest.Session)); hitRequest.Skill.BCards.Where(s => s.Type.Equals((byte)CardType.Capture)).ToList().ForEach(s => s.ApplyBCards(this, hitRequest.Session)); if (battleEntity?.ShellWeaponEffects != null) { foreach (ShellEffectDTO shell in battleEntity.ShellWeaponEffects) { switch (shell.Effect) { case (byte)ShellWeaponEffectType.Blackout: { Buff buff = new Buff(7, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } case (byte)ShellWeaponEffectType.DeadlyBlackout: { Buff buff = new Buff(66, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } case (byte)ShellWeaponEffectType.MinorBleeding: { Buff buff = new Buff(1, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } case (byte)ShellWeaponEffectType.Bleeding: { Buff buff = new Buff(21, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } case (byte)ShellWeaponEffectType.HeavyBleeding: { Buff buff = new Buff(42, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } case (byte)ShellWeaponEffectType.Freeze: { Buff buff = new Buff(27, battleEntity.Level); if (ServerManager.Instance.RandomNumber() < shell.Value) { AddBuff(buff); } break; } } } } } if (DamageList.ContainsKey(hitRequest.Session.Character.CharacterId)) { DamageList[hitRequest.Session.Character.CharacterId] += damage; } else { DamageList.Add(hitRequest.Session.Character.CharacterId, damage); } if (isCaptureSkill) { damage = 0; } if (CurrentHp <= damage) { SetDeathStatement(); } else { CurrentHp -= damage; } // only set the hit delay if we become the monsters target with this hit if (Target == -1) { LastSkill = DateTime.Now; } int nearestDistance = 100; foreach (KeyValuePair <long, long> kvp in DamageList) { ClientSession session = MapInstance.GetSessionByCharacterId(kvp.Key); if (session != null) { int distance = Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = session.Character.PositionX, Y = session.Character.PositionY }); if (distance < nearestDistance) { nearestDistance = distance; Target = session.Character.CharacterId; } } } switch (hitRequest.TargetHitType) { case TargetHitType.SingleTargetHit: if (!isCaptureSkill) { MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.Skill.AttackAnimation, hitRequest.SkillEffect, hitRequest.Session.Character.PositionX, hitRequest.Session.Character.PositionY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); } break; case TargetHitType.SingleTargetHitCombo: MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.SkillCombo.Animation, hitRequest.SkillCombo.Effect, hitRequest.Session.Character.PositionX, hitRequest.Session.Character.PositionY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); break; case TargetHitType.SingleAOETargetHit: switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } if (hitRequest.ShowTargetHitAnimation) { MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.Skill.AttackAnimation, hitRequest.SkillEffect, hitRequest.Session.Character.PositionX, hitRequest.Session.Character.PositionY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); } else { MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, 0, 0, 0, 0, 0, 0, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); } break; case TargetHitType.AOETargetHit: switch (hitmode) { case 1: hitmode = 4; break; case 3: hitmode = 6; break; default: hitmode = 5; break; } MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.Skill.AttackAnimation, hitRequest.SkillEffect, hitRequest.Session.Character.PositionX, hitRequest.Session.Character.PositionY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); break; case TargetHitType.ZoneHit: MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.Skill.AttackAnimation, hitRequest.SkillEffect, hitRequest.MapX, hitRequest.MapY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, 5, (byte)(hitRequest.Skill.SkillType - 1))); break; case TargetHitType.SpecialZoneHit: MapInstance?.Broadcast(StaticPacketHelper.SkillUsed(UserType.Player, hitRequest.Session.Character.CharacterId, 3, MapMonsterId, hitRequest.Skill.SkillVNum, hitRequest.Skill.Cooldown, hitRequest.Skill.AttackAnimation, hitRequest.SkillEffect, hitRequest.Session.Character.PositionX, hitRequest.Session.Character.PositionY, IsAlive, (int)((float)CurrentHp / (float)MaxHp * 100), damage, hitmode, (byte)(hitRequest.Skill.SkillType - 1))); break; } if (CurrentHp <= 0 && !isCaptureSkill) { // generate the kill bonus hitRequest.Session.Character.GenerateKillBonus(this); } } else { // monster already has been killed, send cancel hitRequest.Session.SendPacket(StaticPacketHelper.Cancel(2, MapMonsterId)); } if (IsBoss) { MapInstance.Broadcast(GenerateBoss()); } } // Respawn if (!IsAlive && ShouldRespawn != null && !ShouldRespawn.Value) { MapInstance.RemoveMonster(this); } if (!IsAlive && ShouldRespawn != null && ShouldRespawn.Value) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10d) { respawn(); } } // normal movement else if (Target == -1) { move(); } // target following else if (MapInstance != null) { GetNearestOponent(); HostilityTarget(); ClientSession targetSession = MapInstance.GetSessionByCharacterId(Target); // remove target in some situations if (targetSession == null || targetSession.Character.Invisible || targetSession.Character.Hp <= 0 || CurrentHp <= 0) { RemoveTarget(); return; } NpcMonsterSkill npcMonsterSkill = null; if (Skills != null && ServerManager.Instance.RandomNumber(0, 10) > 8) { npcMonsterSkill = Skills.Where(s => (DateTime.Now - s.LastSkillUse).TotalMilliseconds >= 100 * s.Skill?.Cooldown)?.OrderBy(rnd => _random.Next())?.FirstOrDefault(); } if (npcMonsterSkill?.Skill.TargetType == 1 && npcMonsterSkill?.Skill.HitType == 0) { targetHit(targetSession, npcMonsterSkill); } // check if target is in range if (!targetSession.Character.InvisibleGm && !targetSession.Character.Invisible && targetSession.Character.Hp > 0) { if (npcMonsterSkill != null && CurrentMp >= npcMonsterSkill.Skill.MpCost && Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = targetSession.Character.PositionX, Y = targetSession.Character.PositionY }) < npcMonsterSkill.Skill.Range) { targetHit(targetSession, npcMonsterSkill); } else if (Map.GetDistance(new MapCell { X = MapX, Y = MapY }, new MapCell { X = targetSession.Character.PositionX, Y = targetSession.Character.PositionY }) <= Monster.BasicRange) { targetHit(targetSession, npcMonsterSkill); } else { followTarget(targetSession); } } else { followTarget(targetSession); } } }