diff options
author | Mic Bowman | 2011-08-05 11:13:02 -0700 |
---|---|---|
committer | Mic Bowman | 2011-08-05 11:13:02 -0700 |
commit | c3f579046c4de7a5a65e71e4c02958425fd7998a (patch) | |
tree | b725d9eaa66d5eb7cf1f87ddbb4fa0f4284f71f5 /OpenSim/Region/OptionalModules | |
parent | BulletSim: Parameters settable from ini file. Linksets. Physical property val... (diff) | |
parent | remove the largely unused copy/pasted HandleAgentRequestSit() method (diff) | |
download | opensim-SC_OLD-c3f579046c4de7a5a65e71e4c02958425fd7998a.zip opensim-SC_OLD-c3f579046c4de7a5a65e71e4c02958425fd7998a.tar.gz opensim-SC_OLD-c3f579046c4de7a5a65e71e4c02958425fd7998a.tar.bz2 opensim-SC_OLD-c3f579046c4de7a5a65e71e4c02958425fd7998a.tar.xz |
Merge branch 'master' into bulletsim
Diffstat (limited to 'OpenSim/Region/OptionalModules')
5 files changed, 190 insertions, 25 deletions
diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 88db20e..a0c1ab1 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs | |||
@@ -806,7 +806,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
806 | public event ScriptReset OnScriptReset; | 806 | public event ScriptReset OnScriptReset; |
807 | public event GetScriptRunning OnGetScriptRunning; | 807 | public event GetScriptRunning OnGetScriptRunning; |
808 | public event SetScriptRunning OnSetScriptRunning; | 808 | public event SetScriptRunning OnSetScriptRunning; |
809 | public event UpdateVector OnAutoPilotGo; | 809 | public event Action<Vector3> OnAutoPilotGo; |
810 | public event TerrainUnacked OnUnackedTerrain; | 810 | public event TerrainUnacked OnUnackedTerrain; |
811 | public event ActivateGesture OnActivateGesture; | 811 | public event ActivateGesture OnActivateGesture; |
812 | public event DeactivateGesture OnDeactivateGesture; | 812 | public event DeactivateGesture OnDeactivateGesture; |
@@ -1678,7 +1678,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server | |||
1678 | { | 1678 | { |
1679 | } | 1679 | } |
1680 | 1680 | ||
1681 | public void SendTextBoxRequest(string message, int chatChannel, string objectname, string ownerFirstName, string ownerLastName, UUID objectId) | 1681 | public void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId) |
1682 | { | 1682 | { |
1683 | } | 1683 | } |
1684 | 1684 | ||
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/MonitorServicesModule.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/MonitorServicesModule.cs index d49face..a25e034 100644 --- a/OpenSim/Region/OptionalModules/Framework/Monitoring/MonitorServicesModule.cs +++ b/OpenSim/Region/OptionalModules/Framework/Monitoring/MonitorServicesModule.cs | |||
@@ -47,7 +47,7 @@ namespace OpenSim.Region.OptionalModules.Framework.Monitoring | |||
47 | { | 47 | { |
48 | protected Scene m_scene; | 48 | protected Scene m_scene; |
49 | 49 | ||
50 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 50 | // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
51 | 51 | ||
52 | public string Name { get { return "Services Health Monitoring Module"; } } | 52 | public string Name { get { return "Services Health Monitoring Module"; } } |
53 | 53 | ||
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 15bc1b7..dfc624d 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs | |||
@@ -99,6 +99,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
99 | { | 99 | { |
100 | 100 | ||
101 | } | 101 | } |
102 | |||
102 | public void SendSitResponse(UUID TargetID, Vector3 OffsetPos, Quaternion SitOrientation, bool autopilot, | 103 | public void SendSitResponse(UUID TargetID, Vector3 OffsetPos, Quaternion SitOrientation, bool autopilot, |
103 | Vector3 CameraAtOffset, Vector3 CameraEyeOffset, bool ForceMouseLook) | 104 | Vector3 CameraAtOffset, Vector3 CameraEyeOffset, bool ForceMouseLook) |
104 | { | 105 | { |
@@ -327,7 +328,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
327 | public event ScriptReset OnScriptReset; | 328 | public event ScriptReset OnScriptReset; |
328 | public event GetScriptRunning OnGetScriptRunning; | 329 | public event GetScriptRunning OnGetScriptRunning; |
329 | public event SetScriptRunning OnSetScriptRunning; | 330 | public event SetScriptRunning OnSetScriptRunning; |
330 | public event UpdateVector OnAutoPilotGo; | 331 | public event Action<Vector3> OnAutoPilotGo; |
331 | 332 | ||
332 | public event TerrainUnacked OnUnackedTerrain; | 333 | public event TerrainUnacked OnUnackedTerrain; |
333 | 334 | ||
@@ -1157,7 +1158,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
1157 | { | 1158 | { |
1158 | } | 1159 | } |
1159 | 1160 | ||
1160 | public void SendTextBoxRequest(string message, int chatChannel, string objectname, string ownerFirstName, string ownerLastName, UUID objectId) | 1161 | public void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId) |
1161 | { | 1162 | { |
1162 | } | 1163 | } |
1163 | 1164 | ||
diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index 3cdd06d..4f21d9d 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs | |||
@@ -44,14 +44,75 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
44 | { | 44 | { |
45 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 45 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
46 | 46 | ||
47 | // private const bool m_enabled = false; | 47 | private Dictionary<UUID, NPCAvatar> m_avatars = new Dictionary<UUID, NPCAvatar>(); |
48 | 48 | private Dictionary<UUID, AvatarAppearance> m_appearanceCache = new Dictionary<UUID, AvatarAppearance>(); | |
49 | private Dictionary<UUID,NPCAvatar> m_avatars = new Dictionary<UUID, NPCAvatar>(); | ||
50 | private Dictionary<UUID,AvatarAppearance> m_appearanceCache = new Dictionary<UUID, AvatarAppearance>(); | ||
51 | 49 | ||
52 | public void Initialise(Scene scene, IConfigSource source) | 50 | public void Initialise(Scene scene, IConfigSource source) |
53 | { | 51 | { |
54 | scene.RegisterModuleInterface<INPCModule>(this); | 52 | IConfig config = source.Configs["NPC"]; |
53 | |||
54 | if (config != null && config.GetBoolean("Enabled", false)) | ||
55 | { | ||
56 | scene.RegisterModuleInterface<INPCModule>(this); | ||
57 | scene.EventManager.OnSignificantClientMovement += HandleOnSignificantClientMovement; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | public void HandleOnSignificantClientMovement(ScenePresence presence) | ||
62 | { | ||
63 | lock (m_avatars) | ||
64 | { | ||
65 | if (m_avatars.ContainsKey(presence.UUID) && presence.MovingToTarget) | ||
66 | { | ||
67 | double distanceToTarget = Util.GetDistanceTo(presence.AbsolutePosition, presence.MoveToPositionTarget); | ||
68 | // m_log.DebugFormat( | ||
69 | // "[NPC MODULE]: Abs pos of {0} is {1}, target {2}, distance {3}", | ||
70 | // presence.Name, presence.AbsolutePosition, presence.MoveToPositionTarget, distanceToTarget); | ||
71 | |||
72 | // Check the error term of the current position in relation to the target position | ||
73 | if (distanceToTarget <= ScenePresence.SIGNIFICANT_MOVEMENT) | ||
74 | { | ||
75 | // We are close enough to the target | ||
76 | m_log.DebugFormat("[NPC MODULE]: Stopping movement of npc {0}", presence.Name); | ||
77 | |||
78 | if (presence.PhysicsActor.Flying) | ||
79 | { | ||
80 | Vector3 targetPos = presence.MoveToPositionTarget; | ||
81 | float terrainHeight = (float)presence.Scene.Heightmap[(int)targetPos.X, (int)targetPos.Y]; | ||
82 | if (targetPos.Z - terrainHeight < 0.2) | ||
83 | { | ||
84 | presence.PhysicsActor.Flying = false; | ||
85 | } | ||
86 | } | ||
87 | |||
88 | presence.Velocity = Vector3.Zero; | ||
89 | presence.AbsolutePosition = presence.MoveToPositionTarget; | ||
90 | presence.ResetMoveToTarget(); | ||
91 | |||
92 | // FIXME: This doesn't work | ||
93 | if (presence.PhysicsActor.Flying) | ||
94 | presence.Animator.TrySetMovementAnimation("HOVER"); | ||
95 | else | ||
96 | presence.Animator.TrySetMovementAnimation("STAND"); | ||
97 | } | ||
98 | else | ||
99 | { | ||
100 | m_log.DebugFormat( | ||
101 | "[NPC MODULE]: Updating npc {0} at {1} for next movement to {2}", | ||
102 | presence.Name, presence.AbsolutePosition, presence.MoveToPositionTarget); | ||
103 | |||
104 | Vector3 agent_control_v3 = new Vector3(); | ||
105 | presence.HandleMoveToTargetUpdate(ref agent_control_v3, presence.Rotation); | ||
106 | presence.AddNewMovement(agent_control_v3, presence.Rotation); | ||
107 | } | ||
108 | // | ||
109 | //// presence.DoMoveToPositionUpdate((0, presence.MoveToPositionTarget, null); | ||
110 | |||
111 | // | ||
112 | // | ||
113 | |||
114 | } | ||
115 | } | ||
55 | } | 116 | } |
56 | 117 | ||
57 | private AvatarAppearance GetAppearance(UUID target, Scene scene) | 118 | private AvatarAppearance GetAppearance(UUID target, Scene scene) |
@@ -59,14 +120,21 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
59 | if (m_appearanceCache.ContainsKey(target)) | 120 | if (m_appearanceCache.ContainsKey(target)) |
60 | return m_appearanceCache[target]; | 121 | return m_appearanceCache[target]; |
61 | 122 | ||
62 | AvatarAppearance appearance = scene.AvatarService.GetAppearance(target); | 123 | ScenePresence originalPresence = scene.GetScenePresence(target); |
63 | if (appearance != null) | 124 | |
125 | if (originalPresence != null) | ||
64 | { | 126 | { |
65 | m_appearanceCache.Add(target, appearance); | 127 | AvatarAppearance originalAppearance = originalPresence.Appearance; |
66 | return appearance; | 128 | m_appearanceCache.Add(target, originalAppearance); |
129 | return originalAppearance; | ||
67 | } | 130 | } |
131 | else | ||
132 | { | ||
133 | m_log.DebugFormat( | ||
134 | "[NPC MODULE]: Avatar {0} is not in the scene for us to grab baked textures from them. Using defaults.", target); | ||
68 | 135 | ||
69 | return new AvatarAppearance(); | 136 | return new AvatarAppearance(); |
137 | } | ||
70 | } | 138 | } |
71 | 139 | ||
72 | public UUID CreateNPC(string firstname, string lastname, Vector3 position, Scene scene, UUID cloneAppearanceFrom) | 140 | public UUID CreateNPC(string firstname, string lastname, Vector3 position, Scene scene, UUID cloneAppearanceFrom) |
@@ -88,6 +156,13 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
88 | AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true); | 156 | AvatarAppearance npcAppearance = new AvatarAppearance(originalAppearance, true); |
89 | acd.Appearance = npcAppearance; | 157 | acd.Appearance = npcAppearance; |
90 | 158 | ||
159 | // for (int i = 0; i < acd.Appearance.Texture.FaceTextures.Length; i++) | ||
160 | // { | ||
161 | // m_log.DebugFormat( | ||
162 | // "[NPC MODULE]: NPC avatar {0} has texture id {1} : {2}", | ||
163 | // acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]); | ||
164 | // } | ||
165 | |||
91 | scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); | 166 | scene.AuthenticateHandler.AddNewCircuit(npcAvatar.CircuitCode, acd); |
92 | scene.AddNewClient(npcAvatar); | 167 | scene.AddNewClient(npcAvatar); |
93 | 168 | ||
@@ -118,7 +193,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
118 | return npcAvatar.AgentId; | 193 | return npcAvatar.AgentId; |
119 | } | 194 | } |
120 | 195 | ||
121 | public void Autopilot(UUID agentID, Scene scene, Vector3 pos) | 196 | public void MoveToTarget(UUID agentID, Scene scene, Vector3 pos) |
122 | { | 197 | { |
123 | lock (m_avatars) | 198 | lock (m_avatars) |
124 | { | 199 | { |
@@ -126,7 +201,11 @@ namespace OpenSim.Region.OptionalModules.World.NPC | |||
126 | { | 201 | { |
127 | ScenePresence sp; | 202 | ScenePresence sp; |
128 | scene.TryGetScenePresence(agentID, out sp); | 203 | scene.TryGetScenePresence(agentID, out sp); |
129 | sp.DoAutoPilot(0, pos, m_avatars[agentID]); | 204 | |
205 | m_log.DebugFormat( | ||
206 | "[NPC MODULE]: Moving {0} to {1} in {2}", sp.Name, pos, scene.RegionInfo.RegionName); | ||
207 | |||
208 | sp.MoveToTarget(pos); | ||
130 | } | 209 | } |
131 | } | 210 | } |
132 | } | 211 | } |
diff --git a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs index 899e721..c9dddba 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs | |||
@@ -27,11 +27,13 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Reflection; | 29 | using System.Reflection; |
30 | using log4net; | ||
30 | using Nini.Config; | 31 | using Nini.Config; |
31 | using NUnit.Framework; | 32 | using NUnit.Framework; |
32 | using OpenMetaverse; | 33 | using OpenMetaverse; |
33 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
34 | using OpenSim.Framework.Communications; | 35 | using OpenSim.Framework.Communications; |
36 | using OpenSim.Region.CoreModules.Avatar.AvatarFactory; | ||
35 | using OpenSim.Region.CoreModules.ServiceConnectorsOut.Avatar; | 37 | using OpenSim.Region.CoreModules.ServiceConnectorsOut.Avatar; |
36 | using OpenSim.Region.Framework.Interfaces; | 38 | using OpenSim.Region.Framework.Interfaces; |
37 | using OpenSim.Region.Framework.Scenes; | 39 | using OpenSim.Region.Framework.Scenes; |
@@ -51,21 +53,104 @@ namespace OpenSim.Region.OptionalModules.World.NPC.Tests | |||
51 | // log4net.Config.XmlConfigurator.Configure(); | 53 | // log4net.Config.XmlConfigurator.Configure(); |
52 | 54 | ||
53 | IConfigSource config = new IniConfigSource(); | 55 | IConfigSource config = new IniConfigSource(); |
56 | config.AddConfig("NPC"); | ||
57 | config.Configs["NPC"].Set("Enabled", "true"); | ||
54 | 58 | ||
55 | config.AddConfig("Modules"); | 59 | AvatarFactoryModule afm = new AvatarFactoryModule(); |
56 | config.Configs["Modules"].Set("AvatarServices", "LocalAvatarServicesConnector"); | ||
57 | config.AddConfig("AvatarService"); | ||
58 | config.Configs["AvatarService"].Set("LocalServiceModule", "OpenSim.Services.AvatarService.dll:AvatarService"); | ||
59 | config.Configs["AvatarService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); | ||
60 | |||
61 | TestScene scene = SceneSetupHelpers.SetupScene(); | 60 | TestScene scene = SceneSetupHelpers.SetupScene(); |
62 | SceneSetupHelpers.SetupSceneModules(scene, config, new NPCModule(), new LocalAvatarServicesConnector()); | 61 | SceneSetupHelpers.SetupSceneModules(scene, config, afm, new NPCModule()); |
62 | TestClient originalClient = SceneSetupHelpers.AddClient(scene, TestHelper.ParseTail(0x1)); | ||
63 | // ScenePresence originalAvatar = scene.GetScenePresence(originalClient.AgentId); | ||
64 | |||
65 | // 8 is the index of the first baked texture in AvatarAppearance | ||
66 | UUID originalFace8TextureId = TestHelper.ParseTail(0x10); | ||
67 | Primitive.TextureEntry originalTe = new Primitive.TextureEntry(UUID.Zero); | ||
68 | Primitive.TextureEntryFace originalTef = originalTe.CreateFace(8); | ||
69 | originalTef.TextureID = originalFace8TextureId; | ||
70 | |||
71 | // We also need to add the texture to the asset service, otherwise the AvatarFactoryModule will tell | ||
72 | // ScenePresence.SendInitialData() to reset our entire appearance. | ||
73 | scene.AssetService.Store(AssetHelpers.CreateAsset(originalFace8TextureId)); | ||
74 | |||
75 | afm.SetAppearance(originalClient, originalTe, null); | ||
63 | 76 | ||
64 | INPCModule npcModule = scene.RequestModuleInterface<INPCModule>(); | 77 | INPCModule npcModule = scene.RequestModuleInterface<INPCModule>(); |
65 | UUID npcId = npcModule.CreateNPC("John", "Smith", new Vector3(128, 128, 30), scene, UUID.Zero); | 78 | UUID npcId = npcModule.CreateNPC("John", "Smith", new Vector3(128, 128, 30), scene, originalClient.AgentId); |
66 | 79 | ||
67 | ScenePresence npc = scene.GetScenePresence(npcId); | 80 | ScenePresence npc = scene.GetScenePresence(npcId); |
81 | |||
68 | Assert.That(npc, Is.Not.Null); | 82 | Assert.That(npc, Is.Not.Null); |
83 | Assert.That(npc.Appearance.Texture.FaceTextures[8].TextureID, Is.EqualTo(originalFace8TextureId)); | ||
84 | } | ||
85 | |||
86 | [Test] | ||
87 | public void TestMove() | ||
88 | { | ||
89 | TestHelper.InMethod(); | ||
90 | // log4net.Config.XmlConfigurator.Configure(); | ||
91 | |||
92 | IConfigSource config = new IniConfigSource(); | ||
93 | |||
94 | config.AddConfig("NPC"); | ||
95 | config.Configs["NPC"].Set("Enabled", "true"); | ||
96 | |||
97 | TestScene scene = SceneSetupHelpers.SetupScene(); | ||
98 | SceneSetupHelpers.SetupSceneModules(scene, config, new NPCModule()); | ||
99 | TestClient originalClient = SceneSetupHelpers.AddClient(scene, TestHelper.ParseTail(0x1)); | ||
100 | // ScenePresence originalAvatar = scene.GetScenePresence(originalClient.AgentId); | ||
101 | |||
102 | Vector3 startPos = new Vector3(128, 128, 30); | ||
103 | INPCModule npcModule = scene.RequestModuleInterface<INPCModule>(); | ||
104 | UUID npcId = npcModule.CreateNPC("John", "Smith", startPos, scene, originalClient.AgentId); | ||
105 | |||
106 | ScenePresence npc = scene.GetScenePresence(npcId); | ||
107 | Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos)); | ||
108 | |||
109 | // For now, we'll make the scene presence fly to simplify this test, but this needs to change. | ||
110 | npc.PhysicsActor.Flying = true; | ||
111 | |||
112 | scene.Update(); | ||
113 | Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos)); | ||
114 | |||
115 | Vector3 targetPos = startPos + new Vector3(0, 0, 10); | ||
116 | npcModule.MoveToTarget(npc.UUID, scene, targetPos); | ||
117 | |||
118 | Assert.That(npc.AbsolutePosition, Is.EqualTo(startPos)); | ||
119 | |||
120 | scene.Update(); | ||
121 | |||
122 | // We should really check the exact figure. | ||
123 | Assert.That(npc.AbsolutePosition.X, Is.EqualTo(startPos.X)); | ||
124 | Assert.That(npc.AbsolutePosition.Y, Is.EqualTo(startPos.Y)); | ||
125 | Assert.That(npc.AbsolutePosition.Z, Is.GreaterThan(startPos.Z)); | ||
126 | Assert.That(npc.AbsolutePosition.Z, Is.LessThan(targetPos.Z)); | ||
127 | |||
128 | for (int i = 0; i < 10; i++) | ||
129 | scene.Update(); | ||
130 | |||
131 | double distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos); | ||
132 | Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on first move"); | ||
133 | Assert.That(npc.AbsolutePosition, Is.EqualTo(targetPos)); | ||
134 | |||
135 | // Try a second movement | ||
136 | startPos = npc.AbsolutePosition; | ||
137 | targetPos = startPos + new Vector3(10, 0, 0); | ||
138 | npcModule.MoveToTarget(npc.UUID, scene, targetPos); | ||
139 | |||
140 | scene.Update(); | ||
141 | |||
142 | // We should really check the exact figure. | ||
143 | Assert.That(npc.AbsolutePosition.X, Is.GreaterThan(startPos.X)); | ||
144 | Assert.That(npc.AbsolutePosition.X, Is.LessThan(targetPos.X)); | ||
145 | Assert.That(npc.AbsolutePosition.Y, Is.EqualTo(startPos.Y)); | ||
146 | Assert.That(npc.AbsolutePosition.Z, Is.EqualTo(startPos.Z)); | ||
147 | |||
148 | for (int i = 0; i < 10; i++) | ||
149 | scene.Update(); | ||
150 | |||
151 | distanceToTarget = Util.GetDistanceTo(npc.AbsolutePosition, targetPos); | ||
152 | Assert.That(distanceToTarget, Is.LessThan(1), "NPC not within 1 unit of target position on second move"); | ||
153 | Assert.That(npc.AbsolutePosition, Is.EqualTo(targetPos)); | ||
69 | } | 154 | } |
70 | } | 155 | } |
71 | } \ No newline at end of file | 156 | } \ No newline at end of file |