anyclue why the mobs might move like it do in the video?
|
|
|
|
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;
}
}
}
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() < 1 || 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.X, Monster.Y, Monster.Target.X, Monster.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.MoveSpeed, mob.Monstertype.MoveSpeed + 400))
}
am doing everything you said, Move() being called from the same thread MonsterAction occurs let me make it more clear by posting the MonsterAction()Quote:
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...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() < 1 || 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.X, Monster.Y, Monster.Target.X, Monster.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.MoveSpeed, mob.Monstertype.MoveSpeed + 400))
}
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 :|
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);
}
}