mirror of
https://github.com/hanetzer/sdraw_mods_cvr.git
synced 2025-09-03 18:39:23 +00:00
New mod: PlayerAvatarHistory
Incremented version for nightly to prevent rollback by auto-updater Minor fixes
This commit is contained in:
parent
7f29079109
commit
9f78aa4620
23 changed files with 575 additions and 23 deletions
14
ml_pah/AvatarEntry.cs
Normal file
14
ml_pah/AvatarEntry.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace ml_pah
|
||||
{
|
||||
[Serializable]
|
||||
class AvatarEntry
|
||||
{
|
||||
public string m_id;
|
||||
public string m_name;
|
||||
public string m_imageUrl;
|
||||
public DateTime m_lastUsageDate;
|
||||
public bool m_cached = false;
|
||||
}
|
||||
}
|
205
ml_pah/HistoryManager.cs
Normal file
205
ml_pah/HistoryManager.cs
Normal file
|
@ -0,0 +1,205 @@
|
|||
using ABI_RC.Core.Networking.API;
|
||||
using ABI_RC.Core.Networking.API.Responses;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ml_pah
|
||||
{
|
||||
static class HistoryManager
|
||||
{
|
||||
internal class EntriesUpdateEvent
|
||||
{
|
||||
event Action m_action;
|
||||
public void AddListener(Action p_listener) => m_action += p_listener;
|
||||
public void RemoveListener(Action p_listener) => m_action -= p_listener;
|
||||
public void Invoke() => m_action?.Invoke();
|
||||
}
|
||||
|
||||
public static readonly EntriesUpdateEvent OnEntriesUpdated = new EntriesUpdateEvent();
|
||||
|
||||
static bool ms_initialized = false;
|
||||
static string ms_historyPath;
|
||||
readonly static List<AvatarEntry> ms_avatarEntries = new List<AvatarEntry>();
|
||||
|
||||
static int ms_lastTick = 0;
|
||||
|
||||
// Init
|
||||
internal static void Initialize()
|
||||
{
|
||||
if(!ms_initialized)
|
||||
{
|
||||
ms_historyPath = Path.Combine(MelonLoader.Utils.MelonEnvironment.UserDataDirectory, "PlayerAvatarHistory.json");
|
||||
|
||||
try
|
||||
{
|
||||
if(File.Exists(ms_historyPath))
|
||||
{
|
||||
string l_json = File.ReadAllText(ms_historyPath);
|
||||
List<AvatarEntry> l_entries = JsonConvert.DeserializeObject<List<AvatarEntry>>(l_json);
|
||||
if(l_entries != null)
|
||||
{
|
||||
ms_avatarEntries.AddRange(l_entries);
|
||||
LimitEntries();
|
||||
ms_avatarEntries.Sort((a, b) => a.m_lastUsageDate.CompareTo(b.m_lastUsageDate));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
|
||||
ms_lastTick = Environment.TickCount;
|
||||
Settings.OnAutosaveTimeChanged.AddListener(OnAutosaveTimeChanged);
|
||||
|
||||
ms_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Shutdown()
|
||||
{
|
||||
if(ms_initialized)
|
||||
{
|
||||
SaveHistory();
|
||||
|
||||
Settings.OnAutosaveTimeChanged.RemoveListener(OnAutosaveTimeChanged);
|
||||
|
||||
ms_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update
|
||||
public static void Update()
|
||||
{
|
||||
if(ms_initialized && (Settings.AutosaveTime > 0))
|
||||
{
|
||||
int l_tick = Environment.TickCount;
|
||||
if((l_tick - ms_lastTick) >= (Settings.AutosaveTime * 60000))
|
||||
{
|
||||
MelonLoader.MelonCoroutines.Start(AutosaveCoroutine());
|
||||
ms_lastTick = l_tick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entries
|
||||
internal static List<AvatarEntry> GetAvatarEntries() => ms_avatarEntries;
|
||||
|
||||
internal static void AddEntry(string p_id)
|
||||
{
|
||||
if(ms_initialized)
|
||||
{
|
||||
int l_index = ms_avatarEntries.FindIndex(l_entry => l_entry.m_id == p_id);
|
||||
if(l_index != -1)
|
||||
{
|
||||
ms_avatarEntries[l_index].m_lastUsageDate = DateTime.Now;
|
||||
|
||||
if(l_index != 0)
|
||||
{
|
||||
// Move in list
|
||||
AvatarEntry l_entry = ms_avatarEntries[l_index];
|
||||
ms_avatarEntries.RemoveAt(l_index);
|
||||
ms_avatarEntries.Insert(0, l_entry);
|
||||
|
||||
OnEntriesUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AvatarEntry l_entry = new AvatarEntry();
|
||||
l_entry.m_id = p_id;
|
||||
l_entry.m_name = "Loading ...";
|
||||
l_entry.m_lastUsageDate = DateTime.Now;
|
||||
|
||||
MelonLoader.MelonCoroutines.Start(RequestAvatarInfo(l_entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// History
|
||||
internal static void ClearHistory() => ms_avatarEntries.Clear();
|
||||
|
||||
internal static void SaveHistory()
|
||||
{
|
||||
if(ms_initialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
string l_json = JsonConvert.SerializeObject(ms_avatarEntries, Formatting.Indented);
|
||||
File.WriteAllText(ms_historyPath, l_json);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static IEnumerator AutosaveCoroutine()
|
||||
{
|
||||
List<AvatarEntry> l_listCopy = new List<AvatarEntry>();
|
||||
l_listCopy.AddRange(ms_avatarEntries);
|
||||
|
||||
Task l_task = Task.Run(() => AutosaveTask(l_listCopy));
|
||||
while(!l_task.IsCompleted)
|
||||
yield return null;
|
||||
}
|
||||
|
||||
static async Task AutosaveTask(List<AvatarEntry> p_entries)
|
||||
{
|
||||
try
|
||||
{
|
||||
string l_json = JsonConvert.SerializeObject(p_entries, Formatting.Indented);
|
||||
File.WriteAllText(ms_historyPath, l_json);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
// Network request
|
||||
static IEnumerator RequestAvatarInfo(AvatarEntry p_entry)
|
||||
{
|
||||
Task l_task = Task.Run(() => RequestAvatarInfoTask(p_entry));
|
||||
while(!l_task.IsCompleted)
|
||||
yield return null;
|
||||
|
||||
ms_avatarEntries.Insert(0, p_entry);
|
||||
LimitEntries();
|
||||
OnEntriesUpdated?.Invoke();
|
||||
}
|
||||
|
||||
static async Task RequestAvatarInfoTask(AvatarEntry p_entry)
|
||||
{
|
||||
BaseResponse<AvatarDetailsResponse> l_baseResponse = await ApiConnection.MakeRequest<AvatarDetailsResponse>(ApiConnection.ApiOperation.AvatarDetail, new { avatarID = p_entry.m_id });
|
||||
if(l_baseResponse != null)
|
||||
{
|
||||
if(!l_baseResponse.IsSuccessStatusCode) return;
|
||||
p_entry.m_name = l_baseResponse.Data.Name;
|
||||
p_entry.m_imageUrl = l_baseResponse.Data.ImageUrl;
|
||||
p_entry.m_cached = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
static void OnAutosaveTimeChanged(int p_value)
|
||||
{
|
||||
ms_lastTick = Environment.TickCount;
|
||||
}
|
||||
|
||||
// Utility
|
||||
static void LimitEntries()
|
||||
{
|
||||
int l_currentLimit = Settings.AvatarsLimit;
|
||||
if(ms_avatarEntries.Count > l_currentLimit)
|
||||
ms_avatarEntries.RemoveRange(l_currentLimit, ms_avatarEntries.Count - (l_currentLimit - 1));
|
||||
}
|
||||
}
|
||||
}
|
59
ml_pah/Main.cs
Normal file
59
ml_pah/Main.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using ABI.CCK.Components;
|
||||
using ABI_RC.Core;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using ABI_RC.Systems.GameEventSystem;
|
||||
|
||||
namespace ml_pah
|
||||
{
|
||||
public class PlayerAvatarHistory : MelonLoader.MelonMod
|
||||
{
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Settings.Init();
|
||||
HistoryManager.Initialize();
|
||||
ModUi.Initialize();
|
||||
MelonLoader.MelonCoroutines.Start(WaitForRootLogic());
|
||||
}
|
||||
|
||||
public override void OnDeinitializeMelon()
|
||||
{
|
||||
CVRGameEventSystem.Avatar.OnLocalAvatarLoad.RemoveListener(this.OnLocalAvatarLoad);
|
||||
HistoryManager.OnEntriesUpdated.RemoveListener(this.OnHistoryEntriesUpdated);
|
||||
|
||||
ModUi.Shutdown();
|
||||
HistoryManager.Shutdown();
|
||||
}
|
||||
|
||||
IEnumerator WaitForRootLogic()
|
||||
{
|
||||
while(RootLogic.Instance == null)
|
||||
yield return null;
|
||||
|
||||
CVRGameEventSystem.Avatar.OnLocalAvatarLoad.AddListener(this.OnLocalAvatarLoad);
|
||||
HistoryManager.OnEntriesUpdated.AddListener(this.OnHistoryEntriesUpdated);
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
HistoryManager.Update();
|
||||
}
|
||||
|
||||
// Game events
|
||||
void OnLocalAvatarLoad(CVRAvatar p_avatar)
|
||||
{
|
||||
try
|
||||
{
|
||||
if((p_avatar.AssetInfo != null) && (p_avatar.AssetInfo.objectId.Length > 0))
|
||||
HistoryManager.AddEntry(p_avatar.AssetInfo.objectId);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Mod events
|
||||
void OnHistoryEntriesUpdated() => ModUi.UpdateAvatarsList();
|
||||
}
|
||||
}
|
131
ml_pah/ModUi.cs
Normal file
131
ml_pah/ModUi.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
using ABI_RC.Core.EventSystem;
|
||||
using ABI_RC.Core.InteractionSystem;
|
||||
using BTKUILib.UIObjects;
|
||||
using BTKUILib.UIObjects.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ml_pah
|
||||
{
|
||||
static class ModUi
|
||||
{
|
||||
readonly static string ms_namespace = typeof(ModUi).Namespace;
|
||||
static bool ms_initialized = false;
|
||||
|
||||
static Page ms_page = null;
|
||||
|
||||
static Category ms_settingsCategory = null;
|
||||
static Button ms_settingsClearButton = null;
|
||||
static Button ms_settingSaveButton = null;
|
||||
static SliderFloat ms_settingsEntriesLimit = null;
|
||||
static SliderFloat ms_settingsAutosaveTime = null;
|
||||
|
||||
static Category ms_buttonsCategory = null;
|
||||
static readonly List<Button> ms_avatarButtons = new List<Button>();
|
||||
|
||||
// Init
|
||||
internal static void Initialize()
|
||||
{
|
||||
if(!ms_initialized)
|
||||
{
|
||||
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerAvatarHistory", "guardian", GetIconStream("guardian.png"));
|
||||
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerAvatarHistory", "delete", GetIconStream("delete.png"));
|
||||
BTKUILib.QuickMenuAPI.PrepareIcon("PlayerAvatarHistory", "save", GetIconStream("save.png"));
|
||||
|
||||
ms_page = new Page("PlayerAvatarHistory", "MainPage", true, "guardian");
|
||||
ms_page.MenuTitle = "Player Avatar History";
|
||||
ms_page.MenuSubtitle = "List of last used avatars";
|
||||
|
||||
ms_settingsCategory = ms_page.AddCategory("Settings");
|
||||
|
||||
ms_settingsClearButton = ms_settingsCategory.AddButton("Clear history", "delete", "Clear current history");
|
||||
ms_settingsClearButton.OnPress += ClearHistory;
|
||||
|
||||
ms_settingSaveButton = ms_settingsCategory.AddButton("Save history", "save", "Manually save current history");
|
||||
ms_settingSaveButton.OnPress += SaveHistory;
|
||||
|
||||
ms_settingsEntriesLimit = ms_settingsCategory.AddSlider("History limit", "Number of saved avatar history entries.<p>Warning: Large value can impact performance.", Settings.AvatarsLimit, 10f, 100f, 0);
|
||||
ms_settingsEntriesLimit.OnValueUpdated += OnAvatarLimitChange;
|
||||
|
||||
ms_settingsAutosaveTime = ms_settingsCategory.AddSlider("Autosave period", "Automatic history saving in minutes.", Settings.AutosaveTime, 0f, 60f, 0);
|
||||
ms_settingsAutosaveTime.OnValueUpdated += OnAutosaveTimeChange;
|
||||
|
||||
ms_buttonsCategory = ms_page.AddCategory("Avatars");
|
||||
RegenerateAvatarButtons();
|
||||
|
||||
ms_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Shutdown()
|
||||
{
|
||||
if(ms_initialized)
|
||||
{
|
||||
ms_page = null;
|
||||
|
||||
ms_buttonsCategory = null;
|
||||
ms_avatarButtons.Clear();
|
||||
|
||||
ms_settingsCategory = null;
|
||||
ms_settingSaveButton = null;
|
||||
ms_settingsClearButton = null;
|
||||
|
||||
ms_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// History
|
||||
static void ClearHistory()
|
||||
{
|
||||
BTKUILib.QuickMenuAPI.ShowConfirm(
|
||||
"Clear history", "Are you sure want to clear all avatar history?",
|
||||
() =>
|
||||
{
|
||||
HistoryManager.ClearHistory();
|
||||
RegenerateAvatarButtons();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static void SaveHistory() => HistoryManager.SaveHistory();
|
||||
|
||||
// Update
|
||||
public static void UpdateAvatarsList()
|
||||
{
|
||||
if(ms_initialized)
|
||||
RegenerateAvatarButtons();
|
||||
}
|
||||
|
||||
// Settings
|
||||
static void OnAvatarLimitChange(float p_value) => Settings.SetSetting(Settings.ModSetting.AvatarsLimit, (int)p_value);
|
||||
static void OnAutosaveTimeChange(float p_value) => Settings.SetSetting(Settings.ModSetting.AutosaveTime, (int)p_value);
|
||||
|
||||
// Utility
|
||||
static void RegenerateAvatarButtons()
|
||||
{
|
||||
if(ms_avatarButtons.Count > 0)
|
||||
{
|
||||
foreach(Button l_button in ms_avatarButtons)
|
||||
l_button.Delete();
|
||||
ms_avatarButtons.Clear();
|
||||
}
|
||||
|
||||
foreach(AvatarEntry l_entry in HistoryManager.GetAvatarEntries())
|
||||
{
|
||||
Button l_button = ms_buttonsCategory.AddButton("", "", "", ButtonStyle.FullSizeImage);
|
||||
l_button.ButtonText = (l_entry.m_cached ? l_entry.m_name : "Loading ...");
|
||||
l_button.ButtonIcon = (l_entry.m_cached ? l_entry.m_imageUrl : "");
|
||||
l_button.ButtonTooltip = string.Format("Click to open avatar page, hold to switch avatar.<p>Last used time: {0}", l_entry.m_lastUsageDate.ToString("g"));
|
||||
|
||||
l_button.OnPress += () => ViewManager.Instance.RequestAvatarDetailsPage(l_entry.m_id);
|
||||
l_button.OnHeld += () => AssetManagement.Instance.LoadLocalAvatarFromNetwork(l_entry.m_id);
|
||||
|
||||
ms_avatarButtons.Add(l_button);
|
||||
}
|
||||
}
|
||||
|
||||
static Stream GetIconStream(string p_name) => Assembly.GetExecutingAssembly().GetManifestResourceStream(ms_namespace + ".resources." + p_name);
|
||||
}
|
||||
}
|
4
ml_pah/Properties/AssemblyInfo.cs
Normal file
4
ml_pah/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,4 @@
|
|||
[assembly: MelonLoader.MelonInfo(typeof(ml_pah.PlayerAvatarHistory), "PlayerAvatarHistory", "1.0.0", "SDraw", "https://github.com/SDraw/ml_mods_cvr")]
|
||||
[assembly: MelonLoader.MelonGame(null, "ChilloutVR")]
|
||||
[assembly: MelonLoader.MelonPlatform(MelonLoader.MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonLoader.MelonPlatformDomain(MelonLoader.MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
77
ml_pah/Settings.cs
Normal file
77
ml_pah/Settings.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ml_pah
|
||||
{
|
||||
static class Settings
|
||||
{
|
||||
internal class SettingEvent<T>
|
||||
{
|
||||
event Action<T> m_action;
|
||||
public void AddListener(Action<T> p_listener) => m_action += p_listener;
|
||||
public void RemoveListener(Action<T> p_listener) => m_action -= p_listener;
|
||||
public void Invoke(T p_value) => m_action?.Invoke(p_value);
|
||||
}
|
||||
|
||||
public enum ModSetting
|
||||
{
|
||||
AvatarsLimit = 0,
|
||||
AutosaveTime
|
||||
}
|
||||
|
||||
public static int AvatarsLimit { get; private set; } = 12;
|
||||
public static int AutosaveTime { get; private set; } = 15;
|
||||
|
||||
public static readonly SettingEvent<int> OnAvatarsLimitChanged = new SettingEvent<int>();
|
||||
public static readonly SettingEvent<int> OnAutosaveTimeChanged = new SettingEvent<int>();
|
||||
|
||||
static MelonLoader.MelonPreferences_Category ms_category = null;
|
||||
static List<MelonLoader.MelonPreferences_Entry> ms_entries = null;
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
ms_category = MelonLoader.MelonPreferences.CreateCategory("PAH", "Player Avatar History");
|
||||
|
||||
ms_entries = new List<MelonLoader.MelonPreferences_Entry>()
|
||||
{
|
||||
ms_category.CreateEntry(ModSetting.AvatarsLimit.ToString(), AvatarsLimit),
|
||||
ms_category.CreateEntry(ModSetting.AutosaveTime.ToString(), AutosaveTime)
|
||||
};
|
||||
|
||||
AvatarsLimit = Mathf.Clamp((int)ms_entries[(int)ModSetting.AvatarsLimit].BoxedValue, 10, 100);
|
||||
AutosaveTime = Mathf.Clamp((int)ms_entries[(int)ModSetting.AutosaveTime].BoxedValue, 0, 60);
|
||||
}
|
||||
|
||||
public static void SetSetting(ModSetting p_settings, object p_value)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch(p_settings)
|
||||
{
|
||||
// Booleans
|
||||
case ModSetting.AvatarsLimit:
|
||||
{
|
||||
AvatarsLimit = (int)p_value;
|
||||
OnAvatarsLimitChanged.Invoke(AvatarsLimit);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModSetting.AutosaveTime:
|
||||
{
|
||||
AutosaveTime = (int)p_value;
|
||||
OnAutosaveTimeChanged.Invoke(AutosaveTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(ms_entries != null)
|
||||
ms_entries[(int)p_settings].BoxedValue = p_value;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
MelonLoader.MelonLogger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
60
ml_pah/ml_pah.csproj
Normal file
60
ml_pah/ml_pah.csproj
Normal file
|
@ -0,0 +1,60 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<PackageId>PlayerAvatarHistory</PackageId>
|
||||
<AssemblyName>PlayerAvatarHistory</AssemblyName>
|
||||
<Authors>SDraw</Authors>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<DebugType>embedded</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="resources\delete.png" />
|
||||
<None Remove="resources\guardian.png" />
|
||||
<None Remove="resources\save.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="resources\delete.png" />
|
||||
<EmbeddedResource Include="resources\guardian.png" />
|
||||
<EmbeddedResource Include="resources\save.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Assembly-CSharp">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="BTKUILib">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\Mods\BTKUILib.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="MelonLoader">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\MelonLoader\net35\MelonLoader.dll</HintPath>
|
||||
<Private>false</Private>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Newtonsoft.Json.dll</HintPath>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
<Reference Include="UnityEngine.CoreModule">
|
||||
<HintPath>D:\games\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
|
||||
<SpecificVersion>false</SpecificVersion>
|
||||
<Private>false</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy /y "$(TargetPath)" "D:\Games\Steam\steamapps\common\ChilloutVR\Mods\"" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
BIN
ml_pah/resources/delete.png
Normal file
BIN
ml_pah/resources/delete.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
BIN
ml_pah/resources/guardian.png
Normal file
BIN
ml_pah/resources/guardian.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
BIN
ml_pah/resources/save.png
Normal file
BIN
ml_pah/resources/save.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
Loading…
Add table
Add a link
Reference in a new issue