/// <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); } } }
/// <summary> /// Handle any kind of Monster interaction /// </summary> private void MonsterLife() { if (Monster == null) { return; } // handle hit queue HitRequest hitRequest; while (HitQueue.TryDequeue(out hitRequest)) { if (IsAlive && hitRequest.Session.Character.Hp > 0) { int hitmode = 0; // calculate damage int damage = hitRequest.Session.Character.GenerateDamage(this, hitRequest.Skill, ref hitmode); switch (hitRequest.TargetHitType) { case TargetHitType.SingleTargetHit: { // Target Hit MapInstance?.Broadcast($"su 1 {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 ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.SingleTargetHitCombo: { // Taget Hit Combo MapInstance?.Broadcast($"su 1 {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 ? 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) { MapInstance?.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}"); } MapInstance?.Broadcast($"su 1 {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 ? 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; } MapInstance?.Broadcast($"su 1 {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 ? 1 : 0)} {(int)((float)CurrentHp / (float)Monster.MaxHP * 100)} {damage} {hitmode} {hitRequest.Skill.SkillType - 1}"); break; } case TargetHitType.ZoneHit: { // Zone HIT MapInstance?.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 MapInstance?.Broadcast($"su 1 {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 ? 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 (MapInstance != null) { 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 (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) { 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); } } } }
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(); }
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(); } } } } }
/// <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.PositionX, Y = targetSession.Character.PositionY }); } if (IsMoving) { short maxDistance = 22; if (!Path.Any() && targetSession != null) { short xoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); short yoffset = (short)ServerManager.Instance.RandomNumber(-1, 1); try { Path = MapInstance.Map.PathSearch(new GridPos { X = MapX, Y = MapY }, new GridPos { X = (short)(targetSession.Character.PositionX + xoffset), Y = (short)(targetSession.Character.PositionY + yoffset) }); } catch (Exception ex) { Logger.Log.Error($"Pathfinding using JPSPlus 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 = (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)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; }); Path.RemoveRange(0, maxindex); } if (targetSession == null || MapId != targetSession.Character.MapInstance.Map.MapId || distance > maxDistance) { RemoveTarget(); } } }
private void npcLife() { // Respawn if (CurrentHp <= 0 && ShouldRespawn != null && !ShouldRespawn.Value) { MapInstance.RemoveNpc(this); MapInstance.Broadcast(GenerateOut()); } guardiaOfNosville(); 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(); } } } } }