first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract
{
public interface IColorSwapManager
{
Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer);
void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer);
}
}

View File

@@ -0,0 +1,9 @@
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract
{
public interface IColorSwapStrategy
{
Color SwapColor(Color originalColor);
}
}

View File

@@ -0,0 +1,11 @@
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract
{
public interface ILocalNitroxPlayer : INitroxPlayer
{
//This serves as a "clean" player model to clone from. The root player model is going to be recolored as well
//which would change our HSV filter parameters. Who wants to hit a moving target?
GameObject BodyPrototype { get; }
}
}

View File

@@ -0,0 +1,13 @@
using NitroxModel.MultiplayerSession;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract
{
public interface INitroxPlayer
{
GameObject Body { get; }
GameObject PlayerModel { get; }
string PlayerName { get; }
PlayerSettings PlayerSettings { get; }
}
}

View File

@@ -0,0 +1,7 @@
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract
{
public interface IPlayerModelBuilder
{
void Build(INitroxPlayer player);
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class ColorSwapAsyncOperation
{
private readonly INitroxPlayer nitroxPlayer;
private readonly IEnumerable<IColorSwapManager> colorSwapManagers;
private readonly Dictionary<string, Color[]> texturePixelIndexes;
private int taskCount = -1;
public ColorSwapAsyncOperation(INitroxPlayer nitroxPlayer, IEnumerable<IColorSwapManager> colorSwapManagers)
{
this.nitroxPlayer = nitroxPlayer;
this.colorSwapManagers = colorSwapManagers;
texturePixelIndexes = new Dictionary<string, Color[]>();
}
public void UpdateIndex(string indexKey, Color[] pixels)
{
lock (texturePixelIndexes)
{
if (texturePixelIndexes.ContainsKey(indexKey))
{
throw new ArgumentException($"Texture index key {indexKey} already exists.");
}
texturePixelIndexes.Add(indexKey, pixels);
}
}
public bool IsColorSwapComplete()
{
return taskCount == 0;
}
public ColorSwapAsyncOperation BeginColorSwap()
{
if (taskCount >= 0)
{
throw new InvalidOperationException("This operation has already been started.");
}
List<Action<ColorSwapAsyncOperation>> tasks = colorSwapManagers
.Select(configuration => configuration.CreateColorSwapTask(nitroxPlayer))
.ToList();
taskCount = tasks.Count;
tasks.ForEach(task => ThreadPool.QueueUserWorkItem(ExecuteTask, task));
return this;
}
public void ApplySwappedColors()
{
if (taskCount != 0)
{
throw new InvalidOperationException("Colors must be swapped before the changes can be applied to the player model.");
}
colorSwapManagers.ForEach(manager => manager.ApplyPlayerColor(texturePixelIndexes, nitroxPlayer));
}
private void ExecuteTask(object state)
{
if (state is not Action<ColorSwapAsyncOperation> task)
{
//TODO: We need to handle job cancellation during stabilization to ensure that the client shuts down gracefully.
throw new ArgumentException("Cannot execute a null task.", nameof(state));
}
task.Invoke(this);
Interlocked.Decrement(ref taskCount);
}
}
}

View File

@@ -0,0 +1,19 @@
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class ColorValueRange
{
private readonly float min;
private readonly float max;
public ColorValueRange(float min, float max)
{
this.min = min;
this.max = max;
}
public bool Covers(float targetValue)
{
return min <= targetValue && targetValue <= max;
}
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class DiveSuitColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer diveSuitRenderer = playerModel.GetRenderer(DIVE_SUIT_GAME_OBJECT_NAME);
diveSuitRenderer.material.ApplyClonedTexture();
diveSuitRenderer.materials[1].ApplyClonedTexture();
Color[] bodyTexturePixels = diveSuitRenderer.material.GetMainTexturePixels();
Color[] armTexturePixels = diveSuitRenderer.materials[1].GetMainTexturePixels();
return operation =>
{
HsvSwapper diveSuitFilter = new HsvSwapper(colorSwapStrategy);
diveSuitFilter.SetHueRange(5f, 45f);
diveSuitFilter.SwapColors(bodyTexturePixels);
diveSuitFilter.SwapColors(armTexturePixels);
operation.UpdateIndex(DIVE_SUIT_INDEX_KEY, bodyTexturePixels);
operation.UpdateIndex(DIVE_SUIT_ARMS_INDEX_KEY, armTexturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] bodyPixels = pixelIndex[DIVE_SUIT_INDEX_KEY];
Color[] armSleevesPixels = pixelIndex[DIVE_SUIT_ARMS_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer renderer = playerModel.GetRenderer(DIVE_SUIT_GAME_OBJECT_NAME);
Material torsoMaterial = renderer.material;
torsoMaterial.UpdateMainTextureColors(bodyPixels);
Material armsMaterial = renderer.materials[1];
armsMaterial.UpdateMainTextureColors(armSleevesPixels);
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class FinColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer basicFinRenderer = playerModel.GetRenderer(FINS_GAME_OBJECT_NAME);
basicFinRenderer.material.ApplyClonedTexture();
SkinnedMeshRenderer chargedFinRenderer = playerModel.GetRenderer(CHARGED_FINS_GAME_OBJECT_NAME);
chargedFinRenderer.material.ApplyClonedTexture();
SkinnedMeshRenderer glideFinRenderer = playerModel.GetRenderer(GLIDE_FINS_GAME_OBJECT_NAME);
glideFinRenderer.material.ApplyClonedTexture();
//All fin models use the same texture.
Color[] texturePixels = basicFinRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper finFilter = new HsvSwapper(colorSwapStrategy);
finFilter.SetHueRange(0f, 35f);
finFilter.SwapColors(texturePixels);
operation.UpdateIndex(FINS_INDEX_KEY, texturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] pixels = pixelIndex[FINS_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer basicFinRenderer = playerModel.GetRenderer(FINS_GAME_OBJECT_NAME);
basicFinRenderer.material.UpdateMainTextureColors(pixels);
basicFinRenderer.material.SetTexture("_MainTex", basicFinRenderer.material.mainTexture);
basicFinRenderer.material.SetTexture("_SpecTex", basicFinRenderer.material.mainTexture);
SkinnedMeshRenderer chargedFinRenderer = playerModel.GetRenderer(CHARGED_FINS_GAME_OBJECT_NAME);
chargedFinRenderer.material.UpdateMainTextureColors(pixels);
chargedFinRenderer.material.SetTexture("_MainTex", chargedFinRenderer.material.mainTexture);
chargedFinRenderer.material.SetTexture("_SpecTex", chargedFinRenderer.material.mainTexture);
SkinnedMeshRenderer glideFinRenderer = playerModel.GetRenderer(GLIDE_FINS_GAME_OBJECT_NAME);
glideFinRenderer.material.UpdateMainTextureColors(pixels);
glideFinRenderer.material.SetTexture("_MainTex", glideFinRenderer.material.mainTexture);
glideFinRenderer.material.SetTexture("_SpecTex", glideFinRenderer.material.mainTexture);
}
}
}

View File

@@ -0,0 +1,75 @@
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class HsvSwapper
{
private readonly IColorSwapStrategy colorSwapStrategy;
private ColorValueRange alphaValueRange;
private ColorValueRange hueValueRange;
private ColorValueRange saturationValueRange;
private ColorValueRange vibrancyValueRange;
public HsvSwapper(IColorSwapStrategy colorSwapStrategy)
{
this.colorSwapStrategy = colorSwapStrategy;
hueValueRange = new ColorValueRange(0f, 1f);
saturationValueRange = new ColorValueRange(0f, 1f);
vibrancyValueRange = new ColorValueRange(0f, 1f);
alphaValueRange = new ColorValueRange(0f, 1f);
}
public void SetHueRange(float minHue, float maxHue)
{
float newFilterMinValue = minHue < 1f ? minHue : minHue / 360f;
float newFilterMaxValue = maxHue < 1f ? maxHue : maxHue / 360f;
hueValueRange = new ColorValueRange(newFilterMinValue, newFilterMaxValue);
}
public void SetSaturationRange(float minSaturation, float maxSaturation)
{
float newFilterMinValue = minSaturation <= 1f ? minSaturation : minSaturation / 100f;
float newFilterMaxValue = maxSaturation <= 1f ? maxSaturation : maxSaturation / 100f;
saturationValueRange = new ColorValueRange(newFilterMinValue, newFilterMaxValue);
}
public void SetVibrancyRange(float minVibrancy, float maxVibrancy)
{
float newFilterMinValue = minVibrancy <= 1f ? minVibrancy : minVibrancy / 100f;
float newFilterMaxValue = maxVibrancy <= 1f ? maxVibrancy : maxVibrancy / 100f;
vibrancyValueRange = new ColorValueRange(newFilterMinValue, newFilterMaxValue);
}
public void SetAlphaRange(float minAlpha, float maxAlpha)
{
float newFilterMinValue = minAlpha < 1f ? minAlpha : minAlpha / 255f;
float newFilterMaxValue = maxAlpha < 1f ? maxAlpha : maxAlpha / 255f;
alphaValueRange = new ColorValueRange(newFilterMinValue, newFilterMaxValue);
}
public void SwapColors(Color[] texturePixels)
{
for (int pixelIndex = 0; pixelIndex < texturePixels.Length; pixelIndex++)
{
Color pixel = texturePixels[pixelIndex];
float alpha = pixel.a;
Color.RGBToHSV(pixel, out float hue, out float saturation, out float vibrancy);
if (hueValueRange.Covers(hue) &&
saturationValueRange.Covers(saturation) &&
vibrancyValueRange.Covers(vibrancy) &&
alphaValueRange.Covers(alpha))
{
texturePixels[pixelIndex] = colorSwapStrategy.SwapColor(pixel);
}
}
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class RadiationHelmetColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSaturationVibrancySwapper(playerColor);
SkinnedMeshRenderer radiationHelmetRenderer = playerModel.GetRenderer(RADIATION_HELMET_GAME_OBJECT_NAME);
radiationHelmetRenderer.material.ApplyClonedTexture();
SkinnedMeshRenderer radiationSuitNeckClaspRenderer = playerModel.GetRenderer(RADIATION_SUIT_NECK_CLASP_GAME_OBJECT_NAME);
radiationSuitNeckClaspRenderer.material.ApplyClonedTexture();
Color[] helmetPixels = radiationHelmetRenderer.material.GetMainTexturePixels();
Color[] neckClaspPixels = radiationSuitNeckClaspRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper radiationHelmetFilter = new HsvSwapper(colorSwapStrategy);
radiationHelmetFilter.SetSaturationRange(0f, 35f);
radiationHelmetFilter.SetVibrancyRange(30f, 100f);
radiationHelmetFilter.SwapColors(helmetPixels);
radiationHelmetFilter.SwapColors(neckClaspPixels);
operation.UpdateIndex(RADIATION_HELMET_INDEX_KEY, helmetPixels);
operation.UpdateIndex(RADIATION_SUIT_NECK_CLASP_INDEX_KEY, helmetPixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] helmetPixels = pixelIndex[RADIATION_HELMET_INDEX_KEY];
Color[] neckClaspPixels = pixelIndex[RADIATION_SUIT_NECK_CLASP_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer radiationHelmetRenderer = playerModel.GetRenderer(RADIATION_HELMET_GAME_OBJECT_NAME);
radiationHelmetRenderer.material.UpdateMainTextureColors(helmetPixels);
SkinnedMeshRenderer radiationSuitNeckClaspRenderer = playerModel.GetRenderer(RADIATION_SUIT_NECK_CLASP_GAME_OBJECT_NAME);
radiationSuitNeckClaspRenderer.material.UpdateMainTextureColors(neckClaspPixels);
}
}
}

View File

@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class RadiationSuitColorSwapManager : IColorSwapManager
{
private readonly TextureBlock beltTextureBlock;
private readonly TextureBlock feetTextureBlock;
private readonly TextureBlock legTextureBlock;
public RadiationSuitColorSwapManager()
{
legTextureBlock = new TextureBlock(700, 484, 130, 155);
feetTextureBlock = new TextureBlock(525, 324, 250, 325);
beltTextureBlock = new TextureBlock(570, 0, 454, 1024);
}
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
HueSaturationVibrancySwapper hueSaturationVibrancySwapper = new HueSaturationVibrancySwapper(playerColor);
HueSwapper hueSwapper = new HueSwapper(playerColor);
SkinnedMeshRenderer radiationSuitRenderer = playerModel.GetRenderer(RADIATION_SUIT_GAME_OBJECT_NAME);
radiationSuitRenderer.material.ApplyClonedTexture();
radiationSuitRenderer.materials[1].ApplyClonedTexture();
Color[] legPixelBlock = radiationSuitRenderer.material.GetMainTexturePixelBlock(legTextureBlock);
Color[] feetPixelBlock = radiationSuitRenderer.material.GetMainTexturePixelBlock(feetTextureBlock);
Color[] beltPixelBlock = radiationSuitRenderer.material.GetMainTexturePixelBlock(beltTextureBlock);
Color[] armSleevesPixels = radiationSuitRenderer.materials[1].GetMainTexturePixels();
return operation =>
{
HsvSwapper radiationSuitLegFilter = new HsvSwapper(hueSaturationVibrancySwapper);
radiationSuitLegFilter.SetSaturationRange(0f, 35f);
radiationSuitLegFilter.SetVibrancyRange(40f, 100f);
HsvSwapper radiationSuitArmAndFeetFilter = new HsvSwapper(hueSwapper);
radiationSuitArmAndFeetFilter.SetHueRange(0f, 100f);
radiationSuitArmAndFeetFilter.SetVibrancyRange(30f, 100f);
HsvSwapper radiationSuitBeltFilter = new HsvSwapper(hueSwapper);
radiationSuitBeltFilter.SetVibrancyRange(3f, 100f);
radiationSuitBeltFilter.SetHueRange(0f, 90f);
radiationSuitLegFilter.SwapColors(legPixelBlock);
radiationSuitArmAndFeetFilter.SwapColors(feetPixelBlock);
radiationSuitArmAndFeetFilter.SwapColors(armSleevesPixels);
radiationSuitBeltFilter.SwapColors(beltPixelBlock);
operation.UpdateIndex(RADIATION_SUIT_ARMS_INDEX_KEY, armSleevesPixels);
operation.UpdateIndex(RADIATION_SUIT_LEG_INDEX_KEY, legPixelBlock);
operation.UpdateIndex(RADIATION_SUIT_FEET_INDEX_KEY, feetPixelBlock);
operation.UpdateIndex(RADIATION_SUIT_BELT_INDEX_KEY, beltPixelBlock);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] armSleevesPixels = pixelIndex[RADIATION_SUIT_ARMS_INDEX_KEY];
Color[] legPixels = pixelIndex[RADIATION_SUIT_LEG_INDEX_KEY];
Color[] feetPixels = pixelIndex[RADIATION_SUIT_FEET_INDEX_KEY];
Color[] beltPixels = pixelIndex[RADIATION_SUIT_BELT_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer radiationSuitRenderer = playerModel.GetRenderer(RADIATION_SUIT_GAME_OBJECT_NAME);
radiationSuitRenderer.material.UpdateMainTextureColors(legPixels, legTextureBlock);
radiationSuitRenderer.material.UpdateMainTextureColors(feetPixels, feetTextureBlock);
radiationSuitRenderer.material.UpdateMainTextureColors(beltPixels, beltTextureBlock);
radiationSuitRenderer.materials[1].UpdateMainTextureColors(armSleevesPixels);
radiationSuitRenderer.material.SetTexture("_MainText", radiationSuitRenderer.material.mainTexture);
radiationSuitRenderer.material.SetTexture("_SpecTex", radiationSuitRenderer.material.mainTexture);
radiationSuitRenderer.materials[1].SetTexture("_MainText", radiationSuitRenderer.materials[1].mainTexture);
radiationSuitRenderer.materials[1].SetTexture("_SpecTex", radiationSuitRenderer.materials[1].mainTexture);
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class RadiationSuitVestColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSaturationVibrancySwapper(playerColor);
SkinnedMeshRenderer radiationVestRenderer = playerModel.GetRenderer(RADIATION_SUIT_VEST_GAME_OBJECT_NAME);
radiationVestRenderer.material.ApplyClonedTexture();
Color[] texturePixels = radiationVestRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper radiationSuitVestFilter = new HsvSwapper(colorSwapStrategy);
radiationSuitVestFilter.SetSaturationRange(0f, 35f);
radiationSuitVestFilter.SetVibrancyRange(12f, 100f);
radiationSuitVestFilter.SwapColors(texturePixels);
operation.UpdateIndex(RADIATION_SUIT_VEST_INDEX_KEY, texturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] helmetPixels = pixelIndex[RADIATION_SUIT_VEST_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer radiationHelmetRenderer = playerModel.GetRenderer(RADIATION_SUIT_VEST_GAME_OBJECT_NAME);
radiationHelmetRenderer.material.UpdateMainTextureColors(helmetPixels);
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class RadiationTankColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer radiationTankRenderer = playerModel.GetRenderer(RADIATION_TANK_GAME_OBJECT_NAME);
radiationTankRenderer.material.ApplyClonedTexture();
Color[] texturePixels = radiationTankRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper radiationTankFilter = new HsvSwapper(colorSwapStrategy);
radiationTankFilter.SetHueRange(0f, 85f);
radiationTankFilter.SwapColors(texturePixels);
operation.UpdateIndex(RADIATION_SUIT_TANK_INDEX_KEY, texturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] tankPixels = pixelIndex[RADIATION_SUIT_TANK_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer radiationTankRenderer = playerModel.GetRenderer(RADIATION_TANK_GAME_OBJECT_NAME);
radiationTankRenderer.material.UpdateMainTextureColors(tankPixels);
radiationTankRenderer.material.SetTexture("_MainTex", radiationTankRenderer.material.mainTexture);
radiationTankRenderer.material.SetTexture("_SpecTex", radiationTankRenderer.material.mainTexture);
}
}
}

View File

@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class RebreatherColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer rebreatherRenderer = playerModel.GetRenderer(REBREATHER_GAME_OBJECT_NAME);
rebreatherRenderer.material.ApplyClonedTexture();
FixRebreatherMaterials(playerModel, rebreatherRenderer);
Color[] texturePixels = rebreatherRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper rebreatherFilter = new HsvSwapper(colorSwapStrategy);
rebreatherFilter.SetHueRange(0f, 25f);
rebreatherFilter.SwapColors(texturePixels);
operation.UpdateIndex(REBREATHER_INDEX_KEY, texturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] rebreatherPixelIndexes = pixelIndex[REBREATHER_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer rebreatherRenderer = playerModel.GetRenderer(REBREATHER_GAME_OBJECT_NAME);
rebreatherRenderer.material.UpdateMainTextureColors(rebreatherPixelIndexes);
}
//Clean up of UWE's tech debt from when they gave up on rendering player equipment on the avatar during normal play. Probably best not to read too much into it...
private static void FixRebreatherMaterials(GameObject playerModel, SkinnedMeshRenderer rebreatherRenderer)
{
Shader marmosetShader = playerModel.GetRenderer(NORMAL_HEAD_GAME_OBJECT_NAME).material.shader;
rebreatherRenderer.material.shader = marmosetShader;
rebreatherRenderer.material.SetOverrideTag("RenderType", "TransparentAdditive");
rebreatherRenderer.material.SetOverrideTag("Queue", "Deferred");
rebreatherRenderer.material.shaderKeywords = new List<string>
{"MARMO_ALPHA", "MARMO_PREMULT_ALPHA", "MARMO_SIMPLE_GLASS", "UWE_DITHERALPHA", "MARMO_SPECMAP", "WBOIT", "_NORMALMAP", "_ZWRITE_ON"}.ToArray();
rebreatherRenderer.material.SetTexture("_MainTex", rebreatherRenderer.material.mainTexture);
rebreatherRenderer.material.SetTexture("_SpecTex", rebreatherRenderer.material.mainTexture);
rebreatherRenderer.material.SetTexture("_BumpMap", rebreatherRenderer.material.GetTexture("_BumpMap"));
rebreatherRenderer.materials[2].shader = marmosetShader;
rebreatherRenderer.materials[2].shaderKeywords = new List<string>
{"MARMO_SPECMAP", "_ZWRITE_ON"}.ToArray();
rebreatherRenderer.materials[2].SetTexture("_MainTex", rebreatherRenderer.materials[2].mainTexture);
rebreatherRenderer.materials[2].SetTexture("_SpecTex", rebreatherRenderer.materials[2].mainTexture);
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class ReinforcedSuitColorSwapManager : IColorSwapManager
{
public void PrepareMaterials(GameObject playerModel)
{
SkinnedMeshRenderer reinforcedSuitRenderer = playerModel.GetRenderer(REINFORCED_SUIT_GAME_OBJECT_NAME);
SkinnedMeshRenderer reinforcedSuitGlovesRenderer = playerModel.GetRenderer(REINFORCED_GLOVES_GAME_OBJECT_NAME);
}
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer reinforcedSuitRenderer = playerModel.GetRenderer(REINFORCED_SUIT_GAME_OBJECT_NAME);
reinforcedSuitRenderer.material.ApplyClonedTexture();
reinforcedSuitRenderer.materials[1].ApplyClonedTexture();
SkinnedMeshRenderer reinforcedGloveRenderer = playerModel.GetRenderer(REINFORCED_GLOVES_GAME_OBJECT_NAME);
reinforcedGloveRenderer.material.ApplyClonedTexture();
Color[] suitTexturePixels = reinforcedSuitRenderer.material.GetMainTexturePixels();
Color[] armsTexturePixels = reinforcedSuitRenderer.materials[1].GetMainTexturePixels();
Color[] gloveTexturePixels = reinforcedGloveRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper reinforcedSuitFilter = new HsvSwapper(colorSwapStrategy);
reinforcedSuitFilter.SetHueRange(0f, 20f);
reinforcedSuitFilter.SetSaturationRange(45f, 100f);
reinforcedSuitFilter.SwapColors(suitTexturePixels);
reinforcedSuitFilter.SwapColors(armsTexturePixels);
reinforcedSuitFilter.SwapColors(gloveTexturePixels);
operation.UpdateIndex(REINFORCED_SUIT_INDEX_KEY, suitTexturePixels);
operation.UpdateIndex(REINFORCED_SUIT_ARMS_INDEX_KEY, armsTexturePixels);
operation.UpdateIndex(REINFORCED_GLOVES_INDEX_KEY, gloveTexturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] suitPixelIndexes = pixelIndex[REINFORCED_SUIT_INDEX_KEY];
Color[] armsTexturePixels = pixelIndex[REINFORCED_SUIT_ARMS_INDEX_KEY];
Color[] glovePixelIndexes = pixelIndex[REINFORCED_GLOVES_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer reinforcedSuitRenderer = playerModel.GetRenderer(REINFORCED_SUIT_GAME_OBJECT_NAME);
reinforcedSuitRenderer.material.UpdateMainTextureColors(suitPixelIndexes);
reinforcedSuitRenderer.material.SetTexture("_MainTex", reinforcedSuitRenderer.material.mainTexture);
reinforcedSuitRenderer.material.SetTexture("_SpecTex", reinforcedSuitRenderer.material.mainTexture);
reinforcedSuitRenderer.materials[1].UpdateMainTextureColors(armsTexturePixels);
reinforcedSuitRenderer.materials[1].SetTexture("_MainTex", reinforcedSuitRenderer.materials[1].mainTexture);
reinforcedSuitRenderer.materials[1].SetTexture("_SpecTex", reinforcedSuitRenderer.materials[1].mainTexture);
SkinnedMeshRenderer reinforcedGlovesRenderer = playerModel.GetRenderer(REINFORCED_GLOVES_GAME_OBJECT_NAME);
reinforcedGlovesRenderer.material.UpdateMainTextureColors(glovePixelIndexes);
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class ScubaTankColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer scubaTankRenderer = playerModel.GetRenderer(SCUBA_TANK_GAME_OBJECT_NAME);
scubaTankRenderer.material.ApplyClonedTexture();
Color[] texturePixels = scubaTankRenderer.material.GetMainTexturePixels();
return operation =>
{
HsvSwapper scubaTankFilter = new HsvSwapper(colorSwapStrategy);
scubaTankFilter.SetHueRange(0f, 30f);
scubaTankFilter.SwapColors(texturePixels);
operation.UpdateIndex(SCUBA_TANK_INDEX_KEY, texturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] scubaTankPixelIndexes = pixelIndex[SCUBA_TANK_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer scubaTankRenderer = playerModel.GetRenderer(SCUBA_TANK_GAME_OBJECT_NAME);
scubaTankRenderer.material.UpdateMainTextureColors(scubaTankPixelIndexes);
scubaTankRenderer.material.SetTexture("_MainTex", scubaTankRenderer.material.mainTexture);
scubaTankRenderer.material.SetTexture("_SpecTex", scubaTankRenderer.material.mainTexture);
}
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy;
using NitroxClient.Unity.Helper;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using static NitroxClient.GameLogic.PlayerLogic.PlayerModel.PlayerEquipmentConstants;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class StillSuitColorSwapManager : IColorSwapManager
{
public Action<ColorSwapAsyncOperation> CreateColorSwapTask(INitroxPlayer nitroxPlayer)
{
GameObject playerModel = nitroxPlayer.PlayerModel;
Color playerColor = nitroxPlayer.PlayerSettings.PlayerColor.ToUnity();
IColorSwapStrategy colorSwapStrategy = new HueSwapper(playerColor);
SkinnedMeshRenderer stillSuitRenderer = playerModel.GetRenderer(STILL_SUIT_GAME_OBJECT_NAME);
stillSuitRenderer.material.ApplyClonedTexture();
stillSuitRenderer.materials[1].ApplyClonedTexture();
Color[] bodyTexturePixels = stillSuitRenderer.material.GetMainTexturePixels();
Color[] armsTexturePixels = stillSuitRenderer.materials[1].GetMainTexturePixels();
return operation =>
{
HsvSwapper stillSuitFilter = new HsvSwapper(colorSwapStrategy);
stillSuitFilter.SetHueRange(0f, 75f);
stillSuitFilter.SwapColors(bodyTexturePixels);
stillSuitFilter.SwapColors(armsTexturePixels);
operation.UpdateIndex(STILL_SUIT_INDEX_KEY, bodyTexturePixels);
operation.UpdateIndex(STILL_SUIT_ARMS_INDEX_KEY, armsTexturePixels);
};
}
public void ApplyPlayerColor(Dictionary<string, Color[]> pixelIndex, INitroxPlayer nitroxPlayer)
{
Color[] bodyPixelIndexes = pixelIndex[STILL_SUIT_INDEX_KEY];
Color[] armsPixelIndexes = pixelIndex[STILL_SUIT_ARMS_INDEX_KEY];
GameObject playerModel = nitroxPlayer.PlayerModel;
SkinnedMeshRenderer stillSuitRenderer = playerModel.GetRenderer(STILL_SUIT_GAME_OBJECT_NAME);
stillSuitRenderer.material.UpdateMainTextureColors(bodyPixelIndexes);
stillSuitRenderer.material.SetTexture("_MainTex", stillSuitRenderer.material.mainTexture);
stillSuitRenderer.material.SetTexture("_SpecTex", stillSuitRenderer.material.mainTexture);
stillSuitRenderer.materials[1].UpdateMainTextureColors(armsPixelIndexes);
stillSuitRenderer.materials[1].SetTexture("_MainTex", stillSuitRenderer.materials[1].mainTexture);
stillSuitRenderer.materials[1].SetTexture("_SpecTex", stillSuitRenderer.materials[1].mainTexture);
}
}
}

View File

@@ -0,0 +1,20 @@
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy
{
public class AlphaChannelSwapper : IColorSwapStrategy
{
private readonly float replacementAlpha;
public AlphaChannelSwapper(float replacementAlpha)
{
this.replacementAlpha = replacementAlpha;
}
public Color SwapColor(Color originalColor)
{
return originalColor.WithAlpha(replacementAlpha);
}
}
}

View File

@@ -0,0 +1,27 @@
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy
{
public class HueSaturationVibrancySwapper : IColorSwapStrategy
{
private readonly float replacementHue;
private readonly float replacementSaturation;
private readonly float replacementVibrancy;
public HueSaturationVibrancySwapper(Color playerColor)
{
Color.RGBToHSV(playerColor, out replacementHue, out replacementSaturation, out replacementVibrancy);
}
public Color SwapColor(Color originalColor)
{
float currentAlpha = originalColor.a;
Color.RGBToHSV(originalColor, out float currentHue, out float currentSaturation, out float currentVibrancy);
return Color
.HSVToRGB(replacementHue, replacementSaturation, replacementVibrancy)
.WithAlpha(currentAlpha);
}
}
}

View File

@@ -0,0 +1,25 @@
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap.Strategy
{
public class HueSwapper : IColorSwapStrategy
{
private readonly float replacementHue;
public HueSwapper(Color playerColor)
{
Color.RGBToHSV(playerColor, out replacementHue, out float saturation, out float vibrancy);
}
public Color SwapColor(Color originalColor)
{
float currentAlpha = originalColor.a;
Color.RGBToHSV(originalColor, out float currentHue, out float currentSaturation, out float currentVibrancy);
return Color
.HSVToRGB(replacementHue, currentSaturation, currentVibrancy)
.WithAlpha(currentAlpha);
}
}
}

View File

@@ -0,0 +1,18 @@
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap
{
public class TextureBlock
{
public int X { get; }
public int Y { get; }
public int BlockWidth { get; }
public int BlockHeight { get; }
public TextureBlock(int x, int y, int blockWidth, int blockHeight)
{
X = x;
Y = y;
BlockWidth = blockWidth;
BlockHeight = blockHeight;
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Collections.ObjectModel;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract
{
public interface IEquipmentVisibilityHandler
{
void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment);
}
}

View File

@@ -0,0 +1,33 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class DiveSuitVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject head;
private readonly GameObject body;
private readonly GameObject hands;
public DiveSuitVisibilityHandler(GameObject playerModel)
{
head = playerModel.transform.Find(PlayerEquipmentConstants.NORMAL_HEAD_GAME_OBJECT_NAME).gameObject;
body = playerModel.transform.Find(PlayerEquipmentConstants.DIVE_SUIT_GAME_OBJECT_NAME).gameObject;
hands = playerModel.transform.Find(PlayerEquipmentConstants.NORMAL_HANDS_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool headVisible = !currentEquipment.Contains(TechType.RadiationHelmet) && !currentEquipment.Contains(TechType.Rebreather);
bool bodyVisible = !currentEquipment.Contains(TechType.RadiationSuit) &&
!currentEquipment.Contains(TechType.WaterFiltrationSuit) &&
!currentEquipment.Contains(TechType.ReinforcedDiveSuit);
bool handsVisible = !currentEquipment.Contains(TechType.RadiationGloves) && !currentEquipment.Contains(TechType.ReinforcedGloves);
head.SetActive(headVisible);
body.gameObject.SetActive(bodyVisible);
hands.SetActive(handsVisible);
}
}
}

View File

@@ -0,0 +1,40 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class FinsVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject fins;
private readonly GameObject finsRoot;
private readonly GameObject chargedFins;
private readonly GameObject chargedFinsRoot;
private readonly GameObject glideFins;
private readonly GameObject glideFinsRoot;
public FinsVisibilityHandler(GameObject playerModel)
{
fins = playerModel.transform.Find(PlayerEquipmentConstants.FINS_GAME_OBJECT_NAME).gameObject;
finsRoot = playerModel.transform.Find(PlayerEquipmentConstants.FINS_ROOT_GAME_OBJECT_NAME).gameObject;
chargedFins = playerModel.transform.Find(PlayerEquipmentConstants.CHARGED_FINS_GAME_OBJECT_NAME).gameObject;
chargedFinsRoot = playerModel.transform.Find(PlayerEquipmentConstants.CHARGED_FINS_ROOT_GAME_OBJECT_NAME).gameObject;
glideFins = playerModel.transform.Find(PlayerEquipmentConstants.GLIDE_FINS_GAME_OBJECT_NAME).gameObject;
glideFinsRoot = playerModel.transform.Find(PlayerEquipmentConstants.GLIDE_FINS_ROOT_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool basicFinsVisible = currentEquipment.Contains(TechType.Fins);
bool chargedFinsVisible = currentEquipment.Contains(TechType.SwimChargeFins);
bool glideFinsVisible = currentEquipment.Contains(TechType.UltraGlideFins);
fins.SetActive(basicFinsVisible);
finsRoot.SetActive(basicFinsVisible);
chargedFins.SetActive(chargedFinsVisible);
chargedFinsRoot.SetActive(chargedFinsVisible);
glideFins.SetActive(glideFinsVisible);
glideFinsRoot.SetActive(glideFinsVisible);
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class RadiationSuitVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject head;
private readonly GameObject helmet;
private readonly GameObject gloves;
private readonly GameObject suit;
private readonly GameObject suitNeck;
private readonly GameObject suitVest;
private readonly GameObject tank;
private readonly GameObject tankTubes;
public RadiationSuitVisibilityHandler(GameObject playerModel)
{
head = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_HEAD_GAME_OBJECT_NAME).gameObject;
helmet = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_HELMET_GAME_OBJECT_NAME).gameObject;
gloves = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_GLOVES_GAME_OBJECT_NAME).gameObject;
suit = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_SUIT_GAME_OBJECT_NAME).gameObject;
suitNeck = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_SUIT_NECK_CLASP_GAME_OBJECT_NAME).gameObject;
suitVest = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_SUIT_VEST_GAME_OBJECT_NAME).gameObject;
tank = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_TANK_GAME_OBJECT_NAME).gameObject;
tankTubes = playerModel.transform.Find(PlayerEquipmentConstants.RADIATION_TANK_TUBES_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool tankEquipped = currentEquipment.Contains(TechType.Tank) ||
currentEquipment.Contains(TechType.DoubleTank) ||
currentEquipment.Contains(TechType.HighCapacityTank) ||
currentEquipment.Contains(TechType.PlasteelTank);
bool helmetVisible = currentEquipment.Contains(TechType.RadiationHelmet);
bool glovesVisible = currentEquipment.Contains(TechType.RadiationGloves);
bool bodyVisible = currentEquipment.Contains(TechType.RadiationSuit);
bool vestVisible = bodyVisible || helmetVisible;
bool tankVisible = tankEquipped && vestVisible;
bool tubesVisible = tankVisible && helmetVisible;
head.SetActive(helmetVisible);
helmet.SetActive(helmetVisible);
gloves.SetActive(glovesVisible);
suit.SetActive(bodyVisible);
suitNeck.SetActive(helmetVisible);
suitVest.SetActive(vestVisible);
tank.SetActive(tankVisible);
tankTubes.SetActive(tubesVisible);
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class ReinforcedSuitVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject gloves;
private readonly GameObject suit;
public ReinforcedSuitVisibilityHandler(GameObject playerModel)
{
gloves = playerModel.transform.Find(PlayerEquipmentConstants.REINFORCED_GLOVES_GAME_OBJECT_NAME).gameObject;
suit = playerModel.transform.Find(PlayerEquipmentConstants.REINFORCED_SUIT_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool glovesVisible = currentEquipment.Contains(TechType.ReinforcedGloves);
bool bodyVisible = currentEquipment.Contains(TechType.ReinforcedDiveSuit);
gloves.SetActive(glovesVisible);
suit.SetActive(bodyVisible);
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class ScubaSuitVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject rebreather;
private readonly GameObject scuba;
private readonly GameObject scubaTank;
private readonly GameObject scubaTankTubes;
public ScubaSuitVisibilityHandler(GameObject playerModel)
{
rebreather = playerModel.transform.Find(PlayerEquipmentConstants.REBREATHER_GAME_OBJECT_NAME).gameObject;
scuba = playerModel.transform.Find(PlayerEquipmentConstants.SCUBA_ROOT_GAME_OBJECT_NAME).gameObject;
scubaTank = playerModel.transform.Find(PlayerEquipmentConstants.SCUBA_TANK_GAME_OBJECT_NAME).gameObject;
scubaTankTubes = playerModel.transform.Find(PlayerEquipmentConstants.SCUBA_TANK_TUBES_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool tankEquipped = currentEquipment.Contains(TechType.Tank) ||
currentEquipment.Contains(TechType.DoubleTank) ||
currentEquipment.Contains(TechType.HighCapacityTank) ||
currentEquipment.Contains(TechType.PlasteelTank);
bool rebreatherVisible = currentEquipment.Contains(TechType.Rebreather);
bool radiationHelmetVisible = currentEquipment.Contains(TechType.RadiationHelmet);
bool tankVisible = tankEquipped && !currentEquipment.Contains(TechType.RadiationSuit);
bool tubesVisible = (rebreatherVisible || radiationHelmetVisible) && tankVisible;
bool rootVisible = rebreatherVisible || tankVisible;
rebreather.SetActive(rebreatherVisible);
scuba.SetActive(rootVisible);
scubaTank.SetActive(tankVisible);
scubaTankTubes.SetActive(tubesVisible);
}
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using UnityEngine;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment
{
public class StillSuitVisibilityHandler : IEquipmentVisibilityHandler
{
private readonly GameObject stillSuit;
public StillSuitVisibilityHandler(GameObject playerModel)
{
stillSuit = playerModel.transform.Find(PlayerEquipmentConstants.STILL_SUIT_GAME_OBJECT_NAME).gameObject;
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
bool bodyVisible = currentEquipment.Contains(TechType.WaterFiltrationSuit);
stillSuit.SetActive(bodyVisible);
}
}
}

View File

@@ -0,0 +1,51 @@
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel
{
public static class PlayerEquipmentConstants
{
public const string NORMAL_HEAD_GAME_OBJECT_NAME = "male_geo/diveSuit/diveSuit_head_geo";
public const string NORMAL_HANDS_GAME_OBJECT_NAME = "male_geo/diveSuit/diveSuit_hands_geo";
public const string DIVE_SUIT_GAME_OBJECT_NAME = "male_geo/diveSuit/diveSuit_body_geo";
public const string FINS_ROOT_GAME_OBJECT_NAME = "male_geo/generalSuit";
public const string FINS_GAME_OBJECT_NAME = "male_geo/generalSuit/flippers_basic_geo";
public const string CHARGED_FINS_ROOT_GAME_OBJECT_NAME = "male_geo/SwimChargeFins";
public const string CHARGED_FINS_GAME_OBJECT_NAME = "male_geo/SwimChargeFins/flippers_basic_geo";
public const string GLIDE_FINS_ROOT_GAME_OBJECT_NAME = "male_geo/UltraGlideFins";
public const string GLIDE_FINS_GAME_OBJECT_NAME = "male_geo/UltraGlideFins/flippers_basic_geo";
public const string STILL_SUIT_GAME_OBJECT_NAME = "male_geo/stillSuit/still_suit_01_body_geo";
public const string SCUBA_ROOT_GAME_OBJECT_NAME = "male_geo/scubaSuit";
public const string REBREATHER_GAME_OBJECT_NAME = "male_geo/scubaSuit/scuba_head";
public const string SCUBA_TANK_GAME_OBJECT_NAME = "male_geo/scubaSuit/scuba_vest";
public const string SCUBA_TANK_TUBES_GAME_OBJECT_NAME = "male_geo/scubaSuit/scuba_vest/scuba_breatherTubes";
public const string REINFORCED_SUIT_GAME_OBJECT_NAME = "male_geo/reinforcedSuit/reinforced_suit_01_body_geo";
public const string REINFORCED_GLOVES_GAME_OBJECT_NAME = "male_geo/reinforcedSuit/reinforced_suit_01_glove_geo";
public const string RADIATION_SUIT_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_body_geo";
public const string RADIATION_GLOVES_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_gloves_geo";
public const string RADIATION_SUIT_VEST_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_vest_reducedNeck_geo";
public const string RADIATION_HEAD_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_head_geo";
public const string RADIATION_HELMET_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_vest_reducedNeck_geo/radiationSuit_vest_neckExtension_geo/radiationSuit_helmet_geo 1";
public const string RADIATION_SUIT_NECK_CLASP_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_vest_reducedNeck_geo/radiationSuit_vest_neckExtension_geo";
public const string RADIATION_TANK_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_vest_reducedNeck_geo/radiationSuit_tank_geo 1";
public const string RADIATION_TANK_TUBES_GAME_OBJECT_NAME = "male_geo/radiationSuit/radiationSuit_vest_reducedNeck_geo/radiationSuit_tank_geo 1/radiationSuit_tubes_geo 1";
public const string ITEM_ATTACH_POINT_GAME_OBJECT_NAME = "export_skeleton/head_rig/neck/chest/clav_R/clav_R_aim/shoulder_R/elbow_R/hand_R/attach1";
public const string DIVE_SUIT_INDEX_KEY = "dive-suit";
public const string DIVE_SUIT_ARMS_INDEX_KEY = "dive-suit-arms";
public const string STILL_SUIT_INDEX_KEY = "still-suit";
public const string STILL_SUIT_ARMS_INDEX_KEY = "still-suit-arms";
public const string REBREATHER_INDEX_KEY = "rebreather";
public const string SCUBA_TANK_INDEX_KEY = "scuba-tank";
public const string REINFORCED_SUIT_INDEX_KEY = "reinforced-suit";
public const string REINFORCED_SUIT_ARMS_INDEX_KEY = "reinforced-suit-arms";
public const string REINFORCED_GLOVES_INDEX_KEY = "reinforced-gloves";
public const string RADIATION_SUIT_ARMS_INDEX_KEY = "radiation-suit-arms";
public const string RADIATION_SUIT_LEG_INDEX_KEY = "radiation-suit-leg";
public const string RADIATION_SUIT_FEET_INDEX_KEY = "radiation-suit-feet";
public const string RADIATION_SUIT_BELT_INDEX_KEY = "radiation-suit-belt";
public const string RADIATION_SUIT_VEST_INDEX_KEY = "radiation-suit-vest";
public const string RADIATION_HELMET_INDEX_KEY = "radiation-helmet";
public const string RADIATION_SUIT_NECK_CLASP_INDEX_KEY = "radiation-suit-neck-clasp";
public const string RADIATION_SUIT_TANK_INDEX_KEY = "radiation-suit-tank";
public const string FINS_INDEX_KEY = "fins";
}
}

View File

@@ -0,0 +1,103 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.ColorSwap;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment;
using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Equipment.Abstract;
using NitroxClient.MonoBehaviours;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using Object = UnityEngine.Object;
namespace NitroxClient.GameLogic.PlayerLogic.PlayerModel;
public class PlayerModelManager
{
private readonly IEnumerable<IColorSwapManager> colorSwapManagers;
private List<IEquipmentVisibilityHandler> equipmentVisibilityHandlers;
public PlayerModelManager(IEnumerable<IColorSwapManager> colorSwapManagers)
{
this.colorSwapManagers = colorSwapManagers;
}
public void BeginApplyPlayerColor(INitroxPlayer player)
{
Multiplayer.Main.StartCoroutine(ApplyPlayerColor(player, colorSwapManagers));
}
public void RegisterEquipmentVisibilityHandler(GameObject playerModel)
{
equipmentVisibilityHandlers = new List<IEquipmentVisibilityHandler>
{
new DiveSuitVisibilityHandler(playerModel),
new ScubaSuitVisibilityHandler(playerModel),
new FinsVisibilityHandler(playerModel),
new RadiationSuitVisibilityHandler(playerModel),
new ReinforcedSuitVisibilityHandler(playerModel),
new StillSuitVisibilityHandler(playerModel)
};
}
public void UpdateEquipmentVisibility(ReadOnlyCollection<TechType> currentEquipment)
{
foreach (IEquipmentVisibilityHandler equipmentVisibilityHandler in equipmentVisibilityHandlers)
{
equipmentVisibilityHandler.UpdateEquipmentVisibility(currentEquipment);
}
}
private IEnumerator CreateSignalPrototype(IOut<GameObject> result)
{
CoroutineTask<GameObject> signalHandle = AddressablesUtility.InstantiateAsync("WorldEntities/Environment/Generated/Signal.prefab", Multiplayer.Main.transform, awake: false);
yield return signalHandle;
GameObject go = signalHandle.GetResult();
go.name = "RemotePlayerSignalPrototype";
go.transform.localScale = new Vector3(.5f, .5f, .5f);
go.transform.localPosition = new Vector3(0, 0.8f, 0);
go.SetActive(false);
result.Set(go);
}
public IEnumerator AttachPing(INitroxPlayer player)
{
TaskResult<GameObject> result = new();
yield return CreateSignalPrototype(result);
GameObject signalBase = Object.Instantiate(result.value, player.PlayerModel.transform, false);
signalBase.name = $"signal_{player.PlayerName}";
signalBase.SetActive(true);
PingInstance ping = signalBase.GetComponent<PingInstance>();
ping.Initialize();
ping.SetLabel($"Player {player.PlayerName}");
ping.pingType = PingType.Signal;
// ping will be moved to the player list tab
ping.displayPingInManager = false;
// SignalPing is not required for player as we don't need to display text or anchor to a specific world position
// we also take a dependency on the lack of signalping later to differentiate remote player pings from others.
Object.DestroyImmediate(signalBase.GetComponent<SignalPing>());
SetInGamePingColor(player, ping);
}
private static void SetInGamePingColor(INitroxPlayer player, PingInstance ping)
{
uGUI_Pings pings = Object.FindObjectOfType<uGUI_Pings>();
pings.OnColor(ping.Id, player.PlayerSettings.PlayerColor.ToUnity());
}
private static IEnumerator ApplyPlayerColor(INitroxPlayer player, IEnumerable<IColorSwapManager> colorSwapManagers)
{
ColorSwapAsyncOperation swapOperation = new(player, colorSwapManagers);
swapOperation.BeginColorSwap();
yield return new WaitUntil(() => swapOperation.IsColorSwapComplete());
swapOperation.ApplySwappedColors();
}
}