/src/Mooege/Core/GS/Map/Debug/DebugNavMesh.cs
C# | 347 lines | 263 code | 62 blank | 22 comment | 44 complexity | 64929fb2947935440bc83475f3666e67 MD5 | raw file
1/*
2 * Copyright (C) 2011 mooege project
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19using System.Drawing;
20using System.Linq;
21using System.Threading.Tasks;
22using System.Windows;
23using Mooege.Common.Helpers.Concurrency;
24using Mooege.Core.GS.Actors;
25using Mooege.Core.GS.Players;
26
27namespace Mooege.Core.GS.Map.Debug
28{
29 public class DebugNavMesh
30 {
31 public World World { get; private set; }
32 public Player Player { get; private set; }
33 public Rect Bounds { get { return World.QuadTree.RootNode.Bounds; } }
34
35 public ConcurrentList<Scene> MasterScenes { get; private set; }
36 public ConcurrentList<Scene> SubScenes { get; private set; }
37 public ConcurrentList<Rect> UnWalkableCells { get; private set; }
38 public ConcurrentList<Rect> WalkableCells { get; private set; }
39 public ConcurrentList<Player> Players { get; private set; }
40 public ConcurrentList<Monster> Monsters { get; private set; }
41 public ConcurrentList<NPC> NPCs { get; private set; }
42
43 public bool DrawMasterScenes;
44 public bool DrawSubScenes;
45 public bool DrawWalkableCells;
46 public bool DrawUnwalkableCells;
47 public bool DrawMonsters;
48 public bool DrawNPCs;
49 public bool DrawPlayers;
50 public bool PrintSceneLabels;
51 public bool FillCells;
52 public bool DrawPlayerProximityCircle;
53 public bool DrawPlayerProximityRectangle;
54
55 private readonly Pen _masterScenePen = new Pen(Color.Black, 1.0f);
56 private readonly Pen _subScenePen = new Pen(Color.DarkGray, 1.0f);
57 private readonly Brush _unwalkableBrush = Brushes.Red;
58 private readonly Pen _unwalkablePen = new Pen(Color.Red, 1.0f);
59 private readonly Brush _walkableBrush = Brushes.Blue;
60 private readonly Pen _walkablePen = new Pen(Color.Blue, 1.0f);
61 private readonly Pen _playerProximityPen = new Pen(Brushes.DarkViolet, 2.0f);
62 private readonly Font _sceneFont = new Font("Verdana", 7);
63
64 public DebugNavMesh(World world, Player player = null)
65 {
66 this.World = world;
67 this.Player = player;
68
69 this._subScenePen.DashStyle = System.Drawing.Drawing2D.DashStyle.DashDot;
70
71 this.MasterScenes = new ConcurrentList<Scene>();
72 this.SubScenes = new ConcurrentList<Scene>();
73 this.UnWalkableCells = new ConcurrentList<Rect>();
74 this.WalkableCells = new ConcurrentList<Rect>();
75 this.Players = new ConcurrentList<Player>();
76 this.Monsters = new ConcurrentList<Monster>();
77 this.NPCs = new ConcurrentList<NPC>();
78 }
79
80 #region update
81
82 public void Update(bool processObjectsInAllTheWorld)
83 {
84 this.MasterScenes.Clear();
85 this.SubScenes.Clear();
86 this.WalkableCells.Clear();
87 this.UnWalkableCells.Clear();
88 this.Players.Clear();
89 this.Monsters.Clear();
90 this.NPCs.Clear();
91
92 var scenes = (processObjectsInAllTheWorld || this.Player == null)
93 ? World.QuadTree.Query<Scene>(World.QuadTree.RootNode.Bounds)
94 : this.Player.GetScenesInRegion();
95
96 Parallel.ForEach(scenes, scene =>
97 {
98 if (scene.Parent == null)
99 this.MasterScenes.Add(scene);
100 else
101 this.SubScenes.Add(scene);
102
103 this.AnalyzeScene(scene);
104 });
105
106 var actors = (processObjectsInAllTheWorld || this.Player == null)
107 ? World.QuadTree.Query<Actor>(World.QuadTree.RootNode.Bounds)
108 : this.Player.GetActorsInRange();
109
110 Parallel.ForEach(actors, actor =>
111 {
112 if (actor is Player)
113 this.Players.Add(actor as Player);
114 else if (actor is NPC)
115 this.NPCs.Add(actor as NPC);
116 else if (actor is Monster)
117 this.Monsters.Add(actor as Monster);
118 });
119 }
120
121 private void AnalyzeScene(Scene scene)
122 {
123 Parallel.ForEach(scene.NavZone.NavCells, cell =>
124 {
125 float x = scene.Position.X + cell.Min.X;
126 float y = scene.Position.Y + cell.Min.Y;
127
128 float sizex = cell.Max.X - cell.Min.X;
129 float sizey = cell.Max.Y - cell.Min.Y;
130
131 var rect = new Rect(x, y, sizex, sizey);
132
133 // TODO: Feature request: Also allow drawing of NavCellFlags.NOSpawn, NavCellFlags.LevelAreaBit0, NavCellFlags.LevelAreaBit1 cells. /raist.
134
135 if ((cell.Flags & Mooege.Common.MPQ.FileFormats.Scene.NavCellFlags.AllowWalk) != Mooege.Common.MPQ.FileFormats.Scene.NavCellFlags.AllowWalk)
136 UnWalkableCells.Add(rect);
137 else
138 WalkableCells.Add(rect);
139 });
140 }
141
142 #endregion
143
144 #region drawing
145
146 public Bitmap Draw()
147 {
148 // As quad-tree always has 4 quad-nodes beneath the root node, the quad node's area will be far larger then actual area covered by scenes.
149 // We don't want to draw a bitmap that's as large as quad-tree's area, as it'll be consuming so much memory.
150 // So instead find the rightMostScene and bottomMostScene and limit the drawed bitmaps size according. /raist.
151 // TODO: We can even limit to leftMostScene and topMostScene because player-proximity rendering mode will be also containing large empty areas. /raist.
152
153 Scene rightMostScene = null;
154 Scene bottomMostScene = null;
155
156 foreach (var scene in this.MasterScenes)
157 {
158 if (rightMostScene == null)
159 rightMostScene = scene;
160
161 if (bottomMostScene == null)
162 bottomMostScene = scene;
163
164 if (scene.Bounds.X + scene.Bounds.Width > rightMostScene.Bounds.X + rightMostScene.Bounds.Width)
165 rightMostScene = scene;
166
167 if (scene.Bounds.Y + scene.Bounds.Height > bottomMostScene.Bounds.Y + bottomMostScene.Bounds.Height)
168 bottomMostScene = scene;
169 }
170
171 if (rightMostScene == null || bottomMostScene == null)
172 return null;
173
174 var maxX = (int) (rightMostScene.Bounds.X + rightMostScene.Bounds.Width) + 1;
175 var maxY = (int)(bottomMostScene.Bounds.Y + bottomMostScene.Bounds.Height) + 1;
176
177 var bitmap = new Bitmap(maxX, maxY, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
178
179 using (var graphics = Graphics.FromImage(bitmap))
180 {
181 graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
182 graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
183 graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighSpeed;
184 graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
185
186 graphics.FillRectangle(Brushes.LightGray, 0, 0, bitmap.Width, bitmap.Height);
187
188 this.DrawShapes(graphics);
189
190 if (this.PrintSceneLabels)
191 this.DrawLabels(graphics);
192
193 graphics.Save();
194 }
195
196 return bitmap;
197 }
198
199 private void DrawShapes(Graphics graphics)
200 {
201 if (this.DrawMasterScenes)
202 {
203 foreach (var scene in this.MasterScenes)
204 {
205 this.DrawScene(graphics, scene);
206 }
207 }
208
209 if (this.DrawSubScenes)
210 {
211 foreach (var scene in this.SubScenes)
212 {
213 this.DrawScene(graphics, scene);
214 }
215 }
216
217 if (this.DrawWalkableCells)
218 this.DrawWalkables(graphics);
219
220 if (this.DrawUnwalkableCells)
221 this.DrawUnwalkables(graphics);
222
223 if (this.DrawMonsters)
224 {
225 foreach (var monster in this.Monsters)
226 {
227 this.DrawActor(monster, graphics, Brushes.Green, 7);
228 }
229 }
230
231 if (this.DrawNPCs)
232 {
233 foreach (var npc in this.NPCs)
234 {
235 this.DrawActor(npc, graphics, Brushes.Orange, 7);
236 }
237 }
238
239 if (this.DrawPlayers)
240 {
241 foreach (var player in this.Players)
242 {
243 this.DrawActor(player, graphics, Brushes.DarkViolet, 7);
244 }
245 }
246
247 if(this.DrawPlayerProximityCircle)
248 this.DrawProximityCircle(graphics);
249
250 if(this.DrawPlayerProximityRectangle)
251 this.DrawProximityRectangle(graphics);
252 }
253
254 private void DrawScene(Graphics graphics, Scene scene)
255 {
256 var rect = new Rectangle((int)scene.Bounds.Left, (int)scene.Bounds.Top, (int)scene.Bounds.Width, (int)scene.Bounds.Height);
257 graphics.DrawRectangle(scene.Parent == null ? _masterScenePen : _subScenePen, rect);
258 }
259
260 private void DrawWalkables(Graphics graphics)
261 {
262 foreach (var cell in this.WalkableCells)
263 {
264 var rect = new Rectangle(new System.Drawing.Point((int)cell.Left, (int)cell.Top), new System.Drawing.Size((int)cell.Width, (int)cell.Height));
265
266 if (this.FillCells)
267 graphics.FillRectangle(_walkableBrush, rect);
268 else
269 graphics.DrawRectangle(_walkablePen, rect);
270 }
271 }
272
273 private void DrawUnwalkables(Graphics graphics)
274 {
275 foreach (var cell in this.UnWalkableCells)
276 {
277 var rect = new Rectangle(new System.Drawing.Point((int)cell.Left, (int)cell.Top), new System.Drawing.Size((int)cell.Width, (int)cell.Height));
278
279 if (this.FillCells)
280 graphics.FillRectangle(_unwalkableBrush, rect);
281 else
282 graphics.DrawRectangle(_unwalkablePen, rect);
283 }
284 }
285
286 private void DrawActor(Actor actor, Graphics graphics, Brush brush, int radius)
287 {
288 var rect = new Rectangle((int)actor.Bounds.X, (int)actor.Bounds.Y, (int)actor.Bounds.Width + radius, (int)actor.Bounds.Height + radius);
289 graphics.FillEllipse(brush, rect);
290 }
291
292 private void DrawProximityCircle(Graphics graphics)
293 {
294 if (this.Player == null)
295 return;
296
297 var rect = new RectangleF(this.Player.Position.X - Actor.DefaultQueryProximityRadius,
298 this.Player.Position.Y - Actor.DefaultQueryProximityRadius,
299 Actor.DefaultQueryProximityRadius * 2,
300 Actor.DefaultQueryProximityRadius * 2);
301
302 graphics.DrawEllipse(this._playerProximityPen, rect);
303 }
304
305 private void DrawProximityRectangle(Graphics graphics)
306 {
307 if (this.Player == null)
308 return;
309
310 graphics.DrawRectangle(this._playerProximityPen,
311 this.Player.Position.X - Actor.DefaultQueryProximityLenght/2,
312 this.Player.Position.Y - Actor.DefaultQueryProximityLenght/2,
313 Actor.DefaultQueryProximityLenght,
314 Actor.DefaultQueryProximityLenght);
315 }
316
317 private void DrawLabels(Graphics graphics)
318 {
319 if (this.DrawMasterScenes)
320 {
321 foreach (var scene in this.MasterScenes)
322 {
323 this.DrawSceneLabel(graphics, scene);
324 }
325 }
326
327 if (this.DrawSubScenes)
328 {
329 foreach (var scene in this.SubScenes)
330 {
331 this.DrawSceneLabel(graphics, scene);
332 }
333 }
334 }
335
336 private void DrawSceneLabel(Graphics graphics, Scene scene)
337 {
338 var stringRectangle = new RectangleF((float)scene.Bounds.Left, (float)scene.Bounds.Top, (float)scene.Bounds.Width, (float)scene.Bounds.Height);
339 var drawFormat = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
340
341 if (!string.IsNullOrEmpty(scene.SceneSNO.Name))
342 graphics.DrawString(scene.SceneSNO.Name, _sceneFont, scene.Parent == null ? Brushes.Black : Brushes.Gray, stringRectangle, drawFormat);
343 }
344
345 #endregion
346 }
347}