Last active
February 20, 2018 13:16
-
-
Save HexiDave/436fb5cac57c170e8633c07e1ce4d7f5 to your computer and use it in GitHub Desktop.
A mod for Subnautica to allow players to also pull resources from nearby containers at crafting windows
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using ProtoBuf; | |
using UnityEngine; | |
using UWE; | |
// Token: 0x02000202 RID: 514 | |
[ProtoContract] | |
public sealed class CrafterLogic : MonoBehaviour, IProtoEventListener | |
{ | |
// Token: 0x17000138 RID: 312 | |
// (get) Token: 0x06000B13 RID: 2835 | |
public bool inProgress | |
{ | |
get | |
{ | |
return this.currentTechType != TechType.None && DayNightCycle.main.timePassed < (double)this.timeCraftingEnd; | |
} | |
} | |
// Token: 0x17000139 RID: 313 | |
// (get) Token: 0x06000B14 RID: 2836 | |
public float progress | |
{ | |
get | |
{ | |
float num3 = (float)((double)((float)((double)((float)DayNightCycle.main.timePassed)))); | |
double num2 = (double)(this.timeCraftingEnd - this.timeCraftingBegin); | |
float value = (float)(((double)num3 - (double)this.timeCraftingBegin) / num2); | |
if (this.timeCraftingEnd > this.timeCraftingBegin) | |
{ | |
return Mathf.Clamp01(value); | |
} | |
return -1f; | |
} | |
} | |
// Token: 0x1700013A RID: 314 | |
// (get) Token: 0x06000B15 RID: 2837 | |
public TechType currentTechType | |
{ | |
get | |
{ | |
if (this.linkedIndex > -1) | |
{ | |
ITechData techData = CraftData.Get(this.craftingTechType, false); | |
if (techData != null && this.linkedIndex < techData.linkedItemCount) | |
{ | |
return techData.GetLinkedItem(this.linkedIndex); | |
} | |
} | |
return this.craftingTechType; | |
} | |
} | |
// Token: 0x06000B16 RID: 2838 | |
private void Update() | |
{ | |
if (this.craftingTechType != TechType.None && this.lastTime < (double)this.timeCraftingEnd) | |
{ | |
this.NotifyProgress(this.progress); | |
this.lastTime = DayNightCycle.main.timePassed; | |
if (this.lastTime >= (double)this.timeCraftingEnd) | |
{ | |
this.NotifyEnd(); | |
} | |
} | |
} | |
// Token: 0x06000B17 RID: 2839 | |
public bool Craft(TechType techType, float craftTime) | |
{ | |
if (craftTime > 0f) | |
{ | |
ITechData techData = CraftData.Get(techType, false); | |
if (techData != null) | |
{ | |
this.timeCraftingBegin = DayNightCycle.main.timePassedAsFloat; | |
this.timeCraftingEnd = this.timeCraftingBegin + craftTime + 0.1f; | |
this.craftingTechType = techType; | |
this.linkedIndex = -1; | |
this.numCrafted = techData.craftAmount; | |
this.NotifyChanged(this.craftingTechType); | |
this.NotifyProgress(0f); | |
return true; | |
} | |
} | |
return false; | |
} | |
// Token: 0x06000B18 RID: 2840 | |
public void Reset() | |
{ | |
this.timeCraftingBegin = -1f; | |
this.timeCraftingEnd = -1f; | |
this.craftingTechType = TechType.None; | |
this.linkedIndex = -1; | |
this.numCrafted = 0; | |
this.NotifyChanged(TechType.None); | |
this.NotifyProgress(0f); | |
} | |
// Token: 0x06000B19 RID: 2841 | |
public void TryPickup() | |
{ | |
if (this.craftingTechType == TechType.None || this.progress < 1f) | |
{ | |
return; | |
} | |
ITechData techData = CraftData.Get(this.craftingTechType, false); | |
if (techData != null) | |
{ | |
bool flag = false; | |
while (!flag) | |
{ | |
TechType linkedItem = this.craftingTechType; | |
if (this.linkedIndex != -1) | |
{ | |
linkedItem = techData.GetLinkedItem(this.linkedIndex); | |
} | |
while (this.numCrafted > 0) | |
{ | |
if (!this.TryPickupSingle(linkedItem)) | |
{ | |
return; | |
} | |
this.numCrafted--; | |
} | |
if (this.numCrafted == 0) | |
{ | |
this.linkedIndex++; | |
if (this.linkedIndex < techData.linkedItemCount) | |
{ | |
this.numCrafted = 1; | |
this.NotifyChanged(this.currentTechType); | |
} | |
else | |
{ | |
flag = true; | |
} | |
} | |
} | |
} | |
this.Reset(); | |
} | |
// Token: 0x06000B1A RID: 2842 | |
private bool TryPickupSingle(TechType techType) | |
{ | |
Inventory main = Inventory.main; | |
bool flag = false; | |
GameObject gameObject = CraftData.GetPrefabForTechType(techType, true); | |
if (gameObject == null) | |
{ | |
gameObject = global::Utils.genericLootPrefab; | |
flag = true; | |
} | |
if (!(gameObject != null)) | |
{ | |
UWE.Utils.LogReportFormat("Can't find prefab for TechType.{0}", new object[] | |
{ | |
techType | |
}); | |
return true; | |
} | |
Pickupable component = gameObject.GetComponent<Pickupable>(); | |
if (!(component != null)) | |
{ | |
UWE.Utils.LogReportFormat("Can't find Pickupable component on prefab for TechType.{0}", new object[] | |
{ | |
techType | |
}); | |
return true; | |
} | |
Vector2int itemSize = CraftData.GetItemSize(component.GetTechType()); | |
if (main.HasRoomFor(itemSize.x, itemSize.y)) | |
{ | |
GameObject gameObject2 = UnityEngine.Object.Instantiate<GameObject>(gameObject); | |
component = gameObject2.GetComponent<Pickupable>(); | |
if (flag) | |
{ | |
component.SetTechTypeOverride(techType, true); | |
} | |
CrafterLogic.NotifyCraftEnd(gameObject2, this.craftingTechType); | |
main.ForcePickup(component); | |
Player.main.PlayGrab(); | |
return true; | |
} | |
ErrorMessage.AddMessage(Language.main.Get("InventoryFull")); | |
return false; | |
} | |
// Token: 0x06000B1B RID: 2843 | |
private void NotifyChanged(TechType techType) | |
{ | |
if (this.onItemChanged != null) | |
{ | |
this.onItemChanged(techType); | |
} | |
} | |
// Token: 0x06000B1C RID: 2844 | |
private void NotifyProgress(float progress) | |
{ | |
if (this.onProgress != null) | |
{ | |
this.onProgress(progress); | |
} | |
} | |
// Token: 0x06000B1D RID: 2845 | |
private void NotifyEnd() | |
{ | |
if (this.onDone != null) | |
{ | |
this.onDone(); | |
} | |
} | |
// Token: 0x06000B1E RID: 2846 | |
public static bool IsCraftRecipeUnlocked(TechType techType) | |
{ | |
return !GameModeUtils.RequiresBlueprints() || KnownTech.Contains(techType); | |
} | |
// Token: 0x06000B1F RID: 2847 | |
public static bool IsCraftRecipeFulfilled(TechType techType) | |
{ | |
if (Inventory.main == null) | |
{ | |
return false; | |
} | |
if (!GameModeUtils.RequiresIngredients()) | |
{ | |
return true; | |
} | |
ItemsContainer[] itemContainers = CrafterLogic.FindAllItemsContainersInRange(100f); | |
ITechData techData = CraftData.Get(techType, false); | |
if (techData != null) | |
{ | |
int i = 0; | |
int ingredientCount = techData.ingredientCount; | |
while (i < ingredientCount) | |
{ | |
IIngredient ingredient = techData.GetIngredient(i); | |
if (CrafterLogic.GetTotalPickupCount(ingredient.techType, itemContainers) < ingredient.amount) | |
{ | |
return false; | |
} | |
i++; | |
} | |
return true; | |
} | |
return false; | |
} | |
// Token: 0x06000B20 RID: 2848 | |
public static bool ConsumeEnergy(PowerRelay powerRelay, float amount) | |
{ | |
float num; | |
return !GameModeUtils.RequiresPower() || (!(powerRelay == null) && powerRelay.ConsumeEnergy(amount, out num)); | |
} | |
// Token: 0x06000B21 RID: 2849 | |
public static bool ConsumeResources(TechType techType) | |
{ | |
if (!CrafterLogic.IsCraftRecipeFulfilled(techType)) | |
{ | |
ErrorMessage.AddWarning(Language.main.Get("DontHaveNeededIngredients")); | |
return false; | |
} | |
ItemsContainer[] itemsContainers = CrafterLogic.FindAllItemsContainersInRange(100f); | |
ITechData techData = CraftData.Get(techType, false); | |
if (techData == null) | |
{ | |
return false; | |
} | |
int i = 0; | |
int ingredientCount = techData.ingredientCount; | |
while (i < ingredientCount) | |
{ | |
IIngredient ingredient = techData.GetIngredient(i); | |
TechType techType2 = ingredient.techType; | |
int j = 0; | |
int amount = ingredient.amount; | |
while (j < amount) | |
{ | |
CrafterLogic.DestroyItemInContainers(techType2, itemsContainers); | |
uGUI_IconNotifier.main.Play(techType2, uGUI_IconNotifier.AnimationType.To, null); | |
j++; | |
} | |
i++; | |
} | |
return true; | |
} | |
// Token: 0x06000B22 RID: 2850 | |
public static void NotifyCraftEnd(GameObject target, TechType techType) | |
{ | |
if (target == null) | |
{ | |
return; | |
} | |
CrafterLogic.sCraftTargets.Clear(); | |
target.GetComponentsInChildren<ICraftTarget>(true, CrafterLogic.sCraftTargets); | |
int i = 0; | |
int count = CrafterLogic.sCraftTargets.Count; | |
while (i < count) | |
{ | |
CrafterLogic.sCraftTargets[i].OnCraftEnd(techType); | |
i++; | |
} | |
CrafterLogic.sCraftTargets.Clear(); | |
} | |
// Token: 0x06000B23 RID: 2851 | |
public void OnProtoSerialize(ProtobufSerializer serializer) | |
{ | |
} | |
// Token: 0x06000B24 RID: 2852 | |
public void OnProtoDeserialize(ProtobufSerializer serializer) | |
{ | |
if (this.craftingTechType != TechType.None) | |
{ | |
ITechData techData = CraftData.Get(this.craftingTechType, false); | |
if (techData != null) | |
{ | |
if (this.linkedIndex != -1 && this.linkedIndex >= techData.linkedItemCount) | |
{ | |
this.Reset(); | |
} | |
} | |
else | |
{ | |
this.Reset(); | |
} | |
} | |
DayNightCycle main = DayNightCycle.main; | |
if (main != null) | |
{ | |
this.lastTime = main.timePassed; | |
} | |
this.NotifyChanged(this.currentTechType); | |
this.NotifyProgress(this.progress); | |
} | |
// Token: 0x06000B25 RID: 2853 | |
static CrafterLogic() | |
{ | |
CrafterLogic.sCraftTargets = new List<ICraftTarget>(); | |
} | |
// Token: 0x06000B26 RID: 2854 | |
public static ItemsContainer[] FindAllItemsContainersInRange(float maxDistance = 100f) | |
{ | |
if (DayNightCycle.main.timePassed > 1.0 + CrafterLogic.lastCheckTime || CrafterLogic.nearbyItemContainers == null) | |
{ | |
float num = maxDistance * maxDistance; | |
StorageContainer[] array = UnityEngine.Object.FindObjectsOfType<StorageContainer>(); | |
List<ItemsContainer> list = new List<ItemsContainer>(); | |
foreach (StorageContainer storageContainer in array) | |
{ | |
if (storageContainer.container != null && (Player.main.transform.position - storageContainer.transform.position).sqrMagnitude < num) | |
{ | |
list.Add(storageContainer.container); | |
} | |
} | |
list.Add(Inventory.main.container); | |
CrafterLogic.nearbyItemContainers = list.ToArray(); | |
CrafterLogic.lastCheckTime = DayNightCycle.main.timePassed; | |
} | |
return CrafterLogic.nearbyItemContainers; | |
} | |
// Token: 0x06000B27 RID: 2855 | |
public static int GetTotalPickupCount(TechType techType, ItemsContainer[] itemContainers) | |
{ | |
int num = 0; | |
foreach (ItemsContainer itemsContainer in itemContainers) | |
{ | |
num += itemsContainer.GetCount(techType); | |
} | |
return num; | |
} | |
// Token: 0x06000B28 RID: 2856 | |
public static bool DestroyItemInContainers(TechType techType, ItemsContainer[] itemsContainers) | |
{ | |
for (int i = 0; i < itemsContainers.Length; i++) | |
{ | |
if (itemsContainers[i].DestroyItem(techType)) | |
{ | |
return true; | |
} | |
} | |
return false; | |
} | |
// Token: 0x04000D0A RID: 3338 | |
public CrafterLogic.OnItemChanged onItemChanged; | |
// Token: 0x04000D0B RID: 3339 | |
public CrafterLogic.OnProgress onProgress; | |
// Token: 0x04000D0C RID: 3340 | |
public CrafterLogic.OnDone onDone; | |
// Token: 0x04000D0D RID: 3341 | |
private const int currentVersion = 1; | |
// Token: 0x04000D0E RID: 3342 | |
[ProtoMember(1)] | |
[NonSerialized] | |
public int version = 1; | |
// Token: 0x04000D0F RID: 3343 | |
[ProtoMember(2)] | |
[NonSerialized] | |
public float timeCraftingBegin = -1f; | |
// Token: 0x04000D10 RID: 3344 | |
[ProtoMember(3)] | |
[NonSerialized] | |
public float timeCraftingEnd = -1f; | |
// Token: 0x04000D11 RID: 3345 | |
[ProtoMember(4)] | |
[NonSerialized] | |
public TechType craftingTechType; | |
// Token: 0x04000D12 RID: 3346 | |
[ProtoMember(5)] | |
[NonSerialized] | |
public int linkedIndex = -1; | |
// Token: 0x04000D13 RID: 3347 | |
[ProtoMember(6)] | |
[NonSerialized] | |
public int numCrafted; | |
// Token: 0x04000D14 RID: 3348 | |
private double lastTime; | |
// Token: 0x04000D15 RID: 3349 | |
private static List<ICraftTarget> sCraftTargets; | |
// Token: 0x0400467E RID: 18046 | |
private static double lastCheckTime = 0.0; | |
// Token: 0x0400467F RID: 18047 | |
private static ItemsContainer[] nearbyItemContainers; | |
// Token: 0x02000203 RID: 515 | |
// (Invoke) Token: 0x06000B2A RID: 2858 | |
public delegate void OnItemChanged(TechType techType); | |
// Token: 0x02000204 RID: 516 | |
// (Invoke) Token: 0x06000B2E RID: 2862 | |
public delegate void OnProgress(float progress); | |
// Token: 0x02000205 RID: 517 | |
// (Invoke) Token: 0x06000B32 RID: 2866 | |
public delegate void OnDone(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Until a mod manager is stable, this will probably have to do. | |
1. Grab dnSpy: https://github.com/0xd4d/dnSpy/releases | |
2. Open up dnSpy | |
3. File -> Open; find the file: <Steam library folder>\steamapps\common\Subnautica\Subnautica_Data\Managed\Assembly-CSharp.dll | |
4. Open "Search assemblies" (Ctrl + Shift + K) and adjust the "Search For" drop-down to "Class" and search for: CrafterLogic | |
[It should look like this: https://i.imgur.com/P8Ml9nD.png] | |
5. Double-click the "CrafterLogic" entry | |
6. In the new tab, right-click in the code somewhere and select "Edit Class (C#)" | |
7. Replace everything in that file with the contents below in CrafterLogic.cs (it's mostly adding a few functions - description below) | |
8. Hit the "Compile" button | |
9. File -> Save Module | |
10. Done! Load up Subnautica and test it out! | |
By default, this mod will pull resources from any storage container it can find within 100m, including the player inventory - which it will try to take from last. The tooltip information will be wrong (i.e. it will say you don't have enough resources even though the Fabricator icon is lit), but that's currently only pulling information from the player inventory. When patching is simpler, I'll look at adding that, too. The comments in the file below are ignore and regenerated when compiled, so they shouldn't cause conflicts. | |
The file below only does a few things: | |
1. Adds a few convenience functions (FindAllItemsContainersInRange, GetTotalPickupCount, and DestroyItemInContainers), which are the last 3 functions in the file. | |
2. It replaces the logic in IsCraftRecipeFulfulled to search all containers in range, not just the player. | |
3. It replaces the logic in ConsumeResources with modified code from Inventory.ConsumeResourcesForRecipe - again using the containers in range instead of just the player's inventory. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment