1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-06-28 10:05:23 +00:00

Fixed animation glitches. Started work in animation priority. Fixed memory issues

This commit is contained in:
Tim Sarbin 2018-12-11 18:15:50 -05:00
parent c277cb2d89
commit 6646efd0c3
12 changed files with 140 additions and 103 deletions

View File

@ -6,7 +6,7 @@ using System.Runtime.CompilerServices;
namespace OpenDiablo2.Common.Models namespace OpenDiablo2.Common.Models
{ {
public sealed class ImageFrame : IDisposable public sealed class ImageFrame
{ {
public UInt32 Flip { get; internal set; } public UInt32 Flip { get; internal set; }
public UInt32 Width { get; internal set; } public UInt32 Width { get; internal set; }
@ -18,7 +18,8 @@ namespace OpenDiablo2.Common.Models
public UInt32 Length { get; internal set; } public UInt32 Length { get; internal set; }
public Int16[] ImageData { get; internal set; } public Int16[] ImageData { get; internal set; }
public void Dispose()
~ImageFrame()
{ {
ImageData = null; ImageData = null;
} }
@ -31,7 +32,7 @@ namespace OpenDiablo2.Common.Models
} }
} }
public sealed class ImageSet : IDisposable public sealed class ImageSet
{ {
private UInt32[] framePointers; private UInt32[] framePointers;
public ImageFrame[] Frames { get; private set; } public ImageFrame[] Frames { get; private set; }
@ -117,8 +118,10 @@ namespace OpenDiablo2.Common.Models
return result; return result;
} }
public void Dispose() ~ImageSet()
{ {
framePointers = null;
Frames = Array.Empty<ImageFrame>();
} }
} }
} }

View File

@ -57,6 +57,10 @@ namespace OpenDiablo2.Common.Models
public IEnumerable<COFLayer> Layers { get; private set; } public IEnumerable<COFLayer> Layers { get; private set; }
public IEnumerable<eAnimationFrame> AnimationFrames { get; private set; } public IEnumerable<eAnimationFrame> AnimationFrames { get; private set; }
public byte[] Priority { get; private set; }
public int NumberOfDirections { get; internal set; }
public int FramesPerDirection { get; internal set; }
public int NumberOfLayers { get; internal set; }
public static MPQCOF Load(Stream stream, Dictionary<string, List<AnimationData>> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode) public static MPQCOF Load(Stream stream, Dictionary<string, List<AnimationData>> animations, eHero hero, eWeaponClass weaponClass, eMobMode mobMode, string ShieldCode, string weaponCode)
{ {
@ -69,14 +73,14 @@ namespace OpenDiablo2.Common.Models
var br = new BinaryReader(stream); var br = new BinaryReader(stream);
var numLayers = br.ReadByte(); result.NumberOfLayers = br.ReadByte();
var framesPerDir = br.ReadByte(); result.FramesPerDirection = br.ReadByte();
br.ReadByte(); // Number of directions result.NumberOfDirections = br.ReadByte(); // Number of directions
br.ReadBytes(25); // Skip 25 unknown bytes... br.ReadBytes(25); // Skip 25 unknown bytes...
var layers = new List<COFLayer>(); var layers = new List<COFLayer>();
for (var layerIdx = 0; layerIdx < numLayers; layerIdx++) for (var layerIdx = 0; layerIdx < result.NumberOfLayers; layerIdx++)
{ {
var layer = new COFLayer var layer = new COFLayer
{ {
@ -93,7 +97,8 @@ namespace OpenDiablo2.Common.Models
layer.WeaponCode = weaponCode; layer.WeaponCode = weaponCode;
} }
result.Layers = layers; result.Layers = layers;
result.AnimationFrames = br.ReadBytes(framesPerDir).Select(x => (eAnimationFrame)x); result.AnimationFrames = br.ReadBytes(result.FramesPerDirection).Select(x => (eAnimationFrame)x);
result.Priority = br.ReadBytes(result.FramesPerDirection * result.NumberOfLayers * result.NumberOfDirections);
var cofName = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}".ToUpper(); var cofName = $"{hero.ToToken()}{mobMode.ToToken()}{weaponClass.ToToken()}".ToUpper();
result.Animations = animations[cofName]; result.Animations = animations[cofName];

View File

@ -112,6 +112,7 @@ namespace OpenDiablo2.Core
if (nextScene!= null) if (nextScene!= null)
{ {
currentScene.Dispose();
currentScene = nextScene; currentScene = nextScene;
nextScene = null; nextScene = null;
continue; continue;
@ -131,6 +132,13 @@ namespace OpenDiablo2.Core
} }
public void ChangeScene(eSceneType sceneType) public void ChangeScene(eSceneType sceneType)
=> nextScene = getScene(sceneType); {
var loadingSprite = getRenderWindow().LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading, new Point(300, 400));
getRenderWindow().Clear();
getRenderWindow().Draw(loadingSprite);
getRenderWindow().Sync();
nextScene = getScene(sceneType);
}
} }
} }

View File

@ -269,10 +269,8 @@ namespace OpenDiablo2.Core.GameState_
private IMapInfo GetMap(ref int cellX, ref int cellY) private IMapInfo GetMap(ref int cellX, ref int cellY)
{ {
var x = cellX; var p = new Point(cellX, cellY);
var y = cellY; var map = mapInfo.LastOrDefault(z => z.TileLocation.Contains(p));
var map = mapInfo.LastOrDefault(z => (x >= z.TileLocation.X) && (y >= z.TileLocation.Y)
&& (x < z.TileLocation.Right) && (y < z.TileLocation.Bottom));
if (map == null) if (map == null)
{ {
return null; return null;

View File

@ -43,7 +43,8 @@ namespace OpenDiablo2.Core
} }
public ImageSet GetImageSet(string resourcePath) public ImageSet GetImageSet(string resourcePath)
=> cache.AddOrGetExisting($"ImageSet::{resourcePath}", () => ImageSet.LoadFromStream(mpqProvider.GetStream(resourcePath))); // => cache.AddOrGetExisting($"ImageSet::{resourcePath}", () => ImageSet.LoadFromStream(mpqProvider.GetStream(resourcePath)));
=> ImageSet.LoadFromStream(mpqProvider.GetStream(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"))); => cache.AddOrGetExisting($"Font::{resourcePath}", () => MPQFont.LoadFromStream(mpqProvider.GetStream($"{resourcePath}.DC6"), mpqProvider.GetStream($"{resourcePath}.tbl")));
@ -68,11 +69,12 @@ namespace OpenDiablo2.Core
{ {
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";
return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode, shieldCode, weaponCode); return MPQCOF.Load(mpqProvider.GetStream(path), Animations, hero, weaponClass, mobMode, shieldCode, weaponCode);
}); }, new System.Runtime.Caching.CacheItemPolicy { Priority = System.Runtime.Caching.CacheItemPriority.NotRemovable });
public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette) public MPQDCC GetPlayerDCC(MPQCOF.COFLayer cofLayer, eArmorType armorType, Palette palette)
{ {
// TODO: We need to cache this... return cache.AddOrGetExisting($"PlayerDCC::{cofLayer.GetDCCPath(armorType)}", () =>
{
byte[] binaryData; byte[] binaryData;
var streamPath = cofLayer.GetDCCPath(armorType); var streamPath = cofLayer.GetDCCPath(armorType);
@ -89,25 +91,7 @@ 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

@ -13,7 +13,7 @@ namespace OpenDiablo2.SDL2_
log.Info("Configuring OpenDiablo2.Core service implementations."); log.Info("Configuring OpenDiablo2.Core service implementations.");
builder.RegisterType<SDL2RenderWindow>().AsImplementedInterfaces().SingleInstance(); builder.RegisterType<SDL2RenderWindow>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<SDL2MusicProvider>().AsImplementedInterfaces().SingleInstance(); builder.RegisterType<SDL2SoundProvider>().AsImplementedInterfaces().SingleInstance();
} }
} }
} }

View File

@ -38,7 +38,6 @@ namespace OpenDiablo2.SDL2_
public IntPtr[] SpriteTexture { get; set; } public IntPtr[] SpriteTexture { get; set; }
public int FramesToAnimate { get; set; } public int FramesToAnimate { get; set; }
public int AnimationSpeed { get; set; } public int AnimationSpeed { get; set; }
public int RenderFrameIndex { get; set; }
} }
static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
@ -57,6 +56,7 @@ namespace OpenDiablo2.SDL2_
private readonly List<DirectionCacheItem> directionCache = new List<DirectionCacheItem>(); private readonly List<DirectionCacheItem> directionCache = new List<DirectionCacheItem>();
DirectionCacheItem currentDirectionCache; DirectionCacheItem currentDirectionCache;
private float seconds; private float seconds;
private int renderFrameIndex = 0;
private readonly IResourceManager resourceManager; private readonly IResourceManager resourceManager;
private readonly IPaletteProvider paletteProvider; private readonly IPaletteProvider paletteProvider;
@ -79,13 +79,13 @@ namespace OpenDiablo2.SDL2_
var destRect = new SDL.SDL_Rect var destRect = new SDL.SDL_Rect
{ {
x = pixelOffsetX + currentDirectionCache.SpriteRect[currentDirectionCache.RenderFrameIndex].x, x = pixelOffsetX + currentDirectionCache.SpriteRect[renderFrameIndex].x,
y = pixelOffsetY + currentDirectionCache.SpriteRect[currentDirectionCache.RenderFrameIndex].y, y = pixelOffsetY + currentDirectionCache.SpriteRect[renderFrameIndex].y,
w = currentDirectionCache.SpriteRect[currentDirectionCache.RenderFrameIndex].w, w = currentDirectionCache.SpriteRect[renderFrameIndex].w,
h = currentDirectionCache.SpriteRect[currentDirectionCache.RenderFrameIndex].h h = currentDirectionCache.SpriteRect[renderFrameIndex].h
}; };
SDL.SDL_RenderCopy(renderer, currentDirectionCache.SpriteTexture[currentDirectionCache.RenderFrameIndex], IntPtr.Zero, ref destRect); SDL.SDL_RenderCopy(renderer, currentDirectionCache.SpriteTexture[renderFrameIndex], IntPtr.Zero, ref destRect);
} }
public void Update(long ms) public void Update(long ms)
@ -98,9 +98,9 @@ namespace OpenDiablo2.SDL2_
while (seconds >= animationSeg) while (seconds >= animationSeg)
{ {
seconds -= animationSeg; seconds -= animationSeg;
currentDirectionCache.RenderFrameIndex++; renderFrameIndex++;
if (currentDirectionCache.RenderFrameIndex >= currentDirectionCache.FramesToAnimate) while (renderFrameIndex >= currentDirectionCache.FramesToAnimate)
currentDirectionCache.RenderFrameIndex = 0; renderFrameIndex -= currentDirectionCache.FramesToAnimate;
} }
} }
@ -111,6 +111,7 @@ namespace OpenDiablo2.SDL2_
public void ResetAnimationData() public void ResetAnimationData()
{ {
var lastMobMode = MobMode;
switch (LocationDetails.MovementType) switch (LocationDetails.MovementType)
{ {
case eMovementType.Stopped: case eMovementType.Stopped:
@ -126,23 +127,22 @@ namespace OpenDiablo2.SDL2_
MobMode = eMobMode.PlayerNeutral; MobMode = eMobMode.PlayerNeutral;
break; break;
} }
if (lastMobMode != MobMode)
renderFrameIndex = 0;
currentDirectionCache = directionCache.FirstOrDefault(x => x.MobMode == MobMode && x.Direction == directionConversion[LocationDetails.MovementDirection]); currentDirectionCache = directionCache.FirstOrDefault(x => x.MobMode == MobMode && x.Direction == directionConversion[LocationDetails.MovementDirection]);
if (currentDirectionCache != null) if (currentDirectionCache != null)
{
currentDirectionCache.RenderFrameIndex = 0;
return; return;
}
animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode, ShieldCode, WeaponCode); animationData = resourceManager.GetPlayerAnimation(Hero, WeaponClass, MobMode, ShieldCode, WeaponCode);
if (animationData == null) if (animationData == null)
throw new OpenDiablo2Exception("Could not locate animation for the character!"); throw new OpenDiablo2Exception("Could not locate animation for the character!");
var palette = paletteProvider.PaletteTable["Units"]; var palette = paletteProvider.PaletteTable["Units"];
CacheFrames(animationData.Layers.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette))); CacheFrames(animationData.Layers.Select(layer => resourceManager.GetPlayerDCC(layer, ArmorType, palette)).ToArray());
} }
private unsafe void CacheFrames(IEnumerable<MPQDCC> layerData) private unsafe void CacheFrames(MPQDCC[] layerData)
{ {
var directionCache = new DirectionCacheItem var directionCache = new DirectionCacheItem
{ {
@ -155,7 +155,6 @@ namespace OpenDiablo2.SDL2_
var dirAnimation = animationData.Animations[0]; var dirAnimation = animationData.Animations[0];
directionCache.FramesToAnimate = dirAnimation.FramesPerDirection; directionCache.FramesToAnimate = dirAnimation.FramesPerDirection;
directionCache.AnimationSpeed = dirAnimation.AnimationSpeed; directionCache.AnimationSpeed = dirAnimation.AnimationSpeed;
directionCache.RenderFrameIndex = 0;
var minX = Int32.MaxValue; var minX = Int32.MaxValue;
var minY = Int32.MaxValue; var minY = Int32.MaxValue;
@ -163,7 +162,6 @@ namespace OpenDiablo2.SDL2_
var maxY = Int32.MinValue; var maxY = Int32.MinValue;
var layersIgnored = 0; var layersIgnored = 0;
var layersToRender = new List<MPQDCC>();
foreach (var layer in layerData) foreach (var layer in layerData)
{ {
if (layer == null) if (layer == null)
@ -172,7 +170,6 @@ namespace OpenDiablo2.SDL2_
continue; 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);
@ -195,8 +192,21 @@ 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 layersToRender) var priorities = new int[animationData.NumberOfLayers];
Array.Copy(
animationData.Priority,
(directionConversion[LocationDetails.MovementDirection] * animationData.FramesPerDirection * animationData.NumberOfLayers)
+ (frameIndex * animationData.NumberOfLayers),
priorities,
0,
animationData.NumberOfLayers
);
for (var i = 0; i < layerData.Length; i++)
{ {
//var layer = layerData[priorities[i]];
var layer = layerData[i];
if (layer == null) if (layer == null)
continue; continue;
@ -231,8 +241,9 @@ namespace OpenDiablo2.SDL2_
directionCache.SpriteRect[frameIndex] = new SDL.SDL_Rect { x = minX, y = minY, w = frameW, h = frameH }; directionCache.SpriteRect[frameIndex] = new SDL.SDL_Rect { x = minX, y = minY, w = frameW, h = frameH };
this.directionCache.Add(directionCache); this.directionCache.Add(directionCache);
currentDirectionCache = directionCache;
} }
currentDirectionCache = directionCache;
} }
} }

View File

@ -22,14 +22,14 @@ using SDL2;
namespace OpenDiablo2.SDL2_ namespace OpenDiablo2.SDL2_
{ {
public sealed class SDL2MusicProvider : ISoundProvider public sealed class SDL2SoundProvider : ISoundProvider
{ {
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private IntPtr music = IntPtr.Zero; private IntPtr music = IntPtr.Zero;
private int musicChannel; private int musicChannel;
private byte[] musicBytes; // Cannot be local or GC will destory it with great anger private byte[] musicBytes; // Cannot be local or GC will destory it with great anger
public SDL2MusicProvider() public SDL2SoundProvider()
{ {
if (SDL_mixer.Mix_OpenAudio(22050, SDL_mixer.MIX_DEFAULT_FORMAT, 2, 1024) < 0) if (SDL_mixer.Mix_OpenAudio(22050, SDL_mixer.MIX_DEFAULT_FORMAT, 2, 1024) < 0)
log.Error($"SDL_mixer could not initialize! SDL_mixer Error: {SDL.SDL_GetError()}"); log.Error($"SDL_mixer could not initialize! SDL_mixer Error: {SDL.SDL_GetError()}");

View File

@ -26,7 +26,7 @@ namespace OpenDiablo2.SDL2_
{ {
internal sealed class SDL2Sprite : ISprite internal sealed class SDL2Sprite : ISprite
{ {
internal readonly ImageSet source; internal ImageSet source;
private readonly IntPtr renderer; private readonly IntPtr renderer;
private readonly bool cacheFrames; private readonly bool cacheFrames;
@ -96,7 +96,7 @@ namespace OpenDiablo2.SDL2_
SDL.SDL_SetTextureBlendMode(texture[i], blend ? SDL.SDL_BlendMode.SDL_BLENDMODE_ADD : SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); SDL.SDL_SetTextureBlendMode(texture[i], blend ? SDL.SDL_BlendMode.SDL_BLENDMODE_ADD : SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
} }
else else
if (texture[TextureIndex] != IntPtr.Zero) if (texture != null && texture[TextureIndex] != IntPtr.Zero)
SDL.SDL_SetTextureBlendMode(texture[TextureIndex], blend ? SDL.SDL_BlendMode.SDL_BLENDMODE_ADD : SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND); SDL.SDL_SetTextureBlendMode(texture[TextureIndex], blend ? SDL.SDL_BlendMode.SDL_BLENDMODE_ADD : SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
} }
@ -206,6 +206,23 @@ namespace OpenDiablo2.SDL2_
var framestoClear = cacheFrames ? TotalFrames : 1; var framestoClear = cacheFrames ? TotalFrames : 1;
for (int i = 0; i < framestoClear; i++) for (int i = 0; i < framestoClear; i++)
frameLoaded[i] = false; frameLoaded[i] = false;
DestroyTextures();
}
private void DestroyTextures()
{
var framestoClear = cacheFrames ? TotalFrames : 1;
for (var i = 0; i < framestoClear; i++)
{
if (!frameLoaded[i])
continue;
SDL.SDL_DestroyTexture(texture[i]);
texture[i] = IntPtr.Zero;
frameLoaded[i] = false;
}
texture = new IntPtr[TotalFrames];
} }
public void Dispose() public void Dispose()
@ -213,16 +230,11 @@ namespace OpenDiablo2.SDL2_
if (disposed) if (disposed)
return; return;
var framestoClear = cacheFrames ? TotalFrames : 1; DestroyTextures();
for (var i = 0; i < framestoClear; i++) source = null;
{
SDL.SDL_DestroyTexture(texture[i]);
texture[i] = IntPtr.Zero;
}
texture = Array.Empty<IntPtr>();
disposed = true; disposed = true;
} }
} }
} }

View File

@ -77,18 +77,6 @@ namespace OpenDiablo2.Scenes
urlLabel = renderWindow.CreateLabel(labelFont, new Point(50, 569), "https://github.com/essial/OpenDiablo2/"); urlLabel = renderWindow.CreateLabel(labelFont, new Point(50, 569), "https://github.com/essial/OpenDiablo2/");
urlLabel.TextColor = Color.Magenta; urlLabel.TextColor = Color.Magenta;
var loadingSprite = renderWindow.LoadSprite(ResourcePaths.LoadingScreen, Palettes.Loading, new Point(300, 400));
// Pre-load all the scenes for now until we fix the sdl load problem
var scenesToLoad = new eSceneType[] { eSceneType.SelectHeroClass };
for (int i = 0; i < scenesToLoad.Count(); i++)
{
renderWindow.Clear();
renderWindow.Draw(loadingSprite, (int)(loadingSprite.TotalFrames * (i / (float)scenesToLoad.Count())));
renderWindow.Sync();
getScene(scenesToLoad[i]);
}
soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.BGMTitle)); soundProvider.LoadSong(mpqProvider.GetStream(ResourcePaths.BGMTitle));
soundProvider.PlaySong(); soundProvider.PlaySong();
} }

View File

@ -20,13 +20,25 @@ namespace OpenDiablo2.Scenes
Retreating Retreating
} }
class HeroRenderInfo class HeroRenderInfo : IDisposable
{ {
public ISprite IdleSprite, IdleSelectedSprite, ForwardWalkSprite, ForwardWalkSpriteOverlay, SelectedSprite, SelectedSpriteOverlay, BackWalkSprite, BackWalkSpriteOverlay; public ISprite IdleSprite, IdleSelectedSprite, ForwardWalkSprite, ForwardWalkSpriteOverlay, SelectedSprite, SelectedSpriteOverlay, BackWalkSprite, BackWalkSpriteOverlay;
public eHeroStance Stance; public eHeroStance Stance;
public long ForwardWalkTimeMs, BackWalkTimeMs; public long ForwardWalkTimeMs, BackWalkTimeMs;
public long SpecialFrameTime; public long SpecialFrameTime;
public Rectangle SelectionBounds = new Rectangle(); public Rectangle SelectionBounds = new Rectangle();
public void Dispose()
{
IdleSprite?.Dispose();
IdleSelectedSprite?.Dispose();
ForwardWalkSprite?.Dispose();
ForwardWalkSpriteOverlay?.Dispose();
SelectedSprite?.Dispose();
SelectedSpriteOverlay?.Dispose();
BackWalkSprite?.Dispose();
BackWalkSpriteOverlay?.Dispose();
}
} }
[Scene(eSceneType.SelectHeroClass)] [Scene(eSceneType.SelectHeroClass)]
@ -611,6 +623,12 @@ namespace OpenDiablo2.Scenes
headingFont.Dispose(); headingFont.Dispose();
headingLabel.Dispose(); headingLabel.Dispose();
sfxDictionary.Clear(); sfxDictionary.Clear();
}
foreach (var hri in heroRenderInfo)
hri.Value.Dispose();
heroRenderInfo.Clear();
}
} }
} }

View File

@ -3,6 +3,16 @@
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup> </startup>
<system.runtime.caching>
<memoryCache>
<namedCaches>
<add name="OpenDiablo2.Cache"
cacheMemoryLimitMegabytes="0"
physicalMemoryLimitPercentage="0"
pollingInterval="00:00:05" />
</namedCaches>
</memoryCache>
</system.runtime.caching>
<runtime> <runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly> <dependentAssembly>