diff --git a/OpenDiablo2.Common/Enums/eRenderCellType.cs b/OpenDiablo2.Common/Enums/eRenderCellType.cs index 50efe550..ebf1b71c 100644 --- a/OpenDiablo2.Common/Enums/eRenderCellType.cs +++ b/OpenDiablo2.Common/Enums/eRenderCellType.cs @@ -9,6 +9,8 @@ namespace OpenDiablo2.Common.Enums public enum eRenderCellType { Floor, - Wall + WallUpper, + WallLower, + Roof } } diff --git a/OpenDiablo2.Common/Interfaces/IMapEngine.cs b/OpenDiablo2.Common/Interfaces/IMapEngine.cs index 03171a52..58a9e364 100644 --- a/OpenDiablo2.Common/Interfaces/IMapEngine.cs +++ b/OpenDiablo2.Common/Interfaces/IMapEngine.cs @@ -10,7 +10,5 @@ namespace OpenDiablo2.Common.Interfaces void Update(long ms); void Render(); void NotifyMapChanged(); - MapCellInfo GetMapCellInfo(Guid mapId, Guid tileId); - void SetMapCellInfo(Guid mapId, MapCellInfo cellInfo); } } diff --git a/OpenDiablo2.Common/Interfaces/IRenderWindow.cs b/OpenDiablo2.Common/Interfaces/IRenderWindow.cs index a1a02ab5..efa21e06 100644 --- a/OpenDiablo2.Common/Interfaces/IRenderWindow.cs +++ b/OpenDiablo2.Common/Interfaces/IRenderWindow.cs @@ -25,6 +25,7 @@ namespace OpenDiablo2.Common.Interfaces IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot); void SetCursor(IMouseCursor mouseCursor); void Draw(ILabel label); - void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, int orientation); + MapCellInfo CacheMapCell(MPQDT1Tile mapCell); + void DrawMapCell(MapCellInfo mapCellInfo, int xPixel, int yPixel); } } diff --git a/OpenDiablo2.Common/Models/MapCellInfo.cs b/OpenDiablo2.Common/Models/MapCellInfo.cs index e9a73fdc..6699478e 100644 --- a/OpenDiablo2.Common/Models/MapCellInfo.cs +++ b/OpenDiablo2.Common/Models/MapCellInfo.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using OpenDiablo2.Common.Interfaces; namespace OpenDiablo2.Common.Models @@ -11,12 +7,14 @@ namespace OpenDiablo2.Common.Models // Represents a single cell on a map public sealed class MapCellInfo { - public Guid TileId; - public int OffX; - public int OffY; - public int FrameWidth; - public int FrameHeight; - public Rectangle Rect; - public ITexture Texture; + public Guid TileId { get; set; } + public int AnimationId { get; set; } + public int OffX { get; set; } + public int OffY { get; set; } + public int FrameWidth { get; set; } + public int FrameHeight { get; set; } + public MPQDT1Tile Tile { get; set; } + public Rectangle Rect { get; set; } + public ITexture Texture { get; set; } } } diff --git a/OpenDiablo2.Core/Map Engine/MapEngine.cs b/OpenDiablo2.Core/Map Engine/MapEngine.cs index 37c71bdb..70fd836b 100644 --- a/OpenDiablo2.Core/Map Engine/MapEngine.cs +++ b/OpenDiablo2.Core/Map Engine/MapEngine.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using OpenDiablo2.Common; +using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Models; @@ -81,12 +82,12 @@ namespace OpenDiablo2.Core.Map_Engine var visualX = ((x - y) * (cellSizeX / 2)) - cOffX; var visualY = ((x + y) * (cellSizeY / 2)) - cOffY; - + DrawFloor(x, y, visualX, visualY); DrawWall(x, y, visualX, visualY, false); DrawWall(x, y, visualX, visualY, true); DrawRoof(x, y, visualX, visualY); - // //DrawShadow(x, y, visualX, visualY); + // //DrawShadow(x, y, visualX, visualY); } } @@ -100,34 +101,26 @@ namespace OpenDiablo2.Core.Map_Engine foreach (var wallLayer in gameState.MapData.WallLayers) { - var wall = wallLayer.Props[x + (y * gameState.MapData.Width)]; - var orientation = wallLayer.Orientations[x + (y * gameState.MapData.Width)].Orientation1; + var idx = x + (y * gameState.MapData.Width); - if (orientation != 15) // Only 15 (roof) + var cellInfo = GetMapCellInfo( + gameState.MapData.Id, cx, cy, wallLayer.Props[idx], + eRenderCellType.Roof, + wallLayer.Orientations[idx]); + + if (cellInfo == null) return; - if (wall.Prop1 == 0) - continue; - - if ((wall.Prop4 & 0x80) > 0) - { - if (orientation != 10 && orientation != 11) - return; - } - - var sub_index = wall.Prop2; - var main_index = (wall.Prop3 >> 4) + ((wall.Prop4 & 0x03) << 4); - - var lt = gameState.MapData.LookupTable.First(z => z.MainIndex == main_index && z.SubIndex == sub_index && z.Orientation == orientation); - renderWindow.DrawMapCell(x, y, cx, cy - lt.TileRef.RoofHeight, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, orientation); + renderWindow.DrawMapCell(cellInfo, cx, cy); } } private void DrawShadow(int x, int y, int visualX, int visualY) { - + } + private void DrawFloor(int x, int y, int visualX, int visualY) { if (visualX < -160 || visualX > 800 || visualY < -120 || visualY > 650) @@ -142,16 +135,13 @@ namespace OpenDiablo2.Core.Map_Engine var idx = x + (y * gameState.MapData.Width); if (idx >= floorLayer.Props.Length) break; - var floor = floorLayer.Props[idx]; - if (floor.Prop1 == 0) - continue; + var cellInfo = GetMapCellInfo(gameState.MapData.Id, cx, cy, floorLayer.Props[idx], eRenderCellType.Floor); - var sub_index = floor.Prop2; - var main_index = (floor.Prop3 >> 4) + ((floor.Prop4 & 0x03) << 4); + if (cellInfo == null) + return; - - renderWindow.DrawMapCell(x, y, cx, cy, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, 0); + renderWindow.DrawMapCell(cellInfo, cx, cy); } } @@ -163,31 +153,17 @@ namespace OpenDiablo2.Core.Map_Engine foreach (var wallLayer in gameState.MapData.WallLayers) { - var wall = wallLayer.Props[x + (y * gameState.MapData.Width)]; - var orientation = wallLayer.Orientations[x + (y * gameState.MapData.Width)].Orientation1; + var idx = x + (y * gameState.MapData.Width); + var cellInfo = GetMapCellInfo( + gameState.MapData.Id, cx, cy, wallLayer.Props[idx], + upper ? eRenderCellType.WallUpper : eRenderCellType.WallLower, + wallLayer.Orientations[idx]); - if (wall.Prop1 == 0) - continue; - - if (upper && orientation <= 15) + if (cellInfo == null) return; - - if (orientation == 10 || orientation == 11) - return; // TODO: Support special walls - - if ((wall.Prop4 & 0x80) > 0) - { - if (orientation != 10 && orientation != 11) - return; - } - - var sub_index = wall.Prop2; - var main_index = (wall.Prop3 >> 4) + ((wall.Prop4 & 0x03) << 4); - - var lt = gameState.MapData.LookupTable.First(z => z.MainIndex == main_index && z.SubIndex == sub_index && z.Orientation == orientation); - renderWindow.DrawMapCell(x, y, cx, cy + 80, gameState.MapData, main_index, sub_index, gameState.CurrentPalette, orientation); + renderWindow.DrawMapCell(cellInfo, cx, cy); } } @@ -201,20 +177,124 @@ namespace OpenDiablo2.Core.Map_Engine } - public MapCellInfo GetMapCellInfo(Guid mapId, Guid tileId) + private MapCellInfo GetMapCellInfo( + Guid mapId, + int cellX, + int cellY, + MPQDS1TileProps props, + eRenderCellType cellType, + MPQDS1WallOrientationTileProps wallOrientations = null + ) { - if (!mapDataLookup.ContainsKey(mapId)) + var sub_index = props.Prop2; + var main_index = (props.Prop3 >> 4) + ((props.Prop4 & 0x03) << 4); + var orientation = 0; + + if (cellType == eRenderCellType.Floor) + { + // Floors can't have rotations, should we blow up here? + if (props.Prop1 == 0) + return null; + } + + if (cellType == eRenderCellType.Roof) + { + if (orientation != 15) // Only 15 (roof) + return null; + + if (props.Prop1 == 0) + return null; + + if ((props.Prop4 & 0x80) > 0) + { + if (orientation != 10 && orientation != 11) + return null; + } + } + if (cellType == eRenderCellType.WallUpper || cellType == eRenderCellType.WallLower) + { + orientation = wallOrientations.Orientation1; + + + if (props.Prop1 == 0) + return null; + + // < 15 shouldn't happen for upper wall types, should we even check for this? + if (cellType == eRenderCellType.WallUpper && orientation <= 15) + return null; + + // TODO: Support special walls + if (orientation == 10 || orientation == 11) + return null; + + // This is also a thing apparently + if ((props.Prop4 & 0x80) > 0) + { + if (orientation != 10 && orientation != 11) + return null; + } + + } + + int frame = 0; + var tiles = gameState.MapData.LookupTable + .Where(x => x.MainIndex == main_index && x.SubIndex == sub_index && x.Orientation == orientation) + .Select(x => x.TileRef); + + if (!tiles.Any()) + throw new ApplicationException("Invalid tile id found!"); + + MPQDT1Tile tile = null; + if (tiles.First().Animated) + { +#if DEBUG + if (!tiles.All(x => x.Animated)) + throw new ApplicationException("Some tiles are animated and some aren't..."); + + // TODO: Animated tiles +#endif + } + else + { + if (tiles.Count() > 0) + { + var totalRarity = tiles.Sum(q => q.RarityOrFrameIndex); + var random = new Random(gameState.Seed + cellX + (gameState.MapData.Width * cellY)); + var x = random.Next(totalRarity); + var z = 0; + foreach (var t in tiles) + { + z += t.RarityOrFrameIndex; + if (x <= z) + { + tile = t; + break; + } + } + + if (tile.Animated) + throw new ApplicationException("Why are we randomly finding an animated tile? Something's wrong here."); + } + else tile = tiles.First(); + } + + // This WILL happen to you + if (tile.Width == 0 || tile.Height == 0) return null; - return mapDataLookup[mapId].FirstOrDefault(x => x.TileId == tileId); - } - - public void SetMapCellInfo(Guid mapId, MapCellInfo cellInfo) - { if (!mapDataLookup.ContainsKey(mapId)) mapDataLookup[mapId] = new List(); - mapDataLookup[mapId].Add(cellInfo); + var result = mapDataLookup[mapId].FirstOrDefault(x => x.TileId == tile.Id && x.AnimationId == frame); + if (result != null) + return result; + + var mapCellInfo = renderWindow.CacheMapCell(tile); + mapDataLookup[mapId].Add(mapCellInfo); + + return mapCellInfo; } + + } } diff --git a/OpenDiablo2.SDL2/SDL2RenderWindow.cs b/OpenDiablo2.SDL2/SDL2RenderWindow.cs index db6e3c28..c720adfc 100644 --- a/OpenDiablo2.SDL2/SDL2RenderWindow.cs +++ b/OpenDiablo2.SDL2/SDL2RenderWindow.cs @@ -281,55 +281,12 @@ namespace OpenDiablo2.SDL2_ SDL.SDL_RenderCopy(renderer, lbl.texture, IntPtr.Zero, ref destRect); } - public unsafe void DrawMapCell(int xCell, int yCell, int xPixel, int yPixel, MPQDS1 mapData, int main_index, int sub_index, Palette palette, int orientation) + public unsafe MapCellInfo CacheMapCell(MPQDT1Tile mapCell) { - var tiles = mapData.LookupTable.Where(x => - x.MainIndex == main_index && - x.SubIndex == sub_index && - (orientation == -1 || x.Orientation == orientation)).Select(x => x.TileRef); - - if (!tiles.Any()) - throw new ApplicationException("Invalid tile id found!"); - - - // TODO: This isn't good.. should be remembered in the map engine layer - MPQDT1Tile tile = null; - if (tiles.Count() > 0) - { - var totalRarity = tiles.Sum(q => q.RarityOrFrameIndex); - var random = new Random(gameState.Seed + xCell + (mapData.Width * yCell)); - var x = random.Next(totalRarity); - var z = 0; - foreach (var t in tiles) - { - z += t.RarityOrFrameIndex; - if (x <= z) - { - tile = t; - break; - } - } - } - else tile = tiles.First(); - - // This WILL happen to you - if (tile.Width == 0 || tile.Height == 0) - return; - - var mapCellInfo = getMapEngine().GetMapCellInfo(mapData.Id, tile.Id); ; - if (mapCellInfo != null) - { - var xd = new SDL.SDL_Rect { x = xPixel - mapCellInfo.OffX, y = yPixel - mapCellInfo.OffY, w = mapCellInfo.FrameWidth, h = mapCellInfo.FrameHeight }; - var xs = mapCellInfo.Rect.ToSDL2Rect(); - SDL.SDL_RenderCopy(renderer, ((SDL2Texture)mapCellInfo.Texture).Pointer, ref xs, ref xd); - return; - } - - - var minX = tile.Blocks.Min(x => x.PositionX); - var minY = tile.Blocks.Min(x => x.PositionY); - var maxX = tile.Blocks.Max(x => x.PositionX + 32); - var maxY = tile.Blocks.Max(x => x.PositionY + 32); + var minX = mapCell.Blocks.Min(x => x.PositionX); + var minY = mapCell.Blocks.Min(x => x.PositionY); + var maxX = mapCell.Blocks.Max(x => x.PositionX + 32); + var maxY = mapCell.Blocks.Max(x => x.PositionY + 32); var diffX = maxX - minX; var diffY = maxY - minY; @@ -345,12 +302,9 @@ namespace OpenDiablo2.SDL2_ var texId = SDL.SDL_CreateTexture(renderer, SDL.SDL_PIXELFORMAT_ARGB8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING, frameSize.Width, frameSize.Height); SDL.SDL_SetTextureBlendMode(texId, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); - if (SDL.SDL_LockTexture(texId, IntPtr.Zero, out IntPtr pixels, out int pitch) != 0) - { - log.Error("Could not lock texture for map rendering"); - return; - } + throw new ApplicationException("Could not lock texture for map rendering"); + try { UInt32* data = (UInt32*)pixels; @@ -361,7 +315,7 @@ namespace OpenDiablo2.SDL2_ data[i] = 0x0; - foreach (var block in tile.Blocks) + foreach (var block in mapCell.Blocks) { var index = block.PositionX + offX + ((block.PositionY + offy) * pitchChange); var xx = 0; @@ -372,7 +326,7 @@ namespace OpenDiablo2.SDL2_ { if (colorIndex == 0) continue; - var color = palette.Colors[colorIndex]; + var color = gameState.CurrentPalette.Colors[colorIndex]; if (color > 0) data[index] = color; @@ -398,21 +352,23 @@ namespace OpenDiablo2.SDL2_ SDL.SDL_UnlockTexture(texId); } - var lookup = new MapCellInfo + return new MapCellInfo { FrameHeight = frameSize.Height, FrameWidth = frameSize.Width, OffX = offX, OffY = offy, Rect = srcRect.ToRectangle(), - TileId = tile.Id, + TileId = mapCell.Id, Texture = new SDL2Texture { Pointer = texId } }; + } - getMapEngine().SetMapCellInfo(mapData.Id, lookup); - - var dr = new SDL.SDL_Rect { x = xPixel - lookup.OffX, y = yPixel - lookup.OffY, w = lookup.FrameWidth, h = lookup.FrameHeight }; - SDL.SDL_RenderCopy(renderer, texId, ref srcRect, ref dr); + public void DrawMapCell(MapCellInfo mapCellInfo, int xPixel, int yPixel) + { + var srcRect = new SDL.SDL_Rect { x = 0, y = 0, w = mapCellInfo.FrameWidth, h = Math.Abs(mapCellInfo.FrameHeight) }; + var destRect = new SDL.SDL_Rect { x = xPixel - mapCellInfo.OffX, y = yPixel - mapCellInfo.OffY, w = mapCellInfo.FrameWidth, h = mapCellInfo.FrameHeight}; + SDL.SDL_RenderCopy(renderer, (mapCellInfo.Texture as SDL2Texture).Pointer, ref srcRect, ref destRect); } public unsafe IMouseCursor LoadCursor(ISprite sprite, int frame, Point hotspot)