diff --git a/BossHealthPlugin/BossBarStyle.cs b/BossHealthPlugin/BossBarStyle.cs
index 39296fa..e05dc9c 100644
--- a/BossHealthPlugin/BossBarStyle.cs
+++ b/BossHealthPlugin/BossBarStyle.cs
@@ -5,9 +5,9 @@ namespace Skopje.BossHealth
///
/// Look of the boss health bar. This is the file to edit.
///
- /// Sprites are chosen by the name shown on an Image's Sprite field in the game.
- /// Leave a name empty to copy the in-game XP progress bar sprite.
- /// After changing a name, rebuild the plugin.
+ /// Sprites are chosen by the name shown on an Image's Sprite field in the game.
+ /// Leave a fill name empty to use a solid rectangle.
+ /// After changing a name, rebuild the plugin.
///
public static class BossBarStyle
{
@@ -17,15 +17,17 @@ namespace Skopje.BossHealth
public static readonly Color HealthColor = new Color(0.86f, 0.1f, 0.1f, 1f);
public static readonly Color DelayColor = new Color(1f, 0.48f, 0.08f, 1f);
+ public static readonly Color ImmuneColor = new Color(0.62f, 0.62f, 0.62f, 1f);
+ public static readonly Color ImmuneDelayColor = new Color(0.4f, 0.4f, 0.4f, 1f);
public static readonly Color BackgroundColor = new Color(0.04f, 0.04f, 0.04f, 0.8f);
public static readonly Color LabelColor = Color.white;
- // The white bar waits this long after the last hit, then drains toward the real health.
+ // The orange bar waits this long after the last hit, then drains toward the real health.
public const float DelaySeconds = 0.6f;
public const float DelayDrainPerSecond = 0.75f;
public static readonly Vector2 BarSize = new Vector2(392f, 13f);
- public const float TopClearance = 100f;
+ public const float TopClearance = 25f;
public const float TrackPadding = 2f;
public const float LabelHeight = 26f;
public const float LabelFontSize = 20f;
diff --git a/BossHealthPlugin/BossHealthPlugin.cs b/BossHealthPlugin/BossHealthPlugin.cs
index deb77f6..14db63b 100644
--- a/BossHealthPlugin/BossHealthPlugin.cs
+++ b/BossHealthPlugin/BossHealthPlugin.cs
@@ -193,7 +193,7 @@ namespace Skopje.BossHealth
{
_nextReadTime = Time.unscaledTime + FightReadSeconds;
if (_fights[0].StillGoing(out float ratio))
- Present(_fights[0].Name, ratio);
+ Present(_fights[0].Name, ratio, _fights[0].IsImmune());
}
else
{
@@ -223,10 +223,10 @@ namespace Skopje.BossHealth
}
if (_fights[0].StillGoing(out float ratio))
- Present(_fights[0].Name, ratio);
+ Present(_fights[0].Name, ratio, _fights[0].IsImmune());
}
- void Present(string bossName, float ratio)
+ void Present(string bossName, float ratio, bool immune)
{
EnsureBar();
if (!_hasSample || _bossName != bossName)
@@ -255,9 +255,15 @@ namespace Skopje.BossHealth
if (_label != null)
_label.text = bossName;
if (_healthImage != null)
+ {
+ _healthImage.color = immune ? BossBarStyle.ImmuneColor : BossBarStyle.HealthColor;
_healthImage.fillAmount = _health;
+ }
if (_delayImage != null)
+ {
+ _delayImage.color = immune ? BossBarStyle.ImmuneDelayColor : BossBarStyle.DelayColor;
_delayImage.fillAmount = _delay;
+ }
}
void AnimateDelay()
@@ -443,9 +449,8 @@ namespace Skopje.BossHealth
{
XpProgressBar xpBar = FindXpBar();
Image xpImage = xpBar != null ? xpBar.ProgressBar : null;
- Sprite xpSprite = xpImage != null ? xpImage.sprite : null;
- Sprite healthSprite = ResolveSprite(BossBarStyle.HealthSpriteName, xpSprite) ?? SolidSprite();
- Sprite delaySprite = ResolveSprite(BossBarStyle.DelaySpriteName, healthSprite) ?? healthSprite;
+ Sprite healthSprite = ResolveSprite(BossBarStyle.HealthSpriteName, null) ?? SolidSprite();
+ Sprite delaySprite = ResolveSprite(BossBarStyle.DelaySpriteName, null) ?? healthSprite;
Sprite backgroundSprite = ResolveSprite(BossBarStyle.BackgroundSpriteName, FrameSprite(xpImage, healthSprite));
_barRoot = new GameObject("BossHealthBar");
@@ -846,6 +851,18 @@ namespace Skopje.BossHealth
return obj != null;
}
+ static bool SafeDamageable(MetalskyBoss boss)
+ {
+ try
+ {
+ return boss.IsDamageable;
+ }
+ catch (Exception)
+ {
+ return true;
+ }
+ }
+
static bool SafeDead(MetalskyBoss boss)
{
try
@@ -875,6 +892,7 @@ namespace Skopje.BossHealth
public string Name;
public abstract bool StillGoing(out float ratio);
+ public abstract bool IsImmune();
}
sealed class AnaFight : Fight
@@ -904,6 +922,11 @@ namespace Skopje.BossHealth
return false;
return TryRatio(_boss.EnemyControl, out ratio);
}
+
+ public override bool IsImmune()
+ {
+ return !IsTrue(_boss.IsVulnerable);
+ }
}
sealed class VesnaFight : Fight
@@ -931,6 +954,11 @@ namespace Skopje.BossHealth
return false;
return TryRatio(_boss.EnemyControl, out ratio);
}
+
+ public override bool IsImmune()
+ {
+ return !IsTrue(_boss.Visibility);
+ }
}
sealed class MetalskyFight : Fight
@@ -950,6 +978,11 @@ namespace Skopje.BossHealth
return false;
return TryRatio(_boss.PawnStatus, out ratio);
}
+
+ public override bool IsImmune()
+ {
+ return !SafeDamageable(_boss);
+ }
}
sealed class TeofanFight : Fight
@@ -975,6 +1008,25 @@ namespace Skopje.BossHealth
control = _brain.GetComponentInChildren(true);
return TryRatio(control, out ratio);
}
+
+ public override bool IsImmune()
+ {
+ try
+ {
+ if (_brain.blockHelper != null && _brain.blockHelper.IsBlocking)
+ return true;
+
+ TeofanBossState state = _brain.CurrentState;
+ return state == TeofanBossState.DissolveIn
+ || state == TeofanBossState.FallenInteract
+ || state == TeofanBossState.EndPhase
+ || state == TeofanBossState.Despawned;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
}
}
}
diff --git a/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.dll b/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.dll
index 388b28b..b921070 100644
Binary files a/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.dll and b/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.dll differ
diff --git a/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.pdb b/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.pdb
index 14a4ead..22ffce0 100644
Binary files a/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.pdb and b/BossHealthPlugin/bin/Release/net6.0/Skopje.BossHealth.pdb differ
diff --git a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfo.cs b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfo.cs
index 39f06f9..6ef20ca 100644
--- a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfo.cs
+++ b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfo.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.BossHealth")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.BossHealth")]
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.BossHealth")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfoInputs.cache b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfoInputs.cache
index b0d2506..a150d82 100644
--- a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfoInputs.cache
+++ b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.AssemblyInfoInputs.cache
@@ -1 +1 @@
-0acf9d2f1a6c359332339552562b4ad3180ad1d45517bc93e22ec640b2c677b6
+abb2aafca8f9c38e16a185b614682f24e0da3a5492f3ed24b1aa588b0f35f2fc
diff --git a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.csproj.AssemblyReference.cache b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.csproj.AssemblyReference.cache
index 69b3c6f..1ce1eb6 100644
Binary files a/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.csproj.AssemblyReference.cache and b/BossHealthPlugin/obj/Release/net6.0/BossHealthPlugin.csproj.AssemblyReference.cache differ
diff --git a/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.dll b/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.dll
index 388b28b..b921070 100644
Binary files a/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.dll and b/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.dll differ
diff --git a/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.pdb b/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.pdb
index 14a4ead..22ffce0 100644
Binary files a/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.pdb and b/BossHealthPlugin/obj/Release/net6.0/Skopje.BossHealth.pdb differ
diff --git a/BossHealthPlugin/obj/Release/net6.0/ref/Skopje.BossHealth.dll b/BossHealthPlugin/obj/Release/net6.0/ref/Skopje.BossHealth.dll
index aa5efe0..367f5ab 100644
Binary files a/BossHealthPlugin/obj/Release/net6.0/ref/Skopje.BossHealth.dll and b/BossHealthPlugin/obj/Release/net6.0/ref/Skopje.BossHealth.dll differ
diff --git a/BossHealthPlugin/obj/Release/net6.0/refint/Skopje.BossHealth.dll b/BossHealthPlugin/obj/Release/net6.0/refint/Skopje.BossHealth.dll
index aa5efe0..367f5ab 100644
Binary files a/BossHealthPlugin/obj/Release/net6.0/refint/Skopje.BossHealth.dll and b/BossHealthPlugin/obj/Release/net6.0/refint/Skopje.BossHealth.dll differ
diff --git a/CustomPlayerSkins/CustomPlayerSkinsPlugin.cs b/CustomPlayerSkins/CustomPlayerSkinsPlugin.cs
new file mode 100644
index 0000000..cd94b9a
--- /dev/null
+++ b/CustomPlayerSkins/CustomPlayerSkinsPlugin.cs
@@ -0,0 +1,438 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using BepInEx;
+using BepInEx.Configuration;
+using BepInEx.Logging;
+using BepInEx.Unity.IL2CPP;
+using HarmonyLib;
+using Il2CppInterop.Runtime;
+using Il2CppInterop.Runtime.InteropTypes.Arrays;
+using Skopje.Modding;
+using UnityEngine;
+
+namespace Skopje.CustomPlayerSkins
+{
+ [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
+ public class CustomPlayerSkinsPlugin : BasePlugin
+ {
+ public const string PluginGuid = "com.skopje.customplayerskins";
+ public const string PluginName = "Skopje Custom Player Skins";
+ public const string PluginVersion = "1.0.0";
+
+ internal static ManualLogSource LogSource;
+ internal static ConfigEntry Enabled;
+
+ static Harmony _harmony;
+ static Il2CppSystem.Action _onPlayerStarted;
+
+ public override void Load()
+ {
+ LogSource = Log;
+ Enabled = Config.Bind("Skin", "Enabled", true, "Replace each player outfit with its own PNG in the CustomSkins folder next to this plugin.");
+
+ _harmony = new Harmony(PluginGuid);
+ var refresh = AccessTools.Method(typeof(SkopjePlayer), "RefreshActiveModels");
+ if (refresh == null)
+ LogSource.LogWarning("SkopjePlayer.RefreshActiveModels was not found. The skins are applied when a player spawns.");
+ else
+ _harmony.Patch(refresh, postfix: new HarmonyMethod(typeof(SkinPatches), nameof(SkinPatches.AfterRefreshModels)));
+
+ _onPlayerStarted = DelegateSupport.ConvertDelegate>(new Action(SkinReplacer.ApplyPlayer));
+ ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
+
+ LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit the PNGs in CustomSkins: singleplayer, ranger, medic, engineer, and fighter (the Warrior class).");
+ }
+
+ public override bool Unload()
+ {
+ if (_harmony != null)
+ _harmony.UnpatchSelf();
+ SkinReplacer.Restore();
+ return true;
+ }
+ }
+
+ static class SkinPatches
+ {
+ public static void AfterRefreshModels(SkopjePlayer __instance)
+ {
+ SkinReplacer.ApplyPlayer(__instance);
+ }
+ }
+
+ static class SkinReplacer
+ {
+ sealed class Outfit
+ {
+ public string FileName;
+ public string DumpMaterial;
+ public string[] Materials;
+ public Texture2D Texture;
+ public DateTime WriteTimeUtc;
+ }
+
+ // Fighter is the multiplayer class shown as Warrior on the player icons.
+ static readonly Outfit[] Outfits =
+ {
+ new Outfit
+ {
+ FileName = "singleplayer.png",
+ DumpMaterial = "MAT_Pioneer_Default_Clothes",
+ Materials = new[] { "MAT_Pioneer_Default_Clothes", "MAT_Pioneer_Default_Clothes_FP" },
+ },
+ new Outfit
+ {
+ FileName = "ranger.png",
+ DumpMaterial = "MAT_Pioneer_Ranger_Clothes",
+ Materials = new[] { "MAT_Pioneer_Ranger_Clothes", "MAT_Pioneer_Ranger_Clothes_FP" },
+ },
+ new Outfit
+ {
+ FileName = "medic.png",
+ DumpMaterial = "MAT_Pioneer_Medic_Clothes",
+ Materials = new[] { "MAT_Pioneer_Medic_Clothes", "MAT_Pioneer_Medic_Clothes_FP" },
+ },
+ new Outfit
+ {
+ FileName = "engineer.png",
+ DumpMaterial = "MAT_Pioneer_Engi_Clothes",
+ Materials = new[] { "MAT_Pioneer_Engi_Clothes", "MAT_Pioneer_Engi_Clothes_FP" },
+ },
+ new Outfit
+ {
+ FileName = "fighter.png",
+ DumpMaterial = "MAT_Pioneer_Fighter_Clothes",
+ Materials = new[] { "MAT_Pioneer_Fighter_Clothes", "MAT_Pioneer_Fighter_Clothes_FP" },
+ },
+ };
+
+ static readonly Dictionary _originals = new Dictionary();
+ static float _nextFileCheck;
+ static bool _loggedMiss;
+
+ public static void ApplyPlayer(SkopjePlayer player)
+ {
+ if (!CustomPlayerSkinsPlugin.Enabled.Value)
+ {
+ RevertMaterials();
+ return;
+ }
+ if (!EnsureTextures())
+ return;
+
+ int applied = ApplyLoadedMaterials();
+ if (player != null)
+ {
+ applied += ApplySkins(player.PlayerSkins);
+ applied += ApplyRenderer(player.FirstPersonRenderer);
+ applied += ApplyRenderer(player.ThirdPersonRenderer);
+ }
+
+ if (applied == 0 && !_loggedMiss)
+ {
+ _loggedMiss = true;
+ CustomPlayerSkinsPlugin.LogSource.LogWarning("Custom skins are loaded, but the player clothes materials are not in memory yet.");
+ }
+ else if (applied > 0)
+ {
+ _loggedMiss = false;
+ CustomPlayerSkinsPlugin.LogSource.LogInfo($"Applied {applied} player clothes material{(applied == 1 ? "" : "s")} from CustomSkins.");
+ }
+ }
+
+ public static void Restore()
+ {
+ RevertMaterials();
+ _originals.Clear();
+ for (int i = 0; i < Outfits.Length; i++)
+ {
+ if (Outfits[i].Texture == null)
+ continue;
+ UnityEngine.Object.Destroy(Outfits[i].Texture);
+ Outfits[i].Texture = null;
+ }
+ }
+
+ static void RevertMaterials()
+ {
+ foreach (var pair in _originals)
+ {
+ var material = FindMaterial(pair.Key);
+ if (material != null)
+ material.SetTexture("_MainTex", pair.Value);
+ }
+ }
+
+ static int ApplyLoadedMaterials()
+ {
+ int applied = 0;
+ var materials = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < materials.Length; i++)
+ applied += ApplyMaterial(materials[i]);
+ return applied;
+ }
+
+ static int ApplySkins(Il2CppReferenceArray skins)
+ {
+ if (skins == null)
+ return 0;
+ int applied = 0;
+ for (int i = 0; i < skins.Length; i++)
+ {
+ PlayerSkin skin = skins[i];
+ if (skin == null)
+ continue;
+ applied += ApplyMaterialArray(skin.FPMaterials);
+ applied += ApplyMaterialArray(skin.TPMaterials);
+ }
+ return applied;
+ }
+
+ static int ApplyRenderer(Renderer renderer)
+ {
+ if (renderer == null)
+ return 0;
+ var materials = renderer.sharedMaterials;
+ if (materials == null)
+ return 0;
+ int applied = 0;
+ for (int i = 0; i < materials.Length; i++)
+ applied += ApplyMaterial(materials[i]);
+ return applied;
+ }
+
+ static int ApplyMaterialArray(Il2CppReferenceArray materials)
+ {
+ if (materials == null)
+ return 0;
+ int applied = 0;
+ for (int i = 0; i < materials.Length; i++)
+ applied += ApplyMaterial(materials[i]);
+ return applied;
+ }
+
+ static int ApplyMaterial(Material material)
+ {
+ if (material == null || !material.HasProperty("_MainTex"))
+ return 0;
+ Outfit outfit = FindOutfit(material.name);
+ if (outfit == null || outfit.Texture == null)
+ return 0;
+
+ int id = material.GetInstanceID();
+ if (!_originals.ContainsKey(id))
+ {
+ var current = material.GetTexture("_MainTex");
+ _originals[id] = current;
+ CopySampling(outfit.Texture, current);
+ }
+
+ if (material.GetTexture("_MainTex") == outfit.Texture)
+ return 0;
+
+ material.SetTexture("_MainTex", outfit.Texture);
+ return 1;
+ }
+
+ static Outfit FindOutfit(string materialName)
+ {
+ if (string.IsNullOrEmpty(materialName))
+ return null;
+ for (int i = 0; i < Outfits.Length; i++)
+ {
+ string[] names = Outfits[i].Materials;
+ for (int n = 0; n < names.Length; n++)
+ {
+ string expected = names[n];
+ if (materialName == expected || materialName.StartsWith(expected + " (", StringComparison.Ordinal))
+ return Outfits[i];
+ }
+ }
+ return null;
+ }
+
+ static void CopySampling(Texture2D target, Texture source)
+ {
+ if (target == null || source == null)
+ return;
+ target.wrapMode = source.wrapMode;
+ target.filterMode = source.filterMode;
+ target.anisoLevel = source.anisoLevel;
+ }
+
+ static Material FindMaterial(int instanceId)
+ {
+ var materials = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < materials.Length; i++)
+ {
+ Material material = materials[i];
+ if (material != null && material.GetInstanceID() == instanceId)
+ return material;
+ }
+ return null;
+ }
+
+ static bool EnsureTextures()
+ {
+ if (AnyLoaded() && Time.unscaledTime < _nextFileCheck)
+ return true;
+ _nextFileCheck = Time.unscaledTime + 1f;
+
+ string directory = SkinsDirectory();
+ int ready = 0;
+ for (int i = 0; i < Outfits.Length; i++)
+ {
+ if (EnsureOutfit(Outfits[i], directory))
+ ready++;
+ }
+
+ if (ready == 0)
+ {
+ CustomPlayerSkinsPlugin.LogSource.LogWarning("No outfit PNGs are ready in " + directory);
+ return false;
+ }
+ return true;
+ }
+
+ static bool AnyLoaded()
+ {
+ for (int i = 0; i < Outfits.Length; i++)
+ {
+ if (Outfits[i].Texture != null)
+ return true;
+ }
+ return false;
+ }
+
+ static bool EnsureOutfit(Outfit outfit, string directory)
+ {
+ string path = Path.Combine(directory, outfit.FileName);
+ if (!File.Exists(path))
+ {
+ if (!TryDumpSample(outfit, path))
+ return outfit.Texture != null;
+ }
+
+ DateTime writeTime;
+ try { writeTime = File.GetLastWriteTimeUtc(path); }
+ catch (Exception ex)
+ {
+ CustomPlayerSkinsPlugin.LogSource.LogError("Could not read " + outfit.FileName + ": " + ex.Message);
+ return outfit.Texture != null;
+ }
+
+ if (outfit.Texture != null && outfit.WriteTimeUtc == writeTime)
+ return true;
+
+ byte[] bytes;
+ try { bytes = File.ReadAllBytes(path); }
+ catch (Exception ex)
+ {
+ CustomPlayerSkinsPlugin.LogSource.LogError("Could not read " + outfit.FileName + ": " + ex.Message);
+ return outfit.Texture != null;
+ }
+
+ var texture = new Texture2D(2, 2, TextureFormat.RGBA32, true);
+ texture.name = "SkopjeCustomSkin_" + Path.GetFileNameWithoutExtension(outfit.FileName);
+ texture.hideFlags = HideFlags.HideAndDontSave;
+ if (!ImageConversion.LoadImage(texture, bytes))
+ {
+ UnityEngine.Object.Destroy(texture);
+ CustomPlayerSkinsPlugin.LogSource.LogError(outfit.FileName + " is not a readable image: " + path);
+ return outfit.Texture != null;
+ }
+
+ if (outfit.Texture != null)
+ UnityEngine.Object.Destroy(outfit.Texture);
+ outfit.Texture = texture;
+ outfit.WriteTimeUtc = writeTime;
+ _loggedMiss = false;
+ CustomPlayerSkinsPlugin.LogSource.LogInfo($"Loaded {outfit.FileName} ({texture.width}x{texture.height})");
+ return true;
+ }
+
+ static bool TryDumpSample(Outfit outfit, string path)
+ {
+ Texture source = null;
+ var materials = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < materials.Length && source == null; i++)
+ {
+ Material material = materials[i];
+ if (material == null || material.name != outfit.DumpMaterial)
+ continue;
+ if (!material.HasProperty("_MainTex"))
+ continue;
+ Texture current = material.GetTexture("_MainTex");
+ if (_originals.TryGetValue(material.GetInstanceID(), out Texture original))
+ current = original;
+ source = current;
+ }
+ if (source == null)
+ return false;
+
+ byte[] png = EncodePng(source);
+ if (png == null || png.Length == 0)
+ return false;
+
+ try
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+ File.WriteAllBytes(path, png);
+ CustomPlayerSkinsPlugin.LogSource.LogInfo("Dumped " + outfit.FileName + " from the game into CustomSkins.");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ CustomPlayerSkinsPlugin.LogSource.LogError("Could not write " + outfit.FileName + ": " + ex.Message);
+ return false;
+ }
+ }
+
+ static byte[] EncodePng(Texture source)
+ {
+ var readable = source as Texture2D;
+ bool temporary = false;
+ RenderTexture rt = null;
+ RenderTexture previous = RenderTexture.active;
+ try
+ {
+ if (readable == null || !readable.isReadable)
+ {
+ rt = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
+ Graphics.Blit(source, rt);
+ RenderTexture.active = rt;
+ readable = new Texture2D(source.width, source.height, TextureFormat.RGBA32, false);
+ readable.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0);
+ readable.Apply();
+ temporary = true;
+ }
+
+ var encoded = ImageConversion.EncodeToPNG(readable);
+ if (encoded == null)
+ return null;
+ var bytes = new byte[encoded.Length];
+ for (int i = 0; i < encoded.Length; i++)
+ bytes[i] = encoded[i];
+ return bytes;
+ }
+ finally
+ {
+ RenderTexture.active = previous;
+ if (rt != null)
+ RenderTexture.ReleaseTemporary(rt);
+ if (temporary && readable != null)
+ UnityEngine.Object.Destroy(readable);
+ }
+ }
+
+ static string SkinsDirectory()
+ {
+ string dllDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ if (!string.IsNullOrEmpty(dllDir))
+ return Path.Combine(dllDir, "CustomSkins");
+ return Path.Combine(Paths.PluginPath, "CustomSkins");
+ }
+ }
+}
diff --git a/CustomPlayerSkins/CustomPlayerSkinsPlugin.csproj b/CustomPlayerSkins/CustomPlayerSkinsPlugin.csproj
new file mode 100644
index 0000000..3867a27
--- /dev/null
+++ b/CustomPlayerSkins/CustomPlayerSkinsPlugin.csproj
@@ -0,0 +1,60 @@
+
+
+ net6.0
+ latest
+ disable
+ disable
+ true
+ Skopje.CustomPlayerSkins
+ Skopje.CustomPlayerSkins
+ true
+ D:\SteamLibrary\steamapps\common\Skopje83
+
+
+
+
+ $(GameDir)\BepInEx\core\0Harmony.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Core.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\SkopjeAssembly.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\FishNet.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.ImageConversionModule.dll
+ false
+
+
+
+
+
+
+
diff --git a/CustomPlayerSkins/CustomSkins/engineer.png b/CustomPlayerSkins/CustomSkins/engineer.png
new file mode 100644
index 0000000..6666a90
Binary files /dev/null and b/CustomPlayerSkins/CustomSkins/engineer.png differ
diff --git a/CustomPlayerSkins/CustomSkins/fighter.png b/CustomPlayerSkins/CustomSkins/fighter.png
new file mode 100644
index 0000000..61dff42
Binary files /dev/null and b/CustomPlayerSkins/CustomSkins/fighter.png differ
diff --git a/CustomPlayerSkins/CustomSkins/medic.png b/CustomPlayerSkins/CustomSkins/medic.png
new file mode 100644
index 0000000..f2de69c
Binary files /dev/null and b/CustomPlayerSkins/CustomSkins/medic.png differ
diff --git a/CustomPlayerSkins/CustomSkins/ranger.png b/CustomPlayerSkins/CustomSkins/ranger.png
new file mode 100644
index 0000000..410e766
Binary files /dev/null and b/CustomPlayerSkins/CustomSkins/ranger.png differ
diff --git a/CustomPlayerSkins/CustomSkins/singleplayer.png b/CustomPlayerSkins/CustomSkins/singleplayer.png
new file mode 100644
index 0000000..2ce4b0c
Binary files /dev/null and b/CustomPlayerSkins/CustomSkins/singleplayer.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/engineer.png b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/engineer.png
new file mode 100644
index 0000000..6666a90
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/engineer.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/fighter.png b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/fighter.png
new file mode 100644
index 0000000..61dff42
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/fighter.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/medic.png b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/medic.png
new file mode 100644
index 0000000..f2de69c
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/medic.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/ranger.png b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/ranger.png
new file mode 100644
index 0000000..410e766
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/ranger.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/singleplayer.png b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/singleplayer.png
new file mode 100644
index 0000000..2ce4b0c
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/CustomSkins/singleplayer.png differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.deps.json b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.deps.json
new file mode 100644
index 0000000..0f540b7
--- /dev/null
+++ b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.deps.json
@@ -0,0 +1,23 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v6.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v6.0": {
+ "Skopje.CustomPlayerSkins/1.0.0": {
+ "runtime": {
+ "Skopje.CustomPlayerSkins.dll": {}
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Skopje.CustomPlayerSkins/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.dll b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.dll
new file mode 100644
index 0000000..cc0bb23
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.dll differ
diff --git a/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.pdb b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.pdb
new file mode 100644
index 0000000..09c7ee2
Binary files /dev/null and b/CustomPlayerSkins/bin/Release/net6.0/Skopje.CustomPlayerSkins.pdb differ
diff --git a/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.dgspec.json b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.dgspec.json
new file mode 100644
index 0000000..918967d
--- /dev/null
+++ b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.dgspec.json
@@ -0,0 +1,85 @@
+{
+ "format": 1,
+ "restore": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {}
+ },
+ "projects": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
+ "projectName": "Skopje.CustomPlayerSkins",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.props b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.props
new file mode 100644
index 0000000..ba0d2cb
--- /dev/null
+++ b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.props
@@ -0,0 +1,15 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ PackageReference
+ 7.0.0
+
+
+
+
+
\ No newline at end of file
diff --git a/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.targets b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.targets
new file mode 100644
index 0000000..3dc06ef
--- /dev/null
+++ b/CustomPlayerSkins/obj/CustomPlayerSkinsPlugin.csproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/CustomPlayerSkins/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs b/CustomPlayerSkins/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
new file mode 100644
index 0000000..ed92695
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+//
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfo.cs b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfo.cs
new file mode 100644
index 0000000..00d17b7
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfo.cs
@@ -0,0 +1,22 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomPlayerSkins")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
+[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomPlayerSkins")]
+[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomPlayerSkins")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..c224ac7
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+2039bc0988efa0867d36e92385b47db55a3abd9b1b79233f9907b9909d2a0097
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..4c04389
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,18 @@
+is_global = true
+build_property.TargetFramework = net6.0
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v6.0
+build_property.TargetPlatformMinVersion =
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property.EntryPointFilePath =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = Skopje.CustomPlayerSkins
+build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.EffectiveAnalysisLevelStyle = 6.0
+build_property.EnableCodeStyleSeverity =
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.assets.cache b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.assets.cache
new file mode 100644
index 0000000..87adbd1
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.assets.cache differ
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache
new file mode 100644
index 0000000..1f4a1a0
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache differ
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache
new file mode 100644
index 0000000..cc36eae
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+6c1139a480f7b2b40bb2ca9a6182e2472a2688fad5f37ff82d5b23ba614cabb0
diff --git a/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.FileListAbsolute.txt b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..1b57119
--- /dev/null
+++ b/CustomPlayerSkins/obj/Release/net6.0/CustomPlayerSkinsPlugin.csproj.FileListAbsolute.txt
@@ -0,0 +1,17 @@
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.AssemblyInfo.cs
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\Skopje.CustomPlayerSkins.dll
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\refint\Skopje.CustomPlayerSkins.dll
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\Skopje.CustomPlayerSkins.pdb
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.deps.json
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.dll
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.pdb
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\ref\Skopje.CustomPlayerSkins.dll
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\engineer.png
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\fighter.png
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\medic.png
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\ranger.png
+c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\singleplayer.png
diff --git a/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.dll b/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.dll
new file mode 100644
index 0000000..cc0bb23
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.dll differ
diff --git a/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.pdb b/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.pdb
new file mode 100644
index 0000000..09c7ee2
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/Skopje.CustomPlayerSkins.pdb differ
diff --git a/CustomPlayerSkins/obj/Release/net6.0/ref/Skopje.CustomPlayerSkins.dll b/CustomPlayerSkins/obj/Release/net6.0/ref/Skopje.CustomPlayerSkins.dll
new file mode 100644
index 0000000..8d7e450
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/ref/Skopje.CustomPlayerSkins.dll differ
diff --git a/CustomPlayerSkins/obj/Release/net6.0/refint/Skopje.CustomPlayerSkins.dll b/CustomPlayerSkins/obj/Release/net6.0/refint/Skopje.CustomPlayerSkins.dll
new file mode 100644
index 0000000..8d7e450
Binary files /dev/null and b/CustomPlayerSkins/obj/Release/net6.0/refint/Skopje.CustomPlayerSkins.dll differ
diff --git a/CustomPlayerSkins/obj/project.assets.json b/CustomPlayerSkins/obj/project.assets.json
new file mode 100644
index 0000000..a067448
--- /dev/null
+++ b/CustomPlayerSkins/obj/project.assets.json
@@ -0,0 +1,90 @@
+{
+ "version": 4,
+ "targets": {
+ "net6.0": {}
+ },
+ "libraries": {},
+ "projectFileDependencyGroups": {
+ "net6.0": []
+ },
+ "packageFolders": {
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
+ },
+ "project": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
+ "projectName": "Skopje.CustomPlayerSkins",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomPlayerSkins/obj/project.nuget.cache b/CustomPlayerSkins/obj/project.nuget.cache
new file mode 100644
index 0000000..b8e0839
--- /dev/null
+++ b/CustomPlayerSkins/obj/project.nuget.cache
@@ -0,0 +1,12 @@
+{
+ "version": 2,
+ "dgSpecHash": "7IEzcjI0wFM=",
+ "success": true,
+ "projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
+ "expectedPackageFiles": [
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
+ ],
+ "logs": []
+}
\ No newline at end of file
diff --git a/DamageNumbers/DamageNumbersPlugin.cs b/DamageNumbers/DamageNumbersPlugin.cs
index 369fdf8..cff397f 100644
--- a/DamageNumbers/DamageNumbersPlugin.cs
+++ b/DamageNumbers/DamageNumbersPlugin.cs
@@ -25,6 +25,8 @@ namespace Skopje.DamageNumbers
internal static ConfigEntry OnlyYourDamage;
internal static ConfigEntry Lifetime;
internal static ConfigEntry Size;
+ internal static ConfigEntry ValueMultiplier;
+ internal static ConfigEntry CritTextAbove;
static Harmony _harmony;
static SkopjePlayer _localPlayer;
@@ -51,6 +53,8 @@ namespace Skopje.DamageNumbers
OnlyYourDamage = Config.Bind("Display", "OnlyYourDamage", true, "Only show hits dealt by your pawn. Hits with no owner still show.");
Lifetime = Config.Bind("Display", "Lifetime", 0.8f, "Seconds each number stays on screen.");
Size = Config.Bind("Display", "Size", 1f, "Size multiplier.");
+ ValueMultiplier = Config.Bind("Display", "ValueMultiplier", 1f, "Extra multiplier on the displayed number. The built-in scale stays in place.");
+ CritTextAbove = Config.Bind("Display", "CritTextAbove", 100, "Show the CRIT label when the displayed number is greater than this.");
ClassInjector.RegisterTypeInIl2Cpp();
@@ -181,8 +185,9 @@ namespace Skopje.DamageNumbers
part = enemy.lastDamagedPart.Value;
bool crit = IsCrit(part);
- int shown = ShownDamage(raw, crit);
- DamageTier.Apply(shown, out Color color, out float tierScale);
+ float displayScale = DisplayScale(crit);
+ int shown = ShownDamage(raw, displayScale);
+ DamageTier.Apply(shown, displayScale, out Color color, out float tierScale);
float punch = PunchFor(rawAmount, crit, type) * tierScale;
pos += new Vector3(
UnityEngine.Random.Range(-0.22f, 0.22f),
@@ -280,11 +285,26 @@ namespace Skopje.DamageNumbers
// Real hits in this game are often 1, 2, or 3. Scale them up so the popup
// still tracks the hit, but reads like a chunky score. Crits are doubled.
- static int ShownDamage(float raw, bool crit)
+ static float DisplayScale(bool crit)
+ {
+ float extra = 1f;
+ if (ValueMultiplier != null && !float.IsNaN(ValueMultiplier.Value) && !float.IsInfinity(ValueMultiplier.Value))
+ extra = ValueMultiplier.Value;
+ return (crit ? 200f : 100f) * extra;
+ }
+
+ static int ShownDamage(float raw, float displayScale)
{
- float scale = crit ? 200f : 100f;
float roll = UnityEngine.Random.Range(0.94f, 1.06f);
- return Mathf.Max(1, Mathf.RoundToInt(raw * scale * roll));
+ return Mathf.Max(1, Mathf.RoundToInt(raw * displayScale * roll));
+ }
+
+ internal static bool ShowCritText(int shown, bool crit)
+ {
+ if (!crit)
+ return false;
+ int above = CritTextAbove != null ? CritTextAbove.Value : 100;
+ return shown > above;
}
static bool Claim(EnemyControlAStar enemy, int amount, Vector3 pos)
@@ -416,7 +436,7 @@ namespace Skopje.DamageNumbers
Tilt = UnityEngine.Random.Range(crit ? -18f : -14f, crit ? 18f : 14f),
Amount = amount,
Color = color,
- Text = crit ? "CRIT\n" + amount : amount.ToString()
+ Text = DamageNumbersPlugin.ShowCritText(amount, crit) ? "CRIT\n" + amount : amount.ToString()
};
Measure(display._numbers[slot].Text, out display._numbers[slot].Lines, out display._numbers[slot].Longest);
}
@@ -607,9 +627,13 @@ namespace Skopje.DamageNumbers
static readonly Color LightOrange = new Color(1f, 0.74f, 0.38f, 1f);
static readonly Color Orange = new Color(1f, 0.48f, 0.08f, 1f);
- public static void Apply(int amount, out Color color, out float sizeScale)
+ public static void Apply(int amount, float displayScale, out Color color, out float sizeScale)
{
- if (amount >= 50)
+ int yellow = Band(25, displayScale);
+ int lightOrange = Band(40, displayScale);
+ int orange = Band(50, displayScale);
+
+ if (amount >= orange)
{
color = Orange;
sizeScale = 1.15f;
@@ -617,12 +641,17 @@ namespace Skopje.DamageNumbers
}
sizeScale = 1f;
- if (amount >= 40)
+ if (amount >= lightOrange)
color = LightOrange;
- else if (amount >= 25)
+ else if (amount >= yellow)
color = LightYellow;
else
color = White;
}
+
+ static int Band(int baseValue, float displayScale)
+ {
+ return Mathf.Max(1, Mathf.RoundToInt(baseValue * displayScale));
+ }
}
}
diff --git a/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.dll b/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.dll
index 9574fba..0613b1f 100644
Binary files a/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.dll and b/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.dll differ
diff --git a/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.pdb b/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.pdb
index 066284a..4fc0e7f 100644
Binary files a/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.pdb and b/DamageNumbers/bin/Release/net6.0/Skopje.DamageNumbers.pdb differ
diff --git a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfo.cs b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfo.cs
index 66d0f42..32eb50d 100644
--- a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfo.cs
+++ b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfo.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.DamageNumbers")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.DamageNumbers")]
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.DamageNumbers")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfoInputs.cache b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfoInputs.cache
index 9954ebc..dbf7e50 100644
--- a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfoInputs.cache
+++ b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.AssemblyInfoInputs.cache
@@ -1 +1 @@
-a6bbf0f8cbba65e2618b81c6321ac40b204cddd43585a4acfc7f7e5c36244fcc
+dde6890729b61f58e466ea7b10c19cab8ad8b0c2df507a60a79c69f9ac5267e5
diff --git a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.csproj.AssemblyReference.cache b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.csproj.AssemblyReference.cache
index 1a4a5d3..e4d6795 100644
Binary files a/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.csproj.AssemblyReference.cache and b/DamageNumbers/obj/Release/net6.0/DamageNumbersPlugin.csproj.AssemblyReference.cache differ
diff --git a/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.dll b/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.dll
index 9574fba..0613b1f 100644
Binary files a/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.dll and b/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.dll differ
diff --git a/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.pdb b/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.pdb
index 066284a..4fc0e7f 100644
Binary files a/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.pdb and b/DamageNumbers/obj/Release/net6.0/Skopje.DamageNumbers.pdb differ
diff --git a/DamageNumbers/obj/Release/net6.0/ref/Skopje.DamageNumbers.dll b/DamageNumbers/obj/Release/net6.0/ref/Skopje.DamageNumbers.dll
index 14e2ff6..850c2a1 100644
Binary files a/DamageNumbers/obj/Release/net6.0/ref/Skopje.DamageNumbers.dll and b/DamageNumbers/obj/Release/net6.0/ref/Skopje.DamageNumbers.dll differ
diff --git a/DamageNumbers/obj/Release/net6.0/refint/Skopje.DamageNumbers.dll b/DamageNumbers/obj/Release/net6.0/refint/Skopje.DamageNumbers.dll
index 14e2ff6..850c2a1 100644
Binary files a/DamageNumbers/obj/Release/net6.0/refint/Skopje.DamageNumbers.dll and b/DamageNumbers/obj/Release/net6.0/refint/Skopje.DamageNumbers.dll differ
diff --git a/DomRadio/DomRadioPlugin.cs b/DomRadio/DomRadioPlugin.cs
new file mode 100644
index 0000000..c07573c
--- /dev/null
+++ b/DomRadio/DomRadioPlugin.cs
@@ -0,0 +1,735 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using BepInEx;
+using BepInEx.Configuration;
+using BepInEx.Logging;
+using BepInEx.Unity.IL2CPP;
+using FMODUnity;
+using Il2CppInterop.Runtime;
+using Il2CppInterop.Runtime.Injection;
+using Il2CppInterop.Runtime.InteropTypes.Arrays;
+using Skopje.Modding;
+using UnityEngine;
+
+namespace Skopje.DomRadio
+{
+ [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
+ public class DomRadioPlugin : BasePlugin
+ {
+ public const string PluginGuid = "com.skopje.domradio";
+ public const string PluginName = "Skopje Dom Radio";
+ public const string PluginVersion = "1.0.0";
+
+ internal static ManualLogSource LogSource;
+
+ internal static ConfigEntry Toggle;
+ internal static ConfigEntry NextTrack;
+ internal static ConfigEntry PreviousTrack;
+ internal static ConfigEntry Shuffle;
+ internal static ConfigEntry MuteGameMusic;
+ internal static ConfigEntry MusicBusFilter;
+ internal static ConfigEntry Folder;
+ internal static ConfigEntry ShowNowPlaying;
+
+ internal static SkopjePlayer LocalPlayer;
+
+ static GameObject _host;
+ static Il2CppSystem.Action _onPlayerStarted;
+ static Il2CppSystem.Action _onPlayerStopped;
+
+ public override void Load()
+ {
+ LogSource = Log;
+ Toggle = Config.Bind("Radio", "Toggle", KeyCode.F8, "Turn the radio on or off.");
+ NextTrack = Config.Bind("Radio", "NextTrack", KeyCode.F10, "Skip to the next file.");
+ PreviousTrack = Config.Bind("Radio", "PreviousTrack", KeyCode.F9, "Go back to the previous file.");
+ Shuffle = Config.Bind("Radio", "Shuffle", false, "Play the folder in a random order. Off plays files by name.");
+ MuteGameMusic = Config.Bind("Radio", "MuteGameMusic", true, "While the radio is on, mute the game's music buses. That includes the Dom bus track. Sound effects stay on.");
+ MusicBusFilter = Config.Bind("Radio", "MusicBusFilter", "Music", "Mute FMOD buses whose path contains this text. Music covers the Dom bus home track.");
+ Folder = Config.Bind("Radio", "Folder", "", "Folder of .mp3 and .wav files. Leave empty to use BepInEx/plugins/DomRadio/Music.");
+ ShowNowPlaying = Config.Bind("Radio", "ShowNowPlaying", true, "Show the current file name while the radio is on.");
+
+ ClassInjector.RegisterTypeInIl2Cpp();
+
+ try
+ {
+ Directory.CreateDirectory(MusicDirectory);
+ }
+ catch (Exception ex)
+ {
+ Log.LogWarning("Could not create the music folder: " + ex.Message);
+ }
+
+ _onPlayerStarted = DelegateSupport.ConvertDelegate>(new Action(OnPlayerStarted));
+ _onPlayerStopped = DelegateSupport.ConvertDelegate>(new Action(OnPlayerStopped));
+ ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
+ ModAPI.add_OnPlayerStoppedClient(_onPlayerStopped);
+
+ _host = new GameObject("SkopjeDomRadio");
+ UnityEngine.Object.DontDestroyOnLoad(_host);
+ _host.hideFlags = HideFlags.HideAndDontSave;
+ _host.AddComponent();
+
+ Log.LogInfo($"{PluginName} {PluginVersion} loaded. Put .mp3 and .wav files in {MusicDirectory}. Toggle {Toggle.Value}, next {NextTrack.Value}, previous {PreviousTrack.Value}.");
+ }
+
+ public override bool Unload()
+ {
+ if (DomRadioPlayer.Instance != null)
+ DomRadioPlayer.Instance.TurnOff(false);
+ GameMusic.Release();
+ LocalPlayer = null;
+ if (_onPlayerStarted != null)
+ ModAPI.remove_OnPlayerStartedClient(_onPlayerStarted);
+ if (_onPlayerStopped != null)
+ ModAPI.remove_OnPlayerStoppedClient(_onPlayerStopped);
+ if (_host != null)
+ {
+ UnityEngine.Object.Destroy(_host);
+ _host = null;
+ }
+
+ return true;
+ }
+
+ internal static string MusicDirectory
+ {
+ get
+ {
+ string custom = Folder == null ? null : Folder.Value;
+ if (!string.IsNullOrWhiteSpace(custom))
+ return custom;
+ return Path.Combine(Paths.PluginPath, "DomRadio", "Music");
+ }
+ }
+
+ static void OnPlayerStarted(SkopjePlayer player)
+ {
+ if (player != null && player.IsOwner)
+ LocalPlayer = player;
+ }
+
+ static void OnPlayerStopped(SkopjePlayer player)
+ {
+ if (LocalPlayer == player)
+ LocalPlayer = null;
+ }
+
+ internal static SkopjePlayer FindLocalPlayer()
+ {
+ if (LocalPlayer != null)
+ return LocalPlayer;
+
+ var players = UnityEngine.Object.FindObjectsOfType();
+ if (players == null)
+ return null;
+
+ for (int i = 0; i < players.Length; i++)
+ {
+ SkopjePlayer player = players[i];
+ if (player != null && player.IsOwner)
+ {
+ LocalPlayer = player;
+ return player;
+ }
+ }
+
+ return null;
+ }
+
+ internal static float MusicLevel()
+ {
+ try
+ {
+ OptionsManager manager = OptionsManager.Instance;
+ if (manager == null || manager.OptionsData == null)
+ return 1f;
+
+ return Unit(manager.OptionsData.MasterVolume) * Unit(manager.OptionsData.MusicVolume);
+ }
+ catch (Exception)
+ {
+ return 1f;
+ }
+ }
+
+ static float Unit(float value)
+ {
+ if (value > 1f)
+ value *= 0.01f;
+ return Mathf.Clamp01(value);
+ }
+ }
+
+ public class DomRadioPlayer : MonoBehaviour
+ {
+ const float MissedPlaySeconds = 2f;
+
+ public static DomRadioPlayer Instance { get; private set; }
+
+ readonly List _tracks = new List();
+ FMOD.Sound _sound;
+ FMOD.Channel _channel;
+ bool _hasSound;
+ int _index;
+ int _failures;
+ bool _on;
+ bool _suspended;
+ bool _skipPending;
+ bool _heardPlayback;
+ bool _loggedEmpty;
+ float _playDeadline;
+ string _nowPlaying = string.Empty;
+ string _notice = string.Empty;
+ float _noticeUntil;
+ GUIStyle _label;
+ Texture2D _backdrop;
+ int _styleHeight;
+
+ public DomRadioPlayer(IntPtr pointer) : base(pointer)
+ {
+ }
+
+ void Awake()
+ {
+ Instance = this;
+ }
+
+ void OnDestroy()
+ {
+ TurnOff(false);
+ if (_backdrop != null)
+ Destroy(_backdrop);
+ if (Instance == this)
+ Instance = null;
+ }
+
+ void Update()
+ {
+ bool inside = InDom();
+ if (Pressed(DomRadioPlugin.Toggle.Value))
+ {
+ if (_on)
+ TurnOff(true);
+ else if (inside)
+ TurnOn();
+ else
+ Notify("Radio only plays in the Dom", 2f);
+ }
+
+ if (!_on)
+ return;
+
+ if (!inside)
+ {
+ if (!_suspended)
+ Suspend();
+ return;
+ }
+
+ if (_suspended)
+ Resume();
+
+ if (Pressed(DomRadioPlugin.NextTrack.Value))
+ Advance(1);
+ else if (Pressed(DomRadioPlugin.PreviousTrack.Value))
+ Advance(-1);
+
+ PumpLoad();
+ // Mute the Dom track only while one of our files is actually audible.
+ if (DomRadioPlugin.MuteGameMusic.Value && IsAudible())
+ GameMusic.Hold();
+ else if (!DomRadioPlugin.MuteGameMusic.Value)
+ GameMusic.Release();
+ }
+
+ void OnGUI()
+ {
+ if (Event.current == null || Event.current.type != EventType.Repaint || GUI.skin == null)
+ return;
+
+ EnsureStyle();
+ const float width = 640f;
+ const float height = 36f;
+ float textWidth = width - _label.padding.left - _label.padding.right;
+
+ string text = null;
+ if (_on && !_suspended && DomRadioPlugin.ShowNowPlaying.Value && !string.IsNullOrEmpty(_nowPlaying))
+ text = Fit(_nowPlaying, textWidth);
+ else if (Time.unscaledTime <= _noticeUntil && !string.IsNullOrEmpty(_notice))
+ text = Fit(_notice, textWidth);
+ if (text == null)
+ return;
+
+ var rect = new Rect((Screen.width - width) * 0.5f, Screen.height - 96f, width, height);
+ Color previous = GUI.color;
+ GUI.color = new Color(0f, 0f, 0f, 0.62f);
+ GUI.DrawTexture(rect, _backdrop);
+ GUI.color = previous;
+ GUI.Label(rect, text, _label);
+ }
+
+ public void TurnOn()
+ {
+ ReloadTracks();
+ if (_tracks.Count == 0)
+ {
+ if (!_loggedEmpty)
+ {
+ _loggedEmpty = true;
+ DomRadioPlugin.LogSource.LogInfo("Radio disabled. No .mp3 or .wav files in " + DomRadioPlugin.MusicDirectory + ". Dom music will keep playing.");
+ }
+
+ return;
+ }
+
+ _loggedEmpty = false;
+ _on = true;
+ _failures = 0;
+ _index = 0;
+ _nowPlaying = TrackLabel(_tracks[0]);
+ try
+ {
+ PlayFile(_tracks[0]);
+ }
+ catch (Exception ex)
+ {
+ DomRadioPlugin.LogSource.LogWarning("Could not start the radio: " + ex.Message);
+ TurnOff(false);
+ DomRadioPlugin.LogSource.LogInfo("Radio disabled. Dom music restored.");
+ return;
+ }
+
+ DomRadioPlugin.LogSource.LogInfo("Radio on. " + _tracks.Count + " file(s) in " + DomRadioPlugin.MusicDirectory);
+ }
+
+ public void TurnOff(bool announce)
+ {
+ _on = false;
+ _suspended = false;
+ _skipPending = false;
+ _heardPlayback = false;
+ _nowPlaying = string.Empty;
+ StopSound();
+ GameMusic.Release();
+ if (announce)
+ {
+ Notify("Radio off", 2f);
+ DomRadioPlugin.LogSource.LogInfo("Radio off. Dom music restored.");
+ }
+ }
+
+ void Advance(int direction)
+ {
+ if (_tracks.Count == 0)
+ {
+ TurnOff(true);
+ return;
+ }
+
+ int next = _index + direction;
+ if (next >= _tracks.Count)
+ next = 0;
+ else if (next < 0)
+ next = _tracks.Count - 1;
+
+ _index = next;
+ _nowPlaying = TrackLabel(_tracks[_index]);
+ PlayFile(_tracks[_index]);
+ }
+
+ void ReloadTracks()
+ {
+ _tracks.Clear();
+ string folder = DomRadioPlugin.MusicDirectory;
+ if (!Directory.Exists(folder))
+ {
+ try
+ {
+ Directory.CreateDirectory(folder);
+ }
+ catch (Exception ex)
+ {
+ DomRadioPlugin.LogSource.LogWarning("Could not create the music folder: " + ex.Message);
+ return;
+ }
+ }
+
+ string[] files;
+ try
+ {
+ files = Directory.GetFiles(folder);
+ }
+ catch (Exception ex)
+ {
+ DomRadioPlugin.LogSource.LogWarning("Could not read the music folder: " + ex.Message);
+ return;
+ }
+
+ for (int i = 0; i < files.Length; i++)
+ {
+ string extension = Path.GetExtension(files[i]);
+ if (extension.Equals(".mp3", StringComparison.OrdinalIgnoreCase) || extension.Equals(".wav", StringComparison.OrdinalIgnoreCase))
+ _tracks.Add(files[i]);
+ }
+
+ _tracks.Sort(StringComparer.OrdinalIgnoreCase);
+ if (DomRadioPlugin.Shuffle.Value)
+ ShuffleTracks();
+ }
+
+ void ShuffleTracks()
+ {
+ var random = new System.Random();
+ for (int i = _tracks.Count - 1; i > 0; i--)
+ {
+ int swap = random.Next(i + 1);
+ string file = _tracks[i];
+ _tracks[i] = _tracks[swap];
+ _tracks[swap] = file;
+ }
+ }
+
+ void PlayFile(string path)
+ {
+ StopSound();
+ _heardPlayback = false;
+ _nowPlaying = TrackLabel(path);
+ if (!RuntimeManager.IsInitialized)
+ {
+ DomRadioPlugin.LogSource.LogWarning("Audio system is not ready yet.");
+ _skipPending = true;
+ return;
+ }
+
+ FMOD.System core = RuntimeManager.CoreSystem;
+ if (!core.hasHandle())
+ {
+ DomRadioPlugin.LogSource.LogWarning("Audio system is not ready yet.");
+ _skipPending = true;
+ return;
+ }
+
+ FMOD.MODE mode = FMOD.MODE.CREATESTREAM | FMOD.MODE._2D | FMOD.MODE.LOOP_OFF;
+ FMOD.Sound sound;
+ FMOD.RESULT result = core.createSound(path, mode, out sound);
+ if (result != FMOD.RESULT.OK || !sound.hasHandle())
+ {
+ DomRadioPlugin.LogSource.LogWarning("Could not open " + Path.GetFileName(path) + " (" + result + ").");
+ _skipPending = true;
+ return;
+ }
+
+ FMOD.ChannelGroup master;
+ core.getMasterChannelGroup(out master);
+ FMOD.Channel channel;
+ result = core.playSound(sound, master, false, out channel);
+ if (result != FMOD.RESULT.OK || !channel.hasHandle())
+ {
+ sound.release();
+ DomRadioPlugin.LogSource.LogWarning("Could not play " + Path.GetFileName(path) + " (" + result + ").");
+ _skipPending = true;
+ return;
+ }
+
+ channel.setVolume(DomRadioPlugin.MusicLevel());
+ _sound = sound;
+ _channel = channel;
+ _hasSound = true;
+ _playDeadline = Time.unscaledTime + MissedPlaySeconds;
+ }
+
+ void PumpLoad()
+ {
+ if (_skipPending)
+ {
+ _skipPending = false;
+ OnTrackFailed();
+ return;
+ }
+
+ if (!_hasSound)
+ return;
+
+ if (IsAudible())
+ {
+ _heardPlayback = true;
+ _failures = 0;
+ _channel.setVolume(DomRadioPlugin.MusicLevel());
+ return;
+ }
+
+ if (_heardPlayback || Time.unscaledTime >= _playDeadline)
+ Advance(1);
+ }
+
+ bool IsAudible()
+ {
+ if (!_hasSound || !_channel.hasHandle())
+ return false;
+ bool playing = false;
+ return _channel.isPlaying(out playing) == FMOD.RESULT.OK && playing;
+ }
+
+ void OnTrackFailed()
+ {
+ _failures++;
+ if (_tracks.Count == 0 || _failures >= _tracks.Count)
+ {
+ TurnOff(false);
+ DomRadioPlugin.LogSource.LogInfo("Radio disabled. No playable files in " + DomRadioPlugin.MusicDirectory + ". Dom music restored.");
+ return;
+ }
+
+ Advance(1);
+ }
+
+ void StopSound()
+ {
+ if (_hasSound && _channel.hasHandle())
+ _channel.stop();
+ if (_hasSound && _sound.hasHandle())
+ _sound.release();
+ _hasSound = false;
+ }
+
+ void Notify(string text, float seconds)
+ {
+ _notice = text ?? string.Empty;
+ _noticeUntil = Time.unscaledTime + seconds;
+ }
+
+ static bool _loggedInput;
+
+ static bool Pressed(KeyCode key)
+ {
+ if (key == KeyCode.None)
+ return false;
+ try
+ {
+ return Input.GetKeyDown(key);
+ }
+ catch (Exception ex)
+ {
+ if (!_loggedInput)
+ {
+ _loggedInput = true;
+ DomRadioPlugin.LogSource.LogWarning("Keyboard input is unavailable: " + ex.Message);
+ }
+
+ return false;
+ }
+ }
+
+ bool InDom()
+ {
+ SkopjePlayer player = DomRadioPlugin.FindLocalPlayer();
+ if (player == null)
+ return false;
+
+ try
+ {
+ return player.InsideVehicle != null;
+ }
+ catch (Exception)
+ {
+ DomRadioPlugin.LocalPlayer = null;
+ return false;
+ }
+ }
+
+ void Suspend()
+ {
+ _suspended = true;
+ if (_hasSound && _channel.hasHandle())
+ _channel.setPaused(true);
+ GameMusic.Release();
+ }
+
+ void Resume()
+ {
+ _suspended = false;
+ if (_hasSound && _channel.hasHandle())
+ {
+ _channel.setPaused(false);
+ _channel.setVolume(DomRadioPlugin.MusicLevel());
+ return;
+ }
+
+ if (_index >= 0 && _index < _tracks.Count)
+ PlayFile(_tracks[_index]);
+ }
+
+ string Fit(string text, float width)
+ {
+ if (string.IsNullOrEmpty(text) || _label.CalcSize(new GUIContent(text)).x <= width)
+ return text;
+
+ const string ellipsis = "...";
+ int low = 0;
+ int high = text.Length;
+ while (low < high)
+ {
+ int mid = (low + high + 1) / 2;
+ string candidate = text.Substring(0, mid).TrimEnd() + ellipsis;
+ if (_label.CalcSize(new GUIContent(candidate)).x <= width)
+ low = mid;
+ else
+ high = mid - 1;
+ }
+
+ if (low <= 0)
+ return ellipsis;
+ return text.Substring(0, low).TrimEnd() + ellipsis;
+ }
+
+ static string TrackLabel(string path)
+ {
+ return "Radio — " + Path.GetFileNameWithoutExtension(path);
+ }
+
+ void EnsureStyle()
+ {
+ if (_backdrop == null)
+ {
+ _backdrop = new Texture2D(1, 1);
+ _backdrop.SetPixel(0, 0, Color.white);
+ _backdrop.Apply();
+ }
+
+ if (_label != null && _styleHeight == Screen.height)
+ return;
+
+ _styleHeight = Screen.height;
+ _label = new GUIStyle(GUI.skin.label);
+ _label.alignment = TextAnchor.MiddleCenter;
+ _label.wordWrap = false;
+ _label.clipping = TextClipping.Clip;
+ _label.padding = new RectOffset(12, 12, 0, 0);
+ _label.fontSize = Mathf.Clamp(Mathf.RoundToInt(Screen.height * 0.018f), 14, 28);
+ _label.normal.textColor = Color.white;
+ }
+ }
+
+ // The Dom bus soundtrack is event:/Music/Bus/Home, on the game's Music buses.
+ // Muting those buses leaves the events running, so the track returns when the radio turns off.
+ static class GameMusic
+ {
+ sealed class HeldBus
+ {
+ public FMOD.Studio.Bus Bus;
+ public bool WasMuted;
+ }
+
+ static readonly List _held = new List();
+ static bool _loggedMiss;
+ static float _nextDiscover;
+
+ public static void Hold()
+ {
+ if (!DomRadioPlugin.MuteGameMusic.Value)
+ return;
+ if (!RuntimeManager.IsInitialized)
+ return;
+
+ if (_held.Count == 0 && Time.unscaledTime >= _nextDiscover)
+ {
+ _nextDiscover = Time.unscaledTime + 1f;
+ Discover();
+ }
+
+ int valid = 0;
+ for (int i = 0; i < _held.Count; i++)
+ {
+ FMOD.Studio.Bus bus = _held[i].Bus;
+ if (!bus.isValid())
+ continue;
+ valid++;
+ bus.setMute(true);
+ }
+
+ // Banks reload with a new level. Drop dead handles so the next pass can find the music buses again.
+ if (_held.Count > 0 && valid == 0)
+ {
+ _held.Clear();
+ _nextDiscover = 0f;
+ }
+ }
+
+ public static void Release()
+ {
+ for (int i = 0; i < _held.Count; i++)
+ {
+ FMOD.Studio.Bus bus = _held[i].Bus;
+ if (bus.isValid())
+ bus.setMute(_held[i].WasMuted);
+ }
+
+ _held.Clear();
+ _loggedMiss = false;
+ _nextDiscover = 0f;
+ }
+
+ static void Discover()
+ {
+ try
+ {
+ var studio = RuntimeManager.StudioSystem;
+ if (!studio.isValid())
+ return;
+
+ Il2CppStructArray banks = null;
+ if (studio.getBankList(out banks) != FMOD.RESULT.OK || banks == null || banks.Length == 0)
+ return;
+
+ string filter = DomRadioPlugin.MusicBusFilter.Value ?? string.Empty;
+ var seen = new HashSet(StringComparer.OrdinalIgnoreCase);
+ var others = new List();
+ for (int i = 0; i < banks.Length; i++)
+ {
+ FMOD.Studio.Bank bank = banks[i];
+ if (!bank.isValid())
+ continue;
+
+ Il2CppStructArray buses = null;
+ if (bank.getBusList(out buses) != FMOD.RESULT.OK || buses == null)
+ continue;
+
+ for (int b = 0; b < buses.Length; b++)
+ {
+ FMOD.Studio.Bus bus = buses[b];
+ if (!bus.isValid())
+ continue;
+
+ string path = null;
+ if (bus.getPath(out path) != FMOD.RESULT.OK || string.IsNullOrEmpty(path))
+ continue;
+ if (!seen.Add(path))
+ continue;
+ if (filter.Length == 0 || path.IndexOf(filter, StringComparison.OrdinalIgnoreCase) < 0)
+ {
+ others.Add(path);
+ continue;
+ }
+
+ bool wasMuted = false;
+ bus.getMute(out wasMuted);
+ _held.Add(new HeldBus { Bus = bus, WasMuted = wasMuted });
+ DomRadioPlugin.LogSource.LogInfo("Muting " + path);
+ }
+ }
+
+ if (_held.Count == 0 && !_loggedMiss)
+ {
+ _loggedMiss = true;
+ DomRadioPlugin.LogSource.LogWarning("No music bus matched \"" + filter + "\". Dom music will keep playing. Buses: " + string.Join(", ", others));
+ }
+ }
+ catch (Exception ex)
+ {
+ DomRadioPlugin.LogSource.LogWarning("Could not find the music buses: " + ex.Message);
+ }
+ }
+ }
+}
diff --git a/DomRadio/DomRadioPlugin.csproj b/DomRadio/DomRadioPlugin.csproj
new file mode 100644
index 0000000..7ae9717
--- /dev/null
+++ b/DomRadio/DomRadioPlugin.csproj
@@ -0,0 +1,64 @@
+
+
+ net6.0
+ latest
+ disable
+ disable
+ true
+ Skopje.DomRadio
+ Skopje.DomRadio
+ true
+ D:\SteamLibrary\steamapps\common\Skopje83
+
+
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Core.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\SkopjeAssembly.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\FishNet.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\FMODUnity.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.IMGUIModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.TextRenderingModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.InputLegacyModule.dll
+ false
+
+
+
diff --git a/DomRadio/bin/Release/net6.0/Skopje.DomRadio.deps.json b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.deps.json
new file mode 100644
index 0000000..f3df0bc
--- /dev/null
+++ b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.deps.json
@@ -0,0 +1,23 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v6.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v6.0": {
+ "Skopje.DomRadio/1.0.0": {
+ "runtime": {
+ "Skopje.DomRadio.dll": {}
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Skopje.DomRadio/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/DomRadio/bin/Release/net6.0/Skopje.DomRadio.dll b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.dll
new file mode 100644
index 0000000..2a78d0f
Binary files /dev/null and b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.dll differ
diff --git a/DomRadio/bin/Release/net6.0/Skopje.DomRadio.pdb b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.pdb
new file mode 100644
index 0000000..2089194
Binary files /dev/null and b/DomRadio/bin/Release/net6.0/Skopje.DomRadio.pdb differ
diff --git a/DomRadio/obj/DomRadioPlugin.csproj.nuget.dgspec.json b/DomRadio/obj/DomRadioPlugin.csproj.nuget.dgspec.json
new file mode 100644
index 0000000..b9bb7ca
--- /dev/null
+++ b/DomRadio/obj/DomRadioPlugin.csproj.nuget.dgspec.json
@@ -0,0 +1,85 @@
+{
+ "format": 1,
+ "restore": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj": {}
+ },
+ "projects": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
+ "projectName": "Skopje.DomRadio",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.props b/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.props
new file mode 100644
index 0000000..ba0d2cb
--- /dev/null
+++ b/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.props
@@ -0,0 +1,15 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ PackageReference
+ 7.0.0
+
+
+
+
+
\ No newline at end of file
diff --git a/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.targets b/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.targets
new file mode 100644
index 0000000..3dc06ef
--- /dev/null
+++ b/DomRadio/obj/DomRadioPlugin.csproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/DomRadio/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs b/DomRadio/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
new file mode 100644
index 0000000..ed92695
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+//
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfo.cs b/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfo.cs
new file mode 100644
index 0000000..dace733
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfo.cs
@@ -0,0 +1,22 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.DomRadio")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
+[assembly: System.Reflection.AssemblyProductAttribute("Skopje.DomRadio")]
+[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.DomRadio")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfoInputs.cache b/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..c598668
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/DomRadioPlugin.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+fb5aa0eaad5177616647c1fd58891f07eda98dfbaa1c7f1588f2c8bfe53d1402
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.GeneratedMSBuildEditorConfig.editorconfig b/DomRadio/obj/Release/net6.0/DomRadioPlugin.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..8389d14
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/DomRadioPlugin.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,18 @@
+is_global = true
+build_property.TargetFramework = net6.0
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v6.0
+build_property.TargetPlatformMinVersion =
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property.EntryPointFilePath =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = Skopje.DomRadio
+build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\DomRadio\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.EffectiveAnalysisLevelStyle = 6.0
+build_property.EnableCodeStyleSeverity =
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.assets.cache b/DomRadio/obj/Release/net6.0/DomRadioPlugin.assets.cache
new file mode 100644
index 0000000..ddab172
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/DomRadioPlugin.assets.cache differ
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.AssemblyReference.cache b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.AssemblyReference.cache
new file mode 100644
index 0000000..aad68d4
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.AssemblyReference.cache differ
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.CoreCompileInputs.cache b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.CoreCompileInputs.cache
new file mode 100644
index 0000000..e7e2c06
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+081092add6543b8dcb5ec27e0ed95920f8750f6a3a0b82e5686acb95102220e9
diff --git a/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.FileListAbsolute.txt b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..b41f788
--- /dev/null
+++ b/DomRadio/obj/Release/net6.0/DomRadioPlugin.csproj.FileListAbsolute.txt
@@ -0,0 +1,12 @@
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.csproj.AssemblyReference.cache
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.GeneratedMSBuildEditorConfig.editorconfig
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.AssemblyInfoInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.AssemblyInfo.cs
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.csproj.CoreCompileInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.deps.json
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.dll
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.pdb
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\Skopje.DomRadio.dll
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\refint\Skopje.DomRadio.dll
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\Skopje.DomRadio.pdb
+c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\ref\Skopje.DomRadio.dll
diff --git a/DomRadio/obj/Release/net6.0/Skopje.DomRadio.dll b/DomRadio/obj/Release/net6.0/Skopje.DomRadio.dll
new file mode 100644
index 0000000..2a78d0f
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/Skopje.DomRadio.dll differ
diff --git a/DomRadio/obj/Release/net6.0/Skopje.DomRadio.pdb b/DomRadio/obj/Release/net6.0/Skopje.DomRadio.pdb
new file mode 100644
index 0000000..2089194
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/Skopje.DomRadio.pdb differ
diff --git a/DomRadio/obj/Release/net6.0/ref/Skopje.DomRadio.dll b/DomRadio/obj/Release/net6.0/ref/Skopje.DomRadio.dll
new file mode 100644
index 0000000..8f422b3
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/ref/Skopje.DomRadio.dll differ
diff --git a/DomRadio/obj/Release/net6.0/refint/Skopje.DomRadio.dll b/DomRadio/obj/Release/net6.0/refint/Skopje.DomRadio.dll
new file mode 100644
index 0000000..8f422b3
Binary files /dev/null and b/DomRadio/obj/Release/net6.0/refint/Skopje.DomRadio.dll differ
diff --git a/DomRadio/obj/project.assets.json b/DomRadio/obj/project.assets.json
new file mode 100644
index 0000000..2094ac5
--- /dev/null
+++ b/DomRadio/obj/project.assets.json
@@ -0,0 +1,90 @@
+{
+ "version": 4,
+ "targets": {
+ "net6.0": {}
+ },
+ "libraries": {},
+ "projectFileDependencyGroups": {
+ "net6.0": []
+ },
+ "packageFolders": {
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
+ },
+ "project": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
+ "projectName": "Skopje.DomRadio",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DomRadio/obj/project.nuget.cache b/DomRadio/obj/project.nuget.cache
new file mode 100644
index 0000000..32c0ab8
--- /dev/null
+++ b/DomRadio/obj/project.nuget.cache
@@ -0,0 +1,12 @@
+{
+ "version": 2,
+ "dgSpecHash": "3mbnNdPiqio=",
+ "success": true,
+ "projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
+ "expectedPackageFiles": [
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
+ ],
+ "logs": []
+}
\ No newline at end of file
diff --git a/Minimap/MinimapPlugin.cs b/Minimap/MinimapPlugin.cs
new file mode 100644
index 0000000..438cb0f
--- /dev/null
+++ b/Minimap/MinimapPlugin.cs
@@ -0,0 +1,1037 @@
+using System;
+using System.Collections.Generic;
+using BepInEx;
+using BepInEx.Configuration;
+using BepInEx.Logging;
+using BepInEx.Unity.IL2CPP;
+using Doozy.Engine.UI;
+using Il2CppInterop.Runtime;
+using Il2CppInterop.Runtime.Injection;
+using Skopje.Modding;
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Skopje.Minimap
+{
+ [BepInPlugin(PluginGuid, PluginName, PluginVersion)]
+ public class MinimapPlugin : BasePlugin
+ {
+ public const string PluginGuid = "com.skopje.minimap";
+ public const string PluginName = "Skopje Minimap";
+ public const string PluginVersion = "1.0.0";
+
+ internal static ManualLogSource LogSource;
+
+ internal static ConfigEntry Enabled;
+ internal static ConfigEntry Size;
+ internal static ConfigEntry ViewRadius;
+ internal static ConfigEntry Gap;
+ internal static ConfigEntry ShowMarkers;
+ internal static ConfigEntry MarkerSize;
+
+ internal static SkopjePlayer LocalPlayer;
+
+ // Il2Cpp delegates are collected if nothing managed keeps them alive.
+ static Il2CppSystem.Action _onClientStarted;
+ static Il2CppSystem.Action _onClientStopped;
+ static Il2CppSystem.Action _onPlayerStarted;
+ static Il2CppSystem.Action _onPlayerStopped;
+
+ public override void Load()
+ {
+ LogSource = Log;
+ Enabled = Config.Bind("Minimap", "Enabled", true, "Show the minimap under the clock.");
+ Size = Config.Bind("Minimap", "Size", 256f, new ConfigDescription(
+ "Width and height of the minimap, in the same units as the rest of the HUD.",
+ new AcceptableValueRange(96f, 512f)));
+ ViewRadius = Config.Bind("Minimap", "ViewRadius", 100f, new ConfigDescription(
+ "Meters from the player to the edge of the minimap.",
+ new AcceptableValueRange(25f, 600f)));
+ Gap = Config.Bind("Minimap", "Gap", 6f, new ConfigDescription(
+ "Space between the bottom of the clock and the top of the minimap.",
+ new AcceptableValueRange(0f, 80f)));
+ ShowMarkers = Config.Bind("Minimap", "ShowMarkers", true, "Show the same map icons the full map uses, when they are inside the view.");
+ MarkerSize = Config.Bind("Minimap", "MarkerSize", 16f, new ConfigDescription(
+ "Icon size on the minimap.",
+ new AcceptableValueRange(8f, 40f)));
+
+ ClassInjector.RegisterTypeInIl2Cpp();
+
+ _onClientStarted = ToIl2Cpp(new System.Action(OnClientStarted));
+ _onClientStopped = ToIl2Cpp(new System.Action(OnClientStopped));
+ _onPlayerStarted = ToIl2Cpp(new System.Action(OnPlayerStarted));
+ _onPlayerStopped = ToIl2Cpp(new System.Action(OnPlayerStopped));
+
+ ModAPI.add_OnClientStarted(_onClientStarted);
+ ModAPI.add_OnClientStopped(_onClientStopped);
+ ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
+ ModAPI.add_OnPlayerStoppedClient(_onPlayerStopped);
+
+ LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Config: {Config.ConfigFilePath}");
+ }
+
+ public override bool Unload()
+ {
+ LocalPlayer = null;
+ if (MinimapDisplay.Instance != null)
+ UnityEngine.Object.Destroy(MinimapDisplay.Instance.gameObject);
+ return true;
+ }
+
+ static void OnClientStarted()
+ {
+ if (MinimapDisplay.Instance != null)
+ return;
+
+ var host = new GameObject("SkopjeMinimap");
+ UnityEngine.Object.DontDestroyOnLoad(host);
+ host.AddComponent();
+ LogSource.LogInfo("Minimap display created.");
+ }
+
+ static void OnClientStopped()
+ {
+ LocalPlayer = null;
+ if (MinimapDisplay.Instance != null)
+ UnityEngine.Object.Destroy(MinimapDisplay.Instance.gameObject);
+ }
+
+ static void OnPlayerStarted(SkopjePlayer player)
+ {
+ if (player != null && player.IsOwner)
+ LocalPlayer = player;
+ }
+
+ static void OnPlayerStopped(SkopjePlayer player)
+ {
+ if (LocalPlayer == player)
+ LocalPlayer = null;
+ }
+
+ internal static SkopjePlayer FindLocalPlayer()
+ {
+ if (LocalPlayer != null)
+ return LocalPlayer;
+
+ var players = UnityEngine.Object.FindObjectsOfType();
+ if (players == null)
+ return null;
+
+ SkopjePlayer solo = null;
+ int count = 0;
+ for (int i = 0; i < players.Length; i++)
+ {
+ SkopjePlayer player = players[i];
+ if (player == null)
+ continue;
+ count++;
+ solo = player;
+ if (player.IsOwner)
+ {
+ LocalPlayer = player;
+ return player;
+ }
+ }
+
+ if (count == 1)
+ {
+ LocalPlayer = solo;
+ return solo;
+ }
+
+ return null;
+ }
+
+ static Il2CppSystem.Action ToIl2Cpp(System.Action action)
+ {
+ return DelegateSupport.ConvertDelegate(action);
+ }
+
+ static Il2CppSystem.Action ToIl2Cpp(System.Action action)
+ {
+ return DelegateSupport.ConvertDelegate>(action);
+ }
+ }
+
+ public class MinimapDisplay : MonoBehaviour
+ {
+ const float Border = 2f;
+ const int MaxMarkers = 40;
+ const float SampleMeters = 20f;
+
+ public static MinimapDisplay Instance { get; private set; }
+
+ TimeDisplay _time;
+ UIView _hud;
+ MapControls _map;
+ LevelBounds _bounds;
+ Image _source;
+ Sprite _mapSprite;
+
+ RectTransform _root;
+ RectTransform _window;
+ RectTransform _content;
+ RectTransform _mapRect;
+ Image _mapImage;
+ RectTransform _markersRoot;
+ RectTransform _heading;
+
+ bool _useTopDown = true;
+ Vector2 _fallbackSize;
+ bool _laidOut;
+ bool _loggedReady;
+ bool _loggedTick;
+ bool _loggedWait;
+ float _nextSearch;
+ float _nextMarkerScan;
+ int _errorLogs;
+
+ readonly List _slots = new List();
+ readonly List _nearby = new List();
+ readonly List _nearbyDist = new List();
+ readonly Vector3[] _corners = new Vector3[4];
+
+ static Sprite _arrowSprite;
+ static Sprite _dotSprite;
+
+ public MinimapDisplay(IntPtr pointer) : base(pointer)
+ {
+ }
+
+ void Awake()
+ {
+ Instance = this;
+ }
+
+ void OnDestroy()
+ {
+ if (_root != null)
+ Destroy(_root.gameObject);
+ if (Instance == this)
+ Instance = null;
+ }
+
+ void Update()
+ {
+ try
+ {
+ Tick();
+ }
+ catch (Exception ex)
+ {
+ if (_errorLogs < 3)
+ {
+ _errorLogs++;
+ MinimapPlugin.LogSource.LogWarning("Minimap update failed: " + ex.Message);
+ }
+ }
+ }
+
+ void Tick()
+ {
+ if (!_loggedTick)
+ {
+ _loggedTick = true;
+ MinimapPlugin.LogSource.LogInfo("Minimap ticking.");
+ }
+
+ if (!MinimapPlugin.Enabled.Value)
+ {
+ if (_root != null)
+ _root.gameObject.SetActive(false);
+ return;
+ }
+
+ float now = Time.unscaledTime;
+ bool hudMissing = _hud == null || !_hud.gameObject.activeInHierarchy;
+ if ((_time == null || hudMissing || _mapSprite == null || _bounds == null) && now >= _nextSearch)
+ {
+ _nextSearch = now + 1.5f;
+ ResolveSources();
+ }
+
+ bool hudOn = _hud != null && _hud.gameObject.activeInHierarchy;
+ if (!hudOn)
+ {
+ if (_root != null)
+ _root.gameObject.SetActive(false);
+ if (!_loggedWait && now > 8f)
+ {
+ _loggedWait = true;
+ MinimapPlugin.LogSource.LogInfo("Minimap hidden: the in-game HUD is not active.");
+ }
+ return;
+ }
+
+ EnsureWidget();
+ if (_root == null)
+ return;
+
+ if (!_root.gameObject.activeSelf)
+ _root.gameObject.SetActive(true);
+
+ PlaceUnderTimer();
+
+ SkopjePlayer player = MinimapPlugin.FindLocalPlayer();
+ if (_mapSprite == null || _bounds == null)
+ {
+ if (!_loggedWait && now > 8f)
+ {
+ _loggedWait = true;
+ MinimapPlugin.LogSource.LogInfo(
+ "Minimap frame is showing. Map image found: " + (_mapSprite != null) + ". Level bounds found: " + (_bounds != null) + ".");
+ }
+ return;
+ }
+
+ if (!_laidOut || _mapImage.sprite != _mapSprite)
+ LayoutMap();
+
+ if (player != null && !Follow(player))
+ ShowWholeMap(player);
+ else if (player == null)
+ ShowWholeMap(null);
+
+ if (player != null && MinimapPlugin.ShowMarkers.Value)
+ {
+ if (now >= _nextMarkerScan)
+ {
+ _nextMarkerScan = now + 0.75f;
+ RescanMarkers(player);
+ }
+ MoveMarkers();
+ }
+ else if (_slots.Count > 0)
+ {
+ HideMarkers();
+ }
+ }
+
+ void ResolveSources()
+ {
+ if (_time == null)
+ _time = PickTimer();
+ if (_hud == null || !_hud.gameObject.activeInHierarchy)
+ {
+ UIView found = FindIngameView();
+ if (found != null)
+ _hud = found;
+ }
+ if (_mapSprite == null)
+ {
+ _map = PickMap();
+ _source = _map != null ? FindMapGraphic(_map) : null;
+ if (_source != null)
+ _mapSprite = _source.sprite;
+ if (_mapSprite == null)
+ _mapSprite = FindLooseMapSprite();
+ _laidOut = false;
+ }
+ if (_bounds == null)
+ _bounds = PickBounds();
+ }
+
+ void EnsureWidget()
+ {
+ if (_root != null)
+ return;
+
+ RectTransform parent = _hud.transform as RectTransform;
+ if (parent == null)
+ return;
+
+ int layer = parent.gameObject.layer;
+ _root = CreateRect("Minimap", parent, layer);
+ _root.anchorMin = _root.anchorMax = new Vector2(0f, 1f);
+ _root.pivot = new Vector2(0f, 1f);
+
+ Image border = CreateImage("Border", _root, DotSprite(), new Color(0.95f, 0.93f, 0.86f, 0.95f));
+ Stretch(border.rectTransform, 0f);
+
+ _window = CreateRect("Window", _root, layer);
+ Stretch(_window, Border);
+ _window.gameObject.AddComponent();
+
+ Image backdrop = CreateImage("Backdrop", _window, DotSprite(), new Color(0.07f, 0.09f, 0.08f, 0.94f));
+ Stretch(backdrop.rectTransform, 0f);
+
+ _content = CreateRect("Content", _window, layer);
+ _content.anchorMin = _content.anchorMax = new Vector2(0.5f, 0.5f);
+ _content.pivot = Vector2.zero;
+ _content.anchoredPosition = Vector2.zero;
+
+ _mapImage = CreateImage("Map", _content, _mapSprite, Color.white);
+ _mapRect = _mapImage.rectTransform;
+ _mapRect.anchorMin = _mapRect.anchorMax = Vector2.zero;
+ _mapRect.pivot = new Vector2(0.5f, 0.5f);
+
+ _markersRoot = CreateRect("Markers", _window, layer);
+ Stretch(_markersRoot, 0f);
+
+ _heading = CreateRect("Heading", _window, layer);
+ _heading.anchorMin = _heading.anchorMax = new Vector2(0.5f, 0.5f);
+ _heading.pivot = new Vector2(0.5f, 0.5f);
+ _heading.anchoredPosition = Vector2.zero;
+ _heading.sizeDelta = Vector2.zero;
+
+ Image shadow = CreateImage("ArrowShadow", _heading, ArrowSprite(), new Color(0f, 0f, 0f, 0.9f));
+ shadow.rectTransform.anchorMin = shadow.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
+ shadow.rectTransform.sizeDelta = new Vector2(22f, 22f);
+ Image arrow = CreateImage("Arrow", _heading, ArrowSprite(), Color.white);
+ arrow.rectTransform.anchorMin = arrow.rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
+ arrow.rectTransform.sizeDelta = new Vector2(15f, 15f);
+
+ _root.SetAsLastSibling();
+ _laidOut = false;
+ MinimapPlugin.LogSource.LogInfo("Minimap parented to '" + parent.name + "'.");
+ }
+
+ void LayoutMap()
+ {
+ if (_mapSprite == null)
+ return;
+
+ _mapImage.sprite = _mapSprite;
+ _mapImage.color = Color.white;
+ _mapImage.type = Image.Type.Simple;
+ _mapImage.preserveAspect = false;
+
+ Vector2 bl = new Vector2(0f, 0f);
+ Vector2 tr = new Vector2(1f, 1f);
+ _useTopDown = _bounds != null;
+ if (_bounds != null)
+ {
+ try
+ {
+ bl = _bounds.GetTopDownPositionFrom01(new Vector2(0f, 0f));
+ tr = _bounds.GetTopDownPositionFrom01(new Vector2(1f, 1f));
+ }
+ catch (Exception)
+ {
+ _useTopDown = false;
+ }
+ }
+
+ float span = Mathf.Max(Mathf.Abs(tr.x - bl.x), Mathf.Abs(tr.y - bl.y));
+ if (!_useTopDown || span < 0.0001f || float.IsNaN(span))
+ {
+ _useTopDown = false;
+ Rect spriteRect = _mapSprite.rect;
+ _fallbackSize = new Vector2(Mathf.Max(spriteRect.width, 1f), Mathf.Max(spriteRect.height, 1f));
+ _mapRect.anchoredPosition = _fallbackSize * 0.5f;
+ _mapRect.sizeDelta = _fallbackSize;
+ _mapRect.localScale = Vector3.one;
+ }
+ else
+ {
+ _mapRect.anchoredPosition = (bl + tr) * 0.5f;
+ _mapRect.sizeDelta = new Vector2(Mathf.Abs(tr.x - bl.x), Mathf.Abs(tr.y - bl.y));
+ _mapRect.localScale = new Vector3(tr.x < bl.x ? -1f : 1f, tr.y < bl.y ? -1f : 1f, 1f);
+ }
+
+ _laidOut = true;
+ if (!_loggedReady)
+ {
+ _loggedReady = true;
+ MinimapPlugin.LogSource.LogInfo("Minimap using sprite '" + _mapSprite.name + "'.");
+ }
+ }
+
+ void PlaceUnderTimer()
+ {
+ RectTransform parent = _hud.transform as RectTransform;
+ if (parent == null)
+ return;
+
+ if (_root.parent != parent)
+ {
+ _root.SetParent(parent, false);
+ AdoptLayer(_root, parent.gameObject.layer);
+ _root.SetAsLastSibling();
+ }
+
+ float size = Mathf.Clamp(MinimapPlugin.Size.Value, 96f, 512f);
+ _root.anchorMin = _root.anchorMax = new Vector2(0f, 1f);
+ _root.pivot = new Vector2(0f, 1f);
+ _root.localScale = Vector3.one;
+ _root.sizeDelta = new Vector2(size, size);
+
+ float x = 16f;
+ float y = -72f - MinimapPlugin.Gap.Value;
+ RectTransform clock = ClockRect();
+ if (clock != null)
+ {
+ clock.GetWorldCorners(_corners);
+ Vector3 bottomLeft = _corners[0];
+ Vector3 down = _corners[0] - _corners[1];
+ float paneHeight = down.magnitude;
+ float clockHeight = Mathf.Abs(clock.rect.height);
+ if (clockHeight < 1f)
+ clockHeight = Mathf.Abs(clock.sizeDelta.y);
+ if (paneHeight > 0.5f && clockHeight >= 1f)
+ {
+ bottomLeft += down / paneHeight * (paneHeight * (MinimapPlugin.Gap.Value / clockHeight));
+ Rect parentRect = parent.rect;
+ Vector3 local = parent.InverseTransformPoint(bottomLeft);
+ x = local.x - parentRect.xMin;
+ y = local.y - parentRect.yMax;
+ if (float.IsNaN(x) || float.IsNaN(y) || Mathf.Abs(x) > 4000f || Mathf.Abs(y) > 4000f)
+ {
+ x = 16f;
+ y = -72f - MinimapPlugin.Gap.Value;
+ }
+ }
+ }
+
+ _root.anchoredPosition = new Vector2(x, y);
+ }
+
+ RectTransform ClockRect()
+ {
+ if (_time == null || _time.TimePane == null)
+ return null;
+ return _time.TimePane;
+ }
+
+ bool Follow(SkopjePlayer player)
+ {
+ Vector3 pos = player.transform.position;
+ Vector2 here = ToMap(pos);
+ Vector2 east = ToMap(pos + Vector3.right * SampleMeters) - here;
+ Vector2 north = ToMap(pos + Vector3.forward * SampleMeters) - here;
+ float ppm = east.magnitude / SampleMeters;
+ if (ppm < 0.0000001f)
+ ppm = north.magnitude / SampleMeters;
+ if (ppm < 0.0000001f || float.IsNaN(ppm) || float.IsInfinity(ppm))
+ return false;
+
+ float inner = Mathf.Max(16f, _root.sizeDelta.x - Border * 2f);
+ float radius = Mathf.Clamp(MinimapPlugin.ViewRadius.Value, 25f, 600f);
+ float zoom = (inner * 0.5f) / (radius * ppm);
+ if (float.IsNaN(zoom) || float.IsInfinity(zoom))
+ return false;
+
+ _content.anchoredPosition = -here * zoom;
+ _content.localScale = new Vector3(zoom, zoom, 1f);
+ _heading.anchoredPosition = Vector2.zero;
+
+ Vector3 forward = player.transform.forward;
+ Vector2 mapForward = east * forward.x + north * forward.z;
+ if (mapForward.sqrMagnitude > 0.000001f)
+ {
+ float degrees = Mathf.Atan2(mapForward.x, mapForward.y) * Mathf.Rad2Deg;
+ _heading.localRotation = Quaternion.Euler(0f, 0f, -degrees);
+ }
+
+ _playerMap = here;
+ _zoom = zoom;
+ return true;
+ }
+
+ void ShowWholeMap(SkopjePlayer player)
+ {
+ if (_mapRect == null)
+ return;
+
+ Vector2 size = _mapRect.sizeDelta;
+ float inner = Mathf.Max(16f, _root.sizeDelta.x - Border * 2f);
+ float zoom = inner / Mathf.Max(Mathf.Max(size.x, size.y), 1f);
+ Vector2 center = _mapRect.anchoredPosition;
+ _content.anchoredPosition = -center * zoom;
+ _content.localScale = new Vector3(zoom, zoom, 1f);
+ _playerMap = center;
+ _zoom = zoom;
+
+ if (player == null || _bounds == null)
+ {
+ _heading.anchoredPosition = Vector2.zero;
+ return;
+ }
+
+ try
+ {
+ Vector2 here = ToMap(player.transform.position);
+ _heading.anchoredPosition = (here - center) * zoom;
+ }
+ catch (Exception)
+ {
+ _heading.anchoredPosition = Vector2.zero;
+ }
+ }
+
+ Vector2 _playerMap;
+ float _zoom = 1f;
+
+ Vector2 ToMap(Vector3 world)
+ {
+ Vector2 uv = _bounds.GetLevelObjectPosition01(world);
+ if (!_useTopDown)
+ return new Vector2(uv.x * _fallbackSize.x, uv.y * _fallbackSize.y);
+ return _bounds.GetTopDownPositionFrom01(uv);
+ }
+
+ void RescanMarkers(SkopjePlayer player)
+ {
+ _nearby.Clear();
+ _nearbyDist.Clear();
+
+ float radius = Mathf.Clamp(MinimapPlugin.ViewRadius.Value, 25f, 600f) * 1.25f;
+ float limitSq = radius * radius;
+ Vector3 origin = player.transform.position;
+
+ var indicators = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < indicators.Length; i++)
+ {
+ MapIndicator indicator = indicators[i];
+ if (!IsSceneObject(indicator) || !indicator.gameObject.activeInHierarchy)
+ continue;
+ if (IsLocalPlayerMarker(indicator, player))
+ continue;
+
+ Vector3 delta = indicator.transform.position - origin;
+ delta.y = 0f;
+ float distSq = delta.sqrMagnitude;
+ if (distSq > limitSq)
+ continue;
+
+ InsertNearest(indicator, distSq);
+ }
+
+ int generation = Time.frameCount;
+ float markerSize = Mathf.Clamp(MinimapPlugin.MarkerSize.Value, 8f, 40f);
+ for (int i = 0; i < _nearby.Count; i++)
+ {
+ MapIndicator indicator = _nearby[i];
+ MarkerSlot slot = SlotFor(indicator.GetInstanceID());
+ slot.Indicator = indicator;
+ slot.Generation = generation;
+ slot.Rect.gameObject.SetActive(true);
+
+ Sprite icon = null;
+ try
+ {
+ icon = indicator.GetIcon();
+ }
+ catch (Exception)
+ {
+ icon = null;
+ }
+
+ slot.Image.sprite = icon != null ? icon : DotSprite();
+ slot.Image.color = icon != null ? indicator.Color : new Color(1f, 0.82f, 0.25f, 1f);
+ if (slot.Image.color.a < 0.05f)
+ slot.Image.color = icon != null ? Color.white : new Color(1f, 0.82f, 0.25f, 1f);
+
+ float scale = 1f;
+ try
+ {
+ if (indicator.Scale > 0.05f)
+ scale = Mathf.Clamp(indicator.Scale, 0.4f, 2.5f);
+ }
+ catch (Exception)
+ {
+ scale = 1f;
+ }
+
+ slot.Rect.sizeDelta = Vector2.one * (markerSize * scale);
+ }
+
+ for (int i = 0; i < _slots.Count; i++)
+ {
+ if (_slots[i].Generation != generation)
+ _slots[i].Rect.gameObject.SetActive(false);
+ }
+ }
+
+ void InsertNearest(MapIndicator indicator, float distSq)
+ {
+ int index = _nearby.Count;
+ for (int i = 0; i < _nearby.Count; i++)
+ {
+ if (distSq < _nearbyDist[i])
+ {
+ index = i;
+ break;
+ }
+ }
+
+ if (index >= MaxMarkers)
+ return;
+
+ _nearby.Insert(index, indicator);
+ _nearbyDist.Insert(index, distSq);
+ if (_nearby.Count > MaxMarkers)
+ {
+ _nearby.RemoveAt(_nearby.Count - 1);
+ _nearbyDist.RemoveAt(_nearbyDist.Count - 1);
+ }
+ }
+
+ void MoveMarkers()
+ {
+ float limit = (_root.sizeDelta.x - Border * 2f) * 0.5f + 8f;
+ for (int i = 0; i < _slots.Count; i++)
+ {
+ MarkerSlot slot = _slots[i];
+ if (!slot.Rect.gameObject.activeSelf)
+ continue;
+ if (slot.Indicator == null)
+ {
+ slot.Rect.gameObject.SetActive(false);
+ continue;
+ }
+
+ Vector2 point = ToMap(slot.Indicator.transform.position);
+ Vector2 local = (point - _playerMap) * _zoom;
+ if (Mathf.Abs(local.x) > limit || Mathf.Abs(local.y) > limit)
+ {
+ slot.Image.enabled = false;
+ continue;
+ }
+
+ slot.Image.enabled = true;
+ slot.Rect.anchoredPosition = local;
+ }
+ }
+
+ void HideMarkers()
+ {
+ for (int i = 0; i < _slots.Count; i++)
+ _slots[i].Rect.gameObject.SetActive(false);
+ }
+
+ MarkerSlot SlotFor(int id)
+ {
+ for (int i = 0; i < _slots.Count; i++)
+ {
+ if (_slots[i].Id == id)
+ return _slots[i];
+ }
+
+ var rect = CreateRect("Marker", _markersRoot, _markersRoot.gameObject.layer);
+ rect.anchorMin = rect.anchorMax = new Vector2(0.5f, 0.5f);
+ rect.pivot = new Vector2(0.5f, 0.5f);
+ rect.sizeDelta = Vector2.one * 16f;
+
+ Image image = rect.gameObject.AddComponent();
+ image.raycastTarget = false;
+ image.maskable = true;
+ image.preserveAspect = true;
+
+ var slot = new MarkerSlot
+ {
+ Id = id,
+ Rect = rect,
+ Image = image
+ };
+ _slots.Add(slot);
+ return slot;
+ }
+
+ static bool IsLocalPlayerMarker(MapIndicator indicator, SkopjePlayer player)
+ {
+ if (indicator.transform == player.transform || indicator.transform.IsChildOf(player.transform))
+ return true;
+
+ try
+ {
+ PlayerMapIndicator playerMarker = indicator.GetComponent();
+ return playerMarker != null && playerMarker.Player != null && playerMarker.Player.IsOwner;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ static UIView FindIngameView()
+ {
+ var views = Resources.FindObjectsOfTypeAll();
+ UIView inactive = null;
+ for (int i = 0; i < views.Length; i++)
+ {
+ UIView view = views[i];
+ if (!IsSceneObject(view) || !IsIngameView(view))
+ continue;
+ if (view.gameObject.activeInHierarchy)
+ return view;
+ inactive ??= view;
+ }
+
+ return inactive;
+ }
+
+ static bool IsIngameView(UIView view)
+ {
+ string objectName = view.name;
+ if (objectName == "View - Ingame" || objectName == "View - Ingam")
+ return true;
+
+ try
+ {
+ string viewName = view.ViewName;
+ return viewName == "Ingame" || viewName == "Ingam";
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ static Sprite FindLooseMapSprite()
+ {
+ var sprites = Resources.FindObjectsOfTypeAll();
+ Sprite best = null;
+ float bestArea = 256f * 256f;
+ for (int i = 0; i < sprites.Length; i++)
+ {
+ Sprite sprite = sprites[i];
+ if (sprite == null)
+ continue;
+ string name = sprite.name;
+ if (name.IndexOf("map", StringComparison.OrdinalIgnoreCase) < 0)
+ continue;
+ if (name.IndexOf("icon", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("pin", StringComparison.OrdinalIgnoreCase) >= 0)
+ continue;
+
+ Rect rect = sprite.rect;
+ float area = rect.width * rect.height;
+ if (area > bestArea)
+ {
+ best = sprite;
+ bestArea = area;
+ }
+ }
+
+ return best;
+ }
+
+ static TimeDisplay PickTimer()
+ {
+ var displays = Resources.FindObjectsOfTypeAll();
+ TimeDisplay fallback = null;
+ for (int i = 0; i < displays.Length; i++)
+ {
+ TimeDisplay display = displays[i];
+ if (!IsSceneObject(display) || display.TimePane == null)
+ continue;
+ if (display.gameObject.activeInHierarchy)
+ return display;
+ fallback ??= display;
+ }
+
+ return fallback;
+ }
+
+ static MapControls PickMap()
+ {
+ var maps = Resources.FindObjectsOfTypeAll();
+ MapControls fallback = null;
+ for (int i = 0; i < maps.Length; i++)
+ {
+ MapControls map = maps[i];
+ if (!IsSceneObject(map) || FindMapGraphic(map) == null)
+ continue;
+ if (map.gameObject.activeInHierarchy)
+ return map;
+ fallback ??= map;
+ }
+
+ return fallback;
+ }
+
+ static Image FindMapGraphic(MapControls map)
+ {
+ if (map == null)
+ return null;
+
+ if (map.MapBg != null && map.MapBg.sprite != null && map.MapBg.sprite.rect.width >= 128f)
+ return map.MapBg;
+
+ Image best = map.MapBg != null && map.MapBg.sprite != null ? map.MapBg : null;
+ float bestArea = SpriteArea(best);
+ if (map.MapTransform != null)
+ Consider(map.MapTransform, ref best, ref bestArea);
+
+ var extras = map.FallbackMapTransforms;
+ if (extras != null)
+ {
+ for (int i = 0; i < extras.Length; i++)
+ {
+ if (extras[i] != null)
+ Consider(extras[i], ref best, ref bestArea);
+ }
+ }
+
+ return best;
+ }
+
+ static void Consider(Transform root, ref Image best, ref float bestArea)
+ {
+ var images = root.GetComponentsInChildren(true);
+ for (int i = 0; i < images.Length; i++)
+ {
+ float area = SpriteArea(images[i]);
+ if (area > bestArea)
+ {
+ best = images[i];
+ bestArea = area;
+ }
+ }
+ }
+
+ static float SpriteArea(Image image)
+ {
+ if (image == null || image.sprite == null)
+ return 0f;
+ Rect rect = image.sprite.rect;
+ return rect.width * rect.height;
+ }
+
+ static LevelBounds PickBounds()
+ {
+ var all = Resources.FindObjectsOfTypeAll();
+ LevelBounds best = null;
+ float bestArea = -1f;
+ for (int i = 0; i < all.Length; i++)
+ {
+ LevelBounds bounds = all[i];
+ if (!IsSceneObject(bounds))
+ continue;
+
+ float area = 0f;
+ try
+ {
+ BoxCollider box = bounds.BoxCollider;
+ if (box != null)
+ {
+ Vector3 size = box.bounds.size;
+ area = Mathf.Abs(size.x * size.z);
+ }
+ }
+ catch (Exception)
+ {
+ area = 0f;
+ }
+
+ if (area > bestArea)
+ {
+ bestArea = area;
+ best = bounds;
+ }
+ }
+
+ return best;
+ }
+
+ static bool IsSceneObject(Component component)
+ {
+ return component != null && component.gameObject.scene.IsValid();
+ }
+
+ static RectTransform CreateRect(string name, Transform parent, int layer)
+ {
+ var go = new GameObject(name);
+ go.layer = layer;
+ RectTransform rect = go.AddComponent();
+ rect.SetParent(parent, false);
+ rect.localScale = Vector3.one;
+ rect.localRotation = Quaternion.identity;
+ return rect;
+ }
+
+ static Image CreateImage(string name, Transform parent, Sprite sprite, Color color)
+ {
+ RectTransform rect = CreateRect(name, parent, parent.gameObject.layer);
+ Image image = rect.gameObject.AddComponent();
+ image.sprite = sprite;
+ image.color = color;
+ image.raycastTarget = false;
+ image.maskable = true;
+ return image;
+ }
+
+ static void Stretch(RectTransform rect, float inset)
+ {
+ rect.anchorMin = Vector2.zero;
+ rect.anchorMax = Vector2.one;
+ rect.pivot = new Vector2(0.5f, 0.5f);
+ rect.offsetMin = new Vector2(inset, inset);
+ rect.offsetMax = new Vector2(-inset, -inset);
+ rect.localScale = Vector3.one;
+ }
+
+ static void AdoptLayer(Transform target, int layer)
+ {
+ target.gameObject.layer = layer;
+ for (int i = 0; i < target.childCount; i++)
+ AdoptLayer(target.GetChild(i), layer);
+ }
+
+ static Sprite DotSprite()
+ {
+ if (_dotSprite != null)
+ return _dotSprite;
+
+ var texture = new Texture2D(1, 1, TextureFormat.RGBA32, false);
+ texture.name = "MinimapDot";
+ texture.SetPixel(0, 0, Color.white);
+ texture.Apply();
+ texture.hideFlags = HideFlags.HideAndDontSave;
+ _dotSprite = Sprite.Create(texture, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f);
+ _dotSprite.hideFlags = HideFlags.HideAndDontSave;
+ return _dotSprite;
+ }
+
+ static Sprite ArrowSprite()
+ {
+ if (_arrowSprite != null)
+ return _arrowSprite;
+
+ const int n = 32;
+ var texture = new Texture2D(n, n, TextureFormat.RGBA32, false);
+ texture.name = "MinimapArrow";
+ texture.filterMode = FilterMode.Bilinear;
+ texture.wrapMode = TextureWrapMode.Clamp;
+ var clear = new Color(1f, 1f, 1f, 0f);
+ Vector2 a = new Vector2(15.5f, 29.5f);
+ Vector2 b = new Vector2(4f, 8.5f);
+ Vector2 c = new Vector2(27f, 8.5f);
+ for (int y = 0; y < n; y++)
+ {
+ for (int x = 0; x < n; x++)
+ texture.SetPixel(x, y, PointInTriangle(new Vector2(x + 0.5f, y + 0.5f), a, b, c) ? Color.white : clear);
+ }
+
+ texture.Apply();
+ texture.hideFlags = HideFlags.HideAndDontSave;
+ _arrowSprite = Sprite.Create(texture, new Rect(0f, 0f, n, n), new Vector2(0.5f, 0.5f), n);
+ _arrowSprite.hideFlags = HideFlags.HideAndDontSave;
+ return _arrowSprite;
+ }
+
+ static bool PointInTriangle(Vector2 p, Vector2 a, Vector2 b, Vector2 c)
+ {
+ float d1 = Sign(p, a, b);
+ float d2 = Sign(p, b, c);
+ float d3 = Sign(p, c, a);
+ bool hasNeg = d1 < 0f || d2 < 0f || d3 < 0f;
+ bool hasPos = d1 > 0f || d2 > 0f || d3 > 0f;
+ return !(hasNeg && hasPos);
+ }
+
+ static float Sign(Vector2 p, Vector2 a, Vector2 b)
+ {
+ return (p.x - b.x) * (a.y - b.y) - (a.x - b.x) * (p.y - b.y);
+ }
+
+ sealed class MarkerSlot
+ {
+ public int Id;
+ public int Generation;
+ public MapIndicator Indicator;
+ public RectTransform Rect;
+ public Image Image;
+ }
+ }
+}
diff --git a/Minimap/MinimapPlugin.csproj b/Minimap/MinimapPlugin.csproj
new file mode 100644
index 0000000..3da1773
--- /dev/null
+++ b/Minimap/MinimapPlugin.csproj
@@ -0,0 +1,60 @@
+
+
+ net6.0
+ latest
+ disable
+ disable
+ true
+ Skopje.Minimap
+ Skopje.Minimap
+ true
+ D:\SteamLibrary\steamapps\common\Skopje83
+
+
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Core.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll
+ false
+
+
+ $(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\SkopjeAssembly.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\FishNet.Runtime.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.UI.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\DoozyEngineAssembly.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\UnityEngine.PhysicsModule.dll
+ false
+
+
+ $(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll
+ false
+
+
+
diff --git a/Minimap/bin/Release/net6.0/Skopje.Minimap.deps.json b/Minimap/bin/Release/net6.0/Skopje.Minimap.deps.json
new file mode 100644
index 0000000..7f263df
--- /dev/null
+++ b/Minimap/bin/Release/net6.0/Skopje.Minimap.deps.json
@@ -0,0 +1,23 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v6.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v6.0": {
+ "Skopje.Minimap/1.0.0": {
+ "runtime": {
+ "Skopje.Minimap.dll": {}
+ }
+ }
+ }
+ },
+ "libraries": {
+ "Skopje.Minimap/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minimap/bin/Release/net6.0/Skopje.Minimap.dll b/Minimap/bin/Release/net6.0/Skopje.Minimap.dll
new file mode 100644
index 0000000..6f37307
Binary files /dev/null and b/Minimap/bin/Release/net6.0/Skopje.Minimap.dll differ
diff --git a/Minimap/bin/Release/net6.0/Skopje.Minimap.pdb b/Minimap/bin/Release/net6.0/Skopje.Minimap.pdb
new file mode 100644
index 0000000..3ddc752
Binary files /dev/null and b/Minimap/bin/Release/net6.0/Skopje.Minimap.pdb differ
diff --git a/Minimap/obj/MinimapPlugin.csproj.nuget.dgspec.json b/Minimap/obj/MinimapPlugin.csproj.nuget.dgspec.json
new file mode 100644
index 0000000..422958e
--- /dev/null
+++ b/Minimap/obj/MinimapPlugin.csproj.nuget.dgspec.json
@@ -0,0 +1,85 @@
+{
+ "format": 1,
+ "restore": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj": {}
+ },
+ "projects": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
+ "projectName": "Skopje.Minimap",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minimap/obj/MinimapPlugin.csproj.nuget.g.props b/Minimap/obj/MinimapPlugin.csproj.nuget.g.props
new file mode 100644
index 0000000..ba0d2cb
--- /dev/null
+++ b/Minimap/obj/MinimapPlugin.csproj.nuget.g.props
@@ -0,0 +1,15 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ PackageReference
+ 7.0.0
+
+
+
+
+
\ No newline at end of file
diff --git a/Minimap/obj/MinimapPlugin.csproj.nuget.g.targets b/Minimap/obj/MinimapPlugin.csproj.nuget.g.targets
new file mode 100644
index 0000000..3dc06ef
--- /dev/null
+++ b/Minimap/obj/MinimapPlugin.csproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/Minimap/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs b/Minimap/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
new file mode 100644
index 0000000..ed92695
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/.NETCoreApp,Version=v6.0.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+//
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfo.cs b/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfo.cs
new file mode 100644
index 0000000..9674ae3
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfo.cs
@@ -0,0 +1,22 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.Minimap")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
+[assembly: System.Reflection.AssemblyProductAttribute("Skopje.Minimap")]
+[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.Minimap")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfoInputs.cache b/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..9401367
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/MinimapPlugin.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+76a35bd1005e7595549b8f53ede6d7fe291948c74a3299c2ccd64bca53d35f29
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.GeneratedMSBuildEditorConfig.editorconfig b/Minimap/obj/Release/net6.0/MinimapPlugin.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..7a29a74
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/MinimapPlugin.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,18 @@
+is_global = true
+build_property.TargetFramework = net6.0
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v6.0
+build_property.TargetPlatformMinVersion =
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property.EntryPointFilePath =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = Skopje.Minimap
+build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\Minimap\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.EffectiveAnalysisLevelStyle = 6.0
+build_property.EnableCodeStyleSeverity =
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.assets.cache b/Minimap/obj/Release/net6.0/MinimapPlugin.assets.cache
new file mode 100644
index 0000000..fa32f5d
Binary files /dev/null and b/Minimap/obj/Release/net6.0/MinimapPlugin.assets.cache differ
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.AssemblyReference.cache b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.AssemblyReference.cache
new file mode 100644
index 0000000..ca6262e
Binary files /dev/null and b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.AssemblyReference.cache differ
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.CoreCompileInputs.cache b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.CoreCompileInputs.cache
new file mode 100644
index 0000000..1dcff41
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+d64656e1d47566a242332baae27415b1f54265daad457ca67758136236ded997
diff --git a/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.FileListAbsolute.txt b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..74c34cc
--- /dev/null
+++ b/Minimap/obj/Release/net6.0/MinimapPlugin.csproj.FileListAbsolute.txt
@@ -0,0 +1,12 @@
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.csproj.AssemblyReference.cache
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.GeneratedMSBuildEditorConfig.editorconfig
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.AssemblyInfoInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.AssemblyInfo.cs
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.csproj.CoreCompileInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.deps.json
+c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.dll
+c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.pdb
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\Skopje.Minimap.dll
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\refint\Skopje.Minimap.dll
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\Skopje.Minimap.pdb
+c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\ref\Skopje.Minimap.dll
diff --git a/Minimap/obj/Release/net6.0/Skopje.Minimap.dll b/Minimap/obj/Release/net6.0/Skopje.Minimap.dll
new file mode 100644
index 0000000..6f37307
Binary files /dev/null and b/Minimap/obj/Release/net6.0/Skopje.Minimap.dll differ
diff --git a/Minimap/obj/Release/net6.0/Skopje.Minimap.pdb b/Minimap/obj/Release/net6.0/Skopje.Minimap.pdb
new file mode 100644
index 0000000..3ddc752
Binary files /dev/null and b/Minimap/obj/Release/net6.0/Skopje.Minimap.pdb differ
diff --git a/Minimap/obj/Release/net6.0/ref/Skopje.Minimap.dll b/Minimap/obj/Release/net6.0/ref/Skopje.Minimap.dll
new file mode 100644
index 0000000..aa6b76c
Binary files /dev/null and b/Minimap/obj/Release/net6.0/ref/Skopje.Minimap.dll differ
diff --git a/Minimap/obj/Release/net6.0/refint/Skopje.Minimap.dll b/Minimap/obj/Release/net6.0/refint/Skopje.Minimap.dll
new file mode 100644
index 0000000..aa6b76c
Binary files /dev/null and b/Minimap/obj/Release/net6.0/refint/Skopje.Minimap.dll differ
diff --git a/Minimap/obj/project.assets.json b/Minimap/obj/project.assets.json
new file mode 100644
index 0000000..b10e2f7
--- /dev/null
+++ b/Minimap/obj/project.assets.json
@@ -0,0 +1,90 @@
+{
+ "version": 4,
+ "targets": {
+ "net6.0": {}
+ },
+ "libraries": {},
+ "projectFileDependencyGroups": {
+ "net6.0": []
+ },
+ "packageFolders": {
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
+ },
+ "project": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
+ "projectName": "Skopje.Minimap",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net6.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net6.0": {
+ "framework": "net6.0",
+ "targetAlias": "net6.0",
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[6.0.36, 6.0.36]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Minimap/obj/project.nuget.cache b/Minimap/obj/project.nuget.cache
new file mode 100644
index 0000000..c3789b8
--- /dev/null
+++ b/Minimap/obj/project.nuget.cache
@@ -0,0 +1,12 @@
+{
+ "version": 2,
+ "dgSpecHash": "fEqRFu6N7Gw=",
+ "success": true,
+ "projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
+ "expectedPackageFiles": [
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
+ ],
+ "logs": []
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 5498cbc..bbf1b90 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,18 @@
Mods for Skopje 83 using BepInEx IL2CPP.
+## Instructions to install mods
+
+1. Download BepInEx 6 IL2CPP
+https://builds.bepinex.dev/projects/bepinex_be
+
+Version: Artifact 789
+File: BepInEx-Unity.IL2CPP-win-x64-6.0.0-be.789+6cea286.zip
+
+2. Launch game once to initialize BepInEx classes and folders
+
+3. Drop mods in plugins and config where needed
+
## Mods
| Mod | Description | Version | Status |
@@ -10,3 +22,6 @@ Mods for Skopje 83 using BepInEx IL2CPP.
| Boss Health | On-screen health bar for the current boss. | 1.0.0 |  |
| Horde | Multiplies enemy counts, health, and damage, globally or per zone. | 1.0.0 |  |
| Third Person | Over-the-shoulder camera that can orbit around the player. | 1.0.0 |  |
+| Custom Player Skins | Replaces each outfit from its own PNG in `CustomSkins`: singleplayer, ranger, medic, engineer, and fighter. | 1.0.0 |  |
+| Minimap | Small map under the clock, using the in-game map. | 1.0.0 |  |
+| DOM Radio | Plays your mp3 and wav files inside the Dom, and mutes its music while the radio is on. | 1.0.0 |  |
diff --git a/ThirdPerson/ThirdPersonPlugin.cs b/ThirdPerson/ThirdPersonPlugin.cs
index 0d4f4f1..6ed14ae 100644
--- a/ThirdPerson/ThirdPersonPlugin.cs
+++ b/ThirdPerson/ThirdPersonPlugin.cs
@@ -4,12 +4,12 @@ using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
-using Cinemachine;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Skopje.Modding;
using UnityEngine;
+using UnityEngine.Rendering;
namespace Skopje.ThirdPerson
{
@@ -18,7 +18,7 @@ namespace Skopje.ThirdPerson
{
public const string PluginGuid = "com.skopje.thirdperson";
public const string PluginName = "Skopje Third Person";
- public const string PluginVersion = "1.0.0";
+ public const string PluginVersion = "1.2.0";
internal static ManualLogSource LogSource;
@@ -46,12 +46,12 @@ namespace Skopje.ThirdPerson
LogSource = Log;
Enabled = Config.Bind("Camera", "Enabled", true, "Over-the-shoulder camera on the right. The body keeps its own facing so the camera can orbit around to the face.");
FaceMovement = Config.Bind("Camera", "FaceMovement", false, "Turn the body toward the direction you move. Leave this off to keep the body still while the camera orbits.");
- Distance = Config.Bind("Camera", "Distance", 2.7f, "Meters behind the aim axis.");
- Shoulder = Config.Bind("Camera", "Shoulder", 0.62f, "Meters to the right of the aim axis.");
- Height = Config.Bind("Camera", "Height", 0.22f, "Meters above the pivot.");
- PivotHeight = Config.Bind("Camera", "PivotHeight", 1.25f, "Orbit height above the feet, before crouch.");
- AdsDistance = Config.Bind("Camera", "AdsDistance", 1.55f, "Distance while aiming down sights.");
- AdsShoulder = Config.Bind("Camera", "AdsShoulder", 0.4f, "Right offset while aiming down sights.");
+ Distance = Config.Bind("Camera", "Distance", 3.6f, "Meters behind the eyes.");
+ Shoulder = Config.Bind("Camera", "Shoulder", 1.05f, "Meters to the right of the eyes. Kept even when a wall shortens the boom.");
+ Height = Config.Bind("Camera", "Height", 0.4f, "Meters above the eyes.");
+ PivotHeight = Config.Bind("Camera", "PivotHeight", 1.25f, "Unused. The view is offset from the eye camera so interiors stay aligned.");
+ AdsDistance = Config.Bind("Camera", "AdsDistance", 2.1f, "Distance while aiming down sights.");
+ AdsShoulder = Config.Bind("Camera", "AdsShoulder", 0.75f, "Right offset while aiming down sights.");
FovBonus = Config.Bind("Camera", "FovBonus", 8f, "Extra field of view. Drops to zero while aiming.");
AimDistance = Config.Bind("Camera", "AimDistance", 80f, "Crosshair trace length when nothing is under the reticle.");
@@ -73,9 +73,11 @@ namespace Skopje.ThirdPerson
_driverHost.AddComponent();
_harmony = new Harmony(PluginGuid);
- Patch(AccessTools.Method(typeof(SkopjeFPSCamera), "LateUpdate"), nameof(ThirdPersonPatches.AfterCamera));
- Patch(AccessTools.Method(typeof(CinemachineBrain), "LateUpdate"), nameof(ThirdPersonPatches.AfterBrain));
+ Patch(AccessTools.Method(typeof(RenderPipelineManager), "BeginCameraRendering"), nameof(ThirdPersonPatches.AfterBeginCamera));
+ Patch(AccessTools.Method(typeof(RenderPipelineManager), "EndCameraRendering"), nameof(ThirdPersonPatches.AfterEndCamera));
Patch(AccessTools.Method(typeof(SkopjeCharacterController), "UpdateRotation"), nameof(ThirdPersonPatches.AfterBodyRotation), nameof(ThirdPersonPatches.BeforeBodyRotation));
+ PatchInteract(AccessTools.Method(typeof(InteractController), "Update"));
+ PatchInteract(AccessTools.Method(typeof(InteractController), "LateUpdate"));
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Config: {Config.ConfigFilePath}");
}
@@ -94,6 +96,21 @@ namespace Skopje.ThirdPerson
return true;
}
+ static void PatchInteract(MethodInfo method)
+ {
+ if (method == null)
+ {
+ LogSource.LogWarning("Third person hook was not found: InteractController");
+ return;
+ }
+
+ _harmony.Patch(
+ method,
+ prefix: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.BeforeInteract)),
+ postfix: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.AfterInteract)),
+ finalizer: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.RestoreInteract)));
+ }
+
static void Patch(MethodInfo method, string postfix, string prefix = null)
{
if (method == null)
@@ -121,14 +138,14 @@ namespace Skopje.ThirdPerson
public static class ThirdPersonPatches
{
- public static void AfterCamera(SkopjeFPSCamera __instance)
+ public static void AfterBeginCamera(ScriptableRenderContext context, Camera camera)
{
- ThirdPersonRig.Apply(__instance);
+ ThirdPersonRig.PushView(camera);
}
- public static void AfterBrain()
+ public static void AfterEndCamera(ScriptableRenderContext context, Camera camera)
{
- ThirdPersonRig.Apply(ThirdPersonRig.LocalCamera);
+ ThirdPersonRig.PopView(camera);
}
public static void BeforeBodyRotation(SkopjeCharacterController __instance, ref Quaternion currentRotation)
@@ -141,5 +158,21 @@ namespace Skopje.ThirdPerson
{
ThirdPersonRig.HoldBodyFacing(__instance, ref currentRotation, deltaTime);
}
+
+ public static void BeforeInteract()
+ {
+ ThirdPersonRig.HideBodiesFromInteract();
+ }
+
+ public static void AfterInteract()
+ {
+ ThirdPersonRig.RestoreInteractLayers();
+ }
+
+ public static Exception RestoreInteract(Exception __exception)
+ {
+ ThirdPersonRig.RestoreInteractLayers();
+ return __exception;
+ }
}
}
diff --git a/ThirdPerson/ThirdPersonRig.cs b/ThirdPerson/ThirdPersonRig.cs
index 658b969..c66e2a8 100644
--- a/ThirdPerson/ThirdPersonRig.cs
+++ b/ThirdPerson/ThirdPersonRig.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using UnityEngine;
+using UnityEngine.Rendering;
namespace Skopje.ThirdPerson
{
@@ -13,7 +14,7 @@ namespace Skopje.ThirdPerson
void LateUpdate()
{
- ThirdPersonRig.Apply(ThirdPersonRig.LocalCamera);
+ ThirdPersonRig.PrepareView();
}
}
@@ -22,32 +23,50 @@ namespace Skopje.ThirdPerson
const float MoveThreshold = 0.2f;
const float ExternalTurn = 35f;
- static readonly List Hidden = new List();
+ static readonly List BodyRenderers = new List();
+ static readonly List ArmRenderers = new List();
+ static readonly List ShadowOnly = new List();
+ static readonly List InteractColliders = new List();
+ static readonly List InteractLayers = new List();
static readonly RaycastHit[] Hits = new RaycastHit[24];
- internal static SkopjeFPSCamera LocalCamera;
-
static SkopjePlayer _player;
static bool _holding;
static bool _hasFacing;
static Quaternion _facing;
static Quaternion _incomingFacing;
static bool _hasIncoming;
- static bool _hasLook;
- static Quaternion _look = Quaternion.identity;
- static bool _hasPlacement;
- static Vector3 _placedPos;
- static float _baseFov;
static float _boom = 2.7f;
static float _ads;
static bool _capturedIk;
static bool _aimWasEnabled;
static bool _lookWasEnabled;
- static bool _followWasEnabled;
- static bool _disabledFollow;
static float _nextScan;
static int _errors;
static bool _logged;
+ static int _viewId;
+ static bool _hasView;
+ static Vector3 _viewPos;
+ static Quaternion _viewRot;
+ static float _viewFov;
+ static bool _pushed;
+ static Vector3 _savedPos;
+ static Quaternion _savedRot;
+ static float _savedFov;
+ static bool _maskSaved;
+ static int _savedMask;
+ static int _bodyLayers;
+ static GameObject _thirdRoot;
+ static Vector3 _thirdScale;
+ static bool _scaledThird;
+ static bool _control;
+ static IntPtr _controllerPtr;
+ static Camera _viewCamera;
+ static Vector3 _movePlanar;
+ static float _turnSharpness;
+ static int _interactMask = -1;
+ static bool _layersShifted;
+ static float _nextColliderScan;
public static void OnClientStarted()
{
@@ -57,7 +76,6 @@ namespace Skopje.ThirdPerson
{
Release();
_player = null;
- LocalCamera = null;
}
public static void OnPlayerStarted(SkopjePlayer player)
@@ -72,7 +90,6 @@ namespace Skopje.ThirdPerson
{
Release();
_player = null;
- LocalCamera = null;
}
}
@@ -80,12 +97,12 @@ namespace Skopje.ThirdPerson
{
Release();
_player = null;
- LocalCamera = null;
}
public static void TickBody()
{
- if (!CanControl(_player))
+ RefreshControl();
+ if (!_control)
{
if (_holding)
Release();
@@ -94,30 +111,14 @@ namespace Skopje.ThirdPerson
try
{
- HoldPresentation(_player);
- }
- catch (Exception ex)
- {
- Fail(ex);
- }
- }
-
- public static void Apply(SkopjeFPSCamera fps)
- {
- if (fps == null || !CanControl(fps.Player))
- return;
-
- Camera cam = fps.mainCamera != null ? fps.mainCamera : Camera.main;
- if (cam == null)
- return;
-
- LocalCamera = fps;
- if (_player == null)
- _player = fps.Player;
-
- try
- {
- Place(fps, cam);
+ CacheInteractMask();
+ if (Time.unscaledTime >= _nextColliderScan)
+ {
+ _nextColliderScan = Time.unscaledTime + 0.5f;
+ CacheInteractColliders();
+ }
+
+ Present(_player, null);
}
catch (Exception ex)
{
@@ -125,9 +126,10 @@ namespace Skopje.ThirdPerson
}
}
+ // UpdateRotation runs inside an IL2CPP call. Touching IsOwner from here re-enters the runtime and crashes the process.
public static void RememberBody(SkopjeCharacterController controller, Quaternion currentRotation)
{
- if (!CanControl(controller != null ? controller.Player : null))
+ if (!_control || controller is null || controller.Pointer != _controllerPtr)
{
_hasIncoming = false;
return;
@@ -139,25 +141,18 @@ namespace Skopje.ThirdPerson
public static void HoldBodyFacing(SkopjeCharacterController controller, ref Quaternion currentRotation, float deltaTime)
{
- if (!_hasIncoming || controller == null || !CanControl(controller.Player))
+ if (!_hasIncoming || !_control || controller is null || controller.Pointer != _controllerPtr)
return;
Quaternion held = _hasFacing ? _facing : _incomingFacing;
if (!_hasFacing || Quaternion.Angle(held, _incomingFacing) > ExternalTurn)
held = _incomingFacing;
- if (ThirdPersonPlugin.FaceMovement.Value)
+ if (ThirdPersonPlugin.FaceMovement.Value && _movePlanar.sqrMagnitude > MoveThreshold * MoveThreshold)
{
- Vector3 move = controller.moveInputVector;
- move.y = 0f;
- if (move.sqrMagnitude > MoveThreshold * MoveThreshold)
- {
- float sharp = controller.OrientationSharpness;
- if (sharp < 0.01f)
- sharp = 12f;
- float blend = 1f - Mathf.Exp(-sharp * Mathf.Max(0f, deltaTime));
- held = Quaternion.Slerp(held, Quaternion.LookRotation(move.normalized, Vector3.up), blend);
- }
+ float sharp = _turnSharpness > 0.01f ? _turnSharpness : 12f;
+ float blend = 1f - Mathf.Exp(-sharp * Mathf.Max(0f, deltaTime));
+ held = Quaternion.Slerp(held, Quaternion.LookRotation(_movePlanar.normalized, Vector3.up), blend);
}
_facing = FlatYaw(held);
@@ -165,68 +160,119 @@ namespace Skopje.ThirdPerson
currentRotation = _facing;
}
- static void Place(SkopjeFPSCamera fps, Camera cam)
+ static void RefreshControl()
{
- Quaternion look = ReadLook(fps, cam);
- _look = look;
- _hasLook = true;
+ _control = false;
+ _controllerPtr = IntPtr.Zero;
+ if (!CanControl(_player))
+ return;
- bool fresh = !_hasPlacement || (cam.transform.position - _placedPos).sqrMagnitude > 0.0004f;
- if (fresh)
- _baseFov = cam.fieldOfView;
+ SkopjeCharacterController body = _player.SkopjeCharacterController;
+ SkopjeFPSCamera camera = _player.SkopjeFPSCamera;
+ if (body is null || camera is null || body.Pointer == IntPtr.Zero || camera.Pointer == IntPtr.Zero)
+ return;
- float adsTarget = fps.adsMode ? 1f : 0f;
- _ads = Mathf.MoveTowards(_ads, adsTarget, Time.deltaTime / 0.12f);
+ _controllerPtr = body.Pointer;
+ _movePlanar = body.moveInputVector;
+ _movePlanar.y = 0f;
+ _turnSharpness = body.OrientationSharpness;
+ _control = true;
+ }
- float distance = Mathf.Lerp(Clamp(ThirdPersonPlugin.Distance.Value, 0.8f, 8f), Clamp(ThirdPersonPlugin.AdsDistance.Value, 0.6f, 6f), _ads);
- float shoulder = Mathf.Lerp(Clamp(ThirdPersonPlugin.Shoulder.Value, 0f, 2f), Clamp(ThirdPersonPlugin.AdsShoulder.Value, 0f, 1.5f), _ads);
- float height = Clamp(ThirdPersonPlugin.Height.Value, -1f, 2f);
- float boom = new Vector3(shoulder, height, -distance).magnitude;
- if (!_hasPlacement)
- _boom = boom;
- _boom = Mathf.Lerp(_boom, boom, 1f - Mathf.Exp(-16f * Time.deltaTime));
-
- Vector3 pivot = Pivot(fps.Player);
- Vector3 offset = look * new Vector3(shoulder, height, -distance);
- if (offset.sqrMagnitude < 0.0001f)
- offset = look * Vector3.back;
- Vector3 dir = offset.normalized;
- float allowed = PullIn(fps.Player, pivot, dir, _boom);
- Vector3 camPos = pivot + dir * allowed;
-
- Vector3 aim = AimPoint(fps.Player, pivot, look * Vector3.forward);
- Vector3 toAim = aim - camPos;
- Vector3 up = look * Vector3.up;
- if (toAim.sqrMagnitude < 0.0001f || Mathf.Abs(Vector3.Dot(toAim.normalized, up.normalized)) > 0.995f)
- cam.transform.rotation = look;
- else
- cam.transform.rotation = Quaternion.LookRotation(toAim, up);
-
- cam.transform.position = camPos;
- cam.fieldOfView = _baseFov + Clamp(ThirdPersonPlugin.FovBonus.Value, 0f, 40f) * (1f - _ads);
- _placedPos = camPos;
- _hasPlacement = true;
- KeepBodyYaw(fps.Player);
-
- if (!_logged)
+ public static void PrepareView()
+ {
+ if (_pushed && _viewCamera != null)
{
- _logged = true;
- ThirdPersonPlugin.LogSource.LogInfo("Right-shoulder camera is active. The body does not turn with the mouse.");
+ _viewCamera.transform.SetPositionAndRotation(_savedPos, _savedRot);
+ _viewCamera.fieldOfView = _savedFov;
+ _pushed = false;
+ }
+
+ _hasView = false;
+ _viewId = 0;
+ if (!_control || _player is null)
+ return;
+
+ SkopjeFPSCamera fps = _player.SkopjeFPSCamera;
+ if (fps is null)
+ return;
+
+ Camera cam = fps.mainCamera != null ? fps.mainCamera : Camera.main;
+ if (cam == null)
+ return;
+
+ try
+ {
+ // Offset from the live eye camera. Offset interiors move that camera, not the root position.
+ Quaternion look = ReadLook(fps, cam);
+ Vector3 eye = cam.transform.position;
+ float adsTarget = fps.adsMode ? 1f : 0f;
+ _ads = Mathf.MoveTowards(_ads, adsTarget, Time.deltaTime / 0.12f);
+
+ float distance = Mathf.Lerp(Clamp(ThirdPersonPlugin.Distance.Value, 0.8f, 8f), Clamp(ThirdPersonPlugin.AdsDistance.Value, 0.6f, 6f), _ads);
+ float shoulder = Mathf.Lerp(Clamp(ThirdPersonPlugin.Shoulder.Value, 0f, 2.5f), Clamp(ThirdPersonPlugin.AdsShoulder.Value, 0f, 2f), _ads);
+ float height = Clamp(ThirdPersonPlugin.Height.Value, -1f, 2f);
+ if (_boom <= 0.01f)
+ _boom = distance;
+ _boom = Mathf.Lerp(_boom, distance, 1f - Mathf.Exp(-16f * Time.deltaTime));
+
+ Vector3 side = eye + look * new Vector3(shoulder, height, 0f);
+ Vector3 back = look * Vector3.back;
+ float allowed = PullIn(_player, side, back, _boom);
+ Vector3 camPos = side + back * allowed;
+ Vector3 aim = AimPoint(_player, eye, look * Vector3.forward);
+ Vector3 toAim = aim - camPos;
+ Vector3 up = look * Vector3.up;
+
+ _viewPos = camPos;
+ _viewFov = cam.fieldOfView + Clamp(ThirdPersonPlugin.FovBonus.Value, 0f, 40f) * (1f - _ads);
+ if (toAim.sqrMagnitude < 0.0001f || up.sqrMagnitude < 0.0001f || Mathf.Abs(Vector3.Dot(toAim.normalized, up.normalized)) > 0.995f)
+ _viewRot = look;
+ else
+ _viewRot = Quaternion.LookRotation(toAim, up);
+
+ _viewId = cam.GetInstanceID();
+ _viewCamera = cam;
+ _hasView = true;
+ Present(_player, cam);
+
+ if (!_logged)
+ {
+ _logged = true;
+ ThirdPersonPlugin.LogSource.LogInfo("Right-shoulder view is active. World streaming still uses the eye camera.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Fail(ex);
}
}
- static void KeepBodyYaw(SkopjePlayer player)
+ // Render-only. The gameplay camera stays on the eyes so chunk streaming does not follow the shoulder.
+ public static void PushView(Camera camera)
{
- if (!_hasFacing || player == null)
+ if (!_hasView || camera == null || camera.GetInstanceID() != _viewId || _pushed)
return;
- Transform body = player.transform;
- Vector3 up = body.up;
- Vector3 forward = Vector3.ProjectOnPlane(_facing * Vector3.forward, up);
- if (forward.sqrMagnitude < 0.0001f)
+ _savedPos = camera.transform.position;
+ _savedRot = camera.transform.rotation;
+ _savedFov = camera.fieldOfView;
+ camera.transform.SetPositionAndRotation(_viewPos, _viewRot);
+ camera.fieldOfView = _viewFov;
+ if (_maskSaved)
+ camera.cullingMask |= _bodyLayers;
+ RevealCached();
+ _pushed = true;
+ }
+
+ public static void PopView(Camera camera)
+ {
+ if (!_pushed || camera == null || camera.GetInstanceID() != _viewId)
return;
- body.rotation = Quaternion.LookRotation(forward.normalized, up);
+ camera.transform.SetPositionAndRotation(_savedPos, _savedRot);
+ camera.fieldOfView = _savedFov;
+ _pushed = false;
}
static Quaternion ReadLook(SkopjeFPSCamera fps, Camera cam)
@@ -235,8 +281,6 @@ namespace Skopje.ThirdPerson
return fps.VCamera.transform.rotation;
if (fps.SocketTransform != null && fps.SocketTransform != cam.transform)
return fps.SocketTransform.rotation;
- if (_hasLook && _hasPlacement && (cam.transform.position - _placedPos).sqrMagnitude < 0.0004f)
- return _look;
return cam.transform.rotation;
}
@@ -296,15 +340,24 @@ namespace Skopje.ThirdPerson
return best;
}
- static void HoldPresentation(SkopjePlayer player)
+ static void Present(SkopjePlayer player, Camera cam)
{
GameObject third = player.ThirdPerson;
- if (third != null && !third.activeSelf)
- third.SetActive(true);
+ if (third != null)
+ {
+ if (!third.activeSelf)
+ third.SetActive(true);
+ if (!_scaledThird && third.transform.localScale.sqrMagnitude < 0.0001f)
+ {
+ _thirdScale = third.transform.localScale;
+ third.transform.localScale = Vector3.one;
+ _scaledThird = true;
+ _thirdRoot = third;
+ }
+ }
- GameObject pack = player.ThirdPersonBackpackParent;
- if (pack != null && !pack.activeSelf)
- pack.SetActive(true);
+ if (player.ThirdPersonBackpackParent != null && !player.ThirdPersonBackpackParent.activeSelf)
+ player.ThirdPersonBackpackParent.SetActive(true);
var anim = player.TPAnimationController;
if (anim != null)
@@ -322,56 +375,69 @@ namespace Skopje.ThirdPerson
anim.LookAtMotor.enabled = false;
}
- var follow = player.ThirdPersonFollowCam;
- if (follow != null && follow.Look != null && follow.Look.enabled)
- {
- _followWasEnabled = true;
- follow.Look.enabled = false;
- _disabledFollow = true;
- }
-
if (Time.unscaledTime >= _nextScan)
{
_nextScan = Time.unscaledTime + 0.25f;
- RebuildHidden(player);
+ CacheVisuals(player);
}
- for (int i = 0; i < Hidden.Count; i++)
- {
- if (Hidden[i] != null)
- Hidden[i].forceRenderingOff = true;
- }
+ RevealCached();
+ if (cam != null)
+ IncludeBodyLayers(cam);
_holding = true;
}
- static void RebuildHidden(SkopjePlayer player)
+ static void CacheVisuals(SkopjePlayer player)
{
- var next = new List();
+ RestoreArms();
+ BodyRenderers.Clear();
+ ArmRenderers.Clear();
+ _bodyLayers = 0;
+
Transform third = player.ThirdPerson != null ? player.ThirdPerson.transform : null;
- Collect(player.FirstPerson, third, next);
-
- var weapons = player.WeaponsController;
- if (weapons != null)
+ if (player.ThirdPersonRenderer != null)
+ AddBody(player.ThirdPersonRenderer);
+ if (third != null)
{
- if (weapons.Socket != null)
- Collect(weapons.Socket.gameObject, third, next);
- if (weapons.ThrowSocket != null)
- Collect(weapons.ThrowSocket.gameObject, third, next);
+ Renderer[] found = third.GetComponentsInChildren(true);
+ for (int i = 0; i < found.Length; i++)
+ AddBody(found[i]);
}
- for (int i = 0; i < Hidden.Count; i++)
+ if (player.SkopjeCharacterController != null && player.SkopjeCharacterController.MeshRoot != null)
{
- Renderer old = Hidden[i];
- if (old != null && !next.Contains(old))
- old.forceRenderingOff = false;
+ Renderer[] found = player.SkopjeCharacterController.MeshRoot.GetComponentsInChildren(true);
+ for (int i = 0; i < found.Length; i++)
+ {
+ Renderer renderer = found[i];
+ if (renderer == null)
+ continue;
+ if (third != null && (renderer.transform == third || renderer.transform.IsChildOf(third)))
+ AddBody(renderer);
+ else if (player.FirstPerson == null || !renderer.transform.IsChildOf(player.FirstPerson.transform))
+ AddBody(renderer);
+ }
}
- Hidden.Clear();
- Hidden.AddRange(next);
+ CollectArms(player.FirstPerson, third);
}
- static void Collect(GameObject root, Transform thirdPerson, List into)
+ static void AddBody(Renderer renderer)
+ {
+ if (renderer == null || BodyRenderers.Contains(renderer))
+ return;
+
+ BodyRenderers.Add(renderer);
+ _bodyLayers |= 1 << renderer.gameObject.layer;
+ if (renderer.shadowCastingMode == ShadowCastingMode.ShadowsOnly && !ShadowOnly.Contains(renderer))
+ {
+ ShadowOnly.Add(renderer);
+ renderer.shadowCastingMode = ShadowCastingMode.On;
+ }
+ }
+
+ static void CollectArms(GameObject root, Transform thirdPerson)
{
if (root == null)
return;
@@ -382,24 +448,181 @@ namespace Skopje.ThirdPerson
for (int i = 0; i < found.Length; i++)
{
Renderer renderer = found[i];
- if (renderer == null)
+ if (renderer == null || BodyRenderers.Contains(renderer) || ArmRenderers.Contains(renderer))
continue;
if (thirdPerson != null && renderer.transform.IsChildOf(thirdPerson))
continue;
- if (!into.Contains(renderer))
- into.Add(renderer);
+ ArmRenderers.Add(renderer);
+ }
+ }
+
+ static void RevealCached()
+ {
+ if (_thirdRoot != null && !_thirdRoot.activeSelf)
+ _thirdRoot.SetActive(true);
+
+ for (int i = 0; i < BodyRenderers.Count; i++)
+ {
+ Renderer renderer = BodyRenderers[i];
+ if (renderer == null)
+ continue;
+ renderer.forceRenderingOff = false;
+ renderer.enabled = true;
+ if (renderer is SkinnedMeshRenderer skin)
+ skin.updateWhenOffscreen = true;
+ }
+
+ for (int i = 0; i < ArmRenderers.Count; i++)
+ {
+ if (ArmRenderers[i] != null)
+ ArmRenderers[i].forceRenderingOff = true;
+ }
+ }
+
+ static void IncludeBodyLayers(Camera cam)
+ {
+ if (_bodyLayers == 0)
+ return;
+ if (!_maskSaved)
+ {
+ _savedMask = cam.cullingMask;
+ _maskSaved = true;
+ }
+
+ cam.cullingMask |= _bodyLayers;
+ }
+
+ public static void HideBodiesFromInteract()
+ {
+ RestoreInteractLayers();
+ if (_interactMask == 0 || InteractColliders.Count == 0)
+ return;
+
+ int free = 2;
+ if ((_interactMask & (1 << free)) != 0)
+ {
+ free = -1;
+ for (int layer = 8; layer < 32; layer++)
+ {
+ if ((_interactMask & (1 << layer)) == 0)
+ {
+ free = layer;
+ break;
+ }
+ }
+ }
+
+ if (free < 0)
+ return;
+
+ for (int i = 0; i < InteractColliders.Count; i++)
+ {
+ Collider collider = InteractColliders[i];
+ if (collider == null)
+ {
+ InteractLayers.Add(0);
+ continue;
+ }
+
+ int layer = collider.gameObject.layer;
+ InteractLayers.Add(layer);
+ if ((_interactMask & (1 << layer)) != 0)
+ collider.gameObject.layer = free;
+ }
+
+ _layersShifted = true;
+ }
+
+ public static void RestoreInteractLayers()
+ {
+ if (!_layersShifted)
+ return;
+
+ for (int i = InteractColliders.Count - 1; i >= 0; i--)
+ {
+ Collider collider = InteractColliders[i];
+ if (collider != null && i < InteractLayers.Count)
+ collider.gameObject.layer = InteractLayers[i];
+ }
+
+ InteractLayers.Clear();
+ _layersShifted = false;
+ }
+
+ static void CacheInteractMask()
+ {
+ InteractController interact = _player.InteractController;
+ if (interact == null)
+ {
+ _interactMask = 0;
+ return;
+ }
+
+ _interactMask = interact.BlockingLayers.value | interact.InteractLayers.value;
+ }
+
+ static void CacheInteractColliders()
+ {
+ if (_layersShifted)
+ return;
+
+ InteractColliders.Clear();
+ SkopjePlayer[] players = UnityEngine.Object.FindObjectsOfType();
+ for (int i = 0; i < players.Length; i++)
+ AddPlayerColliders(players[i]);
+ }
+
+ static void AddPlayerColliders(SkopjePlayer player)
+ {
+ if (player == null || player.ThirdPerson == null)
+ return;
+
+ Collider owner = player.OwnerCollider;
+ Collider[] found = player.ThirdPerson.GetComponentsInChildren(true);
+ for (int i = 0; i < found.Length; i++)
+ {
+ Collider collider = found[i];
+ if (collider == null || collider.isTrigger || collider == owner || InteractColliders.Contains(collider))
+ continue;
+ InteractColliders.Add(collider);
+ }
+ }
+
+ static void RestoreArms()
+ {
+ for (int i = 0; i < ArmRenderers.Count; i++)
+ {
+ if (ArmRenderers[i] != null)
+ ArmRenderers[i].forceRenderingOff = false;
}
}
static void Release()
{
- for (int i = 0; i < Hidden.Count; i++)
+ if (_pushed && _viewCamera != null)
{
- if (Hidden[i] != null)
- Hidden[i].forceRenderingOff = false;
+ _viewCamera.transform.SetPositionAndRotation(_savedPos, _savedRot);
+ _viewCamera.fieldOfView = _savedFov;
}
- Hidden.Clear();
+ if (_maskSaved && _viewCamera != null)
+ _viewCamera.cullingMask = _savedMask;
+
+ for (int i = 0; i < ShadowOnly.Count; i++)
+ {
+ if (ShadowOnly[i] != null)
+ ShadowOnly[i].shadowCastingMode = ShadowCastingMode.ShadowsOnly;
+ }
+
+ if (_scaledThird && _thirdRoot != null)
+ _thirdRoot.transform.localScale = _thirdScale;
+
+ RestoreInteractLayers();
+ InteractColliders.Clear();
+ RestoreArms();
+ BodyRenderers.Clear();
+ ArmRenderers.Clear();
+ ShadowOnly.Clear();
if (_player != null)
{
@@ -412,9 +635,6 @@ namespace Skopje.ThirdPerson
anim.LookAtMotor.enabled = _lookWasEnabled;
}
- if (_disabledFollow && _player.ThirdPersonFollowCam != null && _player.ThirdPersonFollowCam.Look != null)
- _player.ThirdPersonFollowCam.Look.enabled = _followWasEnabled;
-
try
{
_player.RefreshActiveModels();
@@ -426,14 +646,20 @@ namespace Skopje.ThirdPerson
}
_capturedIk = false;
- _disabledFollow = false;
_holding = false;
_hasFacing = false;
_hasIncoming = false;
- _hasPlacement = false;
- _hasLook = false;
_ads = 0f;
_logged = false;
+ _control = false;
+ _controllerPtr = IntPtr.Zero;
+ _hasView = false;
+ _pushed = false;
+ _maskSaved = false;
+ _scaledThird = false;
+ _thirdRoot = null;
+ _viewCamera = null;
+ _bodyLayers = 0;
}
static bool CanControl(SkopjePlayer player)
diff --git a/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.dll b/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.dll
index 3d95e43..8ae93fb 100644
Binary files a/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.dll and b/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.dll differ
diff --git a/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.pdb b/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.pdb
index 8c09b11..872d883 100644
Binary files a/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.pdb and b/ThirdPerson/bin/Release/net6.0/Skopje.ThirdPerson.pdb differ
diff --git a/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.dll b/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.dll
index 3d95e43..8ae93fb 100644
Binary files a/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.dll and b/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.dll differ
diff --git a/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.pdb b/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.pdb
index 8c09b11..872d883 100644
Binary files a/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.pdb and b/ThirdPerson/obj/Release/net6.0/Skopje.ThirdPerson.pdb differ
diff --git a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfo.cs b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfo.cs
index f81db69..fa3f346 100644
--- a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfo.cs
+++ b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfo.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.ThirdPerson")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.ThirdPerson")]
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.ThirdPerson")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfoInputs.cache b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfoInputs.cache
index d37f276..d72cd09 100644
--- a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfoInputs.cache
+++ b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.AssemblyInfoInputs.cache
@@ -1 +1 @@
-8ec25d9c2fd66f388ffb60470d85e452afd95523e8f8b9133f1a26f62c293384
+bc8b2b18196c3acb4bcc9f717e44bd539bfb6c46a1061eb8c275c7da7f6dfc9f
diff --git a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.csproj.AssemblyReference.cache b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.csproj.AssemblyReference.cache
index 47e5d12..e6bf606 100644
Binary files a/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.csproj.AssemblyReference.cache and b/ThirdPerson/obj/Release/net6.0/ThirdPersonPlugin.csproj.AssemblyReference.cache differ
diff --git a/ThirdPerson/obj/Release/net6.0/ref/Skopje.ThirdPerson.dll b/ThirdPerson/obj/Release/net6.0/ref/Skopje.ThirdPerson.dll
index d1edbd8..0bf7c2e 100644
Binary files a/ThirdPerson/obj/Release/net6.0/ref/Skopje.ThirdPerson.dll and b/ThirdPerson/obj/Release/net6.0/ref/Skopje.ThirdPerson.dll differ
diff --git a/ThirdPerson/obj/Release/net6.0/refint/Skopje.ThirdPerson.dll b/ThirdPerson/obj/Release/net6.0/refint/Skopje.ThirdPerson.dll
index d1edbd8..0bf7c2e 100644
Binary files a/ThirdPerson/obj/Release/net6.0/refint/Skopje.ThirdPerson.dll and b/ThirdPerson/obj/Release/net6.0/refint/Skopje.ThirdPerson.dll differ
diff --git a/_fix/DumpMap/DumpMap.csproj b/_fix/DumpMap/DumpMap.csproj
new file mode 100644
index 0000000..d5c37af
--- /dev/null
+++ b/_fix/DumpMap/DumpMap.csproj
@@ -0,0 +1,11 @@
+
+
+ Exe
+ net8.0
+ enable
+ disable
+
+
+
+
+
diff --git a/_fix/DumpMap/Program.cs b/_fix/DumpMap/Program.cs
new file mode 100644
index 0000000..eb46d53
--- /dev/null
+++ b/_fix/DumpMap/Program.cs
@@ -0,0 +1,48 @@
+using System.Reflection;
+
+var interop = @"D:\SteamLibrary\steamapps\common\Skopje83\BepInEx\interop";
+var core = @"D:\SteamLibrary\steamapps\common\Skopje83\BepInEx\core";
+var runtime = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
+var paths = new List();
+paths.AddRange(Directory.GetFiles(interop, "*.dll"));
+paths.AddRange(Directory.GetFiles(core, "*.dll"));
+paths.AddRange(Directory.GetFiles(runtime, "*.dll"));
+using var mlc = new MetadataLoadContext(new PathAssemblyResolver(paths));
+
+var names = new HashSet(args);
+var skopje = mlc.LoadFromAssemblyPath(Path.Combine(interop, "SkopjeAssembly.dll"));
+foreach (var type in skopje.GetTypes())
+{
+ if (!names.Contains(type.Name))
+ continue;
+ Console.WriteLine("TYPE " + type.FullName + " : " + type.BaseType?.FullName);
+ var flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly;
+ foreach (var p in type.GetProperties(flags))
+ Console.WriteLine(" P " + Format(p.PropertyType) + " " + p.Name);
+ foreach (var f in type.GetFields(flags))
+ {
+ if (f.Name.StartsWith("Native")) continue;
+ Console.WriteLine(" F " + Format(f.FieldType) + " " + f.Name);
+ }
+ foreach (var m in type.GetMethods(flags))
+ {
+ if (m.Name.StartsWith("get_") || m.Name.StartsWith("set_") || m.Name.Contains("Native")) continue;
+ var parms = string.Join(", ", m.GetParameters().Select(p => Format(p.ParameterType) + " " + p.Name));
+ Console.WriteLine(" M " + Format(m.ReturnType) + " " + m.Name + "(" + parms + ")");
+ }
+}
+
+static string Format(Type t)
+{
+ if (t == null) return "?";
+ if (t.IsGenericType)
+ {
+ var def = t.Name;
+ var tick = def.IndexOf('`');
+ if (tick >= 0) def = def.Substring(0, tick);
+ return def + "<" + string.Join(", ", t.GetGenericArguments().Select(Format)) + ">";
+ }
+ if (t.IsArray)
+ return Format(t.GetElementType()) + "[]";
+ return t.Name;
+}
diff --git a/_fix/DumpMap/bin/Release/net8.0/DumpMap.deps.json b/_fix/DumpMap/bin/Release/net8.0/DumpMap.deps.json
new file mode 100644
index 0000000..da6ce63
--- /dev/null
+++ b/_fix/DumpMap/bin/Release/net8.0/DumpMap.deps.json
@@ -0,0 +1,41 @@
+{
+ "runtimeTarget": {
+ "name": ".NETCoreApp,Version=v8.0",
+ "signature": ""
+ },
+ "compilationOptions": {},
+ "targets": {
+ ".NETCoreApp,Version=v8.0": {
+ "DumpMap/1.0.0": {
+ "dependencies": {
+ "System.Reflection.MetadataLoadContext": "8.0.0"
+ },
+ "runtime": {
+ "DumpMap.dll": {}
+ }
+ },
+ "System.Reflection.MetadataLoadContext/8.0.0": {
+ "runtime": {
+ "lib/net8.0/System.Reflection.MetadataLoadContext.dll": {
+ "assemblyVersion": "8.0.0.0",
+ "fileVersion": "8.0.23.53103"
+ }
+ }
+ }
+ }
+ },
+ "libraries": {
+ "DumpMap/1.0.0": {
+ "type": "project",
+ "serviceable": false,
+ "sha512": ""
+ },
+ "System.Reflection.MetadataLoadContext/8.0.0": {
+ "type": "package",
+ "serviceable": true,
+ "sha512": "sha512-SZxrQ4sQYnIcdwiO3G/lHZopbPYQ2lW0ioT4JezgccWUrKaKbHLJbAGZaDfkYjWcta1pWssAo3MOXLsR0ie4tQ==",
+ "path": "system.reflection.metadataloadcontext/8.0.0",
+ "hashPath": "system.reflection.metadataloadcontext.8.0.0.nupkg.sha512"
+ }
+ }
+}
\ No newline at end of file
diff --git a/_fix/DumpMap/bin/Release/net8.0/DumpMap.dll b/_fix/DumpMap/bin/Release/net8.0/DumpMap.dll
new file mode 100644
index 0000000..1c55246
Binary files /dev/null and b/_fix/DumpMap/bin/Release/net8.0/DumpMap.dll differ
diff --git a/_fix/DumpMap/bin/Release/net8.0/DumpMap.exe b/_fix/DumpMap/bin/Release/net8.0/DumpMap.exe
new file mode 100644
index 0000000..f80c68b
Binary files /dev/null and b/_fix/DumpMap/bin/Release/net8.0/DumpMap.exe differ
diff --git a/_fix/DumpMap/bin/Release/net8.0/DumpMap.pdb b/_fix/DumpMap/bin/Release/net8.0/DumpMap.pdb
new file mode 100644
index 0000000..9ff80b2
Binary files /dev/null and b/_fix/DumpMap/bin/Release/net8.0/DumpMap.pdb differ
diff --git a/_fix/DumpMap/bin/Release/net8.0/DumpMap.runtimeconfig.json b/_fix/DumpMap/bin/Release/net8.0/DumpMap.runtimeconfig.json
new file mode 100644
index 0000000..d784515
--- /dev/null
+++ b/_fix/DumpMap/bin/Release/net8.0/DumpMap.runtimeconfig.json
@@ -0,0 +1,13 @@
+{
+ "runtimeOptions": {
+ "tfm": "net8.0",
+ "framework": {
+ "name": "Microsoft.NETCore.App",
+ "version": "8.0.0"
+ },
+ "configProperties": {
+ "System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
+ "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/_fix/DumpMap/bin/Release/net8.0/System.Reflection.MetadataLoadContext.dll b/_fix/DumpMap/bin/Release/net8.0/System.Reflection.MetadataLoadContext.dll
new file mode 100644
index 0000000..a54bbed
Binary files /dev/null and b/_fix/DumpMap/bin/Release/net8.0/System.Reflection.MetadataLoadContext.dll differ
diff --git a/_fix/DumpMap/obj/DumpMap.csproj.nuget.dgspec.json b/_fix/DumpMap/obj/DumpMap.csproj.nuget.dgspec.json
new file mode 100644
index 0000000..8ac2257
--- /dev/null
+++ b/_fix/DumpMap/obj/DumpMap.csproj.nuget.dgspec.json
@@ -0,0 +1,95 @@
+{
+ "format": 1,
+ "restore": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj": {}
+ },
+ "projects": {
+ "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj",
+ "projectName": "DumpMap",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net8.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net8.0": {
+ "framework": "net8.0",
+ "targetAlias": "net8.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net8.0": {
+ "framework": "net8.0",
+ "targetAlias": "net8.0",
+ "dependencies": {
+ "System.Reflection.MetadataLoadContext": {
+ "target": "Package",
+ "version": "[8.0.0, )"
+ }
+ },
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Host.win-x64",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.props b/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.props
new file mode 100644
index 0000000..ba0d2cb
--- /dev/null
+++ b/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.props
@@ -0,0 +1,15 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ $(UserProfile)\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget
+ PackageReference
+ 7.0.0
+
+
+
+
+
\ No newline at end of file
diff --git a/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.targets b/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.targets
new file mode 100644
index 0000000..3dc06ef
--- /dev/null
+++ b/_fix/DumpMap/obj/DumpMap.csproj.nuget.g.targets
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/_fix/DumpMap/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs b/_fix/DumpMap/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs
new file mode 100644
index 0000000..2217181
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+//
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfo.cs b/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfo.cs
new file mode 100644
index 0000000..18eb210
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfo.cs
@@ -0,0 +1,22 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+using System;
+using System.Reflection;
+
+[assembly: System.Reflection.AssemblyCompanyAttribute("DumpMap")]
+[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
+[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
+[assembly: System.Reflection.AssemblyProductAttribute("DumpMap")]
+[assembly: System.Reflection.AssemblyTitleAttribute("DumpMap")]
+[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
+
+// Generated by the MSBuild WriteCodeFragment class.
+
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfoInputs.cache b/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfoInputs.cache
new file mode 100644
index 0000000..6cdfbae
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.AssemblyInfoInputs.cache
@@ -0,0 +1 @@
+ae0c48002f62f98b7adc5e902b1de30eecca72e82060a4b80499e2417e90a658
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.GeneratedMSBuildEditorConfig.editorconfig b/_fix/DumpMap/obj/Release/net8.0/DumpMap.GeneratedMSBuildEditorConfig.editorconfig
new file mode 100644
index 0000000..9b47015
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.GeneratedMSBuildEditorConfig.editorconfig
@@ -0,0 +1,18 @@
+is_global = true
+build_property.TargetFramework = net8.0
+build_property.TargetFrameworkIdentifier = .NETCoreApp
+build_property.TargetFrameworkVersion = v8.0
+build_property.TargetPlatformMinVersion =
+build_property.UsingMicrosoftNETSdkWeb =
+build_property.ProjectTypeGuids =
+build_property.InvariantGlobalization =
+build_property.PlatformNeutralAssembly =
+build_property.EnforceExtendedAnalyzerRules =
+build_property.EntryPointFilePath =
+build_property._SupportedPlatformList = Linux,macOS,Windows
+build_property.RootNamespace = DumpMap
+build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\
+build_property.EnableComHosting =
+build_property.EnableGeneratedComInterfaceComImportInterop =
+build_property.EffectiveAnalysisLevelStyle = 8.0
+build_property.EnableCodeStyleSeverity =
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.GlobalUsings.g.cs b/_fix/DumpMap/obj/Release/net8.0/DumpMap.GlobalUsings.g.cs
new file mode 100644
index 0000000..d12bcbc
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.GlobalUsings.g.cs
@@ -0,0 +1,8 @@
+//
+global using System;
+global using System.Collections.Generic;
+global using System.IO;
+global using System.Linq;
+global using System.Net.Http;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.assets.cache b/_fix/DumpMap/obj/Release/net8.0/DumpMap.assets.cache
new file mode 100644
index 0000000..fad49f3
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/DumpMap.assets.cache differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.AssemblyReference.cache b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.AssemblyReference.cache
new file mode 100644
index 0000000..457a0ec
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.AssemblyReference.cache differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.CoreCompileInputs.cache b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.CoreCompileInputs.cache
new file mode 100644
index 0000000..5c102b5
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.CoreCompileInputs.cache
@@ -0,0 +1 @@
+4d4771ea8f8d3cf3aeed797e4bf4b6713531421023b94150e57e7de4c9154a1e
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.FileListAbsolute.txt b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.FileListAbsolute.txt
new file mode 100644
index 0000000..ca31af3
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.FileListAbsolute.txt
@@ -0,0 +1,17 @@
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\DumpMap.exe
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\DumpMap.deps.json
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\DumpMap.runtimeconfig.json
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\DumpMap.dll
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\DumpMap.pdb
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.GeneratedMSBuildEditorConfig.editorconfig
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.AssemblyInfoInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.AssemblyInfo.cs
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.csproj.CoreCompileInputs.cache
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.dll
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\refint\DumpMap.dll
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.pdb
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.genruntimeconfig.cache
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\ref\DumpMap.dll
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\bin\Release\net8.0\System.Reflection.MetadataLoadContext.dll
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.csproj.AssemblyReference.cache
+c:\Users\andre\Desktop\SkopjeMods\_fix\DumpMap\obj\Release\net8.0\DumpMap.csproj.Up2Date
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.Up2Date b/_fix/DumpMap/obj/Release/net8.0/DumpMap.csproj.Up2Date
new file mode 100644
index 0000000..e69de29
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.dll b/_fix/DumpMap/obj/Release/net8.0/DumpMap.dll
new file mode 100644
index 0000000..1c55246
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/DumpMap.dll differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.genruntimeconfig.cache b/_fix/DumpMap/obj/Release/net8.0/DumpMap.genruntimeconfig.cache
new file mode 100644
index 0000000..5093584
--- /dev/null
+++ b/_fix/DumpMap/obj/Release/net8.0/DumpMap.genruntimeconfig.cache
@@ -0,0 +1 @@
+02342370508c732cc9f2de508ae0c01444ce9fa716191aa93a72245efb2157e2
diff --git a/_fix/DumpMap/obj/Release/net8.0/DumpMap.pdb b/_fix/DumpMap/obj/Release/net8.0/DumpMap.pdb
new file mode 100644
index 0000000..9ff80b2
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/DumpMap.pdb differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/apphost.exe b/_fix/DumpMap/obj/Release/net8.0/apphost.exe
new file mode 100644
index 0000000..f80c68b
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/apphost.exe differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/ref/DumpMap.dll b/_fix/DumpMap/obj/Release/net8.0/ref/DumpMap.dll
new file mode 100644
index 0000000..510fded
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/ref/DumpMap.dll differ
diff --git a/_fix/DumpMap/obj/Release/net8.0/refint/DumpMap.dll b/_fix/DumpMap/obj/Release/net8.0/refint/DumpMap.dll
new file mode 100644
index 0000000..510fded
Binary files /dev/null and b/_fix/DumpMap/obj/Release/net8.0/refint/DumpMap.dll differ
diff --git a/_fix/DumpMap/obj/project.assets.json b/_fix/DumpMap/obj/project.assets.json
new file mode 100644
index 0000000..b5edfc9
--- /dev/null
+++ b/_fix/DumpMap/obj/project.assets.json
@@ -0,0 +1,249 @@
+{
+ "version": 4,
+ "targets": {
+ "net8.0": {
+ "System.Collections.Immutable/8.0.0": {
+ "type": "package",
+ "compile": {
+ "lib/net8.0/System.Collections.Immutable.dll": {
+ "related": ".xml"
+ }
+ },
+ "runtime": {
+ "lib/net8.0/System.Collections.Immutable.dll": {
+ "related": ".xml"
+ }
+ },
+ "build": {
+ "buildTransitive/net6.0/_._": {}
+ }
+ },
+ "System.Reflection.Metadata/8.0.0": {
+ "type": "package",
+ "dependencies": {
+ "System.Collections.Immutable": "8.0.0"
+ },
+ "compile": {
+ "lib/net8.0/System.Reflection.Metadata.dll": {
+ "related": ".xml"
+ }
+ },
+ "runtime": {
+ "lib/net8.0/System.Reflection.Metadata.dll": {
+ "related": ".xml"
+ }
+ },
+ "build": {
+ "buildTransitive/net6.0/_._": {}
+ }
+ },
+ "System.Reflection.MetadataLoadContext/8.0.0": {
+ "type": "package",
+ "dependencies": {
+ "System.Collections.Immutable": "8.0.0",
+ "System.Reflection.Metadata": "8.0.0"
+ },
+ "compile": {
+ "lib/net8.0/System.Reflection.MetadataLoadContext.dll": {
+ "related": ".xml"
+ }
+ },
+ "runtime": {
+ "lib/net8.0/System.Reflection.MetadataLoadContext.dll": {
+ "related": ".xml"
+ }
+ },
+ "build": {
+ "buildTransitive/net6.0/_._": {}
+ }
+ }
+ }
+ },
+ "libraries": {
+ "System.Collections.Immutable/8.0.0": {
+ "sha512": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==",
+ "type": "package",
+ "path": "system.collections.immutable/8.0.0",
+ "files": [
+ ".nupkg.metadata",
+ ".signature.p7s",
+ "Icon.png",
+ "LICENSE.TXT",
+ "PACKAGE.md",
+ "THIRD-PARTY-NOTICES.TXT",
+ "buildTransitive/net461/System.Collections.Immutable.targets",
+ "buildTransitive/net462/_._",
+ "buildTransitive/net6.0/_._",
+ "buildTransitive/netcoreapp2.0/System.Collections.Immutable.targets",
+ "lib/net462/System.Collections.Immutable.dll",
+ "lib/net462/System.Collections.Immutable.xml",
+ "lib/net6.0/System.Collections.Immutable.dll",
+ "lib/net6.0/System.Collections.Immutable.xml",
+ "lib/net7.0/System.Collections.Immutable.dll",
+ "lib/net7.0/System.Collections.Immutable.xml",
+ "lib/net8.0/System.Collections.Immutable.dll",
+ "lib/net8.0/System.Collections.Immutable.xml",
+ "lib/netstandard2.0/System.Collections.Immutable.dll",
+ "lib/netstandard2.0/System.Collections.Immutable.xml",
+ "system.collections.immutable.8.0.0.nupkg.sha512",
+ "system.collections.immutable.nuspec",
+ "useSharedDesignerContext.txt"
+ ]
+ },
+ "System.Reflection.Metadata/8.0.0": {
+ "sha512": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
+ "type": "package",
+ "path": "system.reflection.metadata/8.0.0",
+ "files": [
+ ".nupkg.metadata",
+ ".signature.p7s",
+ "Icon.png",
+ "LICENSE.TXT",
+ "PACKAGE.md",
+ "THIRD-PARTY-NOTICES.TXT",
+ "buildTransitive/net461/System.Reflection.Metadata.targets",
+ "buildTransitive/net462/_._",
+ "buildTransitive/net6.0/_._",
+ "buildTransitive/netcoreapp2.0/System.Reflection.Metadata.targets",
+ "lib/net462/System.Reflection.Metadata.dll",
+ "lib/net462/System.Reflection.Metadata.xml",
+ "lib/net6.0/System.Reflection.Metadata.dll",
+ "lib/net6.0/System.Reflection.Metadata.xml",
+ "lib/net7.0/System.Reflection.Metadata.dll",
+ "lib/net7.0/System.Reflection.Metadata.xml",
+ "lib/net8.0/System.Reflection.Metadata.dll",
+ "lib/net8.0/System.Reflection.Metadata.xml",
+ "lib/netstandard2.0/System.Reflection.Metadata.dll",
+ "lib/netstandard2.0/System.Reflection.Metadata.xml",
+ "system.reflection.metadata.8.0.0.nupkg.sha512",
+ "system.reflection.metadata.nuspec",
+ "useSharedDesignerContext.txt"
+ ]
+ },
+ "System.Reflection.MetadataLoadContext/8.0.0": {
+ "sha512": "SZxrQ4sQYnIcdwiO3G/lHZopbPYQ2lW0ioT4JezgccWUrKaKbHLJbAGZaDfkYjWcta1pWssAo3MOXLsR0ie4tQ==",
+ "type": "package",
+ "path": "system.reflection.metadataloadcontext/8.0.0",
+ "files": [
+ ".nupkg.metadata",
+ ".signature.p7s",
+ "Icon.png",
+ "LICENSE.TXT",
+ "PACKAGE.md",
+ "THIRD-PARTY-NOTICES.TXT",
+ "buildTransitive/net461/System.Reflection.MetadataLoadContext.targets",
+ "buildTransitive/net462/_._",
+ "buildTransitive/net6.0/_._",
+ "buildTransitive/netcoreapp2.0/System.Reflection.MetadataLoadContext.targets",
+ "lib/net462/System.Reflection.MetadataLoadContext.dll",
+ "lib/net462/System.Reflection.MetadataLoadContext.xml",
+ "lib/net6.0/System.Reflection.MetadataLoadContext.dll",
+ "lib/net6.0/System.Reflection.MetadataLoadContext.xml",
+ "lib/net7.0/System.Reflection.MetadataLoadContext.dll",
+ "lib/net7.0/System.Reflection.MetadataLoadContext.xml",
+ "lib/net8.0/System.Reflection.MetadataLoadContext.dll",
+ "lib/net8.0/System.Reflection.MetadataLoadContext.xml",
+ "lib/netstandard2.0/System.Reflection.MetadataLoadContext.dll",
+ "lib/netstandard2.0/System.Reflection.MetadataLoadContext.xml",
+ "system.reflection.metadataloadcontext.8.0.0.nupkg.sha512",
+ "system.reflection.metadataloadcontext.nuspec",
+ "useSharedDesignerContext.txt"
+ ]
+ }
+ },
+ "projectFileDependencyGroups": {
+ "net8.0": [
+ "System.Reflection.MetadataLoadContext >= 8.0.0"
+ ]
+ },
+ "packageFolders": {
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
+ },
+ "project": {
+ "version": "1.0.0",
+ "restore": {
+ "projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj",
+ "projectName": "DumpMap",
+ "projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj",
+ "packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
+ "outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\obj\\",
+ "projectStyle": "PackageReference",
+ "configFilePaths": [
+ "C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
+ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
+ ],
+ "originalTargetFrameworks": [
+ "net8.0"
+ ],
+ "sources": {
+ "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
+ "https://api.nuget.org/v3/index.json": {}
+ },
+ "frameworks": {
+ "net8.0": {
+ "framework": "net8.0",
+ "targetAlias": "net8.0",
+ "projectReferences": {}
+ }
+ },
+ "warningProperties": {
+ "warnAsError": [
+ "NU1605"
+ ]
+ },
+ "restoreAuditProperties": {
+ "enableAudit": "true",
+ "auditLevel": "low",
+ "auditMode": "direct"
+ },
+ "SdkAnalysisLevel": "10.0.300"
+ },
+ "frameworks": {
+ "net8.0": {
+ "framework": "net8.0",
+ "targetAlias": "net8.0",
+ "dependencies": {
+ "System.Reflection.MetadataLoadContext": {
+ "target": "Package",
+ "version": "[8.0.0, )"
+ }
+ },
+ "imports": [
+ "net461",
+ "net462",
+ "net47",
+ "net471",
+ "net472",
+ "net48",
+ "net481"
+ ],
+ "assetTargetFallback": true,
+ "warn": true,
+ "downloadDependencies": [
+ {
+ "name": "Microsoft.AspNetCore.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Host.win-x64",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.NETCore.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ },
+ {
+ "name": "Microsoft.WindowsDesktop.App.Ref",
+ "version": "[8.0.28, 8.0.28]"
+ }
+ ],
+ "frameworkReferences": {
+ "Microsoft.NETCore.App": {
+ "privateAssets": "all"
+ }
+ },
+ "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/_fix/DumpMap/obj/project.nuget.cache b/_fix/DumpMap/obj/project.nuget.cache
new file mode 100644
index 0000000..4dfd0f6
--- /dev/null
+++ b/_fix/DumpMap/obj/project.nuget.cache
@@ -0,0 +1,16 @@
+{
+ "version": 2,
+ "dgSpecHash": "e2CJM3ZSKiM=",
+ "success": true,
+ "projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\_fix\\DumpMap\\DumpMap.csproj",
+ "expectedPackageFiles": [
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\system.collections.immutable\\8.0.0\\system.collections.immutable.8.0.0.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\system.reflection.metadata\\8.0.0\\system.reflection.metadata.8.0.0.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\system.reflection.metadataloadcontext\\8.0.0\\system.reflection.metadataloadcontext.8.0.0.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\8.0.28\\microsoft.netcore.app.ref.8.0.28.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\8.0.28\\microsoft.windowsdesktop.app.ref.8.0.28.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\8.0.28\\microsoft.aspnetcore.app.ref.8.0.28.nupkg.sha512",
+ "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.host.win-x64\\8.0.28\\microsoft.netcore.app.host.win-x64.8.0.28.nupkg.sha512"
+ ],
+ "logs": []
+}
\ No newline at end of file
diff --git a/_fix/DumpUi/Program.cs b/_fix/DumpUi/Program.cs
index 8d791ad..bbacb0f 100644
--- a/_fix/DumpUi/Program.cs
+++ b/_fix/DumpUi/Program.cs
@@ -31,10 +31,21 @@ void Dump(string file, string typeName)
static string EnumNames(Type t) => string.Join(",", t.GetEnumNames());
var skopje = mlc.LoadFromAssemblyPath(Path.Combine(interop, "SkopjeAssembly.dll"));
+string[] names = { "EnemyBlockHelper", "NotDamageable", "IDamageable" };
foreach (var type in skopje.GetTypes())
{
- if (type.Name != "CompassViewController" && type.Name != "CompassView") continue;
- Console.WriteLine("TYPE " + type.FullName + " : " + type.BaseType?.FullName);
- foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
+ if (!names.Contains(type.Name)) continue;
+ Console.WriteLine("TYPE " + type.Name);
+ if (type.IsEnum)
+ {
+ Console.WriteLine(" " + string.Join(", ", type.GetEnumNames()));
+ continue;
+ }
+ foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
Console.WriteLine(" P " + p.PropertyType.Name + " " + p.Name);
+ foreach (var m in type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
+ {
+ if (m.IsSpecialName) continue;
+ Console.WriteLine(" M " + m.ReturnType.Name + " " + m.Name);
+ }
}
diff --git a/_fix/DumpUi/bin/Release/net8.0/DumpUi.dll b/_fix/DumpUi/bin/Release/net8.0/DumpUi.dll
index 81d65b5..d337b91 100644
Binary files a/_fix/DumpUi/bin/Release/net8.0/DumpUi.dll and b/_fix/DumpUi/bin/Release/net8.0/DumpUi.dll differ
diff --git a/_fix/DumpUi/bin/Release/net8.0/DumpUi.exe b/_fix/DumpUi/bin/Release/net8.0/DumpUi.exe
index 773a7ae..44e63c5 100644
Binary files a/_fix/DumpUi/bin/Release/net8.0/DumpUi.exe and b/_fix/DumpUi/bin/Release/net8.0/DumpUi.exe differ
diff --git a/_fix/DumpUi/bin/Release/net8.0/DumpUi.pdb b/_fix/DumpUi/bin/Release/net8.0/DumpUi.pdb
index d3722dd..e6ab26b 100644
Binary files a/_fix/DumpUi/bin/Release/net8.0/DumpUi.pdb and b/_fix/DumpUi/bin/Release/net8.0/DumpUi.pdb differ
diff --git a/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfo.cs b/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfo.cs
index a6eab44..3e8dda8 100644
--- a/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfo.cs
+++ b/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfo.cs
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("DumpUi")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
[assembly: System.Reflection.AssemblyProductAttribute("DumpUi")]
[assembly: System.Reflection.AssemblyTitleAttribute("DumpUi")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
diff --git a/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfoInputs.cache b/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfoInputs.cache
index 933b0a3..33cc44e 100644
--- a/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfoInputs.cache
+++ b/_fix/DumpUi/obj/Release/net8.0/DumpUi.AssemblyInfoInputs.cache
@@ -1 +1 @@
-1bd7648d90d9d85fede4b666912953baba06df98a1e74e7105064e3c5fcbaae0
+f37b9f7c232818ed923a9fb9b7b83a33eb2edde4f75ff4178f075a26424253c5
diff --git a/_fix/DumpUi/obj/Release/net8.0/DumpUi.dll b/_fix/DumpUi/obj/Release/net8.0/DumpUi.dll
index 81d65b5..d337b91 100644
Binary files a/_fix/DumpUi/obj/Release/net8.0/DumpUi.dll and b/_fix/DumpUi/obj/Release/net8.0/DumpUi.dll differ
diff --git a/_fix/DumpUi/obj/Release/net8.0/DumpUi.pdb b/_fix/DumpUi/obj/Release/net8.0/DumpUi.pdb
index d3722dd..e6ab26b 100644
Binary files a/_fix/DumpUi/obj/Release/net8.0/DumpUi.pdb and b/_fix/DumpUi/obj/Release/net8.0/DumpUi.pdb differ
diff --git a/_fix/DumpUi/obj/Release/net8.0/apphost.exe b/_fix/DumpUi/obj/Release/net8.0/apphost.exe
index 773a7ae..44e63c5 100644
Binary files a/_fix/DumpUi/obj/Release/net8.0/apphost.exe and b/_fix/DumpUi/obj/Release/net8.0/apphost.exe differ
diff --git a/_fix/DumpUi/obj/Release/net8.0/ref/DumpUi.dll b/_fix/DumpUi/obj/Release/net8.0/ref/DumpUi.dll
index 2c5fa42..8cf3719 100644
Binary files a/_fix/DumpUi/obj/Release/net8.0/ref/DumpUi.dll and b/_fix/DumpUi/obj/Release/net8.0/ref/DumpUi.dll differ
diff --git a/_fix/DumpUi/obj/Release/net8.0/refint/DumpUi.dll b/_fix/DumpUi/obj/Release/net8.0/refint/DumpUi.dll
index 2c5fa42..8cf3719 100644
Binary files a/_fix/DumpUi/obj/Release/net8.0/refint/DumpUi.dll and b/_fix/DumpUi/obj/Release/net8.0/refint/DumpUi.dll differ