1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-07-22 13:04:18 -04:00

Added caching. Code cleanup.

This commit is contained in:
Tim Sarbin 2018-12-08 11:51:11 -05:00
parent 98bb55a8ea
commit 2bf53e351c
13 changed files with 209 additions and 92 deletions

View File

@ -18,7 +18,7 @@ namespace OpenDiablo2.Common.Interfaces
MPQFont GetMPQFont(string resourcePath); MPQFont GetMPQFont(string resourcePath);
MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType); MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType);
MPQDT1 GetMPQDT1(string resourcePath); MPQDT1 GetMPQDT1(string resourcePath);
Palette GetPalette(string paletteName); Palette GetPalette(string paletteFile);
MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode); MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode);
MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette); MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette);

View File

@ -0,0 +1,39 @@
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.Caching;
namespace OpenDiablo2.Common.Interfaces
{
/// <summary>
/// Provides access to the cache system.
/// </summary>
public interface ICache
{
/// <summary>
/// Gets an item from the cache. If the item does not exist, the value factory will be executed to
/// generate the value.
/// </summary>
/// <typeparam name="T">The return type of the value</typeparam>
/// <param name="key">The name of the cache key, in the form of Type::X::Y::Z where
/// Type is the base type, and x/y/z are unique identifiers for the item.</param>
/// <param name="valueFactory">A function that returns the correct value if it does not already exist.</param>
/// <param name="cacheItemPolicy">Pass in a new policy to control how this item is handled. Typically you can leave this null.</param>
/// <returns>The item requested</returns>
T AddOrGetExisting<T>(string key, Func<T> valueFactory, CacheItemPolicy cacheItemPolicy = null);
}
}

View File

@ -1,17 +1,31 @@
using System; /* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace OpenDiablo2.Common.Models namespace OpenDiablo2.Common.Models
{ {
public sealed class MPQFont public sealed class MPQFont
{ {
public ImageSet FontImageSet; public ImageSet FontImageSet { get; internal set; }
public Dictionary<byte, Size> CharacterMetric = new Dictionary<byte, Size>(); public Dictionary<char, Size> CharacterMetric { get; internal set; } = new Dictionary<char, Size>();
public static MPQFont LoadFromStream(Stream fontStream, Stream tableStream) public static MPQFont LoadFromStream(Stream fontStream, Stream tableStream)
{ {
@ -31,7 +45,7 @@ namespace OpenDiablo2.Common.Models
br.ReadBytes(3); br.ReadBytes(3);
var size = new Size(br.ReadByte(), br.ReadByte()); var size = new Size(br.ReadByte(), br.ReadByte());
br.ReadBytes(3); br.ReadBytes(3);
var charCode = br.ReadByte(); var charCode = (char)br.ReadByte();
result.CharacterMetric[charCode] = size; result.CharacterMetric[charCode] = size;
br.ReadBytes(5); br.ReadBytes(5);

View File

@ -45,6 +45,7 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
@ -80,6 +81,7 @@
<Compile Include="Enums\Mobs\eMobFlags.cs" /> <Compile Include="Enums\Mobs\eMobFlags.cs" />
<Compile Include="Enums\Mobs\eStatModifierType.cs" /> <Compile Include="Enums\Mobs\eStatModifierType.cs" />
<Compile Include="Interfaces\Drawing\ICharacterRenderer.cs" /> <Compile Include="Interfaces\Drawing\ICharacterRenderer.cs" />
<Compile Include="Interfaces\ICache.cs" />
<Compile Include="Interfaces\IItemManager.cs" /> <Compile Include="Interfaces\IItemManager.cs" />
<Compile Include="Extensions\MobManagerExtensions.cs" /> <Compile Include="Extensions\MobManagerExtensions.cs" />
<Compile Include="Interfaces\IGameServer.cs" /> <Compile Include="Interfaces\IGameServer.cs" />

View File

@ -1,6 +1,21 @@
using Autofac; /* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using Autofac;
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
using OpenDiablo2.Common.Interfaces.Drawing;
using OpenDiablo2.Common.Interfaces.Mobs; using OpenDiablo2.Common.Interfaces.Mobs;
using OpenDiablo2.Core.GameState_; using OpenDiablo2.Core.GameState_;
using OpenDiablo2.Core.Map_Engine; using OpenDiablo2.Core.Map_Engine;
@ -16,6 +31,7 @@ namespace OpenDiablo2.Core
{ {
log.Info("Configuring OpenDiablo2.Core service implementations."); log.Info("Configuring OpenDiablo2.Core service implementations.");
builder.RegisterType<Cache>().As<ICache>().SingleInstance();
builder.RegisterType<Button>().As<IButton>().InstancePerDependency(); builder.RegisterType<Button>().As<IButton>().InstancePerDependency();
builder.RegisterType<EngineDataManager>().As<IEngineDataManager>().SingleInstance(); builder.RegisterType<EngineDataManager>().As<IEngineDataManager>().SingleInstance();
builder.RegisterType<ItemManager>().As<IItemManager>().SingleInstance(); builder.RegisterType<ItemManager>().As<IItemManager>().SingleInstance();

44
OpenDiablo2.Core/Cache.cs Normal file
View File

@ -0,0 +1,44 @@
/* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.Caching;
using OpenDiablo2.Common.Interfaces;
namespace OpenDiablo2.Core
{
public sealed class Cache : ICache
{
private readonly static MemoryCache _cache = new MemoryCache("OpenDiablo2.Cache");
public Cache() { }
public T AddOrGetExisting<T>(string key, Func<T> valueFactory, CacheItemPolicy cacheItemPolicy = null)
{
var newValue = new Lazy<T>(valueFactory);
var oldValue = _cache.AddOrGetExisting(key, newValue, cacheItemPolicy ?? new CacheItemPolicy()) as Lazy<T>;
try
{
return (oldValue ?? newValue).Value;
}
catch
{
_cache.Remove(key);
throw;
}
}
}
}

View File

@ -44,6 +44,7 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
@ -56,6 +57,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AutofacModule.cs" /> <Compile Include="AutofacModule.cs" />
<Compile Include="Cache.cs" />
<Compile Include="ItemManager.cs" /> <Compile Include="ItemManager.cs" />
<Compile Include="EngineDataManager.cs" /> <Compile Include="EngineDataManager.cs" />
<Compile Include="GameEngine.cs" /> <Compile Include="GameEngine.cs" />

View File

@ -1,8 +1,21 @@
using System; /* OpenDiablo 2 - An open source re-implementation of Diablo 2 in C#
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenDiablo2.Common; using OpenDiablo2.Common;
using OpenDiablo2.Common.Enums; using OpenDiablo2.Common.Enums;
using OpenDiablo2.Common.Interfaces; using OpenDiablo2.Common.Interfaces;
@ -12,19 +25,15 @@ namespace OpenDiablo2.Core
{ {
public sealed class ResourceManager : IResourceManager public sealed class ResourceManager : IResourceManager
{ {
private readonly ICache cache;
private readonly IMPQProvider mpqProvider; private readonly IMPQProvider mpqProvider;
private readonly IEngineDataManager engineDataManager; private readonly IEngineDataManager engineDataManager;
private readonly Dictionary<string, ImageSet> ImageSets = new Dictionary<string, ImageSet>(); public Dictionary<string, List<AnimationData>> Animations { get; private set; }
private readonly Dictionary<string, MPQFont> MPQFonts = new Dictionary<string, MPQFont>();
private readonly Dictionary<string, Palette> Palettes = new Dictionary<string, Palette>();
private readonly Dictionary<string, MPQDT1> DTs = new Dictionary<string, MPQDT1>();
private readonly Dictionary<string, MPQCOF> PlayerCOFs = new Dictionary<string, MPQCOF>();
public Dictionary<string, List<AnimationData>> Animations { get; private set; } = new Dictionary<string, List<AnimationData>>(); public ResourceManager(ICache cache, IMPQProvider mpqProvider, IEngineDataManager engineDataManager)
public ResourceManager(IMPQProvider mpqProvider, IEngineDataManager engineDataManager)
{ {
this.cache = cache;
this.mpqProvider = mpqProvider; this.mpqProvider = mpqProvider;
this.engineDataManager = engineDataManager; this.engineDataManager = engineDataManager;
@ -32,64 +41,32 @@ namespace OpenDiablo2.Core
} }
public ImageSet GetImageSet(string resourcePath) public ImageSet GetImageSet(string resourcePath)
{ => cache.AddOrGetExisting($"ImageSet::{resourcePath}", () => ImageSet.LoadFromStream(mpqProvider.GetStream(resourcePath)));
if (!ImageSets.ContainsKey(resourcePath))
ImageSets[resourcePath] = ImageSet.LoadFromStream(mpqProvider.GetStream(resourcePath));
return ImageSets[resourcePath];
}
public MPQFont GetMPQFont(string resourcePath) public MPQFont GetMPQFont(string resourcePath)
{ => cache.AddOrGetExisting($"Font::{resourcePath}", () => MPQFont.LoadFromStream(mpqProvider.GetStream($"{resourcePath}.DC6"), mpqProvider.GetStream($"{resourcePath}.tbl")));
if (!MPQFonts.ContainsKey(resourcePath))
MPQFonts[resourcePath] = MPQFont.LoadFromStream(mpqProvider.GetStream($"{resourcePath}.DC6"), mpqProvider.GetStream($"{resourcePath}.tbl"));
return MPQFonts[resourcePath];
}
public MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType) public MPQDS1 GetMPQDS1(string resourcePath, LevelPreset level, LevelDetail levelDetail, LevelType levelType)
{ => cache.AddOrGetExisting($"DS1::{resourcePath}::{level}::{levelDetail}::{levelType}", ()
var mapName = resourcePath.Replace("data\\global\\tiles\\", "").Replace("\\", "/"); => new MPQDS1(mpqProvider.GetStream(resourcePath), level, levelDetail, levelType, engineDataManager, this) { MapFile = resourcePath });
return new MPQDS1(mpqProvider.GetStream(resourcePath), level, levelDetail, levelType, engineDataManager, this)
{
MapFile = resourcePath
};
}
public Palette GetPalette(string paletteFile) public Palette GetPalette(string paletteFile)
{ => cache.AddOrGetExisting($"Palette::{paletteFile}", () =>
if (!Palettes.ContainsKey(paletteFile))
{ {
var paletteNameParts = paletteFile.Split('\\'); var paletteNameParts = paletteFile.Split('\\');
var paletteName = paletteNameParts[paletteNameParts.Count() - 2]; var paletteName = paletteNameParts[paletteNameParts.Count() - 2];
Palettes[paletteFile] = Palette.LoadFromStream(mpqProvider.GetStream(paletteFile), paletteName); return Palette.LoadFromStream(mpqProvider.GetStream(paletteFile), paletteName);
} });
return Palettes[paletteFile];
}
public MPQDT1 GetMPQDT1(string resourcePath) public MPQDT1 GetMPQDT1(string resourcePath)
{ => cache.AddOrGetExisting($"DT1::{resourcePath}", () => new MPQDT1(mpqProvider.GetStream(resourcePath)));
if (!DTs.ContainsKey(resourcePath))
DTs[resourcePath] = new MPQDT1(mpqProvider.GetStream(resourcePath));
return DTs[resourcePath];
}
public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode) public MPQCOF GetPlayerAnimation(eHero hero, eWeaponClass weaponClass, eMobMode mobMode)
=> cache.AddOrGetExisting($"COF::{hero}::{weaponClass}::{mobMode}", () =>
{ {
var key = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}";
if (PlayerCOFs.ContainsKey(key))
return PlayerCOFs[key];
var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}.cof"; var path = $"{ResourcePaths.PlayerAnimationBase}\\{hero.ToToken()}\\COF\\{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}.cof";
var result = MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode); return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode);
PlayerCOFs[key] = result; });
return result;
}
public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette) public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette)
{ {
@ -107,5 +84,24 @@ namespace OpenDiablo2.Core
var result = new MPQDCC(binaryData, palette); var result = new MPQDCC(binaryData, palette);
return result; return result;
} }
/*
=> cache.AddOrGetExisting($"DCC::{cofLayer}::{armorType}::{palette.Name}", () =>
{
byte[] binaryData;
using (var stream = mpqProvider.GetStream(cofLayer.GetDCCPath(armorType)))
{
if (stream == null)
return null;
binaryData = new byte[stream.Length];
stream.Read(binaryData, 0, (int)stream.Length);
}
var result = new MPQDCC(binaryData, palette);
return result;
});
/*
*/
} }
} }

View File

@ -59,7 +59,6 @@ namespace OpenDiablo2.SDL2_
private readonly IPaletteProvider paletteProvider; private readonly IPaletteProvider paletteProvider;
private MPQCOF animationData; private MPQCOF animationData;
private MPQDCC[] layerData;
static readonly byte[] directionConversion = new byte[] { 3, 15, 4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14 }; static readonly byte[] directionConversion = new byte[] { 3, 15, 4, 8, 0, 9, 5, 10, 1, 11, 6, 12, 2, 13, 7, 14 };
@ -138,19 +137,10 @@ namespace OpenDiablo2.SDL2_
throw new ApplicationException("Could not locate animation for the character!"); throw new ApplicationException("Could not locate animation for the character!");
var palette = paletteProvider.PaletteTable["Units"]; var palette = paletteProvider.PaletteTable["Units"];
var data = animationData.Layers CacheFrames(animationData.Layers.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette)));
.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette))
.ToArray();
log.Warn($"{data.Where(x => x == null).Count()} animation layers were not found!");
layerData = data.Where(x => x != null)
.ToArray();
CacheFrames();
} }
private unsafe void CacheFrames() private unsafe void CacheFrames(IEnumerable<MPQDCC> layerData)
{ {
var cache = new DirectionCacheItem var cache = new DirectionCacheItem
{ {
@ -170,14 +160,26 @@ namespace OpenDiablo2.SDL2_
var maxX = Int32.MinValue; var maxX = Int32.MinValue;
var maxY = Int32.MinValue; var maxY = Int32.MinValue;
var layersIgnored = 0;
var layersToRender = new List<MPQDCC>();
foreach (var layer in layerData) foreach (var layer in layerData)
{ {
if (layer == null)
{
layersIgnored++;
continue;
}
layersToRender.Add(layer);
minX = Math.Min(minX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Left); minX = Math.Min(minX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Left);
minY = Math.Min(minY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Top); minY = Math.Min(minY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Top);
maxX = Math.Max(maxX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Right); maxX = Math.Max(maxX, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Right);
maxY = Math.Max(maxY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Bottom); maxY = Math.Max(maxY, layer.Directions[directionConversion[LocationDetails.MovementDirection]].Box.Bottom);
} }
if (layersIgnored > 0)
log.Warn($"{layersIgnored} animation layer(s) were not found!");
var frameW = (maxX - minX) * 2; // Hack var frameW = (maxX - minX) * 2; // Hack
var frameH = (maxY - minY) * 2; var frameH = (maxY - minY) * 2;
@ -191,8 +193,11 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_LockTexture(texture, IntPtr.Zero, out IntPtr pixels, out int pitch); SDL.SDL_LockTexture(texture, IntPtr.Zero, out IntPtr pixels, out int pitch);
UInt32* data = (UInt32*)pixels; UInt32* data = (UInt32*)pixels;
foreach (var layer in layerData) foreach (var layer in layersToRender)
{ {
if (layer == null)
continue;
var direction = layer.Directions[directionConversion[LocationDetails.MovementDirection]]; var direction = layer.Directions[directionConversion[LocationDetails.MovementDirection]];
var frame = direction.Frames[frameIndex]; var frame = direction.Frames[frameIndex];
@ -212,8 +217,6 @@ namespace OpenDiablo2.SDL2_
continue; continue;
var color = palette.Colors[paletteIndex]; var color = palette.Colors[paletteIndex];
var relativeX = (frame.XOffset - minX);
var relativeY = (frame.YOffset - minY);
var offsetX = x + cell.XOffset + (frame.Box.X - minX); var offsetX = x + cell.XOffset + (frame.Box.X - minX);
var offsetY = y + cell.YOffset + (frame.Box.Y - minY); var offsetY = y + cell.YOffset + (frame.Box.Y - minY);
@ -228,6 +231,7 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_UnlockTexture(texture); SDL.SDL_UnlockTexture(texture);
SDL.SDL_SetTextureBlendMode(texture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); SDL.SDL_SetTextureBlendMode(texture, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
// TODO: Temporary code! // TODO: Temporary code!
cache.SpriteTexture[frameIndex] = texture; cache.SpriteTexture[frameIndex] = texture;
cache.SpriteRect[frameIndex] = new SDL.SDL_Rect { x = minX, y = minY, w = frameW, h = frameH }; cache.SpriteRect[frameIndex] = new SDL.SDL_Rect { x = minX, y = minY, w = frameW, h = frameH };

View File

@ -48,7 +48,7 @@ namespace OpenDiablo2.SDL2_
{ {
int w = 0; int w = 0;
int h = 0; int h = 0;
foreach(byte ch in text) foreach(var ch in text)
{ {
w += font.CharacterMetric[ch].Width; w += font.CharacterMetric[ch].Width;
h = Math.Max(h, font.CharacterMetric[ch].Height); h = Math.Max(h, font.CharacterMetric[ch].Height);

View File

@ -76,7 +76,7 @@ namespace OpenDiablo2.SDL2_
int h = 0; int h = 0;
foreach (var ch in text) foreach (var ch in text)
{ {
var metric = font.font.CharacterMetric[(byte)ch]; var metric = font.font.CharacterMetric[ch];
w += metric.Width; w += metric.Width;
h = Math.Max(Math.Max(h, metric.Height), font.sprite.FrameSize.Height); h = Math.Max(Math.Max(h, metric.Height), font.sprite.FrameSize.Height);
} }
@ -93,12 +93,12 @@ namespace OpenDiablo2.SDL2_
var height = font.sprite.FrameSize.Height; var height = font.sprite.FrameSize.Height;
for (int idx = 0; idx < text.Length; idx++) for (int idx = 0; idx < text.Length; idx++)
{ {
width += font.font.CharacterMetric[(byte)text[idx]].Width; width += font.font.CharacterMetric[text[idx]].Width;
if (width >= MaxWidth) if (width >= MaxWidth)
{ {
idx = lastWordIndex; idx = lastWordIndex;
height += font.font.CharacterMetric[(byte)'|'].Height + 6; height += font.font.CharacterMetric['|'].Height + 6;
width = 0; width = 0;
continue; continue;
} }
@ -146,7 +146,7 @@ namespace OpenDiablo2.SDL2_
foreach (var ch in text) foreach (var ch in text)
{ {
WriteCharacter(cx, cy, (byte)ch); WriteCharacter(cx, cy, (byte)ch);
cx += font.font.CharacterMetric[(byte)ch].Width; cx += font.font.CharacterMetric[ch].Width;
} }
} }
else else
@ -158,7 +158,7 @@ namespace OpenDiablo2.SDL2_
var lastStartX = 0; var lastStartX = 0;
for (int idx = 0; idx < text.Length; idx++) for (int idx = 0; idx < text.Length; idx++)
{ {
width += font.font.CharacterMetric[(byte)text[idx]].Width; width += font.font.CharacterMetric[text[idx]].Width;
if (width >= MaxWidth) if (width >= MaxWidth)
{ {
@ -180,7 +180,7 @@ namespace OpenDiablo2.SDL2_
var y = 0; var y = 0;
foreach(var line in linesToRender) foreach(var line in linesToRender)
{ {
var lineWidth = (line.Sum(c => font.font.CharacterMetric[(byte)c].Width)); var lineWidth = (line.Sum(c => font.font.CharacterMetric[c].Width));
var x = 0; var x = 0;
if (Alignment == eTextAlign.Centered) if (Alignment == eTextAlign.Centered)
@ -191,10 +191,10 @@ namespace OpenDiablo2.SDL2_
foreach (var ch in line) foreach (var ch in line)
{ {
WriteCharacter(x, y, (byte)ch); WriteCharacter(x, y, (byte)ch);
x += font.font.CharacterMetric[(byte)ch].Width; x += font.font.CharacterMetric[ch].Width;
} }
y += font.font.CharacterMetric[(byte)'|'].Height + 6; y += font.font.CharacterMetric['|'].Height + 6;
} }
} }
SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero); SDL.SDL_SetRenderTarget(renderer, IntPtr.Zero);

View File

@ -5,7 +5,7 @@
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid> <ProjectGuid>{2B0CF1AC-06DD-4322-AE8B-FF8A8C70A3CD}</ProjectGuid>
<OutputType>WinExe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>OpenDiablo2</RootNamespace> <RootNamespace>OpenDiablo2</RootNamespace>
<AssemblyName>OpenDiablo2</AssemblyName> <AssemblyName>OpenDiablo2</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>

View File

@ -7,7 +7,7 @@
</root> </root>
<appender name="console" type="log4net.Appender.ConsoleAppender"> <appender name="console" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout"> <layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level %logger - %message%newline" /> <conversionPattern value="%level %type{1}.%method - %message%newline" />
</layout> </layout>
</appender> </appender>
<appender name="file" type="log4net.Appender.RollingFileAppender"> <appender name="file" type="log4net.Appender.RollingFileAppender">