Improved plugins and added new
No files matched your search
@@ -5,9 +5,9 @@ namespace Skopje.BossHealth
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Look of the boss health bar. This is the file to edit.
|
/// Look of the boss health bar. This is the file to edit.
|
||||||
///
|
///
|
||||||
/// Sprites are chosen by the name shown on an Image's Sprite field in the game.
|
/// Sprites are chosen by the name shown on an Image's Sprite field in the game.
|
||||||
/// Leave a name empty to copy the in-game XP progress bar sprite.
|
/// Leave a fill name empty to use a solid rectangle.
|
||||||
/// After changing a name, rebuild the plugin.
|
/// After changing a name, rebuild the plugin.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BossBarStyle
|
public static class BossBarStyle
|
||||||
{
|
{
|
||||||
@@ -17,15 +17,17 @@ namespace Skopje.BossHealth
|
|||||||
|
|
||||||
public static readonly Color HealthColor = new Color(0.86f, 0.1f, 0.1f, 1f);
|
public static readonly Color HealthColor = new Color(0.86f, 0.1f, 0.1f, 1f);
|
||||||
public static readonly Color DelayColor = new Color(1f, 0.48f, 0.08f, 1f);
|
public static readonly Color DelayColor = new Color(1f, 0.48f, 0.08f, 1f);
|
||||||
|
public static readonly Color ImmuneColor = new Color(0.62f, 0.62f, 0.62f, 1f);
|
||||||
|
public static readonly Color ImmuneDelayColor = new Color(0.4f, 0.4f, 0.4f, 1f);
|
||||||
public static readonly Color BackgroundColor = new Color(0.04f, 0.04f, 0.04f, 0.8f);
|
public static readonly Color BackgroundColor = new Color(0.04f, 0.04f, 0.04f, 0.8f);
|
||||||
public static readonly Color LabelColor = Color.white;
|
public static readonly Color LabelColor = Color.white;
|
||||||
|
|
||||||
// The white bar waits this long after the last hit, then drains toward the real health.
|
// The orange bar waits this long after the last hit, then drains toward the real health.
|
||||||
public const float DelaySeconds = 0.6f;
|
public const float DelaySeconds = 0.6f;
|
||||||
public const float DelayDrainPerSecond = 0.75f;
|
public const float DelayDrainPerSecond = 0.75f;
|
||||||
|
|
||||||
public static readonly Vector2 BarSize = new Vector2(392f, 13f);
|
public static readonly Vector2 BarSize = new Vector2(392f, 13f);
|
||||||
public const float TopClearance = 100f;
|
public const float TopClearance = 25f;
|
||||||
public const float TrackPadding = 2f;
|
public const float TrackPadding = 2f;
|
||||||
public const float LabelHeight = 26f;
|
public const float LabelHeight = 26f;
|
||||||
public const float LabelFontSize = 20f;
|
public const float LabelFontSize = 20f;
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ namespace Skopje.BossHealth
|
|||||||
{
|
{
|
||||||
_nextReadTime = Time.unscaledTime + FightReadSeconds;
|
_nextReadTime = Time.unscaledTime + FightReadSeconds;
|
||||||
if (_fights[0].StillGoing(out float ratio))
|
if (_fights[0].StillGoing(out float ratio))
|
||||||
Present(_fights[0].Name, ratio);
|
Present(_fights[0].Name, ratio, _fights[0].IsImmune());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -223,10 +223,10 @@ namespace Skopje.BossHealth
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_fights[0].StillGoing(out float ratio))
|
if (_fights[0].StillGoing(out float ratio))
|
||||||
Present(_fights[0].Name, ratio);
|
Present(_fights[0].Name, ratio, _fights[0].IsImmune());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Present(string bossName, float ratio)
|
void Present(string bossName, float ratio, bool immune)
|
||||||
{
|
{
|
||||||
EnsureBar();
|
EnsureBar();
|
||||||
if (!_hasSample || _bossName != bossName)
|
if (!_hasSample || _bossName != bossName)
|
||||||
@@ -255,9 +255,15 @@ namespace Skopje.BossHealth
|
|||||||
if (_label != null)
|
if (_label != null)
|
||||||
_label.text = bossName;
|
_label.text = bossName;
|
||||||
if (_healthImage != null)
|
if (_healthImage != null)
|
||||||
|
{
|
||||||
|
_healthImage.color = immune ? BossBarStyle.ImmuneColor : BossBarStyle.HealthColor;
|
||||||
_healthImage.fillAmount = _health;
|
_healthImage.fillAmount = _health;
|
||||||
|
}
|
||||||
if (_delayImage != null)
|
if (_delayImage != null)
|
||||||
|
{
|
||||||
|
_delayImage.color = immune ? BossBarStyle.ImmuneDelayColor : BossBarStyle.DelayColor;
|
||||||
_delayImage.fillAmount = _delay;
|
_delayImage.fillAmount = _delay;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimateDelay()
|
void AnimateDelay()
|
||||||
@@ -443,9 +449,8 @@ namespace Skopje.BossHealth
|
|||||||
{
|
{
|
||||||
XpProgressBar xpBar = FindXpBar();
|
XpProgressBar xpBar = FindXpBar();
|
||||||
Image xpImage = xpBar != null ? xpBar.ProgressBar : null;
|
Image xpImage = xpBar != null ? xpBar.ProgressBar : null;
|
||||||
Sprite xpSprite = xpImage != null ? xpImage.sprite : null;
|
Sprite healthSprite = ResolveSprite(BossBarStyle.HealthSpriteName, null) ?? SolidSprite();
|
||||||
Sprite healthSprite = ResolveSprite(BossBarStyle.HealthSpriteName, xpSprite) ?? SolidSprite();
|
Sprite delaySprite = ResolveSprite(BossBarStyle.DelaySpriteName, null) ?? healthSprite;
|
||||||
Sprite delaySprite = ResolveSprite(BossBarStyle.DelaySpriteName, healthSprite) ?? healthSprite;
|
|
||||||
Sprite backgroundSprite = ResolveSprite(BossBarStyle.BackgroundSpriteName, FrameSprite(xpImage, healthSprite));
|
Sprite backgroundSprite = ResolveSprite(BossBarStyle.BackgroundSpriteName, FrameSprite(xpImage, healthSprite));
|
||||||
|
|
||||||
_barRoot = new GameObject("BossHealthBar");
|
_barRoot = new GameObject("BossHealthBar");
|
||||||
@@ -846,6 +851,18 @@ namespace Skopje.BossHealth
|
|||||||
return obj != null;
|
return obj != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SafeDamageable(MetalskyBoss boss)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return boss.IsDamageable;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool SafeDead(MetalskyBoss boss)
|
static bool SafeDead(MetalskyBoss boss)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -875,6 +892,7 @@ namespace Skopje.BossHealth
|
|||||||
public string Name;
|
public string Name;
|
||||||
|
|
||||||
public abstract bool StillGoing(out float ratio);
|
public abstract bool StillGoing(out float ratio);
|
||||||
|
public abstract bool IsImmune();
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AnaFight : Fight
|
sealed class AnaFight : Fight
|
||||||
@@ -904,6 +922,11 @@ namespace Skopje.BossHealth
|
|||||||
return false;
|
return false;
|
||||||
return TryRatio(_boss.EnemyControl, out ratio);
|
return TryRatio(_boss.EnemyControl, out ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsImmune()
|
||||||
|
{
|
||||||
|
return !IsTrue(_boss.IsVulnerable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class VesnaFight : Fight
|
sealed class VesnaFight : Fight
|
||||||
@@ -931,6 +954,11 @@ namespace Skopje.BossHealth
|
|||||||
return false;
|
return false;
|
||||||
return TryRatio(_boss.EnemyControl, out ratio);
|
return TryRatio(_boss.EnemyControl, out ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsImmune()
|
||||||
|
{
|
||||||
|
return !IsTrue(_boss.Visibility);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class MetalskyFight : Fight
|
sealed class MetalskyFight : Fight
|
||||||
@@ -950,6 +978,11 @@ namespace Skopje.BossHealth
|
|||||||
return false;
|
return false;
|
||||||
return TryRatio(_boss.PawnStatus, out ratio);
|
return TryRatio(_boss.PawnStatus, out ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsImmune()
|
||||||
|
{
|
||||||
|
return !SafeDamageable(_boss);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class TeofanFight : Fight
|
sealed class TeofanFight : Fight
|
||||||
@@ -975,6 +1008,25 @@ namespace Skopje.BossHealth
|
|||||||
control = _brain.GetComponentInChildren<EnemyControlAStar>(true);
|
control = _brain.GetComponentInChildren<EnemyControlAStar>(true);
|
||||||
return TryRatio(control, out ratio);
|
return TryRatio(control, out ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool IsImmune()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_brain.blockHelper != null && _brain.blockHelper.IsBlocking)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
TeofanBossState state = _brain.CurrentState;
|
||||||
|
return state == TeofanBossState.DissolveIn
|
||||||
|
|| state == TeofanBossState.FallenInteract
|
||||||
|
|| state == TeofanBossState.EndPhase
|
||||||
|
|| state == TeofanBossState.Despawned;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.BossHealth")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.BossHealth")]
|
||||||
[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")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.BossHealth")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.BossHealth")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.BossHealth")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.BossHealth")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0acf9d2f1a6c359332339552562b4ad3180ad1d45517bc93e22ec640b2c677b6
|
abb2aafca8f9c38e16a185b614682f24e0da3a5492f3ed24b1aa588b0f35f2fc
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using BepInEx;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using BepInEx.Logging;
|
||||||
|
using BepInEx.Unity.IL2CPP;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Il2CppInterop.Runtime;
|
||||||
|
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||||
|
using Skopje.Modding;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Skopje.CustomPlayerSkins
|
||||||
|
{
|
||||||
|
[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
|
||||||
|
public class CustomPlayerSkinsPlugin : BasePlugin
|
||||||
|
{
|
||||||
|
public const string PluginGuid = "com.skopje.customplayerskins";
|
||||||
|
public const string PluginName = "Skopje Custom Player Skins";
|
||||||
|
public const string PluginVersion = "1.0.0";
|
||||||
|
|
||||||
|
internal static ManualLogSource LogSource;
|
||||||
|
internal static ConfigEntry<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 outfit with its own PNG in the CustomSkins folder next to this plugin.");
|
||||||
|
|
||||||
|
_harmony = new Harmony(PluginGuid);
|
||||||
|
var refresh = AccessTools.Method(typeof(SkopjePlayer), "RefreshActiveModels");
|
||||||
|
if (refresh == null)
|
||||||
|
LogSource.LogWarning("SkopjePlayer.RefreshActiveModels was not found. The skins are applied when a player spawns.");
|
||||||
|
else
|
||||||
|
_harmony.Patch(refresh, postfix: new HarmonyMethod(typeof(SkinPatches), nameof(SkinPatches.AfterRefreshModels)));
|
||||||
|
|
||||||
|
_onPlayerStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(SkinReplacer.ApplyPlayer));
|
||||||
|
ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
|
||||||
|
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Edit the PNGs in CustomSkins: singleplayer, ranger, medic, engineer, and fighter (the Warrior class).");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Unload()
|
||||||
|
{
|
||||||
|
if (_harmony != null)
|
||||||
|
_harmony.UnpatchSelf();
|
||||||
|
SkinReplacer.Restore();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SkinPatches
|
||||||
|
{
|
||||||
|
public static void AfterRefreshModels(SkopjePlayer __instance)
|
||||||
|
{
|
||||||
|
SkinReplacer.ApplyPlayer(__instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SkinReplacer
|
||||||
|
{
|
||||||
|
sealed class Outfit
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public string DumpMaterial;
|
||||||
|
public string[] Materials;
|
||||||
|
public Texture2D Texture;
|
||||||
|
public DateTime WriteTimeUtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fighter is the multiplayer class shown as Warrior on the player icons.
|
||||||
|
static readonly Outfit[] Outfits =
|
||||||
|
{
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "singleplayer.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Default_Clothes",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Default_Clothes", "MAT_Pioneer_Default_Clothes_FP" },
|
||||||
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "ranger.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Ranger_Clothes",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Ranger_Clothes", "MAT_Pioneer_Ranger_Clothes_FP" },
|
||||||
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "medic.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Medic_Clothes",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Medic_Clothes", "MAT_Pioneer_Medic_Clothes_FP" },
|
||||||
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "engineer.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Engi_Clothes",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Engi_Clothes", "MAT_Pioneer_Engi_Clothes_FP" },
|
||||||
|
},
|
||||||
|
new Outfit
|
||||||
|
{
|
||||||
|
FileName = "fighter.png",
|
||||||
|
DumpMaterial = "MAT_Pioneer_Fighter_Clothes",
|
||||||
|
Materials = new[] { "MAT_Pioneer_Fighter_Clothes", "MAT_Pioneer_Fighter_Clothes_FP" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static readonly Dictionary<int, Texture> _originals = new Dictionary<int, Texture>();
|
||||||
|
static float _nextFileCheck;
|
||||||
|
static bool _loggedMiss;
|
||||||
|
|
||||||
|
public static void ApplyPlayer(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (!CustomPlayerSkinsPlugin.Enabled.Value)
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!EnsureTextures())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int applied = ApplyLoadedMaterials();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
applied += ApplySkins(player.PlayerSkins);
|
||||||
|
applied += ApplyRenderer(player.FirstPersonRenderer);
|
||||||
|
applied += ApplyRenderer(player.ThirdPersonRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (applied == 0 && !_loggedMiss)
|
||||||
|
{
|
||||||
|
_loggedMiss = true;
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogWarning("Custom skins are loaded, but the player clothes materials are not in memory yet.");
|
||||||
|
}
|
||||||
|
else if (applied > 0)
|
||||||
|
{
|
||||||
|
_loggedMiss = false;
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogInfo($"Applied {applied} player clothes material{(applied == 1 ? "" : "s")} from CustomSkins.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Restore()
|
||||||
|
{
|
||||||
|
RevertMaterials();
|
||||||
|
_originals.Clear();
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
if (Outfits[i].Texture == null)
|
||||||
|
continue;
|
||||||
|
UnityEngine.Object.Destroy(Outfits[i].Texture);
|
||||||
|
Outfits[i].Texture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RevertMaterials()
|
||||||
|
{
|
||||||
|
foreach (var pair in _originals)
|
||||||
|
{
|
||||||
|
var material = FindMaterial(pair.Key);
|
||||||
|
if (material != null)
|
||||||
|
material.SetTexture("_MainTex", pair.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyLoadedMaterials()
|
||||||
|
{
|
||||||
|
int applied = 0;
|
||||||
|
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplySkins(Il2CppReferenceArray<PlayerSkin> skins)
|
||||||
|
{
|
||||||
|
if (skins == null)
|
||||||
|
return 0;
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < skins.Length; i++)
|
||||||
|
{
|
||||||
|
PlayerSkin skin = skins[i];
|
||||||
|
if (skin == null)
|
||||||
|
continue;
|
||||||
|
applied += ApplyMaterialArray(skin.FPMaterials);
|
||||||
|
applied += ApplyMaterialArray(skin.TPMaterials);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyRenderer(Renderer renderer)
|
||||||
|
{
|
||||||
|
if (renderer == null)
|
||||||
|
return 0;
|
||||||
|
var materials = renderer.sharedMaterials;
|
||||||
|
if (materials == null)
|
||||||
|
return 0;
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyMaterialArray(Il2CppReferenceArray<Material> materials)
|
||||||
|
{
|
||||||
|
if (materials == null)
|
||||||
|
return 0;
|
||||||
|
int applied = 0;
|
||||||
|
for (int i = 0; i < materials.Length; i++)
|
||||||
|
applied += ApplyMaterial(materials[i]);
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ApplyMaterial(Material material)
|
||||||
|
{
|
||||||
|
if (material == null || !material.HasProperty("_MainTex"))
|
||||||
|
return 0;
|
||||||
|
Outfit outfit = FindOutfit(material.name);
|
||||||
|
if (outfit == null || outfit.Texture == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int id = material.GetInstanceID();
|
||||||
|
if (!_originals.ContainsKey(id))
|
||||||
|
{
|
||||||
|
var current = material.GetTexture("_MainTex");
|
||||||
|
_originals[id] = current;
|
||||||
|
CopySampling(outfit.Texture, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material.GetTexture("_MainTex") == outfit.Texture)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
material.SetTexture("_MainTex", outfit.Texture);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Outfit FindOutfit(string materialName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(materialName))
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
string[] names = Outfits[i].Materials;
|
||||||
|
for (int n = 0; n < names.Length; n++)
|
||||||
|
{
|
||||||
|
string expected = names[n];
|
||||||
|
if (materialName == expected || materialName.StartsWith(expected + " (", StringComparison.Ordinal))
|
||||||
|
return Outfits[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CopySampling(Texture2D target, Texture source)
|
||||||
|
{
|
||||||
|
if (target == null || source == null)
|
||||||
|
return;
|
||||||
|
target.wrapMode = source.wrapMode;
|
||||||
|
target.filterMode = source.filterMode;
|
||||||
|
target.anisoLevel = source.anisoLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Material FindMaterial(int instanceId)
|
||||||
|
{
|
||||||
|
var materials = Resources.FindObjectsOfTypeAll<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)
|
||||||
|
return true;
|
||||||
|
_nextFileCheck = Time.unscaledTime + 1f;
|
||||||
|
|
||||||
|
string directory = SkinsDirectory();
|
||||||
|
int ready = 0;
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
if (EnsureOutfit(Outfits[i], directory))
|
||||||
|
ready++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ready == 0)
|
||||||
|
{
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogWarning("No outfit PNGs are ready in " + directory);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AnyLoaded()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Outfits.Length; i++)
|
||||||
|
{
|
||||||
|
if (Outfits[i].Texture != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool EnsureOutfit(Outfit outfit, string directory)
|
||||||
|
{
|
||||||
|
string path = Path.Combine(directory, outfit.FileName);
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
if (!TryDumpSample(outfit, path))
|
||||||
|
return outfit.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime writeTime;
|
||||||
|
try { writeTime = File.GetLastWriteTimeUtc(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogError("Could not read " + outfit.FileName + ": " + ex.Message);
|
||||||
|
return outfit.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.Texture != null && outfit.WriteTimeUtc == writeTime)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
byte[] bytes;
|
||||||
|
try { bytes = File.ReadAllBytes(path); }
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogError("Could not read " + outfit.FileName + ": " + ex.Message);
|
||||||
|
return outfit.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var texture = new Texture2D(2, 2, TextureFormat.RGBA32, true);
|
||||||
|
texture.name = "SkopjeCustomSkin_" + Path.GetFileNameWithoutExtension(outfit.FileName);
|
||||||
|
texture.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
if (!ImageConversion.LoadImage(texture, bytes))
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(texture);
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogError(outfit.FileName + " is not a readable image: " + path);
|
||||||
|
return outfit.Texture != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outfit.Texture != null)
|
||||||
|
UnityEngine.Object.Destroy(outfit.Texture);
|
||||||
|
outfit.Texture = texture;
|
||||||
|
outfit.WriteTimeUtc = writeTime;
|
||||||
|
_loggedMiss = false;
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogInfo($"Loaded {outfit.FileName} ({texture.width}x{texture.height})");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TryDumpSample(Outfit outfit, string path)
|
||||||
|
{
|
||||||
|
Texture source = null;
|
||||||
|
var materials = Resources.FindObjectsOfTypeAll<Material>();
|
||||||
|
for (int i = 0; i < materials.Length && source == null; i++)
|
||||||
|
{
|
||||||
|
Material material = materials[i];
|
||||||
|
if (material == null || material.name != outfit.DumpMaterial)
|
||||||
|
continue;
|
||||||
|
if (!material.HasProperty("_MainTex"))
|
||||||
|
continue;
|
||||||
|
Texture current = material.GetTexture("_MainTex");
|
||||||
|
if (_originals.TryGetValue(material.GetInstanceID(), out Texture original))
|
||||||
|
current = original;
|
||||||
|
source = current;
|
||||||
|
}
|
||||||
|
if (source == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
byte[] png = EncodePng(source);
|
||||||
|
if (png == null || png.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
File.WriteAllBytes(path, png);
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogInfo("Dumped " + outfit.FileName + " from the game into CustomSkins.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
CustomPlayerSkinsPlugin.LogSource.LogError("Could not write " + outfit.FileName + ": " + ex.Message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] EncodePng(Texture source)
|
||||||
|
{
|
||||||
|
var readable = source as Texture2D;
|
||||||
|
bool temporary = false;
|
||||||
|
RenderTexture rt = null;
|
||||||
|
RenderTexture previous = RenderTexture.active;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (readable == null || !readable.isReadable)
|
||||||
|
{
|
||||||
|
rt = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
|
||||||
|
Graphics.Blit(source, rt);
|
||||||
|
RenderTexture.active = rt;
|
||||||
|
readable = new Texture2D(source.width, source.height, TextureFormat.RGBA32, false);
|
||||||
|
readable.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0);
|
||||||
|
readable.Apply();
|
||||||
|
temporary = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var encoded = ImageConversion.EncodeToPNG(readable);
|
||||||
|
if (encoded == null)
|
||||||
|
return null;
|
||||||
|
var bytes = new byte[encoded.Length];
|
||||||
|
for (int i = 0; i < encoded.Length; i++)
|
||||||
|
bytes[i] = encoded[i];
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
RenderTexture.active = previous;
|
||||||
|
if (rt != null)
|
||||||
|
RenderTexture.ReleaseTemporary(rt);
|
||||||
|
if (temporary && readable != null)
|
||||||
|
UnityEngine.Object.Destroy(readable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static string SkinsDirectory()
|
||||||
|
{
|
||||||
|
string dllDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||||
|
if (!string.IsNullOrEmpty(dllDir))
|
||||||
|
return Path.Combine(dllDir, "CustomSkins");
|
||||||
|
return Path.Combine(Paths.PluginPath, "CustomSkins");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.CustomPlayerSkins</AssemblyName>
|
||||||
|
<RootNamespace>Skopje.CustomPlayerSkins</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="CustomSkins\*.png" CopyToOutputDirectory="PreserveNewest" Link="CustomSkins\%(Filename)%(Extension)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 369 KiB |
|
After Width: | Height: | Size: 302 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 369 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": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
|
"projectName": "Skopje.CustomPlayerSkins",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.CustomPlayerSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.CustomPlayerSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.CustomPlayerSkins")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2039bc0988efa0867d36e92385b47db55a3abd9b1b79233f9907b9909d2a0097
|
||||||
@@ -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 @@
|
|||||||
|
6c1139a480f7b2b40bb2ca9a6182e2472a2688fad5f37ff82d5b23ba614cabb0
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.csproj.AssemblyReference.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.AssemblyInfoInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.AssemblyInfo.cs
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\CustomPlayerSkinsPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\Skopje.CustomPlayerSkins.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\refint\Skopje.CustomPlayerSkins.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\Skopje.CustomPlayerSkins.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.deps.json
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\Skopje.CustomPlayerSkins.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\obj\Release\net6.0\ref\Skopje.CustomPlayerSkins.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\engineer.png
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\fighter.png
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\medic.png
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\ranger.png
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\CustomPlayerSkins\bin\Release\net6.0\CustomSkins\singleplayer.png
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"targets": {
|
||||||
|
"net6.0": {}
|
||||||
|
},
|
||||||
|
"libraries": {},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net6.0": []
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
|
"projectName": "Skopje.CustomPlayerSkins",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "7IEzcjI0wFM=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\CustomPlayerSkins\\CustomPlayerSkinsPlugin.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ namespace Skopje.DamageNumbers
|
|||||||
internal static ConfigEntry<bool> OnlyYourDamage;
|
internal static ConfigEntry<bool> OnlyYourDamage;
|
||||||
internal static ConfigEntry<float> Lifetime;
|
internal static ConfigEntry<float> Lifetime;
|
||||||
internal static ConfigEntry<float> Size;
|
internal static ConfigEntry<float> Size;
|
||||||
|
internal static ConfigEntry<float> ValueMultiplier;
|
||||||
|
internal static ConfigEntry<int> CritTextAbove;
|
||||||
|
|
||||||
static Harmony _harmony;
|
static Harmony _harmony;
|
||||||
static SkopjePlayer _localPlayer;
|
static SkopjePlayer _localPlayer;
|
||||||
@@ -51,6 +53,8 @@ namespace Skopje.DamageNumbers
|
|||||||
OnlyYourDamage = Config.Bind("Display", "OnlyYourDamage", true, "Only show hits dealt by your pawn. Hits with no owner still show.");
|
OnlyYourDamage = Config.Bind("Display", "OnlyYourDamage", true, "Only show hits dealt by your pawn. Hits with no owner still show.");
|
||||||
Lifetime = Config.Bind("Display", "Lifetime", 0.8f, "Seconds each number stays on screen.");
|
Lifetime = Config.Bind("Display", "Lifetime", 0.8f, "Seconds each number stays on screen.");
|
||||||
Size = Config.Bind("Display", "Size", 1f, "Size multiplier.");
|
Size = Config.Bind("Display", "Size", 1f, "Size multiplier.");
|
||||||
|
ValueMultiplier = Config.Bind("Display", "ValueMultiplier", 1f, "Extra multiplier on the displayed number. The built-in scale stays in place.");
|
||||||
|
CritTextAbove = Config.Bind("Display", "CritTextAbove", 100, "Show the CRIT label when the displayed number is greater than this.");
|
||||||
|
|
||||||
ClassInjector.RegisterTypeInIl2Cpp<DamageNumbersDisplay>();
|
ClassInjector.RegisterTypeInIl2Cpp<DamageNumbersDisplay>();
|
||||||
|
|
||||||
@@ -181,8 +185,9 @@ namespace Skopje.DamageNumbers
|
|||||||
part = enemy.lastDamagedPart.Value;
|
part = enemy.lastDamagedPart.Value;
|
||||||
|
|
||||||
bool crit = IsCrit(part);
|
bool crit = IsCrit(part);
|
||||||
int shown = ShownDamage(raw, crit);
|
float displayScale = DisplayScale(crit);
|
||||||
DamageTier.Apply(shown, out Color color, out float tierScale);
|
int shown = ShownDamage(raw, displayScale);
|
||||||
|
DamageTier.Apply(shown, displayScale, out Color color, out float tierScale);
|
||||||
float punch = PunchFor(rawAmount, crit, type) * tierScale;
|
float punch = PunchFor(rawAmount, crit, type) * tierScale;
|
||||||
pos += new Vector3(
|
pos += new Vector3(
|
||||||
UnityEngine.Random.Range(-0.22f, 0.22f),
|
UnityEngine.Random.Range(-0.22f, 0.22f),
|
||||||
@@ -280,11 +285,26 @@ namespace Skopje.DamageNumbers
|
|||||||
|
|
||||||
// Real hits in this game are often 1, 2, or 3. Scale them up so the popup
|
// Real hits in this game are often 1, 2, or 3. Scale them up so the popup
|
||||||
// still tracks the hit, but reads like a chunky score. Crits are doubled.
|
// still tracks the hit, but reads like a chunky score. Crits are doubled.
|
||||||
static int ShownDamage(float raw, bool crit)
|
static float DisplayScale(bool crit)
|
||||||
|
{
|
||||||
|
float extra = 1f;
|
||||||
|
if (ValueMultiplier != null && !float.IsNaN(ValueMultiplier.Value) && !float.IsInfinity(ValueMultiplier.Value))
|
||||||
|
extra = ValueMultiplier.Value;
|
||||||
|
return (crit ? 200f : 100f) * extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ShownDamage(float raw, float displayScale)
|
||||||
{
|
{
|
||||||
float scale = crit ? 200f : 100f;
|
|
||||||
float roll = UnityEngine.Random.Range(0.94f, 1.06f);
|
float roll = UnityEngine.Random.Range(0.94f, 1.06f);
|
||||||
return Mathf.Max(1, Mathf.RoundToInt(raw * scale * roll));
|
return Mathf.Max(1, Mathf.RoundToInt(raw * displayScale * roll));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool ShowCritText(int shown, bool crit)
|
||||||
|
{
|
||||||
|
if (!crit)
|
||||||
|
return false;
|
||||||
|
int above = CritTextAbove != null ? CritTextAbove.Value : 100;
|
||||||
|
return shown > above;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Claim(EnemyControlAStar enemy, int amount, Vector3 pos)
|
static bool Claim(EnemyControlAStar enemy, int amount, Vector3 pos)
|
||||||
@@ -416,7 +436,7 @@ namespace Skopje.DamageNumbers
|
|||||||
Tilt = UnityEngine.Random.Range(crit ? -18f : -14f, crit ? 18f : 14f),
|
Tilt = UnityEngine.Random.Range(crit ? -18f : -14f, crit ? 18f : 14f),
|
||||||
Amount = amount,
|
Amount = amount,
|
||||||
Color = color,
|
Color = color,
|
||||||
Text = crit ? "CRIT\n" + amount : amount.ToString()
|
Text = DamageNumbersPlugin.ShowCritText(amount, crit) ? "CRIT\n" + amount : amount.ToString()
|
||||||
};
|
};
|
||||||
Measure(display._numbers[slot].Text, out display._numbers[slot].Lines, out display._numbers[slot].Longest);
|
Measure(display._numbers[slot].Text, out display._numbers[slot].Lines, out display._numbers[slot].Longest);
|
||||||
}
|
}
|
||||||
@@ -607,9 +627,13 @@ namespace Skopje.DamageNumbers
|
|||||||
static readonly Color LightOrange = new Color(1f, 0.74f, 0.38f, 1f);
|
static readonly Color LightOrange = new Color(1f, 0.74f, 0.38f, 1f);
|
||||||
static readonly Color Orange = new Color(1f, 0.48f, 0.08f, 1f);
|
static readonly Color Orange = new Color(1f, 0.48f, 0.08f, 1f);
|
||||||
|
|
||||||
public static void Apply(int amount, out Color color, out float sizeScale)
|
public static void Apply(int amount, float displayScale, out Color color, out float sizeScale)
|
||||||
{
|
{
|
||||||
if (amount >= 50)
|
int yellow = Band(25, displayScale);
|
||||||
|
int lightOrange = Band(40, displayScale);
|
||||||
|
int orange = Band(50, displayScale);
|
||||||
|
|
||||||
|
if (amount >= orange)
|
||||||
{
|
{
|
||||||
color = Orange;
|
color = Orange;
|
||||||
sizeScale = 1.15f;
|
sizeScale = 1.15f;
|
||||||
@@ -617,12 +641,17 @@ namespace Skopje.DamageNumbers
|
|||||||
}
|
}
|
||||||
|
|
||||||
sizeScale = 1f;
|
sizeScale = 1f;
|
||||||
if (amount >= 40)
|
if (amount >= lightOrange)
|
||||||
color = LightOrange;
|
color = LightOrange;
|
||||||
else if (amount >= 25)
|
else if (amount >= yellow)
|
||||||
color = LightYellow;
|
color = LightYellow;
|
||||||
else
|
else
|
||||||
color = White;
|
color = White;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int Band(int baseValue, float displayScale)
|
||||||
|
{
|
||||||
|
return Mathf.Max(1, Mathf.RoundToInt(baseValue * displayScale));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ using System.Reflection;
|
|||||||
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.DamageNumbers")]
|
[assembly: System.Reflection.AssemblyCompanyAttribute("Skopje.DamageNumbers")]
|
||||||
[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")]
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
||||||
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.DamageNumbers")]
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.DamageNumbers")]
|
||||||
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.DamageNumbers")]
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.DamageNumbers")]
|
||||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
a6bbf0f8cbba65e2618b81c6321ac40b204cddd43585a4acfc7f7e5c36244fcc
|
dde6890729b61f58e466ea7b10c19cab8ad8b0c2df507a60a79c69f9ac5267e5
|
||||||
@@ -0,0 +1,735 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using BepInEx;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using BepInEx.Logging;
|
||||||
|
using BepInEx.Unity.IL2CPP;
|
||||||
|
using FMODUnity;
|
||||||
|
using Il2CppInterop.Runtime;
|
||||||
|
using Il2CppInterop.Runtime.Injection;
|
||||||
|
using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||||
|
using Skopje.Modding;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Skopje.DomRadio
|
||||||
|
{
|
||||||
|
[BepInPlugin(PluginGuid, PluginName, PluginVersion)]
|
||||||
|
public class DomRadioPlugin : BasePlugin
|
||||||
|
{
|
||||||
|
public const string PluginGuid = "com.skopje.domradio";
|
||||||
|
public const string PluginName = "Skopje Dom Radio";
|
||||||
|
public const string PluginVersion = "1.0.0";
|
||||||
|
|
||||||
|
internal static ManualLogSource LogSource;
|
||||||
|
|
||||||
|
internal static ConfigEntry<KeyCode> Toggle;
|
||||||
|
internal static ConfigEntry<KeyCode> NextTrack;
|
||||||
|
internal static ConfigEntry<KeyCode> PreviousTrack;
|
||||||
|
internal static ConfigEntry<bool> Shuffle;
|
||||||
|
internal static ConfigEntry<bool> MuteGameMusic;
|
||||||
|
internal static ConfigEntry<string> MusicBusFilter;
|
||||||
|
internal static ConfigEntry<string> Folder;
|
||||||
|
internal static ConfigEntry<bool> ShowNowPlaying;
|
||||||
|
|
||||||
|
internal static SkopjePlayer LocalPlayer;
|
||||||
|
|
||||||
|
static GameObject _host;
|
||||||
|
static Il2CppSystem.Action<SkopjePlayer> _onPlayerStarted;
|
||||||
|
static Il2CppSystem.Action<SkopjePlayer> _onPlayerStopped;
|
||||||
|
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
LogSource = Log;
|
||||||
|
Toggle = Config.Bind("Radio", "Toggle", KeyCode.F8, "Turn the radio on or off.");
|
||||||
|
NextTrack = Config.Bind("Radio", "NextTrack", KeyCode.F10, "Skip to the next file.");
|
||||||
|
PreviousTrack = Config.Bind("Radio", "PreviousTrack", KeyCode.F9, "Go back to the previous file.");
|
||||||
|
Shuffle = Config.Bind("Radio", "Shuffle", false, "Play the folder in a random order. Off plays files by name.");
|
||||||
|
MuteGameMusic = Config.Bind("Radio", "MuteGameMusic", true, "While the radio is on, mute the game's music buses. That includes the Dom bus track. Sound effects stay on.");
|
||||||
|
MusicBusFilter = Config.Bind("Radio", "MusicBusFilter", "Music", "Mute FMOD buses whose path contains this text. Music covers the Dom bus home track.");
|
||||||
|
Folder = Config.Bind("Radio", "Folder", "", "Folder of .mp3 and .wav files. Leave empty to use BepInEx/plugins/DomRadio/Music.");
|
||||||
|
ShowNowPlaying = Config.Bind("Radio", "ShowNowPlaying", true, "Show the current file name while the radio is on.");
|
||||||
|
|
||||||
|
ClassInjector.RegisterTypeInIl2Cpp<DomRadioPlayer>();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(MusicDirectory);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.LogWarning("Could not create the music folder: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onPlayerStarted = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(OnPlayerStarted));
|
||||||
|
_onPlayerStopped = DelegateSupport.ConvertDelegate<Il2CppSystem.Action<SkopjePlayer>>(new Action<SkopjePlayer>(OnPlayerStopped));
|
||||||
|
ModAPI.add_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
ModAPI.add_OnPlayerStoppedClient(_onPlayerStopped);
|
||||||
|
|
||||||
|
_host = new GameObject("SkopjeDomRadio");
|
||||||
|
UnityEngine.Object.DontDestroyOnLoad(_host);
|
||||||
|
_host.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
_host.AddComponent<DomRadioPlayer>();
|
||||||
|
|
||||||
|
Log.LogInfo($"{PluginName} {PluginVersion} loaded. Put .mp3 and .wav files in {MusicDirectory}. Toggle {Toggle.Value}, next {NextTrack.Value}, previous {PreviousTrack.Value}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Unload()
|
||||||
|
{
|
||||||
|
if (DomRadioPlayer.Instance != null)
|
||||||
|
DomRadioPlayer.Instance.TurnOff(false);
|
||||||
|
GameMusic.Release();
|
||||||
|
LocalPlayer = null;
|
||||||
|
if (_onPlayerStarted != null)
|
||||||
|
ModAPI.remove_OnPlayerStartedClient(_onPlayerStarted);
|
||||||
|
if (_onPlayerStopped != null)
|
||||||
|
ModAPI.remove_OnPlayerStoppedClient(_onPlayerStopped);
|
||||||
|
if (_host != null)
|
||||||
|
{
|
||||||
|
UnityEngine.Object.Destroy(_host);
|
||||||
|
_host = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string MusicDirectory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string custom = Folder == null ? null : Folder.Value;
|
||||||
|
if (!string.IsNullOrWhiteSpace(custom))
|
||||||
|
return custom;
|
||||||
|
return Path.Combine(Paths.PluginPath, "DomRadio", "Music");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnPlayerStarted(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (player != null && player.IsOwner)
|
||||||
|
LocalPlayer = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnPlayerStopped(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (LocalPlayer == player)
|
||||||
|
LocalPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static SkopjePlayer FindLocalPlayer()
|
||||||
|
{
|
||||||
|
if (LocalPlayer != null)
|
||||||
|
return LocalPlayer;
|
||||||
|
|
||||||
|
var players = UnityEngine.Object.FindObjectsOfType<SkopjePlayer>();
|
||||||
|
if (players == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
for (int i = 0; i < players.Length; i++)
|
||||||
|
{
|
||||||
|
SkopjePlayer player = players[i];
|
||||||
|
if (player != null && player.IsOwner)
|
||||||
|
{
|
||||||
|
LocalPlayer = player;
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static float MusicLevel()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OptionsManager manager = OptionsManager.Instance;
|
||||||
|
if (manager == null || manager.OptionsData == null)
|
||||||
|
return 1f;
|
||||||
|
|
||||||
|
return Unit(manager.OptionsData.MasterVolume) * Unit(manager.OptionsData.MusicVolume);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float Unit(float value)
|
||||||
|
{
|
||||||
|
if (value > 1f)
|
||||||
|
value *= 0.01f;
|
||||||
|
return Mathf.Clamp01(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DomRadioPlayer : MonoBehaviour
|
||||||
|
{
|
||||||
|
const float MissedPlaySeconds = 2f;
|
||||||
|
|
||||||
|
public static DomRadioPlayer Instance { get; private set; }
|
||||||
|
|
||||||
|
readonly List<string> _tracks = new List<string>();
|
||||||
|
FMOD.Sound _sound;
|
||||||
|
FMOD.Channel _channel;
|
||||||
|
bool _hasSound;
|
||||||
|
int _index;
|
||||||
|
int _failures;
|
||||||
|
bool _on;
|
||||||
|
bool _suspended;
|
||||||
|
bool _skipPending;
|
||||||
|
bool _heardPlayback;
|
||||||
|
bool _loggedEmpty;
|
||||||
|
float _playDeadline;
|
||||||
|
string _nowPlaying = string.Empty;
|
||||||
|
string _notice = string.Empty;
|
||||||
|
float _noticeUntil;
|
||||||
|
GUIStyle _label;
|
||||||
|
Texture2D _backdrop;
|
||||||
|
int _styleHeight;
|
||||||
|
|
||||||
|
public DomRadioPlayer(IntPtr pointer) : base(pointer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestroy()
|
||||||
|
{
|
||||||
|
TurnOff(false);
|
||||||
|
if (_backdrop != null)
|
||||||
|
Destroy(_backdrop);
|
||||||
|
if (Instance == this)
|
||||||
|
Instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
bool inside = InDom();
|
||||||
|
if (Pressed(DomRadioPlugin.Toggle.Value))
|
||||||
|
{
|
||||||
|
if (_on)
|
||||||
|
TurnOff(true);
|
||||||
|
else if (inside)
|
||||||
|
TurnOn();
|
||||||
|
else
|
||||||
|
Notify("Radio only plays in the Dom", 2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_on)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!inside)
|
||||||
|
{
|
||||||
|
if (!_suspended)
|
||||||
|
Suspend();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_suspended)
|
||||||
|
Resume();
|
||||||
|
|
||||||
|
if (Pressed(DomRadioPlugin.NextTrack.Value))
|
||||||
|
Advance(1);
|
||||||
|
else if (Pressed(DomRadioPlugin.PreviousTrack.Value))
|
||||||
|
Advance(-1);
|
||||||
|
|
||||||
|
PumpLoad();
|
||||||
|
// Mute the Dom track only while one of our files is actually audible.
|
||||||
|
if (DomRadioPlugin.MuteGameMusic.Value && IsAudible())
|
||||||
|
GameMusic.Hold();
|
||||||
|
else if (!DomRadioPlugin.MuteGameMusic.Value)
|
||||||
|
GameMusic.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
if (Event.current == null || Event.current.type != EventType.Repaint || GUI.skin == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
EnsureStyle();
|
||||||
|
const float width = 640f;
|
||||||
|
const float height = 36f;
|
||||||
|
float textWidth = width - _label.padding.left - _label.padding.right;
|
||||||
|
|
||||||
|
string text = null;
|
||||||
|
if (_on && !_suspended && DomRadioPlugin.ShowNowPlaying.Value && !string.IsNullOrEmpty(_nowPlaying))
|
||||||
|
text = Fit(_nowPlaying, textWidth);
|
||||||
|
else if (Time.unscaledTime <= _noticeUntil && !string.IsNullOrEmpty(_notice))
|
||||||
|
text = Fit(_notice, textWidth);
|
||||||
|
if (text == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var rect = new Rect((Screen.width - width) * 0.5f, Screen.height - 96f, width, height);
|
||||||
|
Color previous = GUI.color;
|
||||||
|
GUI.color = new Color(0f, 0f, 0f, 0.62f);
|
||||||
|
GUI.DrawTexture(rect, _backdrop);
|
||||||
|
GUI.color = previous;
|
||||||
|
GUI.Label(rect, text, _label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TurnOn()
|
||||||
|
{
|
||||||
|
ReloadTracks();
|
||||||
|
if (_tracks.Count == 0)
|
||||||
|
{
|
||||||
|
if (!_loggedEmpty)
|
||||||
|
{
|
||||||
|
_loggedEmpty = true;
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Radio disabled. No .mp3 or .wav files in " + DomRadioPlugin.MusicDirectory + ". Dom music will keep playing.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loggedEmpty = false;
|
||||||
|
_on = true;
|
||||||
|
_failures = 0;
|
||||||
|
_index = 0;
|
||||||
|
_nowPlaying = TrackLabel(_tracks[0]);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PlayFile(_tracks[0]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not start the radio: " + ex.Message);
|
||||||
|
TurnOff(false);
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Radio disabled. Dom music restored.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Radio on. " + _tracks.Count + " file(s) in " + DomRadioPlugin.MusicDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TurnOff(bool announce)
|
||||||
|
{
|
||||||
|
_on = false;
|
||||||
|
_suspended = false;
|
||||||
|
_skipPending = false;
|
||||||
|
_heardPlayback = false;
|
||||||
|
_nowPlaying = string.Empty;
|
||||||
|
StopSound();
|
||||||
|
GameMusic.Release();
|
||||||
|
if (announce)
|
||||||
|
{
|
||||||
|
Notify("Radio off", 2f);
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Radio off. Dom music restored.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Advance(int direction)
|
||||||
|
{
|
||||||
|
if (_tracks.Count == 0)
|
||||||
|
{
|
||||||
|
TurnOff(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int next = _index + direction;
|
||||||
|
if (next >= _tracks.Count)
|
||||||
|
next = 0;
|
||||||
|
else if (next < 0)
|
||||||
|
next = _tracks.Count - 1;
|
||||||
|
|
||||||
|
_index = next;
|
||||||
|
_nowPlaying = TrackLabel(_tracks[_index]);
|
||||||
|
PlayFile(_tracks[_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReloadTracks()
|
||||||
|
{
|
||||||
|
_tracks.Clear();
|
||||||
|
string folder = DomRadioPlugin.MusicDirectory;
|
||||||
|
if (!Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(folder);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not create the music folder: " + ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] files;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
files = Directory.GetFiles(folder);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not read the music folder: " + ex.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < files.Length; i++)
|
||||||
|
{
|
||||||
|
string extension = Path.GetExtension(files[i]);
|
||||||
|
if (extension.Equals(".mp3", StringComparison.OrdinalIgnoreCase) || extension.Equals(".wav", StringComparison.OrdinalIgnoreCase))
|
||||||
|
_tracks.Add(files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_tracks.Sort(StringComparer.OrdinalIgnoreCase);
|
||||||
|
if (DomRadioPlugin.Shuffle.Value)
|
||||||
|
ShuffleTracks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShuffleTracks()
|
||||||
|
{
|
||||||
|
var random = new System.Random();
|
||||||
|
for (int i = _tracks.Count - 1; i > 0; i--)
|
||||||
|
{
|
||||||
|
int swap = random.Next(i + 1);
|
||||||
|
string file = _tracks[i];
|
||||||
|
_tracks[i] = _tracks[swap];
|
||||||
|
_tracks[swap] = file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayFile(string path)
|
||||||
|
{
|
||||||
|
StopSound();
|
||||||
|
_heardPlayback = false;
|
||||||
|
_nowPlaying = TrackLabel(path);
|
||||||
|
if (!RuntimeManager.IsInitialized)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Audio system is not ready yet.");
|
||||||
|
_skipPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD.System core = RuntimeManager.CoreSystem;
|
||||||
|
if (!core.hasHandle())
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Audio system is not ready yet.");
|
||||||
|
_skipPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD.MODE mode = FMOD.MODE.CREATESTREAM | FMOD.MODE._2D | FMOD.MODE.LOOP_OFF;
|
||||||
|
FMOD.Sound sound;
|
||||||
|
FMOD.RESULT result = core.createSound(path, mode, out sound);
|
||||||
|
if (result != FMOD.RESULT.OK || !sound.hasHandle())
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not open " + Path.GetFileName(path) + " (" + result + ").");
|
||||||
|
_skipPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMOD.ChannelGroup master;
|
||||||
|
core.getMasterChannelGroup(out master);
|
||||||
|
FMOD.Channel channel;
|
||||||
|
result = core.playSound(sound, master, false, out channel);
|
||||||
|
if (result != FMOD.RESULT.OK || !channel.hasHandle())
|
||||||
|
{
|
||||||
|
sound.release();
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not play " + Path.GetFileName(path) + " (" + result + ").");
|
||||||
|
_skipPending = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.setVolume(DomRadioPlugin.MusicLevel());
|
||||||
|
_sound = sound;
|
||||||
|
_channel = channel;
|
||||||
|
_hasSound = true;
|
||||||
|
_playDeadline = Time.unscaledTime + MissedPlaySeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PumpLoad()
|
||||||
|
{
|
||||||
|
if (_skipPending)
|
||||||
|
{
|
||||||
|
_skipPending = false;
|
||||||
|
OnTrackFailed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hasSound)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (IsAudible())
|
||||||
|
{
|
||||||
|
_heardPlayback = true;
|
||||||
|
_failures = 0;
|
||||||
|
_channel.setVolume(DomRadioPlugin.MusicLevel());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_heardPlayback || Time.unscaledTime >= _playDeadline)
|
||||||
|
Advance(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAudible()
|
||||||
|
{
|
||||||
|
if (!_hasSound || !_channel.hasHandle())
|
||||||
|
return false;
|
||||||
|
bool playing = false;
|
||||||
|
return _channel.isPlaying(out playing) == FMOD.RESULT.OK && playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnTrackFailed()
|
||||||
|
{
|
||||||
|
_failures++;
|
||||||
|
if (_tracks.Count == 0 || _failures >= _tracks.Count)
|
||||||
|
{
|
||||||
|
TurnOff(false);
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Radio disabled. No playable files in " + DomRadioPlugin.MusicDirectory + ". Dom music restored.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Advance(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopSound()
|
||||||
|
{
|
||||||
|
if (_hasSound && _channel.hasHandle())
|
||||||
|
_channel.stop();
|
||||||
|
if (_hasSound && _sound.hasHandle())
|
||||||
|
_sound.release();
|
||||||
|
_hasSound = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notify(string text, float seconds)
|
||||||
|
{
|
||||||
|
_notice = text ?? string.Empty;
|
||||||
|
_noticeUntil = Time.unscaledTime + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool _loggedInput;
|
||||||
|
|
||||||
|
static bool Pressed(KeyCode key)
|
||||||
|
{
|
||||||
|
if (key == KeyCode.None)
|
||||||
|
return false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Input.GetKeyDown(key);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (!_loggedInput)
|
||||||
|
{
|
||||||
|
_loggedInput = true;
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Keyboard input is unavailable: " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InDom()
|
||||||
|
{
|
||||||
|
SkopjePlayer player = DomRadioPlugin.FindLocalPlayer();
|
||||||
|
if (player == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return player.InsideVehicle != null;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LocalPlayer = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Suspend()
|
||||||
|
{
|
||||||
|
_suspended = true;
|
||||||
|
if (_hasSound && _channel.hasHandle())
|
||||||
|
_channel.setPaused(true);
|
||||||
|
GameMusic.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resume()
|
||||||
|
{
|
||||||
|
_suspended = false;
|
||||||
|
if (_hasSound && _channel.hasHandle())
|
||||||
|
{
|
||||||
|
_channel.setPaused(false);
|
||||||
|
_channel.setVolume(DomRadioPlugin.MusicLevel());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_index >= 0 && _index < _tracks.Count)
|
||||||
|
PlayFile(_tracks[_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Fit(string text, float width)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text) || _label.CalcSize(new GUIContent(text)).x <= width)
|
||||||
|
return text;
|
||||||
|
|
||||||
|
const string ellipsis = "...";
|
||||||
|
int low = 0;
|
||||||
|
int high = text.Length;
|
||||||
|
while (low < high)
|
||||||
|
{
|
||||||
|
int mid = (low + high + 1) / 2;
|
||||||
|
string candidate = text.Substring(0, mid).TrimEnd() + ellipsis;
|
||||||
|
if (_label.CalcSize(new GUIContent(candidate)).x <= width)
|
||||||
|
low = mid;
|
||||||
|
else
|
||||||
|
high = mid - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (low <= 0)
|
||||||
|
return ellipsis;
|
||||||
|
return text.Substring(0, low).TrimEnd() + ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string TrackLabel(string path)
|
||||||
|
{
|
||||||
|
return "Radio — " + Path.GetFileNameWithoutExtension(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureStyle()
|
||||||
|
{
|
||||||
|
if (_backdrop == null)
|
||||||
|
{
|
||||||
|
_backdrop = new Texture2D(1, 1);
|
||||||
|
_backdrop.SetPixel(0, 0, Color.white);
|
||||||
|
_backdrop.Apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_label != null && _styleHeight == Screen.height)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_styleHeight = Screen.height;
|
||||||
|
_label = new GUIStyle(GUI.skin.label);
|
||||||
|
_label.alignment = TextAnchor.MiddleCenter;
|
||||||
|
_label.wordWrap = false;
|
||||||
|
_label.clipping = TextClipping.Clip;
|
||||||
|
_label.padding = new RectOffset(12, 12, 0, 0);
|
||||||
|
_label.fontSize = Mathf.Clamp(Mathf.RoundToInt(Screen.height * 0.018f), 14, 28);
|
||||||
|
_label.normal.textColor = Color.white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Dom bus soundtrack is event:/Music/Bus/Home, on the game's Music buses.
|
||||||
|
// Muting those buses leaves the events running, so the track returns when the radio turns off.
|
||||||
|
static class GameMusic
|
||||||
|
{
|
||||||
|
sealed class HeldBus
|
||||||
|
{
|
||||||
|
public FMOD.Studio.Bus Bus;
|
||||||
|
public bool WasMuted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly List<HeldBus> _held = new List<HeldBus>();
|
||||||
|
static bool _loggedMiss;
|
||||||
|
static float _nextDiscover;
|
||||||
|
|
||||||
|
public static void Hold()
|
||||||
|
{
|
||||||
|
if (!DomRadioPlugin.MuteGameMusic.Value)
|
||||||
|
return;
|
||||||
|
if (!RuntimeManager.IsInitialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_held.Count == 0 && Time.unscaledTime >= _nextDiscover)
|
||||||
|
{
|
||||||
|
_nextDiscover = Time.unscaledTime + 1f;
|
||||||
|
Discover();
|
||||||
|
}
|
||||||
|
|
||||||
|
int valid = 0;
|
||||||
|
for (int i = 0; i < _held.Count; i++)
|
||||||
|
{
|
||||||
|
FMOD.Studio.Bus bus = _held[i].Bus;
|
||||||
|
if (!bus.isValid())
|
||||||
|
continue;
|
||||||
|
valid++;
|
||||||
|
bus.setMute(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Banks reload with a new level. Drop dead handles so the next pass can find the music buses again.
|
||||||
|
if (_held.Count > 0 && valid == 0)
|
||||||
|
{
|
||||||
|
_held.Clear();
|
||||||
|
_nextDiscover = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Release()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _held.Count; i++)
|
||||||
|
{
|
||||||
|
FMOD.Studio.Bus bus = _held[i].Bus;
|
||||||
|
if (bus.isValid())
|
||||||
|
bus.setMute(_held[i].WasMuted);
|
||||||
|
}
|
||||||
|
|
||||||
|
_held.Clear();
|
||||||
|
_loggedMiss = false;
|
||||||
|
_nextDiscover = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Discover()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var studio = RuntimeManager.StudioSystem;
|
||||||
|
if (!studio.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Il2CppStructArray<FMOD.Studio.Bank> banks = null;
|
||||||
|
if (studio.getBankList(out banks) != FMOD.RESULT.OK || banks == null || banks.Length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string filter = DomRadioPlugin.MusicBusFilter.Value ?? string.Empty;
|
||||||
|
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var others = new List<string>();
|
||||||
|
for (int i = 0; i < banks.Length; i++)
|
||||||
|
{
|
||||||
|
FMOD.Studio.Bank bank = banks[i];
|
||||||
|
if (!bank.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Il2CppStructArray<FMOD.Studio.Bus> buses = null;
|
||||||
|
if (bank.getBusList(out buses) != FMOD.RESULT.OK || buses == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (int b = 0; b < buses.Length; b++)
|
||||||
|
{
|
||||||
|
FMOD.Studio.Bus bus = buses[b];
|
||||||
|
if (!bus.isValid())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string path = null;
|
||||||
|
if (bus.getPath(out path) != FMOD.RESULT.OK || string.IsNullOrEmpty(path))
|
||||||
|
continue;
|
||||||
|
if (!seen.Add(path))
|
||||||
|
continue;
|
||||||
|
if (filter.Length == 0 || path.IndexOf(filter, StringComparison.OrdinalIgnoreCase) < 0)
|
||||||
|
{
|
||||||
|
others.Add(path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasMuted = false;
|
||||||
|
bus.getMute(out wasMuted);
|
||||||
|
_held.Add(new HeldBus { Bus = bus, WasMuted = wasMuted });
|
||||||
|
DomRadioPlugin.LogSource.LogInfo("Muting " + path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_held.Count == 0 && !_loggedMiss)
|
||||||
|
{
|
||||||
|
_loggedMiss = true;
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("No music bus matched \"" + filter + "\". Dom music will keep playing. Buses: " + string.Join(", ", others));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
DomRadioPlugin.LogSource.LogWarning("Could not find the music buses: " + ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<AssemblyName>Skopje.DomRadio</AssemblyName>
|
||||||
|
<RootNamespace>Skopje.DomRadio</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="FMODUnity">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\FMODUnity.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.IMGUIModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.IMGUIModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.TextRenderingModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.TextRenderingModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.InputLegacyModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.InputLegacyModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v6.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v6.0": {
|
||||||
|
"Skopje.DomRadio/1.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"Skopje.DomRadio.dll": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Skopje.DomRadio/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
|
||||||
|
"projectName": "Skopje.DomRadio",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.DomRadio")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.DomRadio")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.DomRadio")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fb5aa0eaad5177616647c1fd58891f07eda98dfbaa1c7f1588f2c8bfe53d1402
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net6.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v6.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property.EntryPointFilePath =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = Skopje.DomRadio
|
||||||
|
build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\DomRadio\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
081092add6543b8dcb5ec27e0ed95920f8750f6a3a0b82e5686acb95102220e9
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.csproj.AssemblyReference.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.AssemblyInfoInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.AssemblyInfo.cs
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\DomRadioPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.deps.json
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\bin\Release\net6.0\Skopje.DomRadio.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\Skopje.DomRadio.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\refint\Skopje.DomRadio.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\Skopje.DomRadio.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\DomRadio\obj\Release\net6.0\ref\Skopje.DomRadio.dll
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"targets": {
|
||||||
|
"net6.0": {}
|
||||||
|
},
|
||||||
|
"libraries": {},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net6.0": []
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
|
||||||
|
"projectName": "Skopje.DomRadio",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "3mbnNdPiqio=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\DomRadio\\DomRadioPlugin.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
@@ -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.Minimap</AssemblyName>
|
||||||
|
<RootNamespace>Skopje.Minimap</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="UnityEngine.CoreModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.CoreModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.UI">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.UI.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="DoozyEngineAssembly">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\DoozyEngineAssembly.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.PhysicsModule">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\UnityEngine.PhysicsModule.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Archon.SwissArmyLib">
|
||||||
|
<HintPath>$(GameDir)\BepInEx\interop\Archon.SwissArmyLib.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"runtimeTarget": {
|
||||||
|
"name": ".NETCoreApp,Version=v6.0",
|
||||||
|
"signature": ""
|
||||||
|
},
|
||||||
|
"compilationOptions": {},
|
||||||
|
"targets": {
|
||||||
|
".NETCoreApp,Version=v6.0": {
|
||||||
|
"Skopje.Minimap/1.0.0": {
|
||||||
|
"runtime": {
|
||||||
|
"Skopje.Minimap.dll": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"Skopje.Minimap/1.0.0": {
|
||||||
|
"type": "project",
|
||||||
|
"serviceable": false,
|
||||||
|
"sha512": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
|
||||||
|
"projectName": "Skopje.Minimap",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.Minimap")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+9f4f8bf5235790c04718d7ca24b0f3f76e93b270")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("Skopje.Minimap")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("Skopje.Minimap")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
76a35bd1005e7595549b8f53ede6d7fe291948c74a3299c2ccd64bca53d35f29
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net6.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v6.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property.EntryPointFilePath =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = Skopje.Minimap
|
||||||
|
build_property.ProjectDir = c:\Users\andre\Desktop\SkopjeMods\Minimap\
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
d64656e1d47566a242332baae27415b1f54265daad457ca67758136236ded997
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.csproj.AssemblyReference.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.GeneratedMSBuildEditorConfig.editorconfig
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.AssemblyInfoInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.AssemblyInfo.cs
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\MinimapPlugin.csproj.CoreCompileInputs.cache
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.deps.json
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\bin\Release\net6.0\Skopje.Minimap.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\Skopje.Minimap.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\refint\Skopje.Minimap.dll
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\Skopje.Minimap.pdb
|
||||||
|
c:\Users\andre\Desktop\SkopjeMods\Minimap\obj\Release\net6.0\ref\Skopje.Minimap.dll
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"targets": {
|
||||||
|
"net6.0": {}
|
||||||
|
},
|
||||||
|
"libraries": {},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net6.0": []
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
|
||||||
|
"projectName": "Skopje.Minimap",
|
||||||
|
"projectPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
|
||||||
|
"packagesPath": "C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget",
|
||||||
|
"outputPath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\obj\\",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||||
|
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.300"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"framework": "net6.0",
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.WindowsDesktop.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301\\RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "fEqRFu6N7Gw=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "c:\\Users\\andre\\Desktop\\SkopjeMods\\Minimap\\MinimapPlugin.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"C:\\Users\\andre\\AppData\\Local\\Temp\\cursor-sandbox-cache\\a0404811fc6e51c4c5761fd11591789f\\nuget\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
Mods for Skopje 83 using BepInEx IL2CPP.
|
Mods for Skopje 83 using BepInEx IL2CPP.
|
||||||
|
|
||||||
|
## Instructions to install mods
|
||||||
|
|
||||||
|
1. Download BepInEx 6 IL2CPP
|
||||||
|
https://builds.bepinex.dev/projects/bepinex_be
|
||||||
|
|
||||||
|
Version: Artifact 789
|
||||||
|
File: BepInEx-Unity.IL2CPP-win-x64-6.0.0-be.789+6cea286.zip
|
||||||
|
|
||||||
|
2. Launch game once to initialize BepInEx classes and folders
|
||||||
|
|
||||||
|
3. Drop mods in plugins and config where needed
|
||||||
|
|
||||||
## Mods
|
## Mods
|
||||||
|
|
||||||
| Mod | Description | Version | Status |
|
| Mod | Description | Version | Status |
|
||||||
@@ -10,3 +22,6 @@ Mods for Skopje 83 using BepInEx IL2CPP.
|
|||||||
| Boss Health | On-screen health bar for the current boss. | 1.0.0 |  |
|
| Boss Health | On-screen health bar for the current boss. | 1.0.0 |  |
|
||||||
| Horde | Multiplies enemy counts, health, and damage, globally or per zone. | 1.0.0 |  |
|
| Horde | Multiplies enemy counts, health, and damage, globally or per zone. | 1.0.0 |  |
|
||||||
| Third Person | Over-the-shoulder camera that can orbit around the player. | 1.0.0 |  |
|
| Third Person | Over-the-shoulder camera that can orbit around the player. | 1.0.0 |  |
|
||||||
|
| Custom Player Skins | Replaces each outfit from its own PNG in `CustomSkins`: singleplayer, ranger, medic, engineer, and fighter. | 1.0.0 |  |
|
||||||
|
| Minimap | Small map under the clock, using the in-game map. | 1.0.0 |  |
|
||||||
|
| DOM Radio | Plays your mp3 and wav files inside the Dom, and mutes its music while the radio is on. | 1.0.0 |  |
|
||||||
@@ -4,12 +4,12 @@ using BepInEx;
|
|||||||
using BepInEx.Configuration;
|
using BepInEx.Configuration;
|
||||||
using BepInEx.Logging;
|
using BepInEx.Logging;
|
||||||
using BepInEx.Unity.IL2CPP;
|
using BepInEx.Unity.IL2CPP;
|
||||||
using Cinemachine;
|
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using Il2CppInterop.Runtime;
|
using Il2CppInterop.Runtime;
|
||||||
using Il2CppInterop.Runtime.Injection;
|
using Il2CppInterop.Runtime.Injection;
|
||||||
using Skopje.Modding;
|
using Skopje.Modding;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
namespace Skopje.ThirdPerson
|
namespace Skopje.ThirdPerson
|
||||||
{
|
{
|
||||||
@@ -18,7 +18,7 @@ namespace Skopje.ThirdPerson
|
|||||||
{
|
{
|
||||||
public const string PluginGuid = "com.skopje.thirdperson";
|
public const string PluginGuid = "com.skopje.thirdperson";
|
||||||
public const string PluginName = "Skopje Third Person";
|
public const string PluginName = "Skopje Third Person";
|
||||||
public const string PluginVersion = "1.0.0";
|
public const string PluginVersion = "1.2.0";
|
||||||
|
|
||||||
internal static ManualLogSource LogSource;
|
internal static ManualLogSource LogSource;
|
||||||
|
|
||||||
@@ -46,12 +46,12 @@ namespace Skopje.ThirdPerson
|
|||||||
LogSource = Log;
|
LogSource = Log;
|
||||||
Enabled = Config.Bind("Camera", "Enabled", true, "Over-the-shoulder camera on the right. The body keeps its own facing so the camera can orbit around to the face.");
|
Enabled = Config.Bind("Camera", "Enabled", true, "Over-the-shoulder camera on the right. The body keeps its own facing so the camera can orbit around to the face.");
|
||||||
FaceMovement = Config.Bind("Camera", "FaceMovement", false, "Turn the body toward the direction you move. Leave this off to keep the body still while the camera orbits.");
|
FaceMovement = Config.Bind("Camera", "FaceMovement", false, "Turn the body toward the direction you move. Leave this off to keep the body still while the camera orbits.");
|
||||||
Distance = Config.Bind("Camera", "Distance", 2.7f, "Meters behind the aim axis.");
|
Distance = Config.Bind("Camera", "Distance", 3.6f, "Meters behind the eyes.");
|
||||||
Shoulder = Config.Bind("Camera", "Shoulder", 0.62f, "Meters to the right of the aim axis.");
|
Shoulder = Config.Bind("Camera", "Shoulder", 1.05f, "Meters to the right of the eyes. Kept even when a wall shortens the boom.");
|
||||||
Height = Config.Bind("Camera", "Height", 0.22f, "Meters above the pivot.");
|
Height = Config.Bind("Camera", "Height", 0.4f, "Meters above the eyes.");
|
||||||
PivotHeight = Config.Bind("Camera", "PivotHeight", 1.25f, "Orbit height above the feet, before crouch.");
|
PivotHeight = Config.Bind("Camera", "PivotHeight", 1.25f, "Unused. The view is offset from the eye camera so interiors stay aligned.");
|
||||||
AdsDistance = Config.Bind("Camera", "AdsDistance", 1.55f, "Distance while aiming down sights.");
|
AdsDistance = Config.Bind("Camera", "AdsDistance", 2.1f, "Distance while aiming down sights.");
|
||||||
AdsShoulder = Config.Bind("Camera", "AdsShoulder", 0.4f, "Right offset while aiming down sights.");
|
AdsShoulder = Config.Bind("Camera", "AdsShoulder", 0.75f, "Right offset while aiming down sights.");
|
||||||
FovBonus = Config.Bind("Camera", "FovBonus", 8f, "Extra field of view. Drops to zero while aiming.");
|
FovBonus = Config.Bind("Camera", "FovBonus", 8f, "Extra field of view. Drops to zero while aiming.");
|
||||||
AimDistance = Config.Bind("Camera", "AimDistance", 80f, "Crosshair trace length when nothing is under the reticle.");
|
AimDistance = Config.Bind("Camera", "AimDistance", 80f, "Crosshair trace length when nothing is under the reticle.");
|
||||||
|
|
||||||
@@ -73,9 +73,11 @@ namespace Skopje.ThirdPerson
|
|||||||
_driverHost.AddComponent<ThirdPersonDriver>();
|
_driverHost.AddComponent<ThirdPersonDriver>();
|
||||||
|
|
||||||
_harmony = new Harmony(PluginGuid);
|
_harmony = new Harmony(PluginGuid);
|
||||||
Patch(AccessTools.Method(typeof(SkopjeFPSCamera), "LateUpdate"), nameof(ThirdPersonPatches.AfterCamera));
|
Patch(AccessTools.Method(typeof(RenderPipelineManager), "BeginCameraRendering"), nameof(ThirdPersonPatches.AfterBeginCamera));
|
||||||
Patch(AccessTools.Method(typeof(CinemachineBrain), "LateUpdate"), nameof(ThirdPersonPatches.AfterBrain));
|
Patch(AccessTools.Method(typeof(RenderPipelineManager), "EndCameraRendering"), nameof(ThirdPersonPatches.AfterEndCamera));
|
||||||
Patch(AccessTools.Method(typeof(SkopjeCharacterController), "UpdateRotation"), nameof(ThirdPersonPatches.AfterBodyRotation), nameof(ThirdPersonPatches.BeforeBodyRotation));
|
Patch(AccessTools.Method(typeof(SkopjeCharacterController), "UpdateRotation"), nameof(ThirdPersonPatches.AfterBodyRotation), nameof(ThirdPersonPatches.BeforeBodyRotation));
|
||||||
|
PatchInteract(AccessTools.Method(typeof(InteractController), "Update"));
|
||||||
|
PatchInteract(AccessTools.Method(typeof(InteractController), "LateUpdate"));
|
||||||
|
|
||||||
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Config: {Config.ConfigFilePath}");
|
LogSource.LogInfo($"{PluginName} {PluginVersion} loaded. Config: {Config.ConfigFilePath}");
|
||||||
}
|
}
|
||||||
@@ -94,6 +96,21 @@ namespace Skopje.ThirdPerson
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PatchInteract(MethodInfo method)
|
||||||
|
{
|
||||||
|
if (method == null)
|
||||||
|
{
|
||||||
|
LogSource.LogWarning("Third person hook was not found: InteractController");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_harmony.Patch(
|
||||||
|
method,
|
||||||
|
prefix: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.BeforeInteract)),
|
||||||
|
postfix: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.AfterInteract)),
|
||||||
|
finalizer: new HarmonyMethod(typeof(ThirdPersonPatches), nameof(ThirdPersonPatches.RestoreInteract)));
|
||||||
|
}
|
||||||
|
|
||||||
static void Patch(MethodInfo method, string postfix, string prefix = null)
|
static void Patch(MethodInfo method, string postfix, string prefix = null)
|
||||||
{
|
{
|
||||||
if (method == null)
|
if (method == null)
|
||||||
@@ -121,14 +138,14 @@ namespace Skopje.ThirdPerson
|
|||||||
|
|
||||||
public static class ThirdPersonPatches
|
public static class ThirdPersonPatches
|
||||||
{
|
{
|
||||||
public static void AfterCamera(SkopjeFPSCamera __instance)
|
public static void AfterBeginCamera(ScriptableRenderContext context, Camera camera)
|
||||||
{
|
{
|
||||||
ThirdPersonRig.Apply(__instance);
|
ThirdPersonRig.PushView(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AfterBrain()
|
public static void AfterEndCamera(ScriptableRenderContext context, Camera camera)
|
||||||
{
|
{
|
||||||
ThirdPersonRig.Apply(ThirdPersonRig.LocalCamera);
|
ThirdPersonRig.PopView(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BeforeBodyRotation(SkopjeCharacterController __instance, ref Quaternion currentRotation)
|
public static void BeforeBodyRotation(SkopjeCharacterController __instance, ref Quaternion currentRotation)
|
||||||
@@ -141,5 +158,21 @@ namespace Skopje.ThirdPerson
|
|||||||
{
|
{
|
||||||
ThirdPersonRig.HoldBodyFacing(__instance, ref currentRotation, deltaTime);
|
ThirdPersonRig.HoldBodyFacing(__instance, ref currentRotation, deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void BeforeInteract()
|
||||||
|
{
|
||||||
|
ThirdPersonRig.HideBodiesFromInteract();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AfterInteract()
|
||||||
|
{
|
||||||
|
ThirdPersonRig.RestoreInteractLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Exception RestoreInteract(Exception __exception)
|
||||||
|
{
|
||||||
|
ThirdPersonRig.RestoreInteractLayers();
|
||||||
|
return __exception;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
|
||||||
namespace Skopje.ThirdPerson
|
namespace Skopje.ThirdPerson
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace Skopje.ThirdPerson
|
|||||||
|
|
||||||
void LateUpdate()
|
void LateUpdate()
|
||||||
{
|
{
|
||||||
ThirdPersonRig.Apply(ThirdPersonRig.LocalCamera);
|
ThirdPersonRig.PrepareView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,32 +23,50 @@ namespace Skopje.ThirdPerson
|
|||||||
const float MoveThreshold = 0.2f;
|
const float MoveThreshold = 0.2f;
|
||||||
const float ExternalTurn = 35f;
|
const float ExternalTurn = 35f;
|
||||||
|
|
||||||
static readonly List<Renderer> Hidden = new List<Renderer>();
|
static readonly List<Renderer> BodyRenderers = new List<Renderer>();
|
||||||
|
static readonly List<Renderer> ArmRenderers = new List<Renderer>();
|
||||||
|
static readonly List<Renderer> ShadowOnly = new List<Renderer>();
|
||||||
|
static readonly List<Collider> InteractColliders = new List<Collider>();
|
||||||
|
static readonly List<int> InteractLayers = new List<int>();
|
||||||
static readonly RaycastHit[] Hits = new RaycastHit[24];
|
static readonly RaycastHit[] Hits = new RaycastHit[24];
|
||||||
|
|
||||||
internal static SkopjeFPSCamera LocalCamera;
|
|
||||||
|
|
||||||
static SkopjePlayer _player;
|
static SkopjePlayer _player;
|
||||||
static bool _holding;
|
static bool _holding;
|
||||||
static bool _hasFacing;
|
static bool _hasFacing;
|
||||||
static Quaternion _facing;
|
static Quaternion _facing;
|
||||||
static Quaternion _incomingFacing;
|
static Quaternion _incomingFacing;
|
||||||
static bool _hasIncoming;
|
static bool _hasIncoming;
|
||||||
static bool _hasLook;
|
|
||||||
static Quaternion _look = Quaternion.identity;
|
|
||||||
static bool _hasPlacement;
|
|
||||||
static Vector3 _placedPos;
|
|
||||||
static float _baseFov;
|
|
||||||
static float _boom = 2.7f;
|
static float _boom = 2.7f;
|
||||||
static float _ads;
|
static float _ads;
|
||||||
static bool _capturedIk;
|
static bool _capturedIk;
|
||||||
static bool _aimWasEnabled;
|
static bool _aimWasEnabled;
|
||||||
static bool _lookWasEnabled;
|
static bool _lookWasEnabled;
|
||||||
static bool _followWasEnabled;
|
|
||||||
static bool _disabledFollow;
|
|
||||||
static float _nextScan;
|
static float _nextScan;
|
||||||
static int _errors;
|
static int _errors;
|
||||||
static bool _logged;
|
static bool _logged;
|
||||||
|
static int _viewId;
|
||||||
|
static bool _hasView;
|
||||||
|
static Vector3 _viewPos;
|
||||||
|
static Quaternion _viewRot;
|
||||||
|
static float _viewFov;
|
||||||
|
static bool _pushed;
|
||||||
|
static Vector3 _savedPos;
|
||||||
|
static Quaternion _savedRot;
|
||||||
|
static float _savedFov;
|
||||||
|
static bool _maskSaved;
|
||||||
|
static int _savedMask;
|
||||||
|
static int _bodyLayers;
|
||||||
|
static GameObject _thirdRoot;
|
||||||
|
static Vector3 _thirdScale;
|
||||||
|
static bool _scaledThird;
|
||||||
|
static bool _control;
|
||||||
|
static IntPtr _controllerPtr;
|
||||||
|
static Camera _viewCamera;
|
||||||
|
static Vector3 _movePlanar;
|
||||||
|
static float _turnSharpness;
|
||||||
|
static int _interactMask = -1;
|
||||||
|
static bool _layersShifted;
|
||||||
|
static float _nextColliderScan;
|
||||||
|
|
||||||
public static void OnClientStarted()
|
public static void OnClientStarted()
|
||||||
{
|
{
|
||||||
@@ -57,7 +76,6 @@ namespace Skopje.ThirdPerson
|
|||||||
{
|
{
|
||||||
Release();
|
Release();
|
||||||
_player = null;
|
_player = null;
|
||||||
LocalCamera = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OnPlayerStarted(SkopjePlayer player)
|
public static void OnPlayerStarted(SkopjePlayer player)
|
||||||
@@ -72,7 +90,6 @@ namespace Skopje.ThirdPerson
|
|||||||
{
|
{
|
||||||
Release();
|
Release();
|
||||||
_player = null;
|
_player = null;
|
||||||
LocalCamera = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +97,12 @@ namespace Skopje.ThirdPerson
|
|||||||
{
|
{
|
||||||
Release();
|
Release();
|
||||||
_player = null;
|
_player = null;
|
||||||
LocalCamera = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TickBody()
|
public static void TickBody()
|
||||||
{
|
{
|
||||||
if (!CanControl(_player))
|
RefreshControl();
|
||||||
|
if (!_control)
|
||||||
{
|
{
|
||||||
if (_holding)
|
if (_holding)
|
||||||
Release();
|
Release();
|
||||||
@@ -94,30 +111,14 @@ namespace Skopje.ThirdPerson
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HoldPresentation(_player);
|
CacheInteractMask();
|
||||||
}
|
if (Time.unscaledTime >= _nextColliderScan)
|
||||||
catch (Exception ex)
|
{
|
||||||
{
|
_nextColliderScan = Time.unscaledTime + 0.5f;
|
||||||
Fail(ex);
|
CacheInteractColliders();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
Present(_player, null);
|
||||||
public static void Apply(SkopjeFPSCamera fps)
|
|
||||||
{
|
|
||||||
if (fps == null || !CanControl(fps.Player))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Camera cam = fps.mainCamera != null ? fps.mainCamera : Camera.main;
|
|
||||||
if (cam == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LocalCamera = fps;
|
|
||||||
if (_player == null)
|
|
||||||
_player = fps.Player;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Place(fps, cam);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -125,9 +126,10 @@ namespace Skopje.ThirdPerson
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRotation runs inside an IL2CPP call. Touching IsOwner from here re-enters the runtime and crashes the process.
|
||||||
public static void RememberBody(SkopjeCharacterController controller, Quaternion currentRotation)
|
public static void RememberBody(SkopjeCharacterController controller, Quaternion currentRotation)
|
||||||
{
|
{
|
||||||
if (!CanControl(controller != null ? controller.Player : null))
|
if (!_control || controller is null || controller.Pointer != _controllerPtr)
|
||||||
{
|
{
|
||||||
_hasIncoming = false;
|
_hasIncoming = false;
|
||||||
return;
|
return;
|
||||||
@@ -139,25 +141,18 @@ namespace Skopje.ThirdPerson
|
|||||||
|
|
||||||
public static void HoldBodyFacing(SkopjeCharacterController controller, ref Quaternion currentRotation, float deltaTime)
|
public static void HoldBodyFacing(SkopjeCharacterController controller, ref Quaternion currentRotation, float deltaTime)
|
||||||
{
|
{
|
||||||
if (!_hasIncoming || controller == null || !CanControl(controller.Player))
|
if (!_hasIncoming || !_control || controller is null || controller.Pointer != _controllerPtr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Quaternion held = _hasFacing ? _facing : _incomingFacing;
|
Quaternion held = _hasFacing ? _facing : _incomingFacing;
|
||||||
if (!_hasFacing || Quaternion.Angle(held, _incomingFacing) > ExternalTurn)
|
if (!_hasFacing || Quaternion.Angle(held, _incomingFacing) > ExternalTurn)
|
||||||
held = _incomingFacing;
|
held = _incomingFacing;
|
||||||
|
|
||||||
if (ThirdPersonPlugin.FaceMovement.Value)
|
if (ThirdPersonPlugin.FaceMovement.Value && _movePlanar.sqrMagnitude > MoveThreshold * MoveThreshold)
|
||||||
{
|
{
|
||||||
Vector3 move = controller.moveInputVector;
|
float sharp = _turnSharpness > 0.01f ? _turnSharpness : 12f;
|
||||||
move.y = 0f;
|
float blend = 1f - Mathf.Exp(-sharp * Mathf.Max(0f, deltaTime));
|
||||||
if (move.sqrMagnitude > MoveThreshold * MoveThreshold)
|
held = Quaternion.Slerp(held, Quaternion.LookRotation(_movePlanar.normalized, Vector3.up), blend);
|
||||||
{
|
|
||||||
float sharp = controller.OrientationSharpness;
|
|
||||||
if (sharp < 0.01f)
|
|
||||||
sharp = 12f;
|
|
||||||
float blend = 1f - Mathf.Exp(-sharp * Mathf.Max(0f, deltaTime));
|
|
||||||
held = Quaternion.Slerp(held, Quaternion.LookRotation(move.normalized, Vector3.up), blend);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_facing = FlatYaw(held);
|
_facing = FlatYaw(held);
|
||||||
@@ -165,68 +160,119 @@ namespace Skopje.ThirdPerson
|
|||||||
currentRotation = _facing;
|
currentRotation = _facing;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Place(SkopjeFPSCamera fps, Camera cam)
|
static void RefreshControl()
|
||||||
{
|
{
|
||||||
Quaternion look = ReadLook(fps, cam);
|
_control = false;
|
||||||
_look = look;
|
_controllerPtr = IntPtr.Zero;
|
||||||
_hasLook = true;
|
if (!CanControl(_player))
|
||||||
|
return;
|
||||||
|
|
||||||
bool fresh = !_hasPlacement || (cam.transform.position - _placedPos).sqrMagnitude > 0.0004f;
|
SkopjeCharacterController body = _player.SkopjeCharacterController;
|
||||||
if (fresh)
|
SkopjeFPSCamera camera = _player.SkopjeFPSCamera;
|
||||||
_baseFov = cam.fieldOfView;
|
if (body is null || camera is null || body.Pointer == IntPtr.Zero || camera.Pointer == IntPtr.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
float adsTarget = fps.adsMode ? 1f : 0f;
|
_controllerPtr = body.Pointer;
|
||||||
_ads = Mathf.MoveTowards(_ads, adsTarget, Time.deltaTime / 0.12f);
|
_movePlanar = body.moveInputVector;
|
||||||
|
_movePlanar.y = 0f;
|
||||||
|
_turnSharpness = body.OrientationSharpness;
|
||||||
|
_control = true;
|
||||||
|
}
|
||||||
|
|
||||||
float distance = Mathf.Lerp(Clamp(ThirdPersonPlugin.Distance.Value, 0.8f, 8f), Clamp(ThirdPersonPlugin.AdsDistance.Value, 0.6f, 6f), _ads);
|
public static void PrepareView()
|
||||||
float shoulder = Mathf.Lerp(Clamp(ThirdPersonPlugin.Shoulder.Value, 0f, 2f), Clamp(ThirdPersonPlugin.AdsShoulder.Value, 0f, 1.5f), _ads);
|
{
|
||||||
float height = Clamp(ThirdPersonPlugin.Height.Value, -1f, 2f);
|
if (_pushed && _viewCamera != null)
|
||||||
float boom = new Vector3(shoulder, height, -distance).magnitude;
|
|
||||||
if (!_hasPlacement)
|
|
||||||
_boom = boom;
|
|
||||||
_boom = Mathf.Lerp(_boom, boom, 1f - Mathf.Exp(-16f * Time.deltaTime));
|
|
||||||
|
|
||||||
Vector3 pivot = Pivot(fps.Player);
|
|
||||||
Vector3 offset = look * new Vector3(shoulder, height, -distance);
|
|
||||||
if (offset.sqrMagnitude < 0.0001f)
|
|
||||||
offset = look * Vector3.back;
|
|
||||||
Vector3 dir = offset.normalized;
|
|
||||||
float allowed = PullIn(fps.Player, pivot, dir, _boom);
|
|
||||||
Vector3 camPos = pivot + dir * allowed;
|
|
||||||
|
|
||||||
Vector3 aim = AimPoint(fps.Player, pivot, look * Vector3.forward);
|
|
||||||
Vector3 toAim = aim - camPos;
|
|
||||||
Vector3 up = look * Vector3.up;
|
|
||||||
if (toAim.sqrMagnitude < 0.0001f || Mathf.Abs(Vector3.Dot(toAim.normalized, up.normalized)) > 0.995f)
|
|
||||||
cam.transform.rotation = look;
|
|
||||||
else
|
|
||||||
cam.transform.rotation = Quaternion.LookRotation(toAim, up);
|
|
||||||
|
|
||||||
cam.transform.position = camPos;
|
|
||||||
cam.fieldOfView = _baseFov + Clamp(ThirdPersonPlugin.FovBonus.Value, 0f, 40f) * (1f - _ads);
|
|
||||||
_placedPos = camPos;
|
|
||||||
_hasPlacement = true;
|
|
||||||
KeepBodyYaw(fps.Player);
|
|
||||||
|
|
||||||
if (!_logged)
|
|
||||||
{
|
{
|
||||||
_logged = true;
|
_viewCamera.transform.SetPositionAndRotation(_savedPos, _savedRot);
|
||||||
ThirdPersonPlugin.LogSource.LogInfo("Right-shoulder camera is active. The body does not turn with the mouse.");
|
_viewCamera.fieldOfView = _savedFov;
|
||||||
|
_pushed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasView = false;
|
||||||
|
_viewId = 0;
|
||||||
|
if (!_control || _player is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SkopjeFPSCamera fps = _player.SkopjeFPSCamera;
|
||||||
|
if (fps is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Camera cam = fps.mainCamera != null ? fps.mainCamera : Camera.main;
|
||||||
|
if (cam == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Offset from the live eye camera. Offset interiors move that camera, not the root position.
|
||||||
|
Quaternion look = ReadLook(fps, cam);
|
||||||
|
Vector3 eye = cam.transform.position;
|
||||||
|
float adsTarget = fps.adsMode ? 1f : 0f;
|
||||||
|
_ads = Mathf.MoveTowards(_ads, adsTarget, Time.deltaTime / 0.12f);
|
||||||
|
|
||||||
|
float distance = Mathf.Lerp(Clamp(ThirdPersonPlugin.Distance.Value, 0.8f, 8f), Clamp(ThirdPersonPlugin.AdsDistance.Value, 0.6f, 6f), _ads);
|
||||||
|
float shoulder = Mathf.Lerp(Clamp(ThirdPersonPlugin.Shoulder.Value, 0f, 2.5f), Clamp(ThirdPersonPlugin.AdsShoulder.Value, 0f, 2f), _ads);
|
||||||
|
float height = Clamp(ThirdPersonPlugin.Height.Value, -1f, 2f);
|
||||||
|
if (_boom <= 0.01f)
|
||||||
|
_boom = distance;
|
||||||
|
_boom = Mathf.Lerp(_boom, distance, 1f - Mathf.Exp(-16f * Time.deltaTime));
|
||||||
|
|
||||||
|
Vector3 side = eye + look * new Vector3(shoulder, height, 0f);
|
||||||
|
Vector3 back = look * Vector3.back;
|
||||||
|
float allowed = PullIn(_player, side, back, _boom);
|
||||||
|
Vector3 camPos = side + back * allowed;
|
||||||
|
Vector3 aim = AimPoint(_player, eye, look * Vector3.forward);
|
||||||
|
Vector3 toAim = aim - camPos;
|
||||||
|
Vector3 up = look * Vector3.up;
|
||||||
|
|
||||||
|
_viewPos = camPos;
|
||||||
|
_viewFov = cam.fieldOfView + Clamp(ThirdPersonPlugin.FovBonus.Value, 0f, 40f) * (1f - _ads);
|
||||||
|
if (toAim.sqrMagnitude < 0.0001f || up.sqrMagnitude < 0.0001f || Mathf.Abs(Vector3.Dot(toAim.normalized, up.normalized)) > 0.995f)
|
||||||
|
_viewRot = look;
|
||||||
|
else
|
||||||
|
_viewRot = Quaternion.LookRotation(toAim, up);
|
||||||
|
|
||||||
|
_viewId = cam.GetInstanceID();
|
||||||
|
_viewCamera = cam;
|
||||||
|
_hasView = true;
|
||||||
|
Present(_player, cam);
|
||||||
|
|
||||||
|
if (!_logged)
|
||||||
|
{
|
||||||
|
_logged = true;
|
||||||
|
ThirdPersonPlugin.LogSource.LogInfo("Right-shoulder view is active. World streaming still uses the eye camera.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Fail(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void KeepBodyYaw(SkopjePlayer player)
|
// Render-only. The gameplay camera stays on the eyes so chunk streaming does not follow the shoulder.
|
||||||
|
public static void PushView(Camera camera)
|
||||||
{
|
{
|
||||||
if (!_hasFacing || player == null)
|
if (!_hasView || camera == null || camera.GetInstanceID() != _viewId || _pushed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform body = player.transform;
|
_savedPos = camera.transform.position;
|
||||||
Vector3 up = body.up;
|
_savedRot = camera.transform.rotation;
|
||||||
Vector3 forward = Vector3.ProjectOnPlane(_facing * Vector3.forward, up);
|
_savedFov = camera.fieldOfView;
|
||||||
if (forward.sqrMagnitude < 0.0001f)
|
camera.transform.SetPositionAndRotation(_viewPos, _viewRot);
|
||||||
|
camera.fieldOfView = _viewFov;
|
||||||
|
if (_maskSaved)
|
||||||
|
camera.cullingMask |= _bodyLayers;
|
||||||
|
RevealCached();
|
||||||
|
_pushed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void PopView(Camera camera)
|
||||||
|
{
|
||||||
|
if (!_pushed || camera == null || camera.GetInstanceID() != _viewId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
body.rotation = Quaternion.LookRotation(forward.normalized, up);
|
camera.transform.SetPositionAndRotation(_savedPos, _savedRot);
|
||||||
|
camera.fieldOfView = _savedFov;
|
||||||
|
_pushed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Quaternion ReadLook(SkopjeFPSCamera fps, Camera cam)
|
static Quaternion ReadLook(SkopjeFPSCamera fps, Camera cam)
|
||||||
@@ -235,8 +281,6 @@ namespace Skopje.ThirdPerson
|
|||||||
return fps.VCamera.transform.rotation;
|
return fps.VCamera.transform.rotation;
|
||||||
if (fps.SocketTransform != null && fps.SocketTransform != cam.transform)
|
if (fps.SocketTransform != null && fps.SocketTransform != cam.transform)
|
||||||
return fps.SocketTransform.rotation;
|
return fps.SocketTransform.rotation;
|
||||||
if (_hasLook && _hasPlacement && (cam.transform.position - _placedPos).sqrMagnitude < 0.0004f)
|
|
||||||
return _look;
|
|
||||||
return cam.transform.rotation;
|
return cam.transform.rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,15 +340,24 @@ namespace Skopje.ThirdPerson
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void HoldPresentation(SkopjePlayer player)
|
static void Present(SkopjePlayer player, Camera cam)
|
||||||
{
|
{
|
||||||
GameObject third = player.ThirdPerson;
|
GameObject third = player.ThirdPerson;
|
||||||
if (third != null && !third.activeSelf)
|
if (third != null)
|
||||||
third.SetActive(true);
|
{
|
||||||
|
if (!third.activeSelf)
|
||||||
|
third.SetActive(true);
|
||||||
|
if (!_scaledThird && third.transform.localScale.sqrMagnitude < 0.0001f)
|
||||||
|
{
|
||||||
|
_thirdScale = third.transform.localScale;
|
||||||
|
third.transform.localScale = Vector3.one;
|
||||||
|
_scaledThird = true;
|
||||||
|
_thirdRoot = third;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GameObject pack = player.ThirdPersonBackpackParent;
|
if (player.ThirdPersonBackpackParent != null && !player.ThirdPersonBackpackParent.activeSelf)
|
||||||
if (pack != null && !pack.activeSelf)
|
player.ThirdPersonBackpackParent.SetActive(true);
|
||||||
pack.SetActive(true);
|
|
||||||
|
|
||||||
var anim = player.TPAnimationController;
|
var anim = player.TPAnimationController;
|
||||||
if (anim != null)
|
if (anim != null)
|
||||||
@@ -322,56 +375,69 @@ namespace Skopje.ThirdPerson
|
|||||||
anim.LookAtMotor.enabled = false;
|
anim.LookAtMotor.enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var follow = player.ThirdPersonFollowCam;
|
|
||||||
if (follow != null && follow.Look != null && follow.Look.enabled)
|
|
||||||
{
|
|
||||||
_followWasEnabled = true;
|
|
||||||
follow.Look.enabled = false;
|
|
||||||
_disabledFollow = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Time.unscaledTime >= _nextScan)
|
if (Time.unscaledTime >= _nextScan)
|
||||||
{
|
{
|
||||||
_nextScan = Time.unscaledTime + 0.25f;
|
_nextScan = Time.unscaledTime + 0.25f;
|
||||||
RebuildHidden(player);
|
CacheVisuals(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < Hidden.Count; i++)
|
RevealCached();
|
||||||
{
|
if (cam != null)
|
||||||
if (Hidden[i] != null)
|
IncludeBodyLayers(cam);
|
||||||
Hidden[i].forceRenderingOff = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
_holding = true;
|
_holding = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RebuildHidden(SkopjePlayer player)
|
static void CacheVisuals(SkopjePlayer player)
|
||||||
{
|
{
|
||||||
var next = new List<Renderer>();
|
RestoreArms();
|
||||||
|
BodyRenderers.Clear();
|
||||||
|
ArmRenderers.Clear();
|
||||||
|
_bodyLayers = 0;
|
||||||
|
|
||||||
Transform third = player.ThirdPerson != null ? player.ThirdPerson.transform : null;
|
Transform third = player.ThirdPerson != null ? player.ThirdPerson.transform : null;
|
||||||
Collect(player.FirstPerson, third, next);
|
if (player.ThirdPersonRenderer != null)
|
||||||
|
AddBody(player.ThirdPersonRenderer);
|
||||||
var weapons = player.WeaponsController;
|
if (third != null)
|
||||||
if (weapons != null)
|
|
||||||
{
|
{
|
||||||
if (weapons.Socket != null)
|
Renderer[] found = third.GetComponentsInChildren<Renderer>(true);
|
||||||
Collect(weapons.Socket.gameObject, third, next);
|
for (int i = 0; i < found.Length; i++)
|
||||||
if (weapons.ThrowSocket != null)
|
AddBody(found[i]);
|
||||||
Collect(weapons.ThrowSocket.gameObject, third, next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < Hidden.Count; i++)
|
if (player.SkopjeCharacterController != null && player.SkopjeCharacterController.MeshRoot != null)
|
||||||
{
|
{
|
||||||
Renderer old = Hidden[i];
|
Renderer[] found = player.SkopjeCharacterController.MeshRoot.GetComponentsInChildren<Renderer>(true);
|
||||||
if (old != null && !next.Contains(old))
|
for (int i = 0; i < found.Length; i++)
|
||||||
old.forceRenderingOff = false;
|
{
|
||||||
|
Renderer renderer = found[i];
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
if (third != null && (renderer.transform == third || renderer.transform.IsChildOf(third)))
|
||||||
|
AddBody(renderer);
|
||||||
|
else if (player.FirstPerson == null || !renderer.transform.IsChildOf(player.FirstPerson.transform))
|
||||||
|
AddBody(renderer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Hidden.Clear();
|
CollectArms(player.FirstPerson, third);
|
||||||
Hidden.AddRange(next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Collect(GameObject root, Transform thirdPerson, List<Renderer> into)
|
static void AddBody(Renderer renderer)
|
||||||
|
{
|
||||||
|
if (renderer == null || BodyRenderers.Contains(renderer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
BodyRenderers.Add(renderer);
|
||||||
|
_bodyLayers |= 1 << renderer.gameObject.layer;
|
||||||
|
if (renderer.shadowCastingMode == ShadowCastingMode.ShadowsOnly && !ShadowOnly.Contains(renderer))
|
||||||
|
{
|
||||||
|
ShadowOnly.Add(renderer);
|
||||||
|
renderer.shadowCastingMode = ShadowCastingMode.On;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CollectArms(GameObject root, Transform thirdPerson)
|
||||||
{
|
{
|
||||||
if (root == null)
|
if (root == null)
|
||||||
return;
|
return;
|
||||||
@@ -382,24 +448,181 @@ namespace Skopje.ThirdPerson
|
|||||||
for (int i = 0; i < found.Length; i++)
|
for (int i = 0; i < found.Length; i++)
|
||||||
{
|
{
|
||||||
Renderer renderer = found[i];
|
Renderer renderer = found[i];
|
||||||
if (renderer == null)
|
if (renderer == null || BodyRenderers.Contains(renderer) || ArmRenderers.Contains(renderer))
|
||||||
continue;
|
continue;
|
||||||
if (thirdPerson != null && renderer.transform.IsChildOf(thirdPerson))
|
if (thirdPerson != null && renderer.transform.IsChildOf(thirdPerson))
|
||||||
continue;
|
continue;
|
||||||
if (!into.Contains(renderer))
|
ArmRenderers.Add(renderer);
|
||||||
into.Add(renderer);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RevealCached()
|
||||||
|
{
|
||||||
|
if (_thirdRoot != null && !_thirdRoot.activeSelf)
|
||||||
|
_thirdRoot.SetActive(true);
|
||||||
|
|
||||||
|
for (int i = 0; i < BodyRenderers.Count; i++)
|
||||||
|
{
|
||||||
|
Renderer renderer = BodyRenderers[i];
|
||||||
|
if (renderer == null)
|
||||||
|
continue;
|
||||||
|
renderer.forceRenderingOff = false;
|
||||||
|
renderer.enabled = true;
|
||||||
|
if (renderer is SkinnedMeshRenderer skin)
|
||||||
|
skin.updateWhenOffscreen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ArmRenderers.Count; i++)
|
||||||
|
{
|
||||||
|
if (ArmRenderers[i] != null)
|
||||||
|
ArmRenderers[i].forceRenderingOff = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void IncludeBodyLayers(Camera cam)
|
||||||
|
{
|
||||||
|
if (_bodyLayers == 0)
|
||||||
|
return;
|
||||||
|
if (!_maskSaved)
|
||||||
|
{
|
||||||
|
_savedMask = cam.cullingMask;
|
||||||
|
_maskSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cam.cullingMask |= _bodyLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HideBodiesFromInteract()
|
||||||
|
{
|
||||||
|
RestoreInteractLayers();
|
||||||
|
if (_interactMask == 0 || InteractColliders.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int free = 2;
|
||||||
|
if ((_interactMask & (1 << free)) != 0)
|
||||||
|
{
|
||||||
|
free = -1;
|
||||||
|
for (int layer = 8; layer < 32; layer++)
|
||||||
|
{
|
||||||
|
if ((_interactMask & (1 << layer)) == 0)
|
||||||
|
{
|
||||||
|
free = layer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < InteractColliders.Count; i++)
|
||||||
|
{
|
||||||
|
Collider collider = InteractColliders[i];
|
||||||
|
if (collider == null)
|
||||||
|
{
|
||||||
|
InteractLayers.Add(0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int layer = collider.gameObject.layer;
|
||||||
|
InteractLayers.Add(layer);
|
||||||
|
if ((_interactMask & (1 << layer)) != 0)
|
||||||
|
collider.gameObject.layer = free;
|
||||||
|
}
|
||||||
|
|
||||||
|
_layersShifted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RestoreInteractLayers()
|
||||||
|
{
|
||||||
|
if (!_layersShifted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = InteractColliders.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
Collider collider = InteractColliders[i];
|
||||||
|
if (collider != null && i < InteractLayers.Count)
|
||||||
|
collider.gameObject.layer = InteractLayers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
InteractLayers.Clear();
|
||||||
|
_layersShifted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CacheInteractMask()
|
||||||
|
{
|
||||||
|
InteractController interact = _player.InteractController;
|
||||||
|
if (interact == null)
|
||||||
|
{
|
||||||
|
_interactMask = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_interactMask = interact.BlockingLayers.value | interact.InteractLayers.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CacheInteractColliders()
|
||||||
|
{
|
||||||
|
if (_layersShifted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
InteractColliders.Clear();
|
||||||
|
SkopjePlayer[] players = UnityEngine.Object.FindObjectsOfType<SkopjePlayer>();
|
||||||
|
for (int i = 0; i < players.Length; i++)
|
||||||
|
AddPlayerColliders(players[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AddPlayerColliders(SkopjePlayer player)
|
||||||
|
{
|
||||||
|
if (player == null || player.ThirdPerson == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Collider owner = player.OwnerCollider;
|
||||||
|
Collider[] found = player.ThirdPerson.GetComponentsInChildren<Collider>(true);
|
||||||
|
for (int i = 0; i < found.Length; i++)
|
||||||
|
{
|
||||||
|
Collider collider = found[i];
|
||||||
|
if (collider == null || collider.isTrigger || collider == owner || InteractColliders.Contains(collider))
|
||||||
|
continue;
|
||||||
|
InteractColliders.Add(collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RestoreArms()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ArmRenderers.Count; i++)
|
||||||
|
{
|
||||||
|
if (ArmRenderers[i] != null)
|
||||||
|
ArmRenderers[i].forceRenderingOff = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Release()
|
static void Release()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < Hidden.Count; i++)
|
if (_pushed && _viewCamera != null)
|
||||||
{
|
{
|
||||||
if (Hidden[i] != null)
|
_viewCamera.transform.SetPositionAndRotation(_savedPos, _savedRot);
|
||||||
Hidden[i].forceRenderingOff = false;
|
_viewCamera.fieldOfView = _savedFov;
|
||||||
}
|
}
|
||||||
|
|
||||||
Hidden.Clear();
|
if (_maskSaved && _viewCamera != null)
|
||||||
|
_viewCamera.cullingMask = _savedMask;
|
||||||
|
|
||||||
|
for (int i = 0; i < ShadowOnly.Count; i++)
|
||||||
|
{
|
||||||
|
if (ShadowOnly[i] != null)
|
||||||
|
ShadowOnly[i].shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_scaledThird && _thirdRoot != null)
|
||||||
|
_thirdRoot.transform.localScale = _thirdScale;
|
||||||
|
|
||||||
|
RestoreInteractLayers();
|
||||||
|
InteractColliders.Clear();
|
||||||
|
RestoreArms();
|
||||||
|
BodyRenderers.Clear();
|
||||||
|
ArmRenderers.Clear();
|
||||||
|
ShadowOnly.Clear();
|
||||||
|
|
||||||
if (_player != null)
|
if (_player != null)
|
||||||
{
|
{
|
||||||
@@ -412,9 +635,6 @@ namespace Skopje.ThirdPerson
|
|||||||
anim.LookAtMotor.enabled = _lookWasEnabled;
|
anim.LookAtMotor.enabled = _lookWasEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_disabledFollow && _player.ThirdPersonFollowCam != null && _player.ThirdPersonFollowCam.Look != null)
|
|
||||||
_player.ThirdPersonFollowCam.Look.enabled = _followWasEnabled;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_player.RefreshActiveModels();
|
_player.RefreshActiveModels();
|
||||||
@@ -426,14 +646,20 @@ namespace Skopje.ThirdPerson
|
|||||||
}
|
}
|
||||||
|
|
||||||
_capturedIk = false;
|
_capturedIk = false;
|
||||||
_disabledFollow = false;
|
|
||||||
_holding = false;
|
_holding = false;
|
||||||
_hasFacing = false;
|
_hasFacing = false;
|
||||||
_hasIncoming = false;
|
_hasIncoming = false;
|
||||||
_hasPlacement = false;
|
|
||||||
_hasLook = false;
|
|
||||||
_ads = 0f;
|
_ads = 0f;
|
||||||
_logged = false;
|
_logged = false;
|
||||||
|
_control = false;
|
||||||
|
_controllerPtr = IntPtr.Zero;
|
||||||
|
_hasView = false;
|
||||||
|
_pushed = false;
|
||||||
|
_maskSaved = false;
|
||||||
|
_scaledThird = false;
|
||||||
|
_thirdRoot = null;
|
||||||
|
_viewCamera = null;
|
||||||
|
_bodyLayers = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CanControl(SkopjePlayer player)
|
static bool CanControl(SkopjePlayer player)
|
||||||
|
|||||||