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; } } } } }
/// <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); } } } }
/// <summary> /// Handle any kind of Monster interaction /// </summary> internal void MonsterLife() { // Respawn if (!IsAlive && ShouldRespawn.Value) { double timeDeath = (DateTime.Now - Death).TotalSeconds; if (timeDeath >= Monster.RespawnTime / 10) { Respawn(); } return; } else if (Target == -1) // normal movement { Move(); return; } else // target following { ClientSession targetSession = Map.GetSessionByCharacterId(Target); // remove target in some situations if (targetSession == null || targetSession.Character.Invisible || targetSession.Character.Hp <= 0) { RemoveTarget(); return; } NpcMonsterSkill npcMonsterSkill = null; if (_random.Next(10) > 8) { 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 != null && !targetSession.Character.InvisibleGm && !targetSession.Character.Invisible && 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))) { TargetHit(targetSession, npcMonsterSkill); } else { FollowTarget(targetSession); } } }