Skip to content

Commit

Permalink
Added cooking buff system; better integration with infinite fires
Browse files Browse the repository at this point in the history
  • Loading branch information
ttr committed Jun 2, 2024
1 parent 34c94a6 commit fd03df9
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: MelonInfo(typeof(FireAddons.FireAddons), "FireAddons", "3.0.4", "ttr")]
[assembly: MelonInfo(typeof(FireAddons.FireAddons), "FireAddons", "3.1.1", "ttr")]
[assembly: MelonGame("Hinterland", "TheLongDark")]
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,15 @@ When temperature of fire (heat increase) is above "Burnout temperature", ember t
Embers timer is showed in hover card, next to burn time.
Thanks to [Digitalzombie](https://github.com/DigitalzombieTLD) for help with hover card.

### Cooking time
Cooking time setteings can alter how temperature affect cooking.
If Fire source is below "Minimal Cooking Temperature" cooking will not be happening (no time will be displayed in hovercard of cooking item).
If temperature is above "Minimal Cooking Temperature" but below "Normal Low threshold" cooking time will be extended, by factor of linear scalling between "Low Temp time factor" and 1.
If temperature is between "Normal Low threshold" and "Normal High threshold", cooking time is same as vanilla (factor of 1).
If temperature is between "Normal High threshold" and "Maximal Boost Temperature", cooking time will be shorten, by factor of linear scalling between 1 and "High Temp time factor".
Any temperature above will cause cooking to be scaled by "High Temp time factor" setting.

This also applies to time until food will be ruined (burned), melting snow, boiling and boiling out water.

## Notes / Issues.
Some code was based off [Deus13](https://github.com/Deus13/) [Fire_RV mod](https://github.com/Deus13/Fire_RV)
Binary file added media/cooking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added media/embers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 27 additions & 5 deletions src/FireAddons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace FireAddons
internal class FireAddons : MelonMod
{
private static List<string> fireFixed = new List<string>();
private static int FADSchema = 2;
private static int FADSchema = 3;
private static bool FADForceReload = false;
private static Dictionary<string, FireAddonsData> FAD = new Dictionary<string, FireAddonsData>();
internal static ModDataManager SaveMgr = new ModDataManager("FireAddons", false);
Expand Down Expand Up @@ -222,7 +222,8 @@ private static void WriteFireData(Fire __instance, string guid)
FAD[guid].burnSeconds = __instance.m_ElapsedOnTODSeconds;
FAD[guid].burnMaxSeconds = __instance.m_MaxOnTODSeconds;
FAD[guid].heatTemp = __instance.m_HeatSource.m_MaxTempIncrease;
SaveMgr.Save(JSON.Dump(FAD, EncodeOptions.NoTypeHints), "FAD");
FAD[guid].infinite = __instance.m_IsPerpetual;
SaveMgr.Save(JSON.Dump(FAD, EncodeOptions.NoTypeHints), "FAD");

}
private static void LoadFireData(Fire __instance, string guid)
Expand All @@ -237,6 +238,7 @@ private static void LoadFireData(Fire __instance, string guid)
__instance.m_FuelHeatIncrease = FAD[guid].heatTemp;
__instance.m_HeatSource.m_MaxTempIncrease = FAD[guid].heatTemp;
__instance.m_HeatSource.m_TempIncrease = FAD[guid].heatTemp;
__instance.m_IsPerpetual = FAD[guid].infinite;
CalculateFireTimers(__instance, timeDiff, __instance.m_MaxOnTODSeconds - __instance.m_ElapsedOnTODSeconds, true);

if (__instance.m_Campfire && (__instance.m_MaxOnTODSeconds > __instance.m_ElapsedOnTODSeconds || __instance.m_EmberDurationSecondsTOD > __instance.m_EmberTimer))
Expand Down Expand Up @@ -295,7 +297,7 @@ private static void CalculateFireTimers(Fire __instance, float timeDiff, float r

/* Small word of explanation
* this looks like it's underpowered as next one is ratio in hours and nowhere is converted to seconds
* however deltatime is in seconds, so whole ember store would be burned in second, not hour and doing 2 converstion (delta time and varitBurnSec) just to look clear is insane
* however deltatime is in seconds, so whole ember store would be burned in second, not hour and doing 2 converstion (delta time and varitBurnSec) just to look clear, feels insane
*/

// remove embers per timeDiff
Expand Down Expand Up @@ -333,11 +335,19 @@ private static void ApplyStoredFAD(Fire __instance, string guid)
}
internal static void CalculateEmbers(Fire __instance)
{
if (!__instance.m_IsPerpetual)
string guid = ObjectGuid.GetGuidFromGameObject(__instance.gameObject);
if (__instance.m_IsPerpetual)
{
if (FAD.ContainsKey(guid)) {
WriteFireData(__instance, guid);
FAD.Remove(guid);
MelonLogger.Msg("Fire was switched to infinite - storing it's data: " + guid);
}
}
else
{
float deltaTime = GameManager.GetTimeOfDayComponent().GetTODSeconds(Time.deltaTime);
float remSec = __instance.m_MaxOnTODSeconds - __instance.m_ElapsedOnTODSeconds;
string guid = ObjectGuid.GetGuidFromGameObject(__instance.gameObject);

/* when time acceleration is active, all Update() functions are called, but in end thre is TodMaterial.UpdateAll()
* which will calculate fire time twice.
Expand Down Expand Up @@ -458,7 +468,18 @@ internal static void FeedFire(Panel_FeedFire __instance)
{
_fire.m_NumGeneratedCharcoalPieces -= 1;
}
}

internal static float CookSpeedFromTemp(float temp)
{
// no complex conditions needed due to early returns.
if (!Settings.options.cookingSystem) { return 1; }
if (temp < Settings.options.cookingSystemTempMin) { return float.PositiveInfinity; }
if (temp < Settings.options.cookingSystemTempLow) { return Mathf.Lerp(Settings.options.cookingSystemTimeLow, 1f, Mathf.InverseLerp(Settings.options.cookingSystemTempMin, Settings.options.cookingSystemTempLow, temp)); }
if (temp <= Settings.options.cookingSystemTempHigh) { return 1; }
if (temp <= Settings.options.cookingSystemTempMax) { return Mathf.Lerp(1f, Settings.options.cookingSystemTimeHigh, Mathf.InverseLerp(Settings.options.cookingSystemTempHigh, Settings.options.cookingSystemTempMax, temp)); }
if (temp > Settings.options.cookingSystemTempMax) { return Settings.options.cookingSystemTimeHigh; }
return 1;
}
}
internal class FireAddonsData
Expand All @@ -471,5 +492,6 @@ internal class FireAddonsData
public float burnSeconds;
public float burnMaxSeconds;
public float heatTemp;
public bool infinite;
}
}
52 changes: 21 additions & 31 deletions src/Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ private static void Postfix(Fire __instance)
}

}

[HarmonyPatch(typeof(FireManager), nameof(FireManager.CalculateFireStartSuccess))]
static class FireManager_CalculateFireStartSuccess
{
Expand All @@ -84,7 +84,7 @@ private static void Postfix(FireManager __instance, float __result)
}
}
}

[HarmonyPatch(typeof(Panel_FireStart), nameof(Panel_FireStart.RefreshChanceOfSuccessLabel))]
static class Panel_FireStart_RefreshChanceOfSuccessLabel
{
Expand Down Expand Up @@ -206,7 +206,7 @@ private static void Postfix(Panel_FeedFire __instance)
}

}

[HarmonyPatch(typeof(Panel_FireStart), nameof(Panel_FireStart.FilterItemFuelSource))]
internal static class Panel_FireStart_FilterItemFuelSource
{
Expand All @@ -233,36 +233,11 @@ private static bool Prefix(Panel_FeedFire __instance, GameObject go, ref bool __
{
temp = true;
}
__result = !(fuelSourceItem == null) && temp;
__result = !(fuelSourceItem == null) && temp;
return false;
}
}
/* not needed - below one covers all
[HarmonyPatch(typeof(Campfire), "GetHoverText")]
internal static class Campfire_GetHoverText
{
private static void Postfix(Campfire __instance, ref string __result)
{
if (!__instance.Fire.m_IsPerpetual && __result != null && Settings.options.embersSystem && __instance.Fire.GetFireState() == FireState.FullBurn && __instance.Fire.m_EmberDurationSecondsTOD > 0 && __instance.Fire.m_EmberTimer >= 0)
{
float emberDiff = __instance.Fire.m_EmberDurationSecondsTOD - __instance.Fire.m_EmberTimer;
int emberH = (int)Mathf.Floor(emberDiff / 3600);
int emberM = (int)((emberDiff - (emberH * 3600)) / 60);
// those spaces are needed for 'canvas' as it's calculated based on 1st line lenght
if (__instance.Fire.m_EmberTimer == 0)
{
string[] input = __result.Split('\n');
__result = " " + __instance.name.ToString() + " \n" + input[1] + " & " + emberH.ToString() + "h " + emberM.ToString() + "m\n(" + input[2] + ")";
}
else
{
__result = " " + __instance.name.ToString() + " \n" + Localization.Get("GAMEPLAY_Embers") + ": " + emberH.ToString() + "h " + emberM.ToString() + "m\n(" + __instance.Fire.GetHeatIncreaseText() + ")";
}
}
}
}
*/

[HarmonyPatch(typeof(FireplaceInteraction), nameof(FireplaceInteraction.GetHoverText))]
internal static class FireplaceInteraction_GetHoverText
{
Expand All @@ -279,7 +254,7 @@ public static void Postfix(WoodStove __instance, ref string __result)
string[] input = __result.Split('\n');
if (__instance.Fire.m_EmberTimer == 0)
{

__result = " " + input[0] + " \n" + input[1] + " & " + emberH.ToString() + "h " + emberM.ToString() + "m\n(" + input[2] + ")";
} else
{
Expand All @@ -295,6 +270,7 @@ internal static class CookingPotItem_AttachedFireIsBurning
{
public static void Postfix(CookingPotItem __instance, ref bool __result)
{
if (__instance.m_FireBeingUsed && __instance.m_FireBeingUsed.m_IsPerpetual) { return; }
if (Settings.options.embersSystem && Settings.options.embersSystemNoCooking && __result && __instance.m_FireBeingUsed?.m_EmberTimer > 0)
{
__result = false;
Expand All @@ -307,12 +283,26 @@ internal static class Fire_IsBurning
{
public static void Postfix(Fire __instance, ref bool __result)
{
if (__instance.m_IsPerpetual) { return; }
if (Settings.options.embersSystem && Settings.options.embersSystemNoCooking && __result && __instance?.m_EmberTimer > 0)
{
__result = false;
}
}
}

[HarmonyPatch(typeof(CookingPotItem), nameof(CookingPotItem.UpdateCookingTimeAndState))]
internal static class CookingPotItem_UpdateCookingTimeAndState {
public static void Prefix(CookingPotItem __instance, ref float cookTimeMinutes, ref float readyTimeMinutes)
{
if (Settings.options.cookingSystem && __instance.m_FireBeingUsed)
{
cookTimeMinutes *= FireAddons.CookSpeedFromTemp(__instance.m_FireBeingUsed.GetCurrentTempIncrease());
readyTimeMinutes *= FireAddons.CookSpeedFromTemp(__instance.m_FireBeingUsed.GetCurrentTempIncrease());
//MelonLogger.Msg("Cooking speed2: " + cookTimeMinutes + " " + readyTimeMinutes + " " + FireAddons.CookSpeedFromTemp(__instance.m_FireBeingUsed.GetCurrentTempIncrease()));
}
}

}
}
}
113 changes: 98 additions & 15 deletions src/Settings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using ModSettings;
using System.Reflection;
using UnityEngine.UI;

namespace FireAddons
{
Expand Down Expand Up @@ -169,26 +170,90 @@ internal class FireAddonsSettings : JsonModSettings
[Description("Will disable cooking/boiling while stove/campfire is in 'ember state' when new ember/smoldering mechanic is used. Setting this to false, will make new mechanic useless and they will only 'extend burn time' (yet this ability was requiesed). Recommended: true.")]
public bool embersSystemNoCooking = true;

[Name("Use Charcoal as fuel")]
[Description("Allow using charcal as fuel. Set to false if other mod is adding charcoal as fuel")]
public bool burnCharcoal = true;

[Name("... burn time")]
[Description("Recommended 20 min.")]
[Slider(5, 40)]
public int burnCharcoalTime = 20;
[Section("Cooking Time")]
[Name("Enable cooking time based on temp.")]
[Description("Enabling this will allow to tune time of cooking based on temperature.")]
public bool cookingSystem = true;

[Name("... temp increase")]
[Description("Recommended 5 deg")]
[Slider(0, 15)]
public int burnCharcoalTemp = 5;
[Name("Minimal Cooking Temperature")]
[Description("Below this temperature, cooking will be impossible. 0 to disable.")]
[Slider(0,30)]
public int cookingSystemTempMin = 15;

[Name("Normal Low threshold")]
[Description("Temperature where 1x speed will start")]
[Slider(0, 80)]
public int cookingSystemTempLow = 25;

[Name("Normal High threshold")]
[Description("Temperature where 1x speed will ent")]
[Slider(0, 80)]
public int cookingSystemTempHigh = 40;

[Name("Maximal Boost Temperature")]
[Description("Temperature where maximum time boost is achievied.")]
[Slider(0, 80)]
public int cookingSystemTempMax = 60;

[Name("Low Temp time factor")]
[Description("Time factor at lowest possible cooking temperature")]
[Slider(1f, 5f, 41, NumberFormat = "{0:F1}")]
public float cookingSystemTimeLow = 3f;

[Name("High Temp time factor")]
[Description("Time factor at maximum boost temperature")]
[Slider(0.1f, 1f, 10, NumberFormat = "{0:F1}")]
public float cookingSystemTimeHigh = 0.7f;


[Section("Other")]
[Name("Use Charcoal as fuel")]
[Description("Allow using charcal as fuel. Set to false if other mod is adding charcoal as fuel")]
public bool burnCharcoal = true;

[Name("... burn time")]
[Description("Recommended 20 min.")]
[Slider(5, 40)]
public int burnCharcoalTime = 20;

[Name("... temp increase")]
[Description("Recommended 5 deg")]
[Slider(0, 15)]
public int burnCharcoalTemp = 5;

protected override void OnChange(FieldInfo field, object oldValue, object newValue)
{
RefreshFields();
}
if(field.Name == nameof(cookingSystemTempMin))
{
cookingSystemTempLow = Math.Max((int)newValue, cookingSystemTempLow);
cookingSystemTempHigh = Math.Max((int)newValue, cookingSystemTempHigh);
cookingSystemTempMax = Math.Max((int)newValue, cookingSystemTempMax);
}
else if (field.Name == nameof(cookingSystemTempLow))
{
cookingSystemTempMin = Math.Min((int)newValue, cookingSystemTempMin);
cookingSystemTempHigh = Math.Max((int)newValue, cookingSystemTempHigh);
cookingSystemTempMax = Math.Max((int)newValue, cookingSystemTempMax);
}
else if (field.Name == nameof(cookingSystemTempHigh))
{
cookingSystemTempMin = Math.Min((int)newValue, cookingSystemTempMin);
cookingSystemTempLow = Math.Min((int)newValue, cookingSystemTempLow);
cookingSystemTempMax = Math.Max((int)newValue, cookingSystemTempMax);
}
else if (field.Name == nameof(cookingSystemTempMax))
{
cookingSystemTempMin = Math.Min((int)newValue, cookingSystemTempMin);
cookingSystemTempLow = Math.Min((int)newValue, cookingSystemTempLow);
cookingSystemTempHigh = Math.Min((int)newValue, cookingSystemTempHigh);
}

RefreshFields();
RefreshGUI();
}

internal void RefreshFields()
internal void RefreshFields()
{
if (lanternUse)
{
Expand Down Expand Up @@ -268,7 +333,25 @@ internal void RefreshFields()
SetFieldVisible(nameof(burnCharcoalTime), false);
SetFieldVisible(nameof(burnCharcoalTemp), false);
}
}
if(cookingSystem)
{
SetFieldVisible(nameof(cookingSystemTempHigh), true);
SetFieldVisible(nameof(cookingSystemTempLow), true);
SetFieldVisible(nameof(cookingSystemTempMax), true);
SetFieldVisible(nameof(cookingSystemTempMin), true);
SetFieldVisible(nameof(cookingSystemTimeHigh), true);
SetFieldVisible(nameof(cookingSystemTimeLow), true);
}
else
{
SetFieldVisible(nameof(cookingSystemTempHigh), false);
SetFieldVisible(nameof(cookingSystemTempLow), false);
SetFieldVisible(nameof(cookingSystemTempMax), false);
SetFieldVisible(nameof(cookingSystemTempMin), false);
SetFieldVisible(nameof(cookingSystemTimeHigh), false);
SetFieldVisible(nameof(cookingSystemTimeLow), false);
}
}
}
internal static class Settings
{
Expand Down

0 comments on commit fd03df9

Please sign in to comment.