weird monster movement!

01/15/2013 16:25 Mr_PoP#1
anyclue why the mobs might move like it do in the video?

01/16/2013 04:53 pro4never#2
Because that's how their artificial intelligence was written.

What source is this part of?

Couple things.

#1: Looks like they are all running based on a movement thread. This means that all mobs on server move once every say... 800 ms. This means they all move at exactly the same time.

#2: The pathfinding is pretty poor as they all just kinda clump towards the target. Bit difficult to tell how good or bad the pathfinding is though without first correcting the timing issue.
01/16/2013 15:14 go for it#3
*
01/16/2013 19:58 Mr_PoP#4
it's my own source made it from scratch , and it's a timer thread, which loop every 1sec through an ActiveMonsters(those mobs that being activated when a Hero is around it), if the Mob has a target ,and that target away from the Mob , but within it's walkable range it calls this Method Move()
Code:
private void Move(IMonster mob) {
            if(DateTime.Now >= mob.Timers.LastMovment.AddMilliseconds(mob.Monstertype.MoveSpeed)) {
                mob.Timers.LastMovment = DateTime.Now;
                if(mob.Path == null) return;

                foreach(var loc in mob.Path) {
                    if(mob.IsDead) return;

                    var dir = Calculations.GetDirection(mob.Location.X, mob.Location.Y, loc.X, loc.Y);

                    world.Entities.BattleEntities.Move(mob, Common.MoveType.Run, (x) => {
                        if(world.DMaps[x.Map].Cells[loc.X, loc.Y].Walkable) {
                            x.Direction = dir;
                            world.DMaps[x.Map].Cells[x.Location.X, x.Location.Y].Walkable = true;
                            x.Location = loc;
                            world.DMaps[x.Map].Cells[loc.X, loc.Y].Walkable = false;
                            mob.Path.Remove(loc);
                        } else {
                            mob.Target = null;
                        }
                        return true;
                    });
                    return;
                }
            }
        }
I can't see , what is wrong here ?, since every Mob has a check on it's movement speed , so they are not moving at the same time but one after the another towards the target!

EDIT: this weird movement does not occur all the time , it does from time to time , sometimes it never occurs!!!
01/17/2013 02:48 { Angelius }#5
PHP Code:
 while(true)
{
foreach(var 
Monster in ActiveMonsters.ReturnCopy())//ReturnCopy = thread safe 
{
if(
Monster.IsDead) continue;
if(
DateTime.Now Monster.Timers.LastMovment) continue;
if (
Monster.PathFinding)
{
 if (
Monster.Path.Count() < || Monster.Target == null) { Monster.PathFinding false; continue; } 
//Get the next X/Y from the path list and walk/Remove the X/Y from the path list
}
else 
{
 if (
Monster.Target == null) { continue; } 
Monster.Path GetPath(Monster.XMonster.YMonster.Target.XMonster.Target.Y);
if (
Monster.Path.Count() < 1) continue; 
//Get the next X/Y from the path list and walk/Remove the X/Y from the path list
}
Monster.Timers.LastMovment DateTime.Now.AddMilliseconds(Random.Next(mob.Monstertype.MoveSpeedmob.Monstertype.MoveSpeed 400))


Your Pathfinder should check every X/Y and make sure its valid before adding it to the path list and inside the movement thread you only check to make sure no player is standing in the next X/Y that you are about to move the monster to and if there is a player then you clear the path list and try to get a fresh path if needed...

And what i don't understand is if a timer/thread are executing the Move() void then whats getting the Mob.Path? a different thread ?

And before you allow the monsters to attack the target you should check and make sure that at least 300 MS or so has passed since the last jump or else monsters wold attack the target while he is still in the air and change directions as soon as you make a jump

mob.Timers.LastMovment = DateTime.Now; should be right before the return and not in the top of the void

mob.Target = null; Target should never = null unless the target had left the monster screen and should not depend on the monster view range/attack range

And to me it sounds like

A. Monsters are being controlled by more then 1 thread/timer IE: Move() void is being called randomly from the same/different threads so try and use something like the while loop i posted and see if eliminates the problem

B. You are not sending a valid Direction in the walk packet so spawn 1 Pheasant in twin city.. break point at the top of your monsters thread and track his movements based on the direction and how your loop handles it..

C. Monsters X/Y are not really being updated upon walking and the same process in step B can prove if its being updated or no

Good luck buddy...

Edit: I thought you stopped working on your server :|
01/17/2013 08:58 Mr_PoP#6
Quote:
Originally Posted by { Angelius } View Post
PHP Code:
 while(true)
{
foreach(var 
Monster in ActiveMonsters.ReturnCopy())//ReturnCopy = thread safe 
{
if(
Monster.IsDead) continue;
if(
DateTime.Now Monster.Timers.LastMovment) continue;
if (
Monster.PathFinding)
{
 if (
Monster.Path.Count() < || Monster.Target == null) { Monster.PathFinding false; continue; } 
//Get the next X/Y from the path list and walk/Remove the X/Y from the path list
}
else 
{
 if (
Monster.Target == null) { continue; } 
Monster.Path GetPath(Monster.XMonster.YMonster.Target.XMonster.Target.Y);
if (
Monster.Path.Count() < 1) continue; 
//Get the next X/Y from the path list and walk/Remove the X/Y from the path list
}
Monster.Timers.LastMovment DateTime.Now.AddMilliseconds(Random.Next(mob.Monstertype.MoveSpeedmob.Monstertype.MoveSpeed 400))


Your Pathfinder should check every X/Y and make sure its valid before adding it to the path list and inside the movement thread you only check to make sure no player is standing in the next X/Y that you are about to move the monster to and if there is a player then you clear the path list and try to get a fresh path if needed...

And what i don't understand is if a timer/thread are executing the Move() void then whats getting the Mob.Path? a different thread ?

And before you allow the monsters to attack the target you should check and make sure that at least 300 MS or so has passed since the last jump or else monsters wold attack the target while he is still in the air and change directions as soon as you make a jump

mob.Timers.LastMovment = DateTime.Now; should be right before the return and not in the top of the void

mob.Target = null; Target should never = null unless the target had left the monster screen and should not depend on the monster view range/attack range

And to me it sounds like

A. Monsters are being controlled by more then 1 thread/timer IE: Move() void is being called randomly from the same/different threads so try and use something like the while loop i posted and see if eliminates the problem

B. You are not sending a valid Direction in the walk packet so spawn 1 Pheasant in twin city.. break point at the top of your monsters thread and track his movements based on the direction and how your loop handles it..

C. Monsters X/Y are not really being updated upon walking and the same process in step B can prove if its being updated or no

Good luck buddy...

Edit: I thought you stopped working on your server :|
am doing everything you said, Move() being called from the same thread MonsterAction occurs let me make it more clear by posting the MonsterAction()

Code:
private void MonsterAction() {
            while (true) {
                foreach (var monster in world.Entities.BattleEntities.ActiveMonsters.Values) {
                    #region Guard
                    if (monster.IsGuard) {
                        if (DateTime.Now >= monster.Timers.LastAttack.AddSeconds(4)) {
                            var nearest = world.Entities.Indexer[monster];
                            foreach (var mon in nearest) {
                                if (mon as IMonster != null) {
                                    var mob = mon as IMonster;
                                    if (mob.IsDead) continue;
                                    if (!mob.IsGuard && Calculations.Distance(monster.Location.X, monster.Location.Y, mob.Location.X, mob.Location.Y) <= monster.Monstertype.AttackRange) {
                                        world.Entities.BattleEntities.NotifyMagic(monster, mob.EntityID, mob.Location.X, mob.Location.Y, world.Entities.Database.SkillsManager.GetMagicTypeByIdAndLevel((ushort)monster.Monstertype.MagicType, 0), 24, 0, 0);
                                        monster.Timers.LastAttack = DateTime.Now;
                                        break;
                                    }
                                } else if (mon as IHero != null) {
                                    var hero = mon as IHero;
                                    if (hero.IsDead) continue;
                                    if (hero.ShortFlags.HasFlag(ShortStatusFlags.FlashName) || hero.ShortFlags.HasFlag(ShortStatusFlags.BlackName)) {
                                        world.Entities.BattleEntities.NotifyMagic(monster, hero.EntityID, hero.Location.X, hero.Location.Y, world.Entities.Database.SkillsManager.GetMagicTypeByIdAndLevel((ushort)monster.Monstertype.MagicType, 0), 24, 0, 0);
                                        monster.Timers.LastAttack = DateTime.Now;
                                        break;
                                    }
                                }
                            }
                        } else continue;
                    }
                    #endregion

                    #region NormalMonster
 else {
                        if (monster.Path == null || monster.Target == null) {
                            if (monster.PathTries >= 64) {
                                monster.PathTries = 0;
                                monster.Path = null;
                                monster.Target = null;
                                world.Entities.BattleEntities.ActiveMonsters.TryRemove(monster.EntityID, out tempMonster);
                                continue;
                            }
                            var near = world.Entities.Indexer[monster].Where(x => x is IHero).Cast<IHero>().Where(x => Calculations.Distance(x.Location.X, x.Location.Y, monster.Location.X, monster.Location.Y) <= monster.Monstertype.ViewRange && !x.IsDead);
                            if (near.Count() > 0) hero = near.OrderBy(x => Calculations.Distance(x.Location.X, x.Location.Y, monster.Location.X, monster.Location.Y)).First(); else hero = null;
                            if (hero == null && monster.BirthLocation.Equals(monster.Location)) {
                                world.Entities.BattleEntities.ActiveMonsters.TryRemove(monster.EntityID, out tempMonster);
                                continue;
                            } else if (hero == null && monster.Path == null) {
                                monster.Path = world.DMaps[monster.Map].PathFinder.FindPath(monster.Location, monster.BirthLocation);
                                monster.Target = monster;
                                monster.PathTries++;
                                continue;
                            }
                            monster.Target = hero;
                            monster.Path = world.DMaps[monster.Map].PathFinder.FindPath(monster.Location, hero.Location);
                            monster.PathTries++;
                        }
                        if (Calculations.Distance(monster.Location.X, monster.Location.Y, monster.Target.Location.X, monster.Target.Location.Y) != 1) {
                            Move(monster);
                        } else {
                            if (monster.Target.Equals(monster)) {
                                world.Entities.BattleEntities.ActiveMonsters.TryRemove(monster.EntityID, out tempMonster);
                            } else {
                                if (monster.Monstertype.MagicType == 0 && !monster.IsDead) {
                                    world.Entities.BattleEntities.NotifyMelee(monster, monster.Target);
                                } else {
                                    world.Entities.BattleEntities.NotifyMagic(monster, monster.Target.EntityID, monster.Target.Location.X, monster.Target.Location.Y, world.Entities.Database.SkillsManager.GetMagicTypeByIdAndLevel((ushort)monster.Monstertype.MagicType, 0), (uint)24, 0, 0);
                                }
                                continue;
                            }
                        }
                        if (monster.Path != null) {
                            if (monster.Target == null || monster.Location.Equals(monster.BirthLocation) || monster.Path.Count == 0) {
                                monster.Target = null;
                                monster.Path = null;
                            }
                        }

                    }
                    #endregion
                }
                Thread.Sleep(1000);
            }
        }
so idk what is wrong :\

PS: I DID STOP WORKING ON IT :- I just finished exams was bored , so I tried to fix this shitty bug :(
01/17/2013 11:27 { Angelius }#7
Assuming the foreach (var loc in mob.Path) is not breaking after processing the first location and its looping through all the items in the collection try to get rid of it and replace it with something like
var loc = mob.Path.First(); or var loc = mob.Path.Last(); depends on the mob.Path collection type

And Thread.Sleep(1000); with Thread.Sleep(1); as monsters wont be waiting a second to move again
01/17/2013 13:46 Mr_PoP#8
thanks guys for your replies , I have fixed the issue , it was never my AI or how am handling mobs , it was the stupid client , it was requesting GeneralData with Subtype->( EntityReSpawn = 102) , that was causing the issue , coz when the mob is walking , and client request to respwan it, it looks like that , so I just removed the handling or respawning and it worked fine , I never needed it anyways :D
01/17/2013 14:41 shadowman123#9
Am glad for u .. good luck
01/17/2013 22:50 JohnHeatz#10
As this issue has been resolved, and the fix for it has even been posted, I'd guess you won't be mad if I close it ;)