Fixes and improvements, no more FindObjectsOfType
No files matched your search
@@ -0,0 +1,469 @@
|
|||||||
|
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 Il2CppInterop.Runtime;
|
||||||
|
using Skopje.Modding;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Skopje.CustomDomSkins
|
||||||
|
{
|
||||||
|
[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
|
||||||
|
public class CustomDomSkinsPlugin : BasePlugin
|
||||||
|
{
|
||||||
|
public const string PluginGuid = "com.skopje.customdomskins";
|
||||||
|
public const string PluginName = "Skopje Custom DOM Skins";
|
||||||
|
public const string PluginVersion = "1.0.0";
|
||||||
|
|
||||||
|
internal static ManualLogSource LogSource;
|
||||||
|
internal static ConfigEntry<bool> Enabled;
|
||||||
|
|
||||||
|
static Il2CppSystem.Action<SkopjeGameMode> _onGameModeStarted;
|
||||||
|
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
LogSource = Log;
|
||||||
|
Enabled = Config.Bind("Skin", "Enabled", true, "Replace the DOM bus exterior with the PNGs in the DomSkins folder next to this plugin.");
|
||||||
|
|
||||||
|
_onGameModeStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjeGameMode>>(new Action<SkopjeGameMode>(OnGameModeStarted));
|
||||||
|
ModAPI.add_OnGameModeStartedClient(_onGameModeStarted);
|
||||||
|
|
||||||
|
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit DomSkins/singleplayer.png and DomSkins/multiplayer.png. Those files are the dumped outer-shell textures. Interior, wheels, and lights are left alone.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Unload()
|
||||||
|
{
|
||||||
|
if (_onGameModeStarted != null)
|
||||||
|
ModAPI.remove_OnGameModeStartedClient(_onGameModeStarted);
|
||||||
|
DomSkinReplacer.Restore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnGameModeStarted(SkopjeGameMode gameMode)
|
||||||
|
{
|
||||||
|
if (gameMode == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SkopjeVehicle bus = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bus = gameMode.Bus;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogSource.LogWarning("Could not read the DOM from the game mode: " + ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bus == null)
|
||||||
|
{
|
||||||
|
LogSource.LogInfo("Game mode started without a DOM, so the exterior skin was not applied.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomSkinReplacer.Apply(bus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DomSkinReplacer
|
||||||
|
{
|
||||||
|
sealed class Shell
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public string DumpMaterial;
|
||||||
|
public string SourceTexture;
|
||||||
|
public string MaterialName;
|
||||||
|
public Texture2D Texture;
|
||||||
|
public DateTime WriteTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The two buses do not share a UV layout. Each file is that bus's outer shell only.
|
||||||
|
static readonly Shell[] Shells =
|
||||||
|
{
|
||||||
|
new Shell
|
||||||
|
{
|
||||||
|
FileName = "singleplayer.png",
|
||||||
|
DumpMaterial = "MAT_BusOutside",
|
||||||
|
SourceTexture = "DIF_DOM_OuterShell",
|
||||||
|
MaterialName = "MAT_BusOutside",
|
||||||
|
},
|
||||||
|
new Shell
|
||||||
|
{
|
||||||
|
FileName = "multiplayer.png",
|
||||||
|
DumpMaterial = "MAT_DOMmp_Outside",
|
||||||
|
SourceTexture = "DIF_DOMmp_Outside",
|
||||||
|
MaterialName = "MAT_DOMmp_Outside",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static readonly Dictionary<int, Texture> _originals = new Dictionary<int, Texture>();
|
||||||
|
static readonly Dictionary<int, Material> _materials = new Dictionary<int, Material>();
|
||||||
|
static readonly Dictionary<int, Renderer> _renderers = new Dictionary<int, Renderer>();
|
||||||
|
static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock();
|
||||||
|
static float _nextFileCheck;
|
||||||
|
static bool _loggedApply;
|
||||||
|
static bool _applied;
|
||||||
|
|
||||||
|
public static void Apply(SkopjeVehicle bus)
|
||||||
|
{
|
||||||
|
if (!CustomDomSkinsPlugin.Enabled.Value)
|
||||||
|
{
|
||||||
|
if (_applied)
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
ClearRendererOverrides();
|
||||||
|
_applied = false;
|
||||||
|
_loggedApply = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bus == null || !EnsureTextures(bus))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int applied = ApplyUnder(bus.transform);
|
||||||
|
if (applied > 0)
|
||||||
|
{
|
||||||
|
_applied = true;
|
||||||
|
if (!_loggedApply)
|
||||||
|
{
|
||||||
|
_loggedApply = true;
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogInfo($"Applied {applied} DOM exterior material{(applied == 1 ? "" : "s")} from DomSkins.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Restore()
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
ClearRendererOverrides();
|
||||||
|
_applied = false;
|
||||||
|
_originals.Clear();
|
||||||
|
_materials.Clear();
|
||||||
|
_renderers.Clear();
|
||||||
|
for (int i = 0; i < Shells.Length; i++)
|
||||||
|
{
|
||||||
|
if (Shells[i].Texture == null)
|
||||||
|
continue;
|
||||||
|
UnityEngine.Object.Destroy(Shells[i].Texture);
|
||||||
|
Shells[i].Texture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RevertMaterials()
|
||||||
|
{
|
||||||
|
foreach (var pair in _materials)
|
||||||
|
{
|
||||||
|
Material material = pair.Value;
|
||||||
|
if (material != null && _originals.TryGetValue(pair.Key, out Texture original))
|
||||||
|
material.SetTexture("_MainTex", original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearRendererOverrides()
|
||||||
|
{
|
||||||
|
foreach (var pair in _renderers)
|
||||||
|
{
|
||||||
|
Renderer renderer = pair.Value;
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || !_originals.TryGetValue(material.GetInstanceID(), out Texture original))
|
||||||
|
continue;
|
||||||
|
renderer.GetPropertyBlock(_block, slot);
|
||||||
|
_block.SetTexture("_MainTex", original);
|
||||||
|
renderer.SetPropertyBlock(_block, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyUnder(Transform root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
Renderer[] found = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < found.Length; i++)
|
||||||
|
applied += ApplyRenderer(found[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyRenderer(Renderer renderer)
|
||||||
|
{
|
||||||
|
if (renderer == null)
|
||||||
|
return 0;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
{
|
||||||
|
Shell shell = Resolve(materials[i]);
|
||||||
|
if (shell == null)
|
||||||
|
continue;
|
||||||
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
_renderers[renderer.GetInstanceID()] = renderer;
|
||||||
|
renderer.GetPropertyBlock(_block, i);
|
||||||
|
_block.SetTexture("_MainTex", shell.Texture);
|
||||||
|
renderer.SetPropertyBlock(_block, i);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyMaterial(Material material)
|
||||||
|
{
|
||||||
|
Shell shell = Resolve(material);
|
||||||
|
if (shell == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int id = material.GetInstanceID();
|
||||||
|
if (!_originals.ContainsKey(id))
|
||||||
|
{
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
_originals[id] = current;
|
||||||
|
_materials[id] = material;
|
||||||
|
CopySampling(shell.Texture, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material.GetTexture("_MainTex") == shell.Texture)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
material.SetTexture("_MainTex", shell.Texture);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Shell Resolve(Material material)
|
||||||
|
{
|
||||||
|
if (material == null || !material.HasProperty("_MainTex"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
string materialName = BaseName(material.name);
|
||||||
|
for (int i = 0; i < Shells.Length; i++)
|
||||||
|
{
|
||||||
|
Shell shell = Shells[i];
|
||||||
|
if (shell.Texture == null)
|
||||||
|
continue;
|
||||||
|
if (materialName == shell.MaterialName)
|
||||||
|
return shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (current == null)
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < Shells.Length; i++)
|
||||||
|
{
|
||||||
|
Shell shell = Shells[i];
|
||||||
|
if (shell.Texture == null)
|
||||||
|
continue;
|
||||||
|
if (current == shell.Texture)
|
||||||
|
return shell;
|
||||||
|
if (BaseName(current.name) == shell.SourceTexture)
|
||||||
|
return shell;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string BaseName(string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return name;
|
||||||
|
int cut = name.IndexOf(" (", StringComparison.Ordinal);
|
||||||
|
if (cut > 0)
|
||||||
|
return name.Substring(0, cut);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 bool EnsureTextures(Component source)
|
||||||
|
{
|
||||||
|
if (AnyLoaded() && Time.unscaledTime < _nextFileCheck)
|
||||||
|
return true;
|
||||||
|
_nextFileCheck = Time.unscaledTime + 1f;
|
||||||
|
|
||||||
|
string directory = SkinsDirectory();
|
||||||
|
int ready = 0;
|
||||||
|
for (int i = 0; i < Shells.Length; i++)
|
||||||
|
{
|
||||||
|
if (EnsureShell(Shells[i], directory, source))
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ready == 0)
|
||||||
|
{
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogWarning("No DOM shell PNGs are ready in " + directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AnyLoaded()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Shells.Length; i++)
|
||||||
|
{
|
||||||
|
if (Shells[i].Texture != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool EnsureShell(Shell shell, string directory, Component source)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(directory, shell.FileName);
|
||||||
|
if (!File.Exists(path) && !TryDumpSample(shell, path, source))
|
||||||
|
return shell.Texture != null;
|
||||||
|
|
||||||
|
DateTime writeTime;
|
||||||
|
try { writeTime = File.GetLastWriteTimeUtc(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogError("Could not read " + shell.FileName + ": " + ex.Message);
|
||||||
|
return shell.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shell.Texture != null && shell.WriteTimeUtc == writeTime)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
byte[] bytes;
|
||||||
|
try { bytes = File.ReadAllBytes(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogError("Could not read " + shell.FileName + ": " + ex.Message);
|
||||||
|
return shell.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var texture = new Texture2D(2, 2, TextureFormat.RGBA32, true);
|
||||||
|
texture.name = "SkopjeCustomDomSkin_" + Path.GetFileNameWithoutExtension(shell.FileName);
|
||||||
|
texture.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
if (!ImageConversion.LoadImage(texture, bytes))
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(texture);
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogError(shell.FileName + " is not a readable image: " + path);
|
||||||
|
return shell.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shell.Texture != null)
|
||||||
|
UnityEngine.Object.Destroy(shell.Texture);
|
||||||
|
shell.Texture = texture;
|
||||||
|
shell.WriteTimeUtc = writeTime;
|
||||||
|
_loggedApply = false;
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogInfo($"Loaded {shell.FileName} ({texture.width}x{texture.height})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Texture FindSourceTexture(Component root, string materialName, Texture replacement)
|
||||||
|
{
|
||||||
|
if (root == null || string.IsNullOrEmpty(materialName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Renderer[] renderers = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
{
|
||||||
|
Renderer renderer = renderers[i];
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || BaseName(material.name) != materialName || !material.HasProperty("_MainTex"))
|
||||||
|
continue;
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (_originals.TryGetValue(material.GetInstanceID(), out Texture original) && original != null)
|
||||||
|
current = original;
|
||||||
|
if (current == null || current == replacement)
|
||||||
|
continue;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryDumpSample(Shell shell, string path, Component root)
|
||||||
|
{
|
||||||
|
Texture source = FindSourceTexture(root, shell.DumpMaterial, shell.Texture);
|
||||||
|
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);
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogInfo("Dumped " + shell.FileName + " from the DOM outer shell into DomSkins.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomDomSkinsPlugin.LogSource.LogError("Could not write " + shell.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, "DomSkins");
|
||||||
|
return Path.Combine(Paths.PluginPath, "DomSkins");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AssemblyName>Skopje.CustomDomSkins</AssemblyName>
|
||||||
|
<RootNamespace>Skopje.CustomDomSkins</RootNamespace>
|
||||||
|
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
|
||||||
|
<GameDir Condition="'$(GameDir)' == ''">D:\SteamLibrary\steamapps\common\Skopje83</GameDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="BepInEx.Core">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Core.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="BepInEx.Unity.IL2CPP">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Il2CppInterop.Runtime">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Il2Cppmscorlib">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SkopjeAssembly">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\SkopjeAssembly.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FishNet.Runtime">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\FishNet.Runtime.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Archon.SwissArmyLib">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ImageConversionModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.ImageConversionModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="DomSkins\*.png" CopyToOutputDirectory="PreserveNewest" Link="DomSkins\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 226 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v6.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v6.0": {
|
||||||
|
"Skopje.CustomDomSkins/1.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"Skopje.CustomDomSkins.dll": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Skopje.CustomDomSkins/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 226 KiB |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v6.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v6.0": {
|
||||||
|
"Skopje.CustomDomSkins/1.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"Skopje.CustomDomSkins.dll": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Skopje.CustomDomSkins/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.csproj",
|
||||||
|
"projectName": "Skopje.CustomDomSkins",
|
||||||
|
"projectPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||||
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget</NuGetPackageRoot>
|
||||||
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget</NuGetPackageFolders>
|
||||||
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<SourceRoot Include="C:\Users\andre\AppData\Local\Temp\cursor-sandbox-cache\a0404811fc6e51c4c5761fd11591789f\nuget\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a740fec7c87ba4b24aefb7faf991fc5b69c71bd4")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
35970ee9e256e84e13ebc939b0a4d04c7c0da5edd0eaacbbff9656666dfabdff
|
||||||
@@ -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.CustomDomSkins
|
||||||
|
build_property.ProjectDir = C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
b22e37a4f0cb4e115638d21fe4e91ddc55f8f775e49d88a66f2779f224ecccc3
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Debug\net6.0\DomSkins\multiplayer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Debug\net6.0\DomSkins\singleplayer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Debug\net6.0\Skopje.CustomDomSkins.deps.json
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Debug\net6.0\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Debug\net6.0\Skopje.CustomDomSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\CustomDomSkinsPlugin.csproj.AssemblyReference.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\CustomDomSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\CustomDomSkinsPlugin.AssemblyInfoInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\CustomDomSkinsPlugin.AssemblyInfo.cs
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\CustomDomSkinsPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\refint\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\Skopje.CustomDomSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Debug\net6.0\ref\Skopje.CustomDomSkins.dll
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a740fec7c87ba4b24aefb7faf991fc5b69c71bd4")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomDomSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
311dc5b6a33b4aaf936db0ee34a8ca7409721fb87735f5b00e29f535d2b59505
|
||||||
@@ -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.CustomDomSkins
|
||||||
|
build_property.ProjectDir = C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ab411ce6cfd92c47406b1328966c523f48609c1c8b2ddc42fdefddba568a0a2e
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Release\net6.0\DomSkins\multiplayer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Release\net6.0\DomSkins\singleplayer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Release\net6.0\Skopje.CustomDomSkins.deps.json
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Release\net6.0\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\bin\Release\net6.0\Skopje.CustomDomSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\CustomDomSkinsPlugin.csproj.AssemblyReference.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\CustomDomSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\CustomDomSkinsPlugin.AssemblyInfoInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\CustomDomSkinsPlugin.AssemblyInfo.cs
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\CustomDomSkinsPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\refint\Skopje.CustomDomSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\Skopje.CustomDomSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomDomSkins\obj\Release\net6.0\ref\Skopje.CustomDomSkins.dll
|
||||||
@@ -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\\CustomDomSkins\\CustomDomSkinsPlugin.csproj",
|
||||||
|
"projectName": "Skopje.CustomDomSkins",
|
||||||
|
"projectPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "WumoaMRMnNg=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomDomSkins\\CustomDomSkinsPlugin.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": []
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ using Il2CppInterop.Runtime;
|
|||||||
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||||
using Skopje.Modding;
|
using Skopje.Modding;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
namespace Skopje.CustomPlayerSkins
|
namespace Skopje.CustomPlayerSkins
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
{
|
{
|
||||||
public const string PluginGuid = "com.skopje.customplayerskins";
|
public const string PluginGuid = "com.skopje.customplayerskins";
|
||||||
public const string PluginName = "Skopje Custom Player Skins";
|
public const string PluginName = "Skopje Custom Player Skins";
|
||||||
public const string PluginVersion = "1.0.0";
|
public const string PluginVersion = "1.3.0";
|
||||||
|
|
||||||
internal static ManualLogSource LogSource;
|
internal static ManualLogSource LogSource;
|
||||||
internal static ConfigEntry<bool> Enabled;
|
internal static ConfigEntry<bool> Enabled;
|
||||||
@@ -33,6 +34,13 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
Enabled = Config.Bind("Skin", "Enabled", true, "Replace each player outfit with its own PNG in the CustomSkins folder next to this plugin.");
|
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);
|
_harmony = new Harmony(PluginGuid);
|
||||||
|
var setTexture = AccessTools.Method(typeof(Material), "SetTexture", new[] { typeof(string), typeof(Texture) });
|
||||||
|
if (setTexture != null)
|
||||||
|
_harmony.Patch(setTexture, prefix: new HarmonyMethod(typeof(SkinPatches), nameof(SkinPatches.BeforeSetTexture)));
|
||||||
|
var setTextureId = AccessTools.Method(typeof(Material), "SetTexture", new[] { typeof(int), typeof(Texture) });
|
||||||
|
if (setTextureId != null)
|
||||||
|
_harmony.Patch(setTextureId, prefix: new HarmonyMethod(typeof(SkinPatches), nameof(SkinPatches.BeforeSetTextureId)));
|
||||||
|
|
||||||
var refresh = AccessTools.Method(typeof(SkopjePlayer), "RefreshActiveModels");
|
var refresh = AccessTools.Method(typeof(SkopjePlayer), "RefreshActiveModels");
|
||||||
if (refresh == null)
|
if (refresh == null)
|
||||||
LogSource.LogWarning("SkopjePlayer.RefreshActiveModels was not found. The skins are applied when a player spawns.");
|
LogSource.LogWarning("SkopjePlayer.RefreshActiveModels was not found. The skins are applied when a player spawns.");
|
||||||
@@ -42,13 +50,15 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
_onPlayerStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(SkinReplacer.ApplyPlayer));
|
_onPlayerStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(SkinReplacer.ApplyPlayer));
|
||||||
ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
|
ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
|
||||||
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit the PNGs in CustomSkins: singleplayer, ranger, medic, engineer, and fighter (the Warrior class).");
|
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit the PNGs in CustomSkins: singleplayer, ranger, medic, engineer, fighter (Warrior), prp, and hands.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Unload()
|
public override bool Unload()
|
||||||
{
|
{
|
||||||
if (_harmony != null)
|
if (_harmony != null)
|
||||||
_harmony.UnpatchSelf();
|
_harmony.UnpatchSelf();
|
||||||
|
if (_onPlayerStarted != null)
|
||||||
|
ModAPI.remove_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
SkinReplacer.Restore();
|
SkinReplacer.Restore();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -60,6 +70,18 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
{
|
{
|
||||||
SkinReplacer.ApplyPlayer(__instance);
|
SkinReplacer.ApplyPlayer(__instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void BeforeSetTexture(string name, ref Texture value)
|
||||||
|
{
|
||||||
|
if (name == "_MainTex")
|
||||||
|
SkinReplacer.Redirect(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeforeSetTextureId(int nameID, ref Texture value)
|
||||||
|
{
|
||||||
|
if (nameID == Shader.PropertyToID("_MainTex"))
|
||||||
|
SkinReplacer.Redirect(ref value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class SkinReplacer
|
static class SkinReplacer
|
||||||
@@ -68,6 +90,7 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
{
|
{
|
||||||
public string FileName;
|
public string FileName;
|
||||||
public string DumpMaterial;
|
public string DumpMaterial;
|
||||||
|
public string SourceTexture;
|
||||||
public string[] Materials;
|
public string[] Materials;
|
||||||
public Texture2D Texture;
|
public Texture2D Texture;
|
||||||
public DateTime WriteTimeUtc;
|
public DateTime WriteTimeUtc;
|
||||||
@@ -80,72 +103,84 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
{
|
{
|
||||||
FileName = "singleplayer.png",
|
FileName = "singleplayer.png",
|
||||||
DumpMaterial = "MAT_Pioneer_Default_Clothes",
|
DumpMaterial = "MAT_Pioneer_Default_Clothes",
|
||||||
|
SourceTexture = "DIF_Pioneer_Clothes_Default",
|
||||||
Materials = new[] { "MAT_Pioneer_Default_Clothes", "MAT_Pioneer_Default_Clothes_FP" },
|
Materials = new[] { "MAT_Pioneer_Default_Clothes", "MAT_Pioneer_Default_Clothes_FP" },
|
||||||
},
|
},
|
||||||
new Outfit
|
new Outfit
|
||||||
{
|
{
|
||||||
FileName = "ranger.png",
|
FileName = "ranger.png",
|
||||||
DumpMaterial = "MAT_Pioneer_Ranger_Clothes",
|
DumpMaterial = "MAT_Pioneer_Ranger_Clothes",
|
||||||
|
SourceTexture = "DIF_Pioneer_Clothes_Ranger",
|
||||||
Materials = new[] { "MAT_Pioneer_Ranger_Clothes", "MAT_Pioneer_Ranger_Clothes_FP" },
|
Materials = new[] { "MAT_Pioneer_Ranger_Clothes", "MAT_Pioneer_Ranger_Clothes_FP" },
|
||||||
},
|
},
|
||||||
new Outfit
|
new Outfit
|
||||||
{
|
{
|
||||||
FileName = "medic.png",
|
FileName = "medic.png",
|
||||||
DumpMaterial = "MAT_Pioneer_Medic_Clothes",
|
DumpMaterial = "MAT_Pioneer_Medic_Clothes",
|
||||||
|
SourceTexture = "DIF_Pioneer_Clothes_Medic",
|
||||||
Materials = new[] { "MAT_Pioneer_Medic_Clothes", "MAT_Pioneer_Medic_Clothes_FP" },
|
Materials = new[] { "MAT_Pioneer_Medic_Clothes", "MAT_Pioneer_Medic_Clothes_FP" },
|
||||||
},
|
},
|
||||||
new Outfit
|
new Outfit
|
||||||
{
|
{
|
||||||
FileName = "engineer.png",
|
FileName = "engineer.png",
|
||||||
DumpMaterial = "MAT_Pioneer_Engi_Clothes",
|
DumpMaterial = "MAT_Pioneer_Engi_Clothes",
|
||||||
|
SourceTexture = "DIF_Pioneer_Clothes_Engi",
|
||||||
Materials = new[] { "MAT_Pioneer_Engi_Clothes", "MAT_Pioneer_Engi_Clothes_FP" },
|
Materials = new[] { "MAT_Pioneer_Engi_Clothes", "MAT_Pioneer_Engi_Clothes_FP" },
|
||||||
},
|
},
|
||||||
new Outfit
|
new Outfit
|
||||||
{
|
{
|
||||||
FileName = "fighter.png",
|
FileName = "fighter.png",
|
||||||
DumpMaterial = "MAT_Pioneer_Fighter_Clothes",
|
DumpMaterial = "MAT_Pioneer_Fighter_Clothes",
|
||||||
|
SourceTexture = "DIF_Pioneer_Clothes_Fighter",
|
||||||
Materials = new[] { "MAT_Pioneer_Fighter_Clothes", "MAT_Pioneer_Fighter_Clothes_FP" },
|
Materials = new[] { "MAT_Pioneer_Fighter_Clothes", "MAT_Pioneer_Fighter_Clothes_FP" },
|
||||||
},
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "prp.png",
|
||||||
|
DumpMaterial = "MAT_PRP",
|
||||||
|
SourceTexture = "DIF_PRP",
|
||||||
|
Materials = new[] { "MAT_PRP", "MAT_PRP_FP" },
|
||||||
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "hands.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Naked",
|
||||||
|
SourceTexture = "DIF_FPSHands_Naked",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Naked" },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static readonly Dictionary<int, Texture> _originals = new Dictionary<int, Texture>();
|
static readonly Dictionary<int, Texture> _originals = new Dictionary<int, Texture>();
|
||||||
|
static readonly Dictionary<int, Material> _materials = new Dictionary<int, Material>();
|
||||||
|
static readonly Dictionary<int, Renderer> _renderers = new Dictionary<int, Renderer>();
|
||||||
|
static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock();
|
||||||
static float _nextFileCheck;
|
static float _nextFileCheck;
|
||||||
static bool _loggedMiss;
|
static bool _loggedApply;
|
||||||
|
|
||||||
public static void ApplyPlayer(SkopjePlayer player)
|
public static void ApplyPlayer(SkopjePlayer player)
|
||||||
{
|
{
|
||||||
if (!CustomPlayerSkinsPlugin.Enabled.Value)
|
if (!CustomPlayerSkinsPlugin.Enabled.Value)
|
||||||
{
|
{
|
||||||
RevertMaterials();
|
RevertLive();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!EnsureTextures())
|
if (player == null || !EnsureTextures(player))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int applied = ApplyLoadedMaterials();
|
int applied = ApplyPlayerRenderers(player);
|
||||||
if (player != null)
|
if (applied > 0 && !_loggedApply)
|
||||||
{
|
{
|
||||||
applied += ApplySkins(player.PlayerSkins);
|
_loggedApply = true;
|
||||||
applied += ApplyRenderer(player.FirstPersonRenderer);
|
CustomPlayerSkinsPlugin.LogSource.LogInfo($"Applied {applied} player material{(applied == 1 ? "" : "s")} from CustomSkins.");
|
||||||
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()
|
public static void Restore()
|
||||||
{
|
{
|
||||||
RevertMaterials();
|
RevertLive();
|
||||||
_originals.Clear();
|
_originals.Clear();
|
||||||
|
_materials.Clear();
|
||||||
|
_renderers.Clear();
|
||||||
for (int i = 0; i < Outfits.Length; i++)
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
{
|
{
|
||||||
if (Outfits[i].Texture == null)
|
if (Outfits[i].Texture == null)
|
||||||
@@ -155,23 +190,42 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void RevertLive()
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
ClearRendererOverrides();
|
||||||
|
}
|
||||||
|
|
||||||
static void RevertMaterials()
|
static void RevertMaterials()
|
||||||
{
|
{
|
||||||
foreach (var pair in _originals)
|
foreach (var pair in _materials)
|
||||||
{
|
{
|
||||||
var material = FindMaterial(pair.Key);
|
Material material = pair.Value;
|
||||||
if (material != null)
|
if (material != null && _originals.TryGetValue(pair.Key, out Texture original))
|
||||||
material.SetTexture("_MainTex", pair.Value);
|
material.SetTexture("_MainTex", original);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ApplyLoadedMaterials()
|
static void ClearRendererOverrides()
|
||||||
{
|
{
|
||||||
int applied = 0;
|
foreach (var pair in _renderers)
|
||||||
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
{
|
||||||
for (int i = 0; i < materials.Length; i++)
|
Renderer renderer = pair.Value;
|
||||||
applied += ApplyMaterial(materials[i]);
|
if (renderer == null)
|
||||||
return applied;
|
continue;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || !_originals.TryGetValue(material.GetInstanceID(), out Texture original))
|
||||||
|
continue;
|
||||||
|
renderer.GetPropertyBlock(_block, slot);
|
||||||
|
_block.SetTexture("_MainTex", original);
|
||||||
|
renderer.SetPropertyBlock(_block, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ApplySkins(Il2CppReferenceArray<PlayerSkin> skins)
|
static int ApplySkins(Il2CppReferenceArray<PlayerSkin> skins)
|
||||||
@@ -190,16 +244,103 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ApplyPlayerRenderers(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (player == null)
|
||||||
|
return 0;
|
||||||
|
int applied = ApplySkins(player.PlayerSkins);
|
||||||
|
applied += ApplyRenderer(player.FirstPersonRenderer);
|
||||||
|
applied += ApplyRenderer(player.ThirdPersonRenderer);
|
||||||
|
applied += ApplyRenderer(player.ThirdPersonBackpackRenderer);
|
||||||
|
applied += ApplyOn(player.ThirdPerson);
|
||||||
|
applied += ApplyOn(player.FirstPerson);
|
||||||
|
if (player.SkopjeCharacterController != null)
|
||||||
|
applied += ApplyOn(player.SkopjeCharacterController.MeshRoot);
|
||||||
|
if (player.UtilityController != null)
|
||||||
|
applied += ApplyOn(player.UtilityController.FPSHolder);
|
||||||
|
if (player.WeaponsController != null)
|
||||||
|
applied += ApplyWeapons(player.WeaponsController);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyWeapons(WeaponsController weapons)
|
||||||
|
{
|
||||||
|
if (weapons == null)
|
||||||
|
return 0;
|
||||||
|
int applied = 0;
|
||||||
|
applied += ApplyOn(weapons.Socket);
|
||||||
|
applied += ApplyOn(weapons.ThrowSocket);
|
||||||
|
applied += ApplyOn(weapons.PRPObject);
|
||||||
|
applied += ApplyOn(weapons.BrokenPRPObject);
|
||||||
|
applied += ApplyOn(weapons.KnifeObject);
|
||||||
|
var list = weapons.AvailableWeapons;
|
||||||
|
if (list == null)
|
||||||
|
return applied;
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
Weapon weapon = list[i];
|
||||||
|
if (weapon == null)
|
||||||
|
continue;
|
||||||
|
applied += ApplyOn(weapon.WeaponModel);
|
||||||
|
applied += ApplyOn(weapon.LeftHandWeaponModel);
|
||||||
|
applied += ApplyOn(weapon.PhysicsWeaponModel);
|
||||||
|
applied += ApplyOn(weapon.PhysicsLeftHandWeaponModel);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyOn(GameObject root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
return ApplyUnder(root.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyOn(Component root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
return ApplyUnder(root.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyUnder(Transform root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
Renderer[] found = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < found.Length; i++)
|
||||||
|
applied += ApplyRenderer(found[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
static int ApplyRenderer(Renderer renderer)
|
static int ApplyRenderer(Renderer renderer)
|
||||||
{
|
{
|
||||||
if (renderer == null)
|
if (renderer == null)
|
||||||
return 0;
|
return 0;
|
||||||
var materials = renderer.sharedMaterials;
|
int applied = ApplyMaterialSlots(renderer, renderer.sharedMaterials, false);
|
||||||
|
applied += ApplyMaterialSlots(renderer, renderer.materials, true);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyMaterialSlots(Renderer renderer, Material[] materials, bool propertyBlock)
|
||||||
|
{
|
||||||
if (materials == null)
|
if (materials == null)
|
||||||
return 0;
|
return 0;
|
||||||
int applied = 0;
|
int applied = 0;
|
||||||
for (int i = 0; i < materials.Length; i++)
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
{
|
||||||
|
Outfit outfit = Resolve(materials[i]);
|
||||||
|
if (outfit == null)
|
||||||
|
continue;
|
||||||
applied += ApplyMaterial(materials[i]);
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
_renderers[renderer.GetInstanceID()] = renderer;
|
||||||
|
if (!propertyBlock)
|
||||||
|
continue;
|
||||||
|
renderer.GetPropertyBlock(_block, i);
|
||||||
|
_block.SetTexture("_MainTex", outfit.Texture);
|
||||||
|
renderer.SetPropertyBlock(_block, i);
|
||||||
|
}
|
||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,10 +356,8 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
|
|
||||||
static int ApplyMaterial(Material material)
|
static int ApplyMaterial(Material material)
|
||||||
{
|
{
|
||||||
if (material == null || !material.HasProperty("_MainTex"))
|
Outfit outfit = Resolve(material);
|
||||||
return 0;
|
if (outfit == null)
|
||||||
Outfit outfit = FindOutfit(material.name);
|
|
||||||
if (outfit == null || outfit.Texture == null)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int id = material.GetInstanceID();
|
int id = material.GetInstanceID();
|
||||||
@@ -226,6 +365,7 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
{
|
{
|
||||||
var current = material.GetTexture("_MainTex");
|
var current = material.GetTexture("_MainTex");
|
||||||
_originals[id] = current;
|
_originals[id] = current;
|
||||||
|
_materials[id] = material;
|
||||||
CopySampling(outfit.Texture, current);
|
CopySampling(outfit.Texture, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +376,54 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Outfit Resolve(Material material)
|
||||||
|
{
|
||||||
|
if (material == null || !material.HasProperty("_MainTex"))
|
||||||
|
return null;
|
||||||
|
Outfit byName = FindOutfit(material.name);
|
||||||
|
if (byName != null && byName.Texture != null)
|
||||||
|
return byName;
|
||||||
|
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (current == null)
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
Outfit outfit = Outfits[i];
|
||||||
|
if (outfit.Texture == null)
|
||||||
|
continue;
|
||||||
|
if (current == outfit.Texture)
|
||||||
|
return outfit;
|
||||||
|
if (TextureNameMatches(current.name, outfit.SourceTexture))
|
||||||
|
return outfit;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Redirect(ref Texture value)
|
||||||
|
{
|
||||||
|
if (value == null || !CustomPlayerSkinsPlugin.Enabled.Value)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
Outfit outfit = Outfits[i];
|
||||||
|
if (outfit.Texture == null || value == outfit.Texture)
|
||||||
|
continue;
|
||||||
|
if (TextureNameMatches(value.name, outfit.SourceTexture))
|
||||||
|
{
|
||||||
|
value = outfit.Texture;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TextureNameMatches(string textureName, string sourceName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(textureName) || string.IsNullOrEmpty(sourceName))
|
||||||
|
return false;
|
||||||
|
return textureName == sourceName || textureName.StartsWith(sourceName + " ", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
static Outfit FindOutfit(string materialName)
|
static Outfit FindOutfit(string materialName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(materialName))
|
if (string.IsNullOrEmpty(materialName))
|
||||||
@@ -262,19 +450,7 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
target.anisoLevel = source.anisoLevel;
|
target.anisoLevel = source.anisoLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Material FindMaterial(int instanceId)
|
static bool EnsureTextures(Component source)
|
||||||
{
|
|
||||||
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
|
||||||
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)
|
if (AnyLoaded() && Time.unscaledTime < _nextFileCheck)
|
||||||
return true;
|
return true;
|
||||||
@@ -284,7 +460,7 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
int ready = 0;
|
int ready = 0;
|
||||||
for (int i = 0; i < Outfits.Length; i++)
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
{
|
{
|
||||||
if (EnsureOutfit(Outfits[i], directory))
|
if (EnsureOutfit(Outfits[i], directory, source))
|
||||||
ready++;
|
ready++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,12 +482,12 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool EnsureOutfit(Outfit outfit, string directory)
|
static bool EnsureOutfit(Outfit outfit, string directory, Component source)
|
||||||
{
|
{
|
||||||
string path = Path.Combine(directory, outfit.FileName);
|
string path = Path.Combine(directory, outfit.FileName);
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
{
|
{
|
||||||
if (!TryDumpSample(outfit, path))
|
if (!TryDumpSample(outfit, path, source))
|
||||||
return outfit.Texture != null;
|
return outfit.Texture != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,27 +524,48 @@ namespace Skopje.CustomPlayerSkins
|
|||||||
UnityEngine.Object.Destroy(outfit.Texture);
|
UnityEngine.Object.Destroy(outfit.Texture);
|
||||||
outfit.Texture = texture;
|
outfit.Texture = texture;
|
||||||
outfit.WriteTimeUtc = writeTime;
|
outfit.WriteTimeUtc = writeTime;
|
||||||
_loggedMiss = false;
|
_loggedApply = false;
|
||||||
CustomPlayerSkinsPlugin.LogSource.LogInfo($"Loaded {outfit.FileName} ({texture.width}x{texture.height})");
|
CustomPlayerSkinsPlugin.LogSource.LogInfo($"Loaded {outfit.FileName} ({texture.width}x{texture.height})");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TryDumpSample(Outfit outfit, string path)
|
static Texture FindSourceTexture(Component root, string materialName)
|
||||||
{
|
{
|
||||||
Texture source = null;
|
if (root == null || string.IsNullOrEmpty(materialName))
|
||||||
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
return null;
|
||||||
for (int i = 0; i < materials.Length && source == null; i++)
|
|
||||||
|
Renderer[] renderers = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
{
|
{
|
||||||
Material material = materials[i];
|
Renderer renderer = renderers[i];
|
||||||
if (material == null || material.name != outfit.DumpMaterial)
|
if (renderer == null)
|
||||||
continue;
|
continue;
|
||||||
if (!material.HasProperty("_MainTex"))
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || !NameIs(material.name, materialName) || !material.HasProperty("_MainTex"))
|
||||||
continue;
|
continue;
|
||||||
Texture current = material.GetTexture("_MainTex");
|
Texture current = material.GetTexture("_MainTex");
|
||||||
if (_originals.TryGetValue(material.GetInstanceID(), out Texture original))
|
if (_originals.TryGetValue(material.GetInstanceID(), out Texture original) && original != null)
|
||||||
current = original;
|
current = original;
|
||||||
source = current;
|
if (current != null)
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NameIs(string materialName, string expected)
|
||||||
|
{
|
||||||
|
return materialName == expected || materialName.StartsWith(expected + " (", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryDumpSample(Outfit outfit, string path, Component root)
|
||||||
|
{
|
||||||
|
Texture source = FindSourceTexture(root, outfit.DumpMaterial);
|
||||||
if (source == null)
|
if (source == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 369 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
After Width: | Height: | Size: 325 KiB |
@@ -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": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 223 KiB |
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"format": 1,
|
"format": 1,
|
||||||
"restore": {
|
"restore": {
|
||||||
"c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {}
|
"C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {}
|
||||||
},
|
},
|
||||||
"projects": {
|
"projects": {
|
||||||
"c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {
|
"C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"restore": {
|
"restore": {
|
||||||
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
"projectUniqueName": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
"projectName": "Skopje.CustomPlayerSkins",
|
"projectName": "Skopje.CustomPlayerSkins",
|
||||||
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
"projectPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
|
"outputPath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
|
||||||
"projectStyle": "PackageReference",
|
"projectStyle": "PackageReference",
|
||||||
"configFilePaths": [
|
"configFilePaths": [
|
||||||
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomPlayerSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a740fec7c87ba4b24aefb7faf991fc5b69c71bd4")]
|
||||||
|
[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.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2bc044bb67784bf521f2d79f70353573c1c458dec677af07046aae05ab5a56f9
|
||||||
@@ -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 =
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
a4e5920d93c0578ae388d9ae5af84bfefa9392a9f32e963e525e98ca55621af9
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\engineer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\fighter.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\hands.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\medic.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\prp.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\ranger.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\CustomSkins\singleplayer.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\Skopje.CustomPlayerSkins.deps.json
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\Skopje.CustomPlayerSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Debug\net6.0\Skopje.CustomPlayerSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\CustomPlayerSkinsPlugin.AssemblyInfo.cs
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\Skopje.CustomPlayerSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\refint\Skopje.CustomPlayerSkins.dll
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\Skopje.CustomPlayerSkins.pdb
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Debug\net6.0\ref\Skopje.CustomPlayerSkins.dll
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomPlayerSkins")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.CustomPlayerSkins")]
|
||||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a740fec7c87ba4b24aefb7faf991fc5b69c71bd4")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomPlayerSkins")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomPlayerSkins")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomPlayerSkins")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomPlayerSkins")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2039bc0988efa0867d36e92385b47db55a3abd9b1b79233f9907b9909d2a0097
|
b1a23811162af313e7a654eb6faf4da69b5eaa3caf2793482c0aa899d054f814
|
||||||
@@ -11,7 +11,7 @@ build_property.EnforceExtendedAnalyzerRules =
|
|||||||
build_property.EntryPointFilePath =
|
build_property.EntryPointFilePath =
|
||||||
build_property._SupportedPlatformList = Linux,macOS,Windows
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
build_property.RootNamespace = Skopje.CustomPlayerSkins
|
build_property.RootNamespace = Skopje.CustomPlayerSkins
|
||||||
build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\
|
build_property.ProjectDir = C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\
|
||||||
build_property.EnableComHosting =
|
build_property.EnableComHosting =
|
||||||
build_property.EnableGeneratedComInterfaceComImportInterop =
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
build_property.EffectiveAnalysisLevelStyle = 6.0
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
|||||||
@@ -15,3 +15,5 @@ c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSki
|
|||||||
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\medic.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\ranger.png
|
||||||
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\singleplayer.png
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\singleplayer.png
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\prp.png
|
||||||
|
C:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\hands.png
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"dgSpecHash": "7IEzcjI0wFM=",
|
"dgSpecHash": "zHwEQo8wyAU=",
|
||||||
"success": true,
|
"success": true,
|
||||||
"projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
"projectFilePath": "C:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
"expectedPackageFiles": [
|
"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.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.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
|||||||
@@ -0,0 +1,718 @@
|
|||||||
|
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 Skopje.Modding;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Skopje.CustomWeaponSkins
|
||||||
|
{
|
||||||
|
[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
|
||||||
|
public class CustomWeaponSkinsPlugin : BasePlugin
|
||||||
|
{
|
||||||
|
public const string PluginGuid = "com.skopje.customweaponskins";
|
||||||
|
public const string PluginName = "Skopje Custom Weapon Skins";
|
||||||
|
public const string PluginVersion = "1.1.0";
|
||||||
|
|
||||||
|
internal static ManualLogSource LogSource;
|
||||||
|
internal static ConfigEntry<bool> Enabled;
|
||||||
|
|
||||||
|
static Harmony _harmony;
|
||||||
|
static Il2CppSystem.Action<SkopjePlayer> _onPlayerStarted;
|
||||||
|
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
LogSource = Log;
|
||||||
|
Enabled = Config.Bind("Skin", "Enabled", true, "Replace each player weapon with its own PNG in the WeaponSkins folder next to this plugin.");
|
||||||
|
|
||||||
|
_harmony = new Harmony(PluginGuid);
|
||||||
|
var setTexture = AccessTools.Method(typeof(Material), "SetTexture", new[] { typeof(string), typeof(Texture) });
|
||||||
|
if (setTexture != null)
|
||||||
|
_harmony.Patch(setTexture, prefix: new HarmonyMethod(typeof(WeaponSkinPatches), nameof(WeaponSkinPatches.BeforeSetTexture)));
|
||||||
|
var setTextureId = AccessTools.Method(typeof(Material), "SetTexture", new[] { typeof(int), typeof(Texture) });
|
||||||
|
if (setTextureId != null)
|
||||||
|
_harmony.Patch(setTextureId, prefix: new HarmonyMethod(typeof(WeaponSkinPatches), nameof(WeaponSkinPatches.BeforeSetTextureId)));
|
||||||
|
|
||||||
|
_onPlayerStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(WeaponSkinReplacer.ApplyPlayer));
|
||||||
|
ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
|
||||||
|
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit the PNGs in WeaponSkins. Each file is one weapon: kalash, m143, knife, lmg_heavy, lmg_medium, bozdogun, hammer, rusty_pipe, wrench, toxic_axe, electric_blade, udar, revolver, pupin, karab, osa, vector, gruev_shotgun, winko, skull, grom, snooper, spore_gun, tesla_gun, and frag_grenade.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Unload()
|
||||||
|
{
|
||||||
|
if (_harmony != null)
|
||||||
|
_harmony.UnpatchSelf();
|
||||||
|
if (_onPlayerStarted != null)
|
||||||
|
ModAPI.remove_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
WeaponSkinReplacer.Restore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class WeaponSkinPatches
|
||||||
|
{
|
||||||
|
public static void BeforeSetTexture(string name, ref Texture value)
|
||||||
|
{
|
||||||
|
if (name == "_MainTex")
|
||||||
|
WeaponSkinReplacer.Redirect(ref value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void BeforeSetTextureId(int nameID, ref Texture value)
|
||||||
|
{
|
||||||
|
if (nameID == Shader.PropertyToID("_MainTex"))
|
||||||
|
WeaponSkinReplacer.Redirect(ref value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class WeaponSkinReplacer
|
||||||
|
{
|
||||||
|
sealed class WeaponSkin
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public string DumpMaterial;
|
||||||
|
public string SourceTexture;
|
||||||
|
public string[] Materials;
|
||||||
|
public Texture2D Texture;
|
||||||
|
public DateTime WriteTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One file per weapon. First-person, item, scope, and charged variants share that weapon's albedo.
|
||||||
|
static readonly WeaponSkin[] Weapons =
|
||||||
|
{
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "kalash.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Kalash",
|
||||||
|
SourceTexture = "DIF_Weapon_Kalash",
|
||||||
|
Materials = new[] { "MAT_Weapon_Kalash", "MAT_Weapon_Kalash_FP", "MAT_Weapon_Kalash_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "m143.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_M143",
|
||||||
|
SourceTexture = "DIF_Weapon_M143",
|
||||||
|
Materials = new[] { "MAT_Weapon_M143", "MAT_Weapon_M143_FP", "MAT_Weapon_M143_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "knife.png",
|
||||||
|
DumpMaterial = "MAT_Knife_FP",
|
||||||
|
SourceTexture = "DIF_Knife",
|
||||||
|
Materials = new[] { "MAT_Knife_FP" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "lmg_heavy.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_LMG_Heavy",
|
||||||
|
SourceTexture = "DIF_Weapon_LMG_Heavy",
|
||||||
|
Materials = new[] { "MAT_Weapon_LMG_Heavy", "MAT_Weapon_LMG_Heavy_FP", "MAT_Weapon_LMG_Heavy_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "lmg_medium.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_LMGMedium",
|
||||||
|
SourceTexture = "DIF_Weapon_LMGMedium",
|
||||||
|
Materials = new[] { "MAT_Weapon_LMGMedium", "MAT_Weapon_LMGMedium_FP", "MAT_Weapon_LMGMedium_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "bozdogun.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Bozdogun",
|
||||||
|
SourceTexture = "DIF_Weapon_Bozdogun",
|
||||||
|
Materials = new[] { "MAT_Weapon_Bozdogun", "MAT_Weapon_Bozdogun_FP", "MAT_Weapon_Bozdogun_FP_Charged", "MAT_Weapon_Bozdogun_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "hammer.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Hammer",
|
||||||
|
SourceTexture = "DIF_Weapon_Slab",
|
||||||
|
Materials = new[] { "MAT_Weapon_Hammer", "MAT_Weapon_Hammer_FP", "MAT_Weapon_Hammer_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "rusty_pipe.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Rusty_Pipe",
|
||||||
|
SourceTexture = "DIF_Rusty_Pipe",
|
||||||
|
Materials = new[] { "MAT_Weapon_Rusty_Pipe", "MAT_Weapon_Rusty_Pipe_FP", "MAT_Weapon_Rusty_Pipe_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "wrench.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Wrench",
|
||||||
|
SourceTexture = "DIF_Weapon_Wrench",
|
||||||
|
Materials = new[] { "MAT_Weapon_Wrench", "MAT_Weapon_Wrench_FP", "MAT_Weapon_Wrench_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "toxic_axe.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Toxic_Axe",
|
||||||
|
SourceTexture = "DIF_Toxic_Axe",
|
||||||
|
Materials = new[] { "MAT_Weapon_Toxic_Axe", "MAT_Weapon_Toxic_Axe_FP" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "electric_blade.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Electric_Blade",
|
||||||
|
SourceTexture = "DIF_ElectricBlade",
|
||||||
|
Materials = new[] { "MAT_Weapon_Electric_Blade", "MAT_Weapon_Electric_Blade_FP", "MAT_Weapon_Electric_Blade_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "udar.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_UDAR",
|
||||||
|
SourceTexture = "DIF_Weapon_UDAR",
|
||||||
|
Materials = new[] { "MAT_Weapon_UDAR", "MAT_Weapon_UDAR_FP", "MAT_Weapon_UDAR_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "revolver.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Revolver",
|
||||||
|
SourceTexture = "DIF_Weapon_Revolver",
|
||||||
|
Materials = new[] { "MAT_Weapon_Revolver", "MAT_Weapon_Revolver_FP", "MAT_Weapon_Revolver_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "pupin.png",
|
||||||
|
DumpMaterial = "MAT_Pupin",
|
||||||
|
SourceTexture = "DIF_Weapon_Pupin",
|
||||||
|
Materials = new[] { "MAT_Pupin", "MAT_Pupin_FP", "MAT_Pupin_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "karab.png",
|
||||||
|
DumpMaterial = "MAT_Karab",
|
||||||
|
SourceTexture = "DIF_Weapon_Karab",
|
||||||
|
Materials = new[] { "MAT_Karab", "MAT_Karab_FP", "MAT_Karab_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "osa.png",
|
||||||
|
DumpMaterial = "MAT_Osa",
|
||||||
|
SourceTexture = "DIF_Weapon_Osa",
|
||||||
|
Materials = new[] { "MAT_Osa", "MAT_Osa_FP", "MAT_Osa_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "vector.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Vector",
|
||||||
|
SourceTexture = "DIF_Weapon_Vector",
|
||||||
|
Materials = new[] { "MAT_Weapon_Vector", "MAT_Weapon_Vector_FP", "MAT_Weapon_Vector_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "gruev_shotgun.png",
|
||||||
|
DumpMaterial = "MAT_GruevShotgun",
|
||||||
|
SourceTexture = "DIF_Weapon_Gruev_Shotgun",
|
||||||
|
Materials = new[] { "MAT_GruevShotgun", "MAT_GruevShotgun_FP", "MAT_GruevShotgun_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "winko.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Winko",
|
||||||
|
SourceTexture = "DIF_Weapon_Winko",
|
||||||
|
Materials = new[] { "MAT_Weapon_Winko", "MAT_Weapon_Winko_FP", "MAT_Weapon_Winko_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "skull.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Skull",
|
||||||
|
SourceTexture = "DIF_Weapon_Skull",
|
||||||
|
Materials = new[] { "MAT_Weapon_Skull", "MAT_Weapon_Skull_FP", "MAT_Weapon_Skull_Item", "MAT_Weapon_Skull_Empty" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "grom.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Grom",
|
||||||
|
SourceTexture = "DIF_Weapon_Grom",
|
||||||
|
Materials = new[] { "MAT_Weapon_Grom", "MAT_Weapon_Grom_FP", "MAT_Weapon_Grom_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "snooper.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_Snooper",
|
||||||
|
SourceTexture = "DIF_Weapon_Snooper",
|
||||||
|
Materials = new[] { "MAT_Weapon_Snooper", "MAT_Weapon_Snooper_FP", "MAT_Weapon_Snooper_Item", "MAT_Weapon_Snooper_Scope", "MAT_Weapon_Snooper_Scope_FP" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "spore_gun.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_SporeGun",
|
||||||
|
SourceTexture = "DIF_Weapon_SporeGun",
|
||||||
|
Materials = new[] { "MAT_Weapon_SporeGun", "MAT_Weapon_SporeGun_FP", "MAT_Weapon_SporeGun_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "tesla_gun.png",
|
||||||
|
DumpMaterial = "MAT_Weapon_TeslaGun",
|
||||||
|
SourceTexture = "DIF_Weapon_Teslagun",
|
||||||
|
Materials = new[] { "MAT_Weapon_TeslaGun", "MAT_Weapon_TeslaGun_FP", "MAT_Weapon_TeslaGun_Item" },
|
||||||
|
},
|
||||||
|
new WeaponSkin
|
||||||
|
{
|
||||||
|
FileName = "frag_grenade.png",
|
||||||
|
DumpMaterial = "MAT_FragGrenade",
|
||||||
|
SourceTexture = "DIF_FragGrenade",
|
||||||
|
Materials = new[] { "MAT_FragGrenade" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static readonly Dictionary<int, Texture> _originals = new Dictionary<int, Texture>();
|
||||||
|
static readonly Dictionary<int, Material> _materials = new Dictionary<int, Material>();
|
||||||
|
static readonly Dictionary<int, Renderer> _renderers = new Dictionary<int, Renderer>();
|
||||||
|
static readonly MaterialPropertyBlock _block = new MaterialPropertyBlock();
|
||||||
|
static float _nextFileCheck;
|
||||||
|
static bool _loggedApply;
|
||||||
|
static bool _applied;
|
||||||
|
|
||||||
|
public static void ApplyPlayer(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (!CustomWeaponSkinsPlugin.Enabled.Value)
|
||||||
|
{
|
||||||
|
if (_applied)
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
ClearRendererOverrides();
|
||||||
|
_applied = false;
|
||||||
|
_loggedApply = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (player == null || !EnsureTextures(player))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int applied = ApplyPlayerWeapons(player);
|
||||||
|
if (applied > 0)
|
||||||
|
{
|
||||||
|
_applied = true;
|
||||||
|
if (!_loggedApply)
|
||||||
|
{
|
||||||
|
_loggedApply = true;
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogInfo($"Applied {applied} weapon material{(applied == 1 ? "" : "s")} from WeaponSkins.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Restore()
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
ClearRendererOverrides();
|
||||||
|
_applied = false;
|
||||||
|
_originals.Clear();
|
||||||
|
_materials.Clear();
|
||||||
|
_renderers.Clear();
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
if (Weapons[i].Texture == null)
|
||||||
|
continue;
|
||||||
|
UnityEngine.Object.Destroy(Weapons[i].Texture);
|
||||||
|
Weapons[i].Texture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Redirect(ref Texture value)
|
||||||
|
{
|
||||||
|
if (value == null || !CustomWeaponSkinsPlugin.Enabled.Value)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
WeaponSkin weapon = Weapons[i];
|
||||||
|
if (weapon.Texture == null || value == weapon.Texture)
|
||||||
|
continue;
|
||||||
|
if (TextureNameMatches(value.name, weapon.SourceTexture))
|
||||||
|
{
|
||||||
|
value = weapon.Texture;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RevertMaterials()
|
||||||
|
{
|
||||||
|
foreach (var pair in _materials)
|
||||||
|
{
|
||||||
|
Material material = pair.Value;
|
||||||
|
if (material != null && _originals.TryGetValue(pair.Key, out Texture original))
|
||||||
|
material.SetTexture("_MainTex", original);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearRendererOverrides()
|
||||||
|
{
|
||||||
|
foreach (var pair in _renderers)
|
||||||
|
{
|
||||||
|
Renderer renderer = pair.Value;
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || !_originals.TryGetValue(material.GetInstanceID(), out Texture original))
|
||||||
|
continue;
|
||||||
|
renderer.GetPropertyBlock(_block, slot);
|
||||||
|
_block.SetTexture("_MainTex", original);
|
||||||
|
renderer.SetPropertyBlock(_block, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyPlayerWeapons(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
WeaponsController weapons = player.WeaponsController;
|
||||||
|
if (weapons == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int applied = 0;
|
||||||
|
applied += ApplyOn(weapons.Socket);
|
||||||
|
applied += ApplyOn(weapons.ThrowSocket);
|
||||||
|
applied += ApplyOn(weapons.PRPObject);
|
||||||
|
applied += ApplyOn(weapons.BrokenPRPObject);
|
||||||
|
applied += ApplyOn(weapons.KnifeObject);
|
||||||
|
var list = weapons.AvailableWeapons;
|
||||||
|
if (list == null)
|
||||||
|
return applied;
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
Weapon weapon = list[i];
|
||||||
|
if (weapon == null)
|
||||||
|
continue;
|
||||||
|
applied += ApplyOn(weapon.WeaponModel);
|
||||||
|
applied += ApplyOn(weapon.LeftHandWeaponModel);
|
||||||
|
applied += ApplyOn(weapon.PhysicsWeaponModel);
|
||||||
|
applied += ApplyOn(weapon.PhysicsLeftHandWeaponModel);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyOn(GameObject root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
return ApplyUnder(root.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyOn(Component root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
return ApplyUnder(root.transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyUnder(Transform root)
|
||||||
|
{
|
||||||
|
if (root == null)
|
||||||
|
return 0;
|
||||||
|
Renderer[] found = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < found.Length; i++)
|
||||||
|
applied += ApplyRenderer(found[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyRenderer(Renderer renderer)
|
||||||
|
{
|
||||||
|
if (renderer == null)
|
||||||
|
return 0;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
{
|
||||||
|
WeaponSkin weapon = Resolve(materials[i]);
|
||||||
|
if (weapon == null)
|
||||||
|
continue;
|
||||||
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
_renderers[renderer.GetInstanceID()] = renderer;
|
||||||
|
renderer.GetPropertyBlock(_block, i);
|
||||||
|
_block.SetTexture("_MainTex", weapon.Texture);
|
||||||
|
renderer.SetPropertyBlock(_block, i);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyMaterial(Material material)
|
||||||
|
{
|
||||||
|
WeaponSkin weapon = Resolve(material);
|
||||||
|
if (weapon == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int id = material.GetInstanceID();
|
||||||
|
if (!_originals.ContainsKey(id))
|
||||||
|
{
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
_originals[id] = current;
|
||||||
|
_materials[id] = material;
|
||||||
|
CopySampling(weapon.Texture, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material.GetTexture("_MainTex") == weapon.Texture)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
material.SetTexture("_MainTex", weapon.Texture);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WeaponSkin Resolve(Material material)
|
||||||
|
{
|
||||||
|
if (material == null || !material.HasProperty("_MainTex"))
|
||||||
|
return null;
|
||||||
|
WeaponSkin byName = FindWeapon(material.name);
|
||||||
|
if (byName != null && byName.Texture != null)
|
||||||
|
return byName;
|
||||||
|
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (current == null)
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
WeaponSkin weapon = Weapons[i];
|
||||||
|
if (weapon.Texture == null)
|
||||||
|
continue;
|
||||||
|
if (current == weapon.Texture)
|
||||||
|
return weapon;
|
||||||
|
if (TextureNameMatches(current.name, weapon.SourceTexture))
|
||||||
|
return weapon;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TextureNameMatches(string textureName, string sourceName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(textureName) || string.IsNullOrEmpty(sourceName))
|
||||||
|
return false;
|
||||||
|
return textureName == sourceName || textureName.StartsWith(sourceName + " ", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static WeaponSkin FindWeapon(string materialName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(materialName))
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
string[] names = Weapons[i].Materials;
|
||||||
|
for (int n = 0; n < names.Length; n++)
|
||||||
|
{
|
||||||
|
string expected = names[n];
|
||||||
|
if (materialName == expected || materialName.StartsWith(expected + " (", StringComparison.Ordinal))
|
||||||
|
return Weapons[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 bool EnsureTextures(Component source)
|
||||||
|
{
|
||||||
|
if (AnyLoaded() && Time.unscaledTime < _nextFileCheck)
|
||||||
|
return true;
|
||||||
|
_nextFileCheck = Time.unscaledTime + 1f;
|
||||||
|
|
||||||
|
string directory = SkinsDirectory();
|
||||||
|
int ready = 0;
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
if (EnsureWeapon(Weapons[i], directory, source))
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ready == 0)
|
||||||
|
{
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogWarning("No weapon PNGs are ready in " + directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AnyLoaded()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
if (Weapons[i].Texture != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool EnsureWeapon(WeaponSkin weapon, string directory, Component source)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(directory, weapon.FileName);
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
if (!TryDumpSample(weapon, path, source))
|
||||||
|
return weapon.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime writeTime;
|
||||||
|
try { writeTime = File.GetLastWriteTimeUtc(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogError("Could not read " + weapon.FileName + ": " + ex.Message);
|
||||||
|
return weapon.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weapon.Texture != null && weapon.WriteTimeUtc == writeTime)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
byte[] bytes;
|
||||||
|
try { bytes = File.ReadAllBytes(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogError("Could not read " + weapon.FileName + ": " + ex.Message);
|
||||||
|
return weapon.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var texture = new Texture2D(2, 2, TextureFormat.RGBA32, true);
|
||||||
|
texture.name = "SkopjeCustomWeaponSkin_" + Path.GetFileNameWithoutExtension(weapon.FileName);
|
||||||
|
texture.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
if (!ImageConversion.LoadImage(texture, bytes))
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(texture);
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogError(weapon.FileName + " is not a readable image: " + path);
|
||||||
|
return weapon.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weapon.Texture != null)
|
||||||
|
UnityEngine.Object.Destroy(weapon.Texture);
|
||||||
|
weapon.Texture = texture;
|
||||||
|
weapon.WriteTimeUtc = writeTime;
|
||||||
|
_loggedApply = false;
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogInfo($"Loaded {weapon.FileName} ({texture.width}x{texture.height})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryDumpSample(WeaponSkin weapon, string path, Component root)
|
||||||
|
{
|
||||||
|
Texture source = FindSourceTexture(root, weapon.DumpMaterial);
|
||||||
|
if (source == null || IsCustomTexture(source))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
byte[] png = EncodePng(source);
|
||||||
|
if (png == null || png.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
File.WriteAllBytes(path, png);
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogInfo("Dumped " + weapon.FileName + " from the game into WeaponSkins.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomWeaponSkinsPlugin.LogSource.LogError("Could not write " + weapon.FileName + ": " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Texture FindSourceTexture(Component root, string materialName)
|
||||||
|
{
|
||||||
|
if (root == null || string.IsNullOrEmpty(materialName))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Renderer[] renderers = root.GetComponentsInChildren<Renderer>(true);
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
{
|
||||||
|
Renderer renderer = renderers[i];
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
Material[] materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
continue;
|
||||||
|
for (int slot = 0; slot < materials.Length; slot++)
|
||||||
|
{
|
||||||
|
Material material = materials[slot];
|
||||||
|
if (material == null || !NameIs(material.name, materialName) || !material.HasProperty("_MainTex"))
|
||||||
|
continue;
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (_originals.TryGetValue(material.GetInstanceID(), out Texture original) && original != null && !IsCustomTexture(original))
|
||||||
|
current = original;
|
||||||
|
if (current == null || IsCustomTexture(current))
|
||||||
|
continue;
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool NameIs(string materialName, string expected)
|
||||||
|
{
|
||||||
|
return materialName == expected || materialName.StartsWith(expected + " (", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsCustomTexture(Texture texture)
|
||||||
|
{
|
||||||
|
if (texture == null)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < Weapons.Length; i++)
|
||||||
|
{
|
||||||
|
if (Weapons[i].Texture == texture)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
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, "WeaponSkins");
|
||||||
|
return Path.Combine(Paths.PluginPath, "WeaponSkins");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AssemblyName>Skopje.CustomWeaponSkins</AssemblyName>
|
||||||
|
<RootNamespace>Skopje.CustomWeaponSkins</RootNamespace>
|
||||||
|
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
|
||||||
|
<GameDir Condition="'$(GameDir)' == ''">D:\SteamLibrary\steamapps\common\Skopje83</GameDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\0Harmony.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="BepInEx.Core">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Core.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="BepInEx.Unity.IL2CPP">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\BepInEx.Unity.IL2CPP.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Il2CppInterop.Runtime">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\core\Il2CppInterop.Runtime.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Il2Cppmscorlib">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\Il2Cppmscorlib.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SkopjeAssembly">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\SkopjeAssembly.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FishNet.Runtime">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\FishNet.Runtime.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Archon.SwissArmyLib">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.ImageConversionModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.ImageConversionModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="WeaponSkins\*.png" CopyToOutputDirectory="PreserveNewest" Link="WeaponSkins\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 230 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 503 KiB |
|
After Width: | Height: | Size: 445 KiB |
|
After Width: | Height: | Size: 656 KiB |
|
After Width: | Height: | Size: 550 KiB |
|
After Width: | Height: | Size: 515 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 637 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 474 KiB |
|
After Width: | Height: | Size: 419 KiB |
|
After Width: | Height: | Size: 550 KiB |
|
After Width: | Height: | Size: 533 KiB |