source: 2013/30/DenisZ/CastleMaster/CastleMaster/CastleMaster/World/Level.cs @ 4764

Revision 4764, 18.5 KB checked in by dezhidki, 8 years ago (diff)
Line 
1using CastleMaster.Entities;
2using CastleMaster.Entities.TileEntities;
3using CastleMaster.Graphics;
4using CastleMaster.Guis;
5using CastleMaster.Physics;
6using CastleMaster.Players;
7using CastleMaster.Units;
8using CastleMaster.Units.Mobs;
9using CastleMaster.World.Tiles;
10using Microsoft.Xna.Framework;
11using Microsoft.Xna.Framework.Graphics;
12using System.Collections.Generic;
13using Viewport = CastleMaster.Graphics.Viewport;
14
15namespace CastleMaster.World
16{
17
18    public class EntityComprarer : Comparer<Entity>
19    {
20        /// <summary>
21        /// Compares entities between them.
22        /// </summary>
23        /// <param name="e1">Entity 1.</param>
24        /// <param name="e2">Entity 2.</param>
25        /// <returns>-1 = e1 render before e2 (e1 -> e2). +1 = e1 render after e2 (e2 -> e1).</returns>
26        public override int Compare(Entity e1, Entity e2)
27        {
28            BoundingRectangle br1 = e1.BoundingRectangle;
29            BoundingRectangle br2 = e2.BoundingRectangle;
30
31            if (br1.ZFar >= br2.ZNear)
32            {
33                //if ((e1.Z + e1.X - e1.RenderOffset.Y) - e1.RenderOffset.Y < (e2.Z + e2.X - e2.RenderOffset.Y) - e2.RenderOffset.Y) return -1;
34                return +1;
35            }
36            if (br1.ZNear <= br2.ZFar)
37                return -1;
38            if (br1.XRight <= br2.XLeft)
39                return -1;
40            if (br1.XLeft >= br2.XRight)
41                return +1;
42
43            return 0;
44        }
45    }
46
47    public class Level
48    {
49        public const int TILE_VOID = 0;
50        public const int UPDATES_IN_TICK = 500;
51        protected List<Tile> registeredTiles;
52        protected int[] tiles;
53        protected byte[] data;
54
55        private List<Entity> entities;
56        private List<TileEntity> tileEntities;
57        private List<Entity>[] entitiesInTiles;
58        private List<Entity> entitesToRender;
59        private List<Unit>[] units;
60        private int width, height;
61        protected Texture2D tileMap;
62        private EntityComprarer comparer = new EntityComprarer();
63        private TimerHelper waterAnimation = new TimerHelper(10, 2).Start();
64        private Player[] players;
65
66        public Level(Texture2D tileMap)
67        {
68            this.tileMap = tileMap;
69            width = tileMap.Width;
70            height = tileMap.Height;
71
72            players = new Player[2];
73            tiles = new int[width * height];
74            data = new byte[width * height];
75            registeredTiles = new List<Tile>();
76
77            units = new List<Unit>[2];
78            units[0] = new List<Unit>();
79            units[1] = new List<Unit>();
80
81            entities = new List<Entity>();
82            tileEntities = new List<TileEntity>();
83            entitiesInTiles = new List<Entity>[width * height];
84            for (int i = 0; i < width * height; i++)
85                entitiesInTiles[i] = new List<Entity>();
86            entitesToRender = new List<Entity>();
87        }
88
89        public List<Unit>[] Units { get { return units; } }
90
91        public Player[] Players { get { return players; } }
92
93        public int[] Tiles { get { return tiles; } }
94
95        public int Width { get { return width; } }
96
97        public int Height { get { return height; } }
98
99        public List<Tile> RegisteredTiles { get { return registeredTiles; } }
100
101        public int WaterTimer { get { return waterAnimation.CurrentFrame; } }
102
103        protected virtual void InitTiles() { }
104
105        public virtual void InitLevel()
106        {
107            new Tile(this);
108            InitTiles();
109        }
110
111        public void RenderBackground(Camera camera, RenderHelper renderer)
112        {
113            float w = (Game.WIDTH / Viewport.FLOOR_TILE_WIDTH) / Viewport.ZOOM;
114            float h = ((Game.HEIGHT - GuiPlayer.GUI_BAR_HEIGHT) / Viewport.Y_STEP) / Viewport.ZOOM;
115
116            int x0 = (int)((camera.XLeft / Viewport.FLOOR_TILE_WIDTH) / Viewport.ZOOM) - 3;
117            int y0 = (int)((camera.YTop / Viewport.Y_STEP) / Viewport.ZOOM) - 5;
118            int x1 = (int)(x0 + w) + 5;
119            int y1 = (int)(y0 + h) + 7;
120
121            renderer.SetOffset(camera);
122            int tp;
123            for (int y = y0; y < y1; y++)
124            {
125                for (int x = x0; x < x1; x++)
126                {
127                    int xTile = x + (y >> 1) + (y & 1);
128                    int zTile = (y >> 1) - x;
129
130                    if (xTile >= 0 && zTile >= 0 && xTile < width && zTile < height)
131                    {
132                        tp = xTile + zTile * width;
133                        Tile t = registeredTiles[tiles[tp]];
134                        if (t.ID != TILE_VOID)
135                            t.Render(renderer, this, new Vector2((x * Viewport.FLOOR_TILE_WIDTH + (y & 1) * Viewport.X_STEP) * Viewport.ZOOM, (y * Viewport.Y_STEP + Viewport.Y_STEP) * Viewport.ZOOM), xTile, zTile, data[tp]);
136                        entitiesInTiles[tp].ForEach(AddEntityToRenderList);
137                    }
138                }
139            }
140
141            renderer.SetOffset();
142        }
143
144        public void AddEntityToRenderList(Entity ent)
145        {
146            if (!ent.IsRendering) entitesToRender.Add(ent);
147            ent.IsRendering = true;
148        }
149
150        public void RenderEntities(Camera camera, RenderHelper renderer)
151        {
152            if (entitesToRender.Count > 0)
153            {
154                entitesToRender.Sort(comparer);
155
156                renderer.SetOffset(camera);
157
158                foreach (Entity ent in entitesToRender)
159                {
160                    ent.Render(renderer);
161                    ent.IsRendering = false;
162                }
163
164                renderer.SetOffset();
165
166                entitesToRender.Clear();
167            }
168        }
169
170        public void Update()
171        {
172            waterAnimation.UpdateStep();
173
174            for (int i = 0; i < UPDATES_IN_TICK; i++)
175            {
176                int xTile = Game.Random.Next(width);
177                int zTile = Game.Random.Next(height);
178
179                registeredTiles[tiles[xTile + zTile * width]].Update(this, xTile, zTile);
180
181                if (tileEntities.Count > 0)
182                {
183                    TileEntity te = tileEntities[Game.Random.Next(tileEntities.Count)];
184                    te.Update();
185                    if (te.Removed)
186                    {
187                        RemoveEntity(te);
188                        TakeEntity(te, te.XTile, te.ZTile);
189                    }
190                }
191            }
192
193            for (int i = 0; i < entities.Count; i++)
194            {
195                Entity ent = entities[i];
196
197                int xTile_old = (int)(ent.X / Viewport.TILESIZE);
198                int zTile_old = (int)(ent.Z / Viewport.TILESIZE);
199
200                ent.Update();
201
202                if (ent.Removed)
203                {
204                    RemoveEntity(ent);
205                    TakeEntity(ent, xTile_old, zTile_old);
206                    i--;
207                    continue;
208                }
209
210                int xTile = (int)(ent.X / Viewport.TILESIZE);
211                int zTile = (int)(ent.Z / Viewport.TILESIZE);
212
213                if (xTile != xTile_old || zTile != zTile_old)
214                {
215                    TakeEntity(ent, xTile_old, zTile_old);
216                    InsertEntity(ent, xTile, zTile);
217                }
218            }
219        }
220
221        public void RemoveUnit(Unit u)
222        {
223            units[u.Owner.Team.ID].Remove(u);
224        }
225
226        public void SetPlayer(Player player, int id)
227        {
228            players[id] = player;
229        }
230
231        public void SetTile(int tileX, int tileZ, int tileID)
232        {
233            tiles[tileX + tileZ * width] = tileID;
234        }
235
236        public void SetData(int tileX, int tileZ, byte dataVal)
237        {
238            data[tileX + tileZ * width] = dataVal;
239        }
240
241        public Tile GetTile(int tileX, int tileZ)
242        {
243            return IsValidPos(tileX, tileZ) ? registeredTiles[tiles[tileX + tileZ * width]] : registeredTiles[TILE_VOID];
244        }
245
246        public byte GetData(int tileX, int tileZ)
247        {
248            return IsValidPos(tileX, tileZ) ? data[tileX + tileZ * width] : (byte)0;
249        }
250
251        public void AddEntity(Entity ent, float xPos, float zPos)
252        {
253            int xTile = (int)(xPos / Viewport.TILESIZE);
254            int zTile = (int)(zPos / Viewport.TILESIZE);
255
256            if (!IsValidPos(xTile, zTile)) return;
257
258            ent.X = xPos;
259            ent.Z = zPos;
260
261            ent.Init();
262
263            TileEntity te = ent as TileEntity;
264            if (te != null)
265                tileEntities.Add(te);
266            else
267                entities.Add(ent);
268
269            Unit u = ent as Unit;
270            if (u != null)
271            {
272                units[u.Owner.Team.ID].Add(u);
273            }
274
275            InsertEntity(ent, xTile, zTile);
276        }
277
278        private void InsertEntity(Entity ent, int xTile, int zTile)
279        {
280            entitiesInTiles[xTile + zTile * width].Add(ent);
281        }
282
283        private void TakeEntity(Entity ent, int xTile, int zTile)
284        {
285            entitiesInTiles[xTile + zTile * width].Remove(ent);
286        }
287
288        private void RemoveEntity(Entity ent)
289        {
290            ent.OnRemoved();
291
292            entities.Remove(ent);
293
294            TakeEntity(ent, (int)(ent.X / Viewport.TILESIZE), (int)(ent.Z / Viewport.TILESIZE));
295        }
296
297        public TileEntity GetTileEntity(Entity ent, int xTile, int zTile)
298        {
299            TileEntity te = null;
300            List<Entity> ents = entitiesInTiles[xTile + zTile * width];
301            if (ents.Count > 0)
302                te = ents[0] as TileEntity;
303            if (te == null) return null;
304
305            for (int z = zTile - 1; z <= zTile + 1; z++)
306            {
307                if (z < 0 || z >= height) continue;
308                for (int x = xTile - 1; x <= xTile + 1; x++)
309                {
310                    if (x < 0 || x >= width) continue;
311
312                    ents = entitiesInTiles[x + z * width];
313
314                    if (ents.Count > 0)
315                    {
316                        Entity e = ents[0];
317                        if (e != te && !e.Blocks(te)) return te;
318                    }
319                    else return te;
320                }
321            }
322
323            return null;
324        }
325
326        public List<Unit> GetUnitsWithinScreenSpace(float x0, float y0, float x1, float y1, Team team)
327        {
328            List<Unit> result = new List<Unit>();
329
330            foreach (Unit u in units[team.ID])
331            {
332                if (u.IsSelectable && u.IntersectsWithScreenSpace(x0, y0, x1, y1))
333                    result.Add(u);
334            }
335
336            return result;
337        }
338
339        public List<T> GetUnitsWithinScreenSpace<T>(BoundingRectangle rect, Team team) where T : Unit
340        {
341            List<T> result = new List<T>();
342
343            foreach (Unit u in units[team.ID])
344            {
345                T unit = u as T;
346                if (unit != null && unit.IsSelectable && unit.IntersectsWithScreenSpace(rect.XLeft, rect.ZFar, rect.XRight, rect.ZNear))
347                    result.Add(unit);
348            }
349
350            return result;
351        }
352
353        public List<BoundingRectangle> GetCollidables(Entity ent, BoundingRectangle br = null)
354        {
355            List<BoundingRectangle> result = new List<BoundingRectangle>();
356
357            BoundingRectangle entBR = br == null ? ent.BoundingRectangle : br;
358
359            int x0 = (int)(entBR.XLeft / Viewport.TILESIZE) - 1;
360            int z0 = (int)(entBR.ZFar / Viewport.TILESIZE) - 1;
361            int x1 = (int)(entBR.XRight / Viewport.TILESIZE) + 1;
362            int z1 = (int)(entBR.ZNear / Viewport.TILESIZE) + 1;
363
364            for (int z = z0; z <= z1; z++)
365            {
366                if (z < 0 || z >= height) continue;
367                for (int x = x0; x <= x1; x++)
368                {
369                    if (x < 0 || x >= width) continue;
370
371                    List<Entity> entits = entitiesInTiles[x + z * width];
372
373                    foreach (Entity e in entits)
374                    {
375                        if (e != ent && entBR.Intersects(e.BoundingRectangle))
376                            e.AddBoundingRect(ref result, ent);
377                    }
378
379                    Tile t = registeredTiles[tiles[x + z * width]];
380                    if (t.IsSolidTo(ent) && t.GetBoundingRect(x, z).Intersects(entBR))
381                        t.AddBoundingRect(ref result, x, z);
382                }
383            }
384
385            return result;
386        }
387
388
389        public List<Unit> GetNearbyEnemyUnits(Team enemyTeam, int xTile, int zTile, int radius)
390        {
391            List<Unit> result = new List<Unit>();
392
393            for (int z = zTile - radius; z <= zTile + radius; z++)
394            {
395                if (z < 0 || z >= height) continue;
396                for (int x = xTile - radius; x <= xTile + radius; x++)
397                {
398                    if (x < 0 || x >= width) continue;
399
400                    List<Entity> ents = entitiesInTiles[x + z * width];
401
402                    foreach (Entity ent in ents)
403                    {
404                        Mob m = ent as Mob;
405                        if (m != null && m.Owner.Team.ID == enemyTeam.ID)
406                            result.Add(m);
407                    }
408                }
409            }
410
411            return result;
412        }
413
414        private bool IsValidPos(int tileX, int tileZ)
415        {
416            return (tileX >= 0 && tileZ >= 0 && tileX < width && tileZ < height);
417        }
418
419        public bool[] BuildSolidnessTable(Mob mob, bool excludeEndSolidness = false)
420        {
421            bool[] result = new bool[tiles.Length];
422
423            for (int i = 0; i < tiles.Length; i++)
424            {
425                Tile t = registeredTiles[tiles[i]];
426                if (t is TileFloor && excludeEndSolidness)
427                    result[i] = false;
428                else
429                    result[i] = tiles[i] == TILE_VOID || t.IsSolid;
430
431                List<Entity> entInTiles = entitiesInTiles[i];
432
433                if (entInTiles.Count > 0)
434                {
435                    TileEntity te = entInTiles[0] as TileEntity;
436                    result[i] = te != null && te.Blocks(mob);
437                }
438            }
439
440            return result;
441        }
442
443        public int GetClosestDiagonalOpenPos(Entity ent)
444        {
445            int xTile = (int)(ent.X / Viewport.TILESIZE);
446            int zTile = (int)(ent.Z / Viewport.TILESIZE);
447
448            bool blocks = false;
449            int tp;
450
451            for (int z = zTile - 1; z <= zTile + 1; z++)
452            {
453                if (z < 0 || z >= height) continue;
454                if (z == zTile) continue;
455
456                tp = xTile + z * width;
457                Tile t = registeredTiles[tiles[tp]];
458                if (t.ID == TILE_VOID || t.IsSolidTo(ent)) blocks |= true;
459
460                List<Entity> ents = entitiesInTiles[tp];
461
462                foreach (Entity e in ents)
463                {
464                    if (e != ent)
465                        blocks |= e.Blocks(ent);
466                }
467                if (!blocks) return tp;
468            }
469
470            blocks = false;
471            for (int x = xTile - 1; x <= xTile + 1; x++)
472            {
473                if (x < 0 || x >= width) continue;
474                if (x == xTile) continue;
475
476                tp = x + zTile * width;
477                Tile t = registeredTiles[tiles[tp]];
478                if (t.ID == TILE_VOID || t.IsSolidTo(ent)) blocks |= true;
479
480                List<Entity> ents = entitiesInTiles[tp];
481
482                foreach (Entity e in ents)
483                {
484                    if (e != ent)
485                        blocks |= e.Blocks(ent);
486                }
487                if (!blocks) return tp;
488            }
489
490            return -1;
491        }
492
493        public T GetNearestEntity<T>(Entity caller, int radius) where T : Entity
494        {
495            T nearest = null;
496            float nearestDist = 0.0F, currentDist = 0.0F;
497
498            int xTile = (int)(caller.X / Viewport.TILESIZE);
499            int zTile = (int)(caller.Z / Viewport.TILESIZE);
500
501            List<T> inRadius = new List<T>();
502            for (int z = zTile - radius; z <= zTile + radius; z++)
503            {
504                if (z < 0 || z >= height) continue;
505                for (int x = xTile - radius; x <= xTile + radius; x++)
506                {
507                    if (x < 0 || x >= width) continue;
508                    if (x == xTile && z == zTile) continue;
509
510                    entitiesInTiles[x + z * width].ForEach(delegate(Entity ent)
511                    {
512                        T e = ent as T;
513                        if (e != null) 
514                            inRadius.Add(e);
515                    });
516                }
517            }
518
519            foreach (T ent in inRadius)
520            {
521                currentDist = ent.DistanceToSqr(caller.X, caller.Z);
522                if (nearest == null || currentDist < nearestDist)
523                {
524                    nearest = ent;
525                    nearestDist = currentDist;
526                }
527            }
528
529            return nearest;
530        }
531
532        public bool SolidTo(Entity ent, int xTile, int zTile)
533        {
534            if (registeredTiles[tiles[xTile + zTile * width]].IsSolidTo(ent))
535                return true;
536
537            foreach (Entity e in entitiesInTiles[xTile + zTile * width])
538                if (e.Blocks(ent)) return true;
539
540            return false;
541        }
542
543        public List<int> GetEntitySolidnessList(Entity caller, int xTile, int zTile, int radius)
544        {
545            List<int> result = new List<int>();
546
547            for (int z = zTile - radius; z <= zTile + radius; z++)
548            {
549                if (z < 0 || z >= height) continue;
550                for (int x = xTile - radius; x <= xTile + radius; x++)
551                {
552                    if (x < 0 || x >= width) continue;
553                    if (x == xTile && z == zTile) continue;
554
555                    foreach (Entity ent in entitiesInTiles[x + z * width])
556                    {
557                        if (ent.Blocks(caller))
558                        {
559                            result.Add(x + z * width);
560                            continue;
561                        }
562                    }
563                }
564            }
565
566            return result;
567        }
568    }
569}
Note: See TracBrowser for help on using the repository browser.