/// <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> A search implementation which executes each /// <see cref="Searchable"/> in its own thread and waits for each search to complete /// and merge the results back together. /// </summary> public override TopDocs Search(Weight weight, Filter filter, int nDocs) { HitQueue hq = new HitQueue(nDocs, false); object lockObj = new object(); Task<TopDocs>[] tasks = new Task<TopDocs>[searchables.Length]; //search each searchable for (int i = 0; i < searchables.Length; i++) { int cur = i; tasks[i] = Task.Factory.StartNew(() => MultiSearcherCallableNoSort(ThreadLock.MonitorLock, lockObj, searchables[cur], weight, filter, nDocs, hq, cur, starts)); } int totalHits = 0; float maxScore = float.NegativeInfinity; Task.WaitAll(tasks); foreach(TopDocs topDocs in tasks.Select(x => x.Result)) { totalHits += topDocs.TotalHits; maxScore = Math.Max(maxScore, topDocs.MaxScore); } ScoreDoc[] scoreDocs = new ScoreDoc[hq.Size()]; for (int i = hq.Size() - 1; i >= 0; i--) // put docs in array scoreDocs[i] = hq.Pop(); return new TopDocs(totalHits, scoreDocs, maxScore); }
public override TopDocs Search(Weight weight, Filter filter, int nDocs) { HitQueue hq = new HitQueue(nDocs, false); int totalHits = 0; var lockObj = new object(); for (int i = 0; i < searchables.Length; i++) { // search each searcher // use NullLock, we don't care about synchronization for these TopDocs docs = MultiSearcherCallableNoSort(ThreadLock.NullLock, lockObj, searchables[i], weight, filter, nDocs, hq, i, starts); totalHits += docs.TotalHits; // update totalHits } ScoreDoc[] scoreDocs2 = new ScoreDoc[hq.Size()]; for (int i = hq.Size() - 1; i >= 0; i--) // put docs in array scoreDocs2[i] = hq.Pop(); float maxScore = (totalHits == 0)?System.Single.NegativeInfinity:scoreDocs2[0].Score; return new TopDocs(totalHits, scoreDocs2, maxScore); }