using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using AzuEPI;
using AzuEPI.Core.Slots;
using AzuExtendedPlayerInventory;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ItemManager;
using JetBrains.Annotations;
using LocalizationManager;
using Magic_Supremacy;
using Microsoft.CodeAnalysis;
using PieceManager;
using ServerSync;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;
[assembly: AssemblyFileVersion("3.0.0")]
[assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")]
[assembly: ComVisible(false)]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyProduct("Magic_Supremacy")]
[assembly: AssemblyCompany("Dreanegade")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyTitle("Magic_Supremacy")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("3.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[<89a66323-74c2-44da-9bfd-8539ae0bdfa0>Embedded]
internal sealed class <89a66323-74c2-44da-9bfd-8539ae0bdfa0>EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[<89a66323-74c2-44da-9bfd-8539ae0bdfa0>Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class <d174e93b-eb8b-4056-a8c2-4fa27d63ad65>NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public <d174e93b-eb8b-4056-a8c2-4fa27d63ad65>NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public <d174e93b-eb8b-4056-a8c2-4fa27d63ad65>NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
[<89a66323-74c2-44da-9bfd-8539ae0bdfa0>Embedded]
internal sealed class <89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContextAttribute : Attribute
{
public readonly byte Flag;
public <89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
public static class ExtraTooltip
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private static class DevLog
{
public const bool Enabled = false;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public enum TooltipAttackSlot
{
Main,
Secondary
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class TooltipLinkDef
{
public readonly string ItemPrefabName;
public readonly TooltipAttackSlot AttackSlot;
public readonly List<TooltipEntry> Entries;
public TooltipLinkDef(string itemPrefabName, TooltipAttackSlot attackSlot, string statusEffectPrefabName)
{
ItemPrefabName = itemPrefabName;
AttackSlot = attackSlot;
Entries = new List<TooltipEntry>
{
new TooltipSELine(statusEffectPrefabName)
};
}
public TooltipLinkDef(string itemPrefabName, TooltipAttackSlot attackSlot, params TooltipEntry[] entries)
{
ItemPrefabName = itemPrefabName;
AttackSlot = attackSlot;
Entries = new List<TooltipEntry>(entries);
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private abstract class TooltipEntry
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
public abstract string Build(string itemPrefabName, int qualityLevel, float skillLevel);
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class TooltipSELine : TooltipEntry
{
private readonly string StatusEffectPrefabName;
public TooltipSELine(string statusEffectPrefabName)
{
StatusEffectPrefabName = statusEffectPrefabName;
}
public override string Build(string itemPrefabName, int qualityLevel, float skillLevel)
{
StatusEffect val = FindStatusEffect(StatusEffectPrefabName);
if ((Object)(object)val == (Object)null)
{
DLog("StatusEffect not found: " + StatusEffectPrefabName + " for item " + itemPrefabName + ".");
return "";
}
string text = BuildVanillaStatusEffectTooltip(val, qualityLevel, skillLevel);
if (string.IsNullOrEmpty(text))
{
DLog("Tooltip skipped for " + itemPrefabName + ": empty tooltip from " + StatusEffectPrefabName + ".");
return "";
}
return text;
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class TooltipTextLine : TooltipEntry
{
private readonly string TextKeyOrText;
private readonly string Color;
public TooltipTextLine(string textKeyOrText, string color = null)
{
TextKeyOrText = textKeyOrText;
Color = color;
}
public override string Build(string itemPrefabName, int qualityLevel, float skillLevel)
{
return Colorize(Localize(TextKeyOrText), Color);
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class TooltipInlineLine : TooltipEntry
{
private readonly List<TooltipInlinePart> Parts;
public TooltipInlineLine(params TooltipInlinePart[] parts)
{
Parts = new List<TooltipInlinePart>(parts);
}
public override string Build(string itemPrefabName, int qualityLevel, float skillLevel)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (TooltipInlinePart part in Parts)
{
if (part != null)
{
stringBuilder.Append(part.Build());
}
}
return stringBuilder.ToString();
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class TooltipInlinePart
{
private readonly string TextKeyOrText;
private readonly string Color;
public TooltipInlinePart(string textKeyOrText, string color = null)
{
TextKeyOrText = textKeyOrText;
Color = color;
}
public string Build()
{
return Colorize(Localize(TextKeyOrText), Color);
}
}
[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
{
typeof(ItemData),
typeof(int),
typeof(bool),
typeof(float),
typeof(int)
})]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private static class ItemData_GetTooltip_ExtraTooltipPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(ItemData item, int qualityLevel, bool crafting, float worldLevel, int stackOverride, ref string __result)
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
try
{
if (item == null || item.m_shared == null)
{
return;
}
string itemPrefabName = GetItemPrefabName(item);
if (string.IsNullOrEmpty(itemPrefabName))
{
return;
}
List<TooltipLinkDef> defsForItem = GetDefsForItem(itemPrefabName);
if (defsForItem.Count != 0)
{
float skillLevel = 0f;
if ((Object)(object)Player.m_localPlayer != (Object)null)
{
skillLevel = ((Character)Player.m_localPlayer).GetSkillLevel(item.m_shared.m_skillType);
}
StringBuilder stringBuilder = new StringBuilder();
AppendEntriesForSlot(stringBuilder, defsForItem, TooltipAttackSlot.Main, qualityLevel, skillLevel);
AppendEntriesForSlot(stringBuilder, defsForItem, TooltipAttackSlot.Secondary, qualityLevel, skillLevel);
if (stringBuilder.Length <= 0)
{
DLog("Tooltip skipped for " + itemPrefabName + ": no valid extra tooltip was generated.");
return;
}
__result = __result + "\n\n" + stringBuilder.ToString().TrimEnd(Array.Empty<char>());
DLog("Extra tooltip added for " + itemPrefabName + ".");
}
}
catch (Exception arg)
{
DLog($"ERROR while adding extra tooltip: {arg}");
}
}
}
private const string MainAttackHeaderColor = "#FFC654";
private const string SecondaryAttackHeaderColor = "#FFC654";
private const string VanillaSENameColor = "orange";
private const string HealValueColor = "#C8FF6A";
private const string MainAttackKey = "$main_attack_DO";
private const string SecondaryAttackKey = "$secondary_attack_DO";
private static readonly List<TooltipLinkDef> Defs = new List<TooltipLinkDef>
{
new TooltipLinkDef("StaffTotemcallerTotemDO", TooltipAttackSlot.Main, "SE_TotemSlow_DO"),
new TooltipLinkDef("StaffTotemcallerTotemDO", TooltipAttackSlot.Secondary, "SE_TotemBuff_DO"),
new TooltipLinkDef("StaffTotemcallerBoomerangDO", TooltipAttackSlot.Secondary, "SE_TotemcallerOrbit_DO"),
new TooltipLinkDef("StaffMushroomcallerMushroomDO", TooltipAttackSlot.Main, new TooltipTextLine("$aoe_trap_damage_DO")),
new TooltipLinkDef("StaffMushroomcallerMushroomDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_area_light_DO")),
new TooltipLinkDef("StaffDeathcallerBlasterDO", TooltipAttackSlot.Secondary, "SE_DeathcallerDebuff_DO"),
new TooltipLinkDef("BowFrostcallerIceDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_trap_damage_DO"), new TooltipTextLine("$aoe_area_debuff_DO"), new TooltipTextLine(" "), new TooltipSELine("SE_FrostcallerAura_DO")),
new TooltipLinkDef("StaffFrostcallerShotgunDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_area_spawn_DO"), new TooltipTextLine("$aoe_area_obstacle_DO")),
new TooltipLinkDef("StaffFirecallerRingDO", TooltipAttackSlot.Secondary, "SE_FirecallerOrbit_DO"),
new TooltipLinkDef("StaffStonecallerBurstDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_area_obstacle_DO")),
new TooltipLinkDef("StaffWindcallerBuffDO", TooltipAttackSlot.Main, "SE_WindcallerShield_DO"),
new TooltipLinkDef("StaffWindcallerBuffDO", TooltipAttackSlot.Secondary, "SE_WindcallerPetal_DO"),
new TooltipLinkDef("StaffLightcallerHealDO", TooltipAttackSlot.Main, new TooltipTextLine("$aoe_trap_heal_DO"), new TooltipInlineLine(new TooltipInlinePart("$heal_DO"), new TooltipInlinePart(": "), new TooltipInlinePart("40", "orange"))),
new TooltipLinkDef("StaffLightcallerHealDO", TooltipAttackSlot.Secondary, "SE_LightcallerHeal_DO"),
new TooltipLinkDef("StaffLightcallerSphereDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_area_light_DO"), new TooltipTextLine("$aoe_area_damage_DO")),
new TooltipLinkDef("StaffStormcallerSphereDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_trap_damage_DO")),
new TooltipLinkDef("StaffStormcallerShockerDO", TooltipAttackSlot.Secondary, "SE_StormcallerOrbit_DO"),
new TooltipLinkDef("StaffBloodcallerHealDO", TooltipAttackSlot.Main, "SE_BloodcallerHeal_DO"),
new TooltipLinkDef("StaffBloodcallerHealDO", TooltipAttackSlot.Secondary, "SE_BloodBuff_DO"),
new TooltipLinkDef("StaffBloodcallerShotgunDO", TooltipAttackSlot.Secondary, new TooltipTextLine("$aoe_area_damage_DO"), new TooltipTextLine(" "), new TooltipTextLine("$aoe_area_debuff_DO"), new TooltipTextLine(" "), new TooltipSELine("SE_BloodDebuff_DO"))
};
private static void AppendEntriesForSlot(StringBuilder sb, List<TooltipLinkDef> itemDefs, TooltipAttackSlot slot, int qualityLevel, float skillLevel)
{
bool flag = false;
foreach (TooltipLinkDef itemDef in itemDefs)
{
if (itemDef.AttackSlot != slot)
{
continue;
}
string value = BuildDefTooltip(itemDef, qualityLevel, skillLevel);
if (string.IsNullOrEmpty(value))
{
continue;
}
if (!flag)
{
if (sb.Length > 0)
{
sb.Append("\n\n");
}
sb.Append(GetAttackHeader(slot));
sb.Append("\n");
flag = true;
}
else
{
sb.Append("\n\n");
}
sb.Append(value);
}
}
private static string BuildDefTooltip(TooltipLinkDef def, int qualityLevel, float skillLevel)
{
if (def == null || def.Entries == null || def.Entries.Count == 0)
{
return "";
}
StringBuilder stringBuilder = new StringBuilder();
foreach (TooltipEntry entry in def.Entries)
{
if (entry == null)
{
continue;
}
string value = entry.Build(def.ItemPrefabName, qualityLevel, skillLevel);
if (!string.IsNullOrEmpty(value))
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append("\n");
}
stringBuilder.Append(value);
}
}
return stringBuilder.ToString();
}
private static string BuildVanillaStatusEffectTooltip(StatusEffect statusEffect, int qualityLevel, float skillLevel)
{
if ((Object)(object)statusEffect == (Object)null)
{
return "";
}
statusEffect.SetLevel(qualityLevel, skillLevel);
return "<color=orange>" + statusEffect.m_name + "</color>\n" + statusEffect.GetTooltipString();
}
private static string GetAttackHeader(TooltipAttackSlot slot)
{
return slot switch
{
TooltipAttackSlot.Main => Colorize(Localize("$main_attack_DO"), "#FFC654"),
TooltipAttackSlot.Secondary => Colorize(Localize("$secondary_attack_DO"), "#FFC654"),
_ => "",
};
}
private static string Colorize(string text, string color)
{
if (string.IsNullOrEmpty(text))
{
return "";
}
if (string.IsNullOrEmpty(color))
{
return text;
}
return "<color=" + color + ">" + text + "</color>";
}
private static string Localize(string keyOrText)
{
if (string.IsNullOrEmpty(keyOrText))
{
return "";
}
if (Localization.instance == null)
{
return keyOrText;
}
if (keyOrText.StartsWith("$"))
{
return Localization.instance.Localize(keyOrText);
}
return keyOrText;
}
private static List<TooltipLinkDef> GetDefsForItem(string itemPrefabName)
{
List<TooltipLinkDef> list = new List<TooltipLinkDef>();
foreach (TooltipLinkDef def in Defs)
{
if (def.ItemPrefabName == itemPrefabName)
{
list.Add(def);
}
}
return list;
}
private static StatusEffect FindStatusEffect(string statusEffectPrefabName)
{
if ((Object)(object)ObjectDB.instance == (Object)null)
{
return null;
}
foreach (StatusEffect statusEffect in ObjectDB.instance.m_StatusEffects)
{
if (!((Object)(object)statusEffect == (Object)null))
{
if (((Object)statusEffect).name == statusEffectPrefabName)
{
return statusEffect;
}
if (Utils.GetPrefabName(((Object)statusEffect).name) == statusEffectPrefabName)
{
return statusEffect;
}
}
}
return null;
}
private static string GetItemPrefabName(ItemData item)
{
if (item == null)
{
return "";
}
if ((Object)(object)item.m_dropPrefab != (Object)null)
{
return Utils.GetPrefabName(((Object)item.m_dropPrefab).name);
}
if (item.m_shared != null && !string.IsNullOrEmpty(item.m_shared.m_name))
{
GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item.m_shared.m_name) : null);
if ((Object)(object)val != (Object)null)
{
return Utils.GetPrefabName(((Object)val).name);
}
}
return "";
}
private static void DLog(string message)
{
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public static class ItemDropExtendedStats
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private static class DevLog
{
public const bool Enabled = false;
public const bool Skill = false;
public const bool CarryWeight = false;
public const bool MovementSpeed = false;
public const bool Adrenaline = false;
public const bool Tooltips = false;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public enum ExtendedStatType
{
SkillLevelBonus,
CarryWeightBonus,
MovementSpeedModifier,
AdrenalineGainModifier
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public class ExtendedStatEntry
{
public string PrefabName;
public ExtendedStatType StatType;
public SkillType SkillType;
public float Value;
public ExtendedStatEntry(string prefab, ExtendedStatType statType, float value)
{
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
PrefabName = prefab;
StatType = statType;
SkillType = (SkillType)0;
Value = value;
}
public ExtendedStatEntry(string prefab, ExtendedStatType statType, SkillType skillType, float value)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
PrefabName = prefab;
StatType = statType;
SkillType = skillType;
Value = value;
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(Skills), "GetSkillLevel")]
public static class Skills_GetSkillLevel_ItemDropExtendedStatsPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(Skills __instance, SkillType skillType, ref float __result)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
try
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
Player player = __instance.m_player;
if (!((Object)(object)player == (Object)null) && (int)skillType != 0)
{
float equippedSkillBonus = GetEquippedSkillBonus(player, skillType);
if (equippedSkillBonus != 0f)
{
__result += equippedSkillBonus;
DLogSkill($"Applied item-based skill bonus to {skillType}: +{equippedSkillBonus}, result={__result}");
}
}
}
catch (Exception ex)
{
LogError("Exception in Skills_GetSkillLevel_ItemDropExtendedStatsPatch: " + ex);
}
}
}
[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class Player_GetMaxCarryWeight_ItemDropExtendedStatsPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(Player __instance, ref float __result)
{
try
{
if (!((Object)(object)__instance == (Object)null))
{
float equippedBonusByType = GetEquippedBonusByType(__instance, ExtendedStatType.CarryWeightBonus);
if (equippedBonusByType != 0f)
{
__result += equippedBonusByType;
DLogCarry($"Applied item-based carry weight bonus: +{equippedBonusByType}, result={__result}");
}
}
}
catch (Exception ex)
{
LogError("Exception in Player_GetMaxCarryWeight_ItemDropExtendedStatsPatch: " + ex);
}
}
}
[HarmonyPatch(typeof(SEMan), "ApplyStatusEffectSpeedMods")]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class SEMan_ApplyStatusEffectSpeedMods_ItemDropExtendedStatsPatch
{
private static void Prefix(ref float speed, out float __state)
{
__state = speed;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(SEMan __instance, ref float speed, Vector3 dir, float __state)
{
try
{
Player playerFromSEMan = GetPlayerFromSEMan(__instance);
if ((Object)(object)playerFromSEMan == (Object)null)
{
return;
}
float equippedBonusByType = GetEquippedBonusByType(playerFromSEMan, ExtendedStatType.MovementSpeedModifier);
if (equippedBonusByType != 0f)
{
speed += __state * equippedBonusByType;
if (speed < 0f)
{
speed = 0f;
}
DLogMovement($"Applied item-based movement speed modifier: {equippedBonusByType * 100f:+0;-0}% baseSpeed={__state}, result={speed}");
}
}
catch (Exception ex)
{
LogError("Exception in SEMan_ApplyStatusEffectSpeedMods_ItemDropExtendedStatsPatch: " + ex);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(SEMan), "ModifyAdrenaline")]
public static class SEMan_ModifyAdrenaline_ItemDropExtendedStatsPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(SEMan __instance, float baseValue, ref float use)
{
try
{
Player playerFromSEMan = GetPlayerFromSEMan(__instance);
if (!((Object)(object)playerFromSEMan == (Object)null))
{
float equippedBonusByType = GetEquippedBonusByType(playerFromSEMan, ExtendedStatType.AdrenalineGainModifier);
if (equippedBonusByType != 0f)
{
use += baseValue * equippedBonusByType;
DLogAdrenaline($"Applied item-based adrenaline gain modifier: {equippedBonusByType * 100f:+0;-0}% baseValue={baseValue}, result={use}");
}
}
}
catch (Exception ex)
{
LogError("Exception in SEMan_ModifyAdrenaline_ItemDropExtendedStatsPatch: " + ex);
}
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
[HarmonyPatch]
public static class ItemData_StaticGetTooltip_ItemDropExtendedStatsPatch
{
private static MethodBase TargetMethod()
{
return AccessTools.Method(typeof(ItemData), "GetTooltip", new Type[5]
{
typeof(ItemData),
typeof(int),
typeof(bool),
typeof(float),
typeof(int)
}, (Type[])null);
}
private static void Postfix(ItemData item, int qualityLevel, bool crafting, float worldLevel, int stackOverride, ref string __result)
{
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
try
{
if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
{
return;
}
string name = ((Object)item.m_dropPrefab).name;
DLogTooltip($"Static GetTooltip Postfix called for {name}, q={qualityLevel}, crafting={crafting}, worldLevel={worldLevel}, stackOverride={stackOverride}");
string text = BuildExtendedTooltipBlock(item);
if (string.IsNullOrEmpty(text))
{
return;
}
float num = 0f;
if ((Object)(object)Player.m_localPlayer != (Object)null)
{
num = ((Character)Player.m_localPlayer).GetSkillLevel(item.m_shared.m_skillType);
}
string statusEffectTooltip = item.GetStatusEffectTooltip(qualityLevel, num);
string chainTooltip = item.GetChainTooltip(qualityLevel, num);
string setStatusEffectTooltip = item.GetSetStatusEffectTooltip(qualityLevel, num);
int num2 = -1;
if (!string.IsNullOrEmpty(statusEffectTooltip))
{
int num3 = __result.IndexOf(statusEffectTooltip, StringComparison.Ordinal);
if (num3 >= 0)
{
num2 = ((num3 - 2 >= 0 && __result.Substring(num3 - 2, 2) == "\n\n") ? (num3 - 2) : num3);
}
}
if (num2 < 0 && !string.IsNullOrEmpty(chainTooltip))
{
int num4 = __result.IndexOf(chainTooltip, StringComparison.Ordinal);
if (num4 >= 0)
{
num2 = ((num4 - 2 >= 0 && __result.Substring(num4 - 2, 2) == "\n\n") ? (num4 - 2) : num4);
}
}
if (num2 < 0)
{
int num5 = __result.IndexOf("\n$item_eitrregen_modifier:", StringComparison.Ordinal);
if (num5 >= 0)
{
num2 = num5;
}
}
if (num2 < 0 && !string.IsNullOrEmpty(setStatusEffectTooltip))
{
int num6 = __result.IndexOf("\n\n$item_seteffect", StringComparison.Ordinal);
if (num6 >= 0)
{
num2 = num6;
}
}
if (num2 < 0)
{
int num7 = __result.IndexOf("\n$item_fulladrenaline:", StringComparison.Ordinal);
if (num7 >= 0)
{
num2 = num7;
}
}
if (num2 >= 0)
{
DLogTooltip("Inserting extended ItemDrop stats before special/effect section for " + name);
__result = __result.Insert(num2, text);
}
else
{
DLogTooltip("No special/effect section found, appending extended ItemDrop stats at end for " + name);
__result += text;
}
}
catch (Exception ex)
{
LogError("Exception in ItemData_StaticGetTooltip_ItemDropExtendedStatsPatch: " + ex);
}
}
}
public static readonly List<ExtendedStatEntry> ExtendedStatsConfig = new List<ExtendedStatEntry>
{
new ExtendedStatEntry("ArmorMushroomcallerChestDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 2f),
new ExtendedStatEntry("ArmorTotemcallerChestDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 2f),
new ExtendedStatEntry("ArmorDeathcallerLegsDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 4f),
new ExtendedStatEntry("ArmorFrostcallerHelmetDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 6f),
new ExtendedStatEntry("ArmorFirecallerChestDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 8f),
new ExtendedStatEntry("ArmorStonecallerChestDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 8f),
new ExtendedStatEntry("ArmorWindcallerLegsDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 10f),
new ExtendedStatEntry("ArmorLightcallerLegsDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 10f),
new ExtendedStatEntry("ArmorStormcallerHelmetDO", ExtendedStatType.SkillLevelBonus, (SkillType)9, 12f),
new ExtendedStatEntry("ArmorBloodcallerHelmetDO", ExtendedStatType.SkillLevelBonus, (SkillType)10, 12f),
new ExtendedStatEntry("ArmorTotemcallerCapeDO", ExtendedStatType.MovementSpeedModifier, 0.04f),
new ExtendedStatEntry("ArmorMushroomcallerCapeDO", ExtendedStatType.CarryWeightBonus, 20f),
new ExtendedStatEntry("ArmorFirecallerCapeDO", ExtendedStatType.AdrenalineGainModifier, 0.15f),
new ExtendedStatEntry("ArmorStormcallerCapeDO", ExtendedStatType.MovementSpeedModifier, 0.09f),
new ExtendedStatEntry("ArmorBloodcallerCapeDO", ExtendedStatType.SkillLevelBonus, (SkillType)10, 20f)
};
private static FieldRef<SEMan, Character> _semanCharacterRef;
private static bool _semanInit;
private static void DLogSkill(string msg)
{
}
private static void DLogCarry(string msg)
{
}
private static void DLogMovement(string msg)
{
}
private static void DLogAdrenaline(string msg)
{
}
private static void DLogTooltip(string msg)
{
}
private static void LogError(string msg)
{
}
private static void EnsureSEManInit()
{
if (!_semanInit)
{
_semanInit = true;
_semanCharacterRef = AccessTools.FieldRefAccess<SEMan, Character>("m_character");
}
}
private static Player GetPlayerFromSEMan(SEMan seman)
{
if (seman == null)
{
return null;
}
try
{
EnsureSEManInit();
Character obj = _semanCharacterRef.Invoke(seman);
return (Player)(object)((obj is Player) ? obj : null);
}
catch (Exception ex)
{
LogError("Exception in GetPlayerFromSEMan: " + ex);
return null;
}
}
private static float GetEquippedSkillBonus(Player player, SkillType requestedSkill)
{
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Invalid comparison between Unknown and I4
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)player == (Object)null)
{
return 0f;
}
float num = 0f;
try
{
Inventory inventory = ((Humanoid)player).GetInventory();
if (inventory == null)
{
return 0f;
}
List<ItemData> equippedItems = inventory.GetEquippedItems();
for (int i = 0; i < equippedItems.Count; i++)
{
ItemData val = equippedItems[i];
if (val == null || (Object)(object)val.m_dropPrefab == (Object)null)
{
continue;
}
string name = ((Object)val.m_dropPrefab).name;
for (int j = 0; j < ExtendedStatsConfig.Count; j++)
{
ExtendedStatEntry extendedStatEntry = ExtendedStatsConfig[j];
if (extendedStatEntry.StatType == ExtendedStatType.SkillLevelBonus && string.Equals(name, extendedStatEntry.PrefabName, StringComparison.OrdinalIgnoreCase) && ((int)extendedStatEntry.SkillType == 999 || extendedStatEntry.SkillType == requestedSkill))
{
num += extendedStatEntry.Value;
DLogSkill($"Matched {name} for {requestedSkill}: +{extendedStatEntry.Value}");
}
}
}
}
catch (Exception ex)
{
LogError("Exception in GetEquippedSkillBonus: " + ex);
}
return num;
}
private static float GetEquippedBonusByType(Player player, ExtendedStatType statType)
{
if ((Object)(object)player == (Object)null)
{
return 0f;
}
float num = 0f;
try
{
Inventory inventory = ((Humanoid)player).GetInventory();
if (inventory == null)
{
return 0f;
}
List<ItemData> equippedItems = inventory.GetEquippedItems();
for (int i = 0; i < equippedItems.Count; i++)
{
ItemData val = equippedItems[i];
if (val == null || (Object)(object)val.m_dropPrefab == (Object)null)
{
continue;
}
string name = ((Object)val.m_dropPrefab).name;
for (int j = 0; j < ExtendedStatsConfig.Count; j++)
{
ExtendedStatEntry extendedStatEntry = ExtendedStatsConfig[j];
if (extendedStatEntry.StatType == statType && string.Equals(name, extendedStatEntry.PrefabName, StringComparison.OrdinalIgnoreCase))
{
num += extendedStatEntry.Value;
}
}
}
}
catch (Exception ex)
{
LogError($"Exception in GetEquippedBonusByType({statType}): " + ex);
}
return num;
}
private static string BuildExtendedTooltipBlock(ItemData item)
{
if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
{
return string.Empty;
}
string name = ((Object)item.m_dropPrefab).name;
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < ExtendedStatsConfig.Count; i++)
{
ExtendedStatEntry extendedStatEntry = ExtendedStatsConfig[i];
if (!string.Equals(name, extendedStatEntry.PrefabName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
switch (extendedStatEntry.StatType)
{
case ExtendedStatType.SkillLevelBonus:
{
string text = "$skill_" + ((object)(SkillType)(ref extendedStatEntry.SkillType)).ToString().ToLower();
string arg = text;
if (Localization.instance != null)
{
arg = Localization.instance.Localize(text);
}
stringBuilder.AppendFormat("\n{0} <color=orange>{1}</color>", arg, extendedStatEntry.Value.ToString("+0;-0"));
break;
}
case ExtendedStatType.CarryWeightBonus:
stringBuilder.AppendFormat("\n$se_max_carryweight: <color=orange>{0}</color>", extendedStatEntry.Value.ToString("+0;-0"));
break;
case ExtendedStatType.MovementSpeedModifier:
stringBuilder.AppendFormat("\n$item_movement_modifier: <color=orange>{0}%</color>", (extendedStatEntry.Value * 100f).ToString("+0;-0"));
break;
case ExtendedStatType.AdrenalineGainModifier:
stringBuilder.AppendFormat("\n$se_adrenaline: <color=orange>{0}%</color>", (extendedStatEntry.Value * 100f).ToString("+0;-0"));
break;
}
}
return stringBuilder.ToString();
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public static class TooltipColor
{
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class StatusEffectTextColorDef
{
public readonly string StatusEffectPrefabName;
public readonly string TooltipColor;
public StatusEffectTextColorDef(string statusEffectPrefabName, string tooltipColor)
{
StatusEffectPrefabName = statusEffectPrefabName;
TooltipColor = tooltipColor;
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(SE_Stats), "GetTooltipString")]
private static class SE_Stats_GetTooltipString_TooltipColorPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(SE_Stats __instance, ref string __result)
{
try
{
if (!((Object)(object)__instance == (Object)null) && !string.IsNullOrEmpty(__result))
{
StatusEffectTextColorDef statusEffectTextColorDef = GetStatusEffectTextColorDef(Utils.GetPrefabName(((Object)__instance).name));
if (statusEffectTextColorDef != null && !string.IsNullOrEmpty(statusEffectTextColorDef.TooltipColor))
{
ApplyTooltipDescriptionColor(__instance, statusEffectTextColorDef.TooltipColor, ref __result);
}
}
}
catch (Exception ex)
{
Debug.LogError((object)("[TooltipColor] ERROR: " + ex));
}
}
}
private static readonly List<StatusEffectTextColorDef> StatusEffectTextColorDefs = new List<StatusEffectTextColorDef>
{
new StatusEffectTextColorDef("SE_MushroomcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_TotemcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_DeathcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_FrostcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_FirecallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_StonecallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_WindcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_LightcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_StormcallerArmor_DO", "#f0bdff"),
new StatusEffectTextColorDef("SE_BloodcallerArmor_DO", "#f0bdff")
};
private static void ApplyTooltipDescriptionColor(SE_Stats se, string color, ref string result)
{
if ((Object)(object)se == (Object)null || string.IsNullOrEmpty(color))
{
return;
}
string tooltip = ((StatusEffect)se).m_tooltip;
if (!string.IsNullOrEmpty(tooltip))
{
if (result.StartsWith(tooltip + "\n\n"))
{
result = Colorize(tooltip, color) + result.Substring(tooltip.Length);
}
else if (result.StartsWith(tooltip + "\n"))
{
result = Colorize(tooltip, color) + result.Substring(tooltip.Length);
}
else if (result == tooltip)
{
result = Colorize(tooltip, color);
}
}
}
private static StatusEffectTextColorDef GetStatusEffectTextColorDef(string sePrefabName)
{
if (string.IsNullOrEmpty(sePrefabName))
{
return null;
}
foreach (StatusEffectTextColorDef statusEffectTextColorDef in StatusEffectTextColorDefs)
{
if (statusEffectTextColorDef != null && statusEffectTextColorDef.StatusEffectPrefabName == sePrefabName)
{
return statusEffectTextColorDef;
}
}
return null;
}
private static string Colorize(string text, string color)
{
if (string.IsNullOrEmpty(text))
{
return "";
}
if (string.IsNullOrEmpty(color))
{
return text;
}
return "<color=" + color + ">" + text + "</color>";
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
internal sealed class ConfigurationManagerAttributes
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public delegate void CustomHotkeyDrawerFunc(ConfigEntryBase setting, ref bool isCurrentlyAcceptingInput);
public bool? ShowRangeAsPercent;
public Action<ConfigEntryBase> CustomDrawer;
public CustomHotkeyDrawerFunc CustomHotkeyDrawer;
public bool? Browsable;
public string Category;
public object DefaultValue;
public bool? HideDefaultButton;
public bool? HideSettingName;
public string Description;
public string DispName;
public int? Order;
public bool? ReadOnly;
public bool? IsAdvanced;
public Func<object, string> ObjToStr;
public Func<string, object> StrToObj;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public static class ExtraStats
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private static class DevLog
{
public const bool Enabled = false;
public const bool AttackSpeed = false;
public const bool WetImmune = false;
public const bool Tooltips = false;
public const bool FoodStats = false;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public enum ExtraStatType
{
Health,
Stamina,
Eitr,
AttackSpeed,
WetImmune
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public enum ExtraSourceType
{
ItemDrop,
StatusEffect
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public class ExtraStatEntry
{
public string PrefabName;
public ExtraStatType StatType;
public float Value;
public float? DisplayValue;
public ExtraSourceType SourceType;
public ExtraStatEntry(string prefab, ExtraStatType stat, float value, ExtraSourceType source)
{
PrefabName = prefab;
StatType = stat;
Value = value;
DisplayValue = null;
SourceType = source;
}
public ExtraStatEntry(string prefab, ExtraStatType stat, float value, float? displayValue, ExtraSourceType source)
{
PrefabName = prefab;
StatType = stat;
Value = value;
DisplayValue = displayValue;
SourceType = source;
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
public static class Player_GetTotalFoodValue_ExtraStatsPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr)
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
float num = 0f;
float num2 = 0f;
float num3 = 0f;
try
{
Inventory inventory = ((Humanoid)__instance).GetInventory();
if (inventory != null)
{
foreach (ItemData equippedItem in inventory.GetEquippedItems())
{
if (equippedItem != null && !((Object)(object)equippedItem.m_dropPrefab == (Object)null))
{
string name = ((Object)equippedItem.m_dropPrefab).name;
GetItemDropBonusesForPrefab(name, out var extraHp, out var extraStamina, out var extraEitr, out var _, out var _);
if (extraHp != 0f || extraStamina != 0f || extraEitr != 0f)
{
num += extraHp;
num2 += extraStamina;
num3 += extraEitr;
DLogFood($"ItemDrop matched for food stats: {name} → +HP {extraHp}, +Stamina {extraStamina}, +Eitr {extraEitr}");
}
}
}
}
if (((Character)__instance).m_seman != null && ((Character)__instance).m_seman.m_statusEffects != null)
{
foreach (StatusEffect statusEffect in ((Character)__instance).m_seman.m_statusEffects)
{
if (!((Object)(object)statusEffect == (Object)null))
{
string name2 = ((Object)statusEffect).name;
GetStatusEffectBonuses(name2, out var extraHp2, out var extraStamina2, out var extraEitr2, out var _, out var _);
if (extraHp2 != 0f || extraStamina2 != 0f || extraEitr2 != 0f)
{
num += extraHp2;
num2 += extraStamina2;
num3 += extraEitr2;
string prefabName = Utils.GetPrefabName(name2);
DLogFood($"StatusEffect matched for food stats: {name2} (clean={prefabName}) → +HP {extraHp2}, +Stamina {extraStamina2}, +Eitr {extraEitr2}");
}
}
}
}
}
catch (Exception ex)
{
LogError("Exception in ExtraStats Player.GetTotalFoodValue Postfix: " + ex);
}
if (num != 0f || num2 != 0f || num3 != 0f)
{
DLogFood($"Total food bonuses applied to player: +HP {num}, +Stamina {num2}, +Eitr {num3}");
hp += num;
stamina += num2;
eitr += num3;
}
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
[HarmonyPatch]
public static class ItemData_StaticGetTooltip_ExtraStatsPatch
{
private static MethodBase TargetMethod()
{
return AccessTools.Method(typeof(ItemData), "GetTooltip", new Type[5]
{
typeof(ItemData),
typeof(int),
typeof(bool),
typeof(float),
typeof(int)
}, (Type[])null);
}
private static void Postfix(ItemData item, int qualityLevel, bool crafting, float worldLevel, int stackOverride, ref string __result)
{
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
try
{
if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
{
return;
}
string name = ((Object)item.m_dropPrefab).name;
DLogTooltip($"Static GetTooltip Postfix called for {name}, q={qualityLevel}, crafting={crafting}, worldLevel={worldLevel}, stackOverride={stackOverride}");
GetItemDropBonusesForPrefab(name, out var extraHp, out var extraStamina, out var extraEitr, out var _, out var displayAttackSpeedPercent, out var wetImmune);
if (extraHp == 0f && extraStamina == 0f && extraEitr == 0f && displayAttackSpeedPercent == 0f && !wetImmune)
{
return;
}
string text = BuildExtraStatsTooltipBlock(extraHp, extraStamina, extraEitr, displayAttackSpeedPercent, wetImmune);
if (string.IsNullOrEmpty(text))
{
return;
}
float num = 0f;
if ((Object)(object)Player.m_localPlayer != (Object)null)
{
num = ((Character)Player.m_localPlayer).GetSkillLevel(item.m_shared.m_skillType);
}
string statusEffectTooltip = item.GetStatusEffectTooltip(qualityLevel, num);
string chainTooltip = item.GetChainTooltip(qualityLevel, num);
string setStatusEffectTooltip = item.GetSetStatusEffectTooltip(qualityLevel, num);
int num2 = -1;
if (!string.IsNullOrEmpty(statusEffectTooltip))
{
int num3 = __result.IndexOf(statusEffectTooltip, StringComparison.Ordinal);
if (num3 >= 0)
{
num2 = ((num3 - 2 >= 0 && __result.Substring(num3 - 2, 2) == "\n\n") ? (num3 - 2) : num3);
}
}
if (num2 < 0 && !string.IsNullOrEmpty(chainTooltip))
{
int num4 = __result.IndexOf(chainTooltip, StringComparison.Ordinal);
if (num4 >= 0)
{
num2 = ((num4 - 2 >= 0 && __result.Substring(num4 - 2, 2) == "\n\n") ? (num4 - 2) : num4);
}
}
if (num2 < 0)
{
int num5 = __result.IndexOf("\n$item_eitrregen_modifier:", StringComparison.Ordinal);
if (num5 >= 0)
{
num2 = num5;
}
}
if (num2 < 0 && !string.IsNullOrEmpty(setStatusEffectTooltip))
{
int num6 = __result.IndexOf("\n\n$item_seteffect", StringComparison.Ordinal);
if (num6 >= 0)
{
num2 = num6;
}
}
if (num2 < 0)
{
int num7 = __result.IndexOf("\n$item_fulladrenaline:", StringComparison.Ordinal);
if (num7 >= 0)
{
num2 = num7;
}
}
if (num2 >= 0)
{
DLogTooltip("Inserting ItemDrop extras before special/effect section for " + name);
__result = __result.Insert(num2, text);
}
else
{
DLogTooltip("No special/effect section found, appending ItemDrop extras at end for " + name);
__result += text;
}
}
catch (Exception ex)
{
LogError("Exception in ItemData_StaticGetTooltip_ExtraStatsPatch: " + ex);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(SE_Stats), "GetTooltipString")]
public static class SE_Stats_GetTooltipString_ExtraStatsPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(SE_Stats __instance, ref string __result)
{
try
{
if ((Object)(object)__instance == (Object)null)
{
return;
}
string name = ((Object)__instance).name;
string prefabName = Utils.GetPrefabName(name);
DLogTooltip("SE GetTooltipString for " + name + " (clean=" + prefabName + ")");
GetStatusEffectBonuses(name, out var extraHp, out var extraStamina, out var extraEitr, out var _, out var displayAttackSpeedPercent, out var wetImmune);
if (extraHp == 0f && extraStamina == 0f && extraEitr == 0f && displayAttackSpeedPercent == 0f && !wetImmune)
{
return;
}
string text = BuildExtraStatsTooltipBlock(extraHp, extraStamina, extraEitr, displayAttackSpeedPercent, wetImmune);
if (!string.IsNullOrEmpty(text))
{
if (text.StartsWith("\n"))
{
text = text.Substring(1);
}
DLogTooltip($"Appending SE extras for {name} (clean={prefabName}): HP {extraHp}, Stamina {extraStamina}, Eitr {extraEitr}, AttackSpeed(Display)% {displayAttackSpeedPercent}, WetImmune {wetImmune}");
__result += text;
}
}
catch (Exception ex)
{
LogError("Exception in SE_Stats_GetTooltipString_ExtraStatsPatch: " + ex);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private sealed class HumanoidState
{
public float DesiredSpeed = 1f;
public float LastAppliedSpeed = 1f;
public bool HasDesiredSpeed;
public bool WasInAttack;
public int ActiveAttackDepth;
public float ActiveAttackMul = 1f;
}
[HarmonyPatch(typeof(Humanoid), "StartAttack", new Type[]
{
typeof(Character),
typeof(bool)
})]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class Humanoid_StartAttack_AttackSpeedPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPostfix]
private static void Postfix(Humanoid __instance, Character target, bool secondaryAttack, ref bool __result)
{
EnsureAttackInit();
if (!__result)
{
return;
}
HumanoidState attackState = GetAttackState(__instance);
float num = ComputeTotalAttackSpeedPercentForHumanoid(__instance);
if (num == 0f)
{
attackState.DesiredSpeed = 1f;
attackState.HasDesiredSpeed = false;
ApplyAnimSpeed(__instance, attackState, 1f);
attackState.ActiveAttackDepth = 0;
attackState.ActiveAttackMul = 1f;
return;
}
float num2 = 1f + num / 100f;
if (num2 <= 0f || float.IsNaN(num2) || float.IsInfinity(num2))
{
attackState.DesiredSpeed = 1f;
attackState.HasDesiredSpeed = false;
ApplyAnimSpeed(__instance, attackState, 1f);
attackState.ActiveAttackDepth = 0;
attackState.ActiveAttackMul = 1f;
}
else
{
attackState.DesiredSpeed = num2;
attackState.HasDesiredSpeed = true;
ApplyAnimSpeed(__instance, attackState, num2);
attackState.ActiveAttackDepth++;
attackState.ActiveAttackMul = num2;
DLogAttackSpeed($"StartAttack: total AttackSpeed%={num} → mul={num2} (activeDepth={attackState.ActiveAttackDepth})");
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(Attack), "Stop")]
public static class Attack_Stop_AttackSpeedResetPatch
{
[HarmonyPostfix]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(Attack __instance)
{
try
{
EnsureAttackInit();
if (__instance != null)
{
Humanoid character = __instance.m_character;
if (!((Object)(object)character == (Object)null))
{
ResetForHumanoid(character);
DLogAttackSpeed("Attack.Stop: ResetForHumanoid");
}
}
}
catch (Exception)
{
}
}
}
[HarmonyPatch(typeof(Humanoid), "UpdateAttack")]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class Humanoid_UpdateAttack_AttackSpeedDtPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPrefix]
private static void Prefix(Humanoid __instance, ref float dt)
{
try
{
EnsureAttackInit();
if ((Object)(object)__instance == (Object)null)
{
return;
}
HumanoidState attackState = GetAttackState(__instance);
if (attackState.ActiveAttackDepth > 0)
{
float activeAttackMul = attackState.ActiveAttackMul;
if (!(activeAttackMul <= 0f) && !float.IsNaN(activeAttackMul) && !float.IsInfinity(activeAttackMul))
{
dt *= activeAttackMul;
}
}
}
catch (Exception ex)
{
LogError("Exception in Humanoid_UpdateAttack_AttackSpeedDtPatch: " + ex);
}
}
}
[HarmonyPatch(typeof(Humanoid), "CustomFixedUpdate")]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class Humanoid_CustomFixedUpdate_AttackSpeedPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPostfix]
private static void Postfix(Humanoid __instance, float fixedDeltaTime)
{
EnsureAttackInit();
HumanoidState attackState = GetAttackState(__instance);
bool flag = ((Character)__instance).InAttack();
if (flag)
{
if (attackState.HasDesiredSpeed)
{
ApplyAnimSpeed(__instance, attackState, attackState.DesiredSpeed);
}
else
{
ApplyAnimSpeed(__instance, attackState, 1f);
}
}
else if (attackState.WasInAttack)
{
ApplyAnimSpeed(__instance, attackState, 1f);
attackState.HasDesiredSpeed = false;
attackState.DesiredSpeed = 1f;
}
attackState.WasInAttack = flag;
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(Character), "RPC_Stagger")]
public static class Character_RPC_Stagger_AttackSpeedResetPatch
{
[HarmonyPostfix]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Postfix(Character __instance, long sender, Vector3 forceDirection)
{
try
{
EnsureAttackInit();
Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null);
if (!((Object)(object)val == (Object)null))
{
ResetForHumanoid(val);
}
}
catch (Exception)
{
}
}
}
[HarmonyPatch(typeof(Character), "OnDeath")]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class Character_OnDeath_AttackSpeedResetPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPostfix]
private static void Postfix(Character __instance)
{
try
{
EnsureAttackInit();
Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null);
if (!((Object)(object)val == (Object)null))
{
ResetForHumanoid(val);
}
}
catch (Exception)
{
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(Character), "FreezeFrame")]
public static class Character_FreezeFrame_AttackSpeedScalePatch
{
[HarmonyPrefix]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static void Prefix(Character __instance, ref float duration)
{
try
{
EnsureAttackInit();
if ((Object)(object)__instance == (Object)null || Mathf.Abs(duration - 0.15f) > 0.01f)
{
return;
}
Humanoid val = (Humanoid)(object)((__instance is Humanoid) ? __instance : null);
if ((Object)(object)val == (Object)null)
{
return;
}
HumanoidState attackState = GetAttackState(val);
if (!attackState.HasDesiredSpeed)
{
return;
}
float desiredSpeed = attackState.DesiredSpeed;
if (!(desiredSpeed <= 1.001f) && !float.IsNaN(desiredSpeed) && !float.IsInfinity(desiredSpeed))
{
float num = duration / desiredSpeed;
if (num < 0.03f)
{
num = 0.03f;
}
DLogAttackSpeed($"FreezeFrame scale: {duration:F3} -> {num:F3} (mul={desiredSpeed:F3})");
duration = num;
}
}
catch (Exception ex)
{
LogError("Exception in Character_FreezeFrame_AttackSpeedScalePatch: " + ex);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(CharacterAnimEvent), "UpdateFreezeFrame")]
public static class CharacterAnimEvent_UpdateFreezeFrame_ReapplySpeedPatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPostfix]
private static void Postfix(CharacterAnimEvent __instance, float dt)
{
try
{
EnsureAttackInit();
if ((Object)(object)__instance == (Object)null)
{
return;
}
Character componentInParent = ((Component)__instance).GetComponentInParent<Character>();
Humanoid val = (Humanoid)(object)((componentInParent is Humanoid) ? componentInParent : null);
if (!((Object)(object)val == (Object)null))
{
HumanoidState attackState = GetAttackState(val);
if (attackState.HasDesiredSpeed && (attackState.ActiveAttackDepth > 0 || ((Character)val).InAttack()))
{
ApplyAnimSpeed(val, attackState, attackState.DesiredSpeed);
}
else
{
ApplyAnimSpeed(val, attackState, 1f);
}
}
}
catch (Exception ex)
{
LogError("Exception in CharacterAnimEvent_UpdateFreezeFrame_ReapplySpeedPatch: " + ex);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
{
typeof(int),
typeof(bool),
typeof(int),
typeof(float)
})]
public static class SEMan_AddStatusEffect_ByHash_WetImmunePatch
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[HarmonyPrefix]
private static bool Prefix(SEMan __instance, int nameHash, bool resetTime, int itemLevel, float skillLevel)
{
try
{
EnsureWetInit();
if (nameHash != SEMan.s_statusEffectWet)
{
return true;
}
Character val = null;
try
{
val = _semanCharacterRef.Invoke(__instance);
}
catch (Exception)
{
val = null;
}
if ((Object)(object)val != (Object)null && HasWetImmune(val))
{
DLogWet("Blocked Wet (AddStatusEffect by hash) due to WetImmune");
return false;
}
}
catch (Exception ex2)
{
LogError("Exception in SEMan_AddStatusEffect_ByHash_WetImmunePatch: " + ex2);
}
return true;
}
}
[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
{
typeof(StatusEffect),
typeof(bool),
typeof(int),
typeof(float)
})]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
public static class SEMan_AddStatusEffect_BySE_WetImmunePatch
{
[HarmonyPrefix]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private static bool Prefix(SEMan __instance, StatusEffect statusEffect, bool resetTime, int itemLevel, float skillLevel, ref StatusEffect __result)
{
try
{
if ((Object)(object)statusEffect == (Object)null)
{
return true;
}
if (statusEffect.NameHash() != SEMan.s_statusEffectWet)
{
return true;
}
Character character = statusEffect.m_character;
if ((Object)(object)character != (Object)null && HasWetImmune(character))
{
DLogWet("Blocked Wet (AddStatusEffect by SE) due to WetImmune");
__result = null;
return false;
}
}
catch (Exception ex)
{
LogError("Exception in SEMan_AddStatusEffect_BySE_WetImmunePatch: " + ex);
}
return true;
}
}
public static bool DEBUG = false;
public static readonly List<ExtraStatEntry> ExtraStatsConfig = new List<ExtraStatEntry>
{
new ExtraStatEntry("ArmorMushroomcallerLegsDO", ExtraStatType.Eitr, 5f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorTotemcallerLegsDO", ExtraStatType.Health, 5f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorDeathcallerHelmetDO", ExtraStatType.Eitr, 10f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorFrostcallerChestDO", ExtraStatType.Eitr, 15f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorFirecallerLegsDO", ExtraStatType.Eitr, 20f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorStonecallerLegsDO", ExtraStatType.Eitr, 20f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorWindcallerHelmetDO", ExtraStatType.Eitr, 25f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorLightcallerHelmetDO", ExtraStatType.Eitr, 25f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorStormcallerChestDO", ExtraStatType.Eitr, 30f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorBloodcallerChestDO", ExtraStatType.Health, 20f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorFrostcallerCapeDO", ExtraStatType.Eitr, 10f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorWindcallerCapeDO", ExtraStatType.Stamina, 20f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("ArmorBloodcallerCapeDO", ExtraStatType.Health, 50f, ExtraSourceType.ItemDrop),
new ExtraStatEntry("SE_MushroomcallerBelt_DO", ExtraStatType.Eitr, 10f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_DeathcallerBelt_DO", ExtraStatType.Eitr, 15f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_FrostcallerBelt_DO", ExtraStatType.Eitr, 15f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_FirecallerBelt_DO", ExtraStatType.Eitr, 20f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_StonecallerBelt_DO", ExtraStatType.Eitr, 20f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_LightcallerBelt_DO", ExtraStatType.Eitr, 20f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_StormcallerBelt_DO", ExtraStatType.Eitr, 25f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_TotemcallerBelt_DO", ExtraStatType.Health, 10f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_WindcallerBelt_DO", ExtraStatType.Stamina, 15f, ExtraSourceType.StatusEffect),
new ExtraStatEntry("SE_BloodBuff_DO", ExtraStatType.Health, 50f, ExtraSourceType.StatusEffect)
};
private static readonly ConditionalWeakTable<Humanoid, HumanoidState> _attackState = new ConditionalWeakTable<Humanoid, HumanoidState>();
private static FieldRef<Character, ZSyncAnimation> _zanimRef;
private static bool _attackInit;
private const float HitFreeze_Default = 0.15f;
private const float HitFreeze_Epsilon = 0.01f;
private const float HitFreeze_Min = 0.03f;
private static FieldRef<SEMan, Character> _semanCharacterRef;
private static bool _wetInit;
private static void LogInfo(string msg)
{
}
private static void LogError(string msg)
{
}
private static void DLogAttackSpeed(string msg)
{
}
private static void DLogWet(string msg)
{
}
private static void DLogTooltip(string msg)
{
}
private static void DLogFood(string msg)
{
}
private static void GetItemDropBonusesForPrefab(string prefabName, out float extraHp, out float extraStamina, out float extraEitr, out float extraAttackSpeedPercent, out float displayAttackSpeedPercent, out bool wetImmune)
{
extraHp = 0f;
extraStamina = 0f;
extraEitr = 0f;
extraAttackSpeedPercent = 0f;
displayAttackSpeedPercent = 0f;
wetImmune = false;
if (string.IsNullOrEmpty(prefabName))
{
return;
}
for (int i = 0; i < ExtraStatsConfig.Count; i++)
{
ExtraStatEntry extraStatEntry = ExtraStatsConfig[i];
if (extraStatEntry.SourceType == ExtraSourceType.ItemDrop && string.Equals(prefabName, extraStatEntry.PrefabName, StringComparison.OrdinalIgnoreCase))
{
switch (extraStatEntry.StatType)
{
case ExtraStatType.Health:
extraHp += extraStatEntry.Value;
break;
case ExtraStatType.Stamina:
extraStamina += extraStatEntry.Value;
break;
case ExtraStatType.Eitr:
extraEitr += extraStatEntry.Value;
break;
case ExtraStatType.AttackSpeed:
extraAttackSpeedPercent += extraStatEntry.Value;
displayAttackSpeedPercent += (extraStatEntry.DisplayValue.HasValue ? extraStatEntry.DisplayValue.Value : extraStatEntry.Value);
break;
case ExtraStatType.WetImmune:
wetImmune = true;
break;
}
}
}
}
private static void GetItemDropBonusesForPrefab(string prefabName, out float extraHp, out float extraStamina, out float extraEitr, out float extraAttackSpeedPercent, out bool wetImmune)
{
GetItemDropBonusesForPrefab(prefabName, out extraHp, out extraStamina, out extraEitr, out extraAttackSpeedPercent, out var _, out wetImmune);
}
private static void GetStatusEffectBonuses(string seRawName, out float extraHp, out float extraStamina, out float extraEitr, out float extraAttackSpeedPercent, out float displayAttackSpeedPercent, out bool wetImmune)
{
extraHp = 0f;
extraStamina = 0f;
extraEitr = 0f;
extraAttackSpeedPercent = 0f;
displayAttackSpeedPercent = 0f;
wetImmune = false;
if (string.IsNullOrEmpty(seRawName))
{
return;
}
string prefabName = Utils.GetPrefabName(seRawName);
for (int i = 0; i < ExtraStatsConfig.Count; i++)
{
ExtraStatEntry extraStatEntry = ExtraStatsConfig[i];
if (extraStatEntry.SourceType == ExtraSourceType.StatusEffect && string.Equals(prefabName, extraStatEntry.PrefabName, StringComparison.OrdinalIgnoreCase))
{
switch (extraStatEntry.StatType)
{
case ExtraStatType.Health:
extraHp += extraStatEntry.Value;
break;
case ExtraStatType.Stamina:
extraStamina += extraStatEntry.Value;
break;
case ExtraStatType.Eitr:
extraEitr += extraStatEntry.Value;
break;
case ExtraStatType.AttackSpeed:
extraAttackSpeedPercent += extraStatEntry.Value;
displayAttackSpeedPercent += (extraStatEntry.DisplayValue.HasValue ? extraStatEntry.DisplayValue.Value : extraStatEntry.Value);
break;
case ExtraStatType.WetImmune:
wetImmune = true;
break;
}
}
}
}
private static void GetStatusEffectBonuses(string seRawName, out float extraHp, out float extraStamina, out float extraEitr, out float extraAttackSpeedPercent, out bool wetImmune)
{
GetStatusEffectBonuses(seRawName, out extraHp, out extraStamina, out extraEitr, out extraAttackSpeedPercent, out var _, out wetImmune);
}
private static string BuildExtraStatsTooltipBlock(float extraHp, float extraStamina, float extraEitr, float displayAttackSpeedPercent, bool wetImmune)
{
if (extraHp == 0f && extraStamina == 0f && extraEitr == 0f && displayAttackSpeedPercent == 0f && !wetImmune)
{
return string.Empty;
}
StringBuilder stringBuilder = new StringBuilder();
string text = "$extrastats_health_DO";
string text2 = "$extrastats_stamina_DO";
string text3 = "$extrastats_eitr_DO";
string text4 = "$extrastats_attackspeed_DO";
string text5 = "$extrastats_immune_DO";
string text6 = "$extrastats_wet_DO";
if (Localization.instance != null)
{
text = Localization.instance.Localize(text);
text2 = Localization.instance.Localize(text2);
text3 = Localization.instance.Localize(text3);
text4 = Localization.instance.Localize(text4);
text5 = Localization.instance.Localize(text5);
text6 = Localization.instance.Localize(text6);
}
if (extraHp != 0f)
{
string arg = ((extraHp >= 0f) ? "+" : "");
stringBuilder.Append($"\n{text}: <color=#f27979>{arg}{extraHp:F0}</color>");
}
if (extraStamina != 0f)
{
string arg2 = ((extraStamina >= 0f) ? "+" : "");
stringBuilder.Append($"\n{text2}: <color=#ffff80>{arg2}{extraStamina:F0}</color>");
}
if (extraEitr != 0f)
{
string arg3 = ((extraEitr >= 0f) ? "+" : "");
stringBuilder.Append($"\n{text3}: <color=#8686ee>{arg3}{extraEitr:F0}</color>");
}
if (displayAttackSpeedPercent != 0f)
{
int num = Mathf.RoundToInt(displayAttackSpeedPercent);
string arg4 = ((num >= 0) ? "+" : "");
stringBuilder.Append($"\n{text4}: <color=orange>{arg4}{num}%</color>");
}
if (wetImmune)
{
stringBuilder.Append("\n" + text5 + ": <color=orange>" + text6 + "</color>");
}
return stringBuilder.ToString();
}
private static bool HasWetImmune(Character c)
{
if ((Object)(object)c == (Object)null)
{
return false;
}
try
{
Player val = (Player)(object)((c is Player) ? c : null);
if ((Object)(object)val != (Object)null)
{
Inventory inventory = ((Humanoid)val).GetInventory();
if (inventory != null)
{
List<ItemData> equippedItems = inventory.GetEquippedItems();
for (int i = 0; i < equippedItems.Count; i++)
{
ItemData val2 = equippedItems[i];
if (val2 != null && !((Object)(object)val2.m_dropPrefab == (Object)null))
{
string name = ((Object)val2.m_dropPrefab).name;
GetItemDropBonusesForPrefab(name, out var _, out var _, out var _, out var _, out var _, out var wetImmune);
if (wetImmune)
{
DLogWet("WetImmune from ItemDrop: " + name);
return true;
}
}
}
}
}
if (c.m_seman != null && c.m_seman.m_statusEffects != null)
{
List<StatusEffect> statusEffects = c.m_seman.m_statusEffects;
for (int j = 0; j < statusEffects.Count; j++)
{
StatusEffect val3 = statusEffects[j];
if (!((Object)(object)val3 == (Object)null))
{
string name2 = ((Object)val3).name;
GetStatusEffectBonuses(name2, out var _, out var _, out var _, out var _, out var _, out var wetImmune2);
if (wetImmune2)
{
string prefabName = Utils.GetPrefabName(name2);
DLogWet("WetImmune from StatusEffect: " + name2 + " (clean=" + prefabName + ")");
return true;
}
}
}
}
}
catch (Exception ex)
{
LogError("Exception in HasWetImmune: " + ex);
}
return false;
}
private static void EnsureAttackInit()
{
if (!_attackInit)
{
_attackInit = true;
_zanimRef = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim");
}
}
private static HumanoidState GetAttackState(Humanoid h)
{
return _attackState.GetValue(h, (Humanoid _) => new HumanoidState());
}
private static void ApplyAnimSpeed(Humanoid h, HumanoidState state, float speed)
{
ZSyncAnimation val = null;
try
{
val = _zanimRef.Invoke((Character)(object)h);
}
catch (Exception)
{
return;
}
if (!((Object)(object)val == (Object)null) && !Mathf.Approximately(state.LastAppliedSpeed, speed))
{
val.SetSpeed(speed);
state.LastAppliedSpeed = speed;
}
}
private static void ResetForHumanoid(Humanoid h)
{
if (!((Object)(object)h == (Object)null))
{
EnsureAttackInit();
HumanoidState attackState = GetAttackState(h);
ApplyAnimSpeed(h, attackState, 1f);
attackState.HasDesiredSpeed = false;
attackState.DesiredSpeed = 1f;
attackState.WasInAttack = false;
attackState.ActiveAttackDepth = 0;
attackState.ActiveAttackMul = 1f;
}
}
private static float ComputeTotalAttackSpeedPercentForHumanoid(Humanoid h)
{
float num = 0f;
if ((Object)(object)h == (Object)null)
{
return 0f;
}
try
{
Player val = (Player)(object)((h is Player) ? h : null);
if ((Object)(object)val != (Object)null)
{
Inventory inventory = ((Humanoid)val).GetInventory();
if (inventory != null)
{
List<ItemData> equippedItems = inventory.GetEquippedItems();
for (int i = 0; i < equippedItems.Count; i++)
{
ItemData val2 = equippedItems[i];
if (val2 != null && !((Object)(object)val2.m_dropPrefab == (Object)null))
{
string name = ((Object)val2.m_dropPrefab).name;
GetItemDropBonusesForPrefab(name, out var _, out var _, out var _, out var extraAttackSpeedPercent, out var _, out var _);
if (extraAttackSpeedPercent != 0f)
{
num += extraAttackSpeedPercent;
DLogAttackSpeed($"AttackSpeed from ItemDrop: {name} → {extraAttackSpeedPercent}%");
}
}
}
}
}
if (((Character)h).m_seman != null && ((Character)h).m_seman.m_statusEffects != null)
{
List<StatusEffect> statusEffects = ((Character)h).m_seman.m_statusEffects;
for (int j = 0; j < statusEffects.Count; j++)
{
StatusEffect val3 = statusEffects[j];
if (!((Object)(object)val3 == (Object)null))
{
string name2 = ((Object)val3).name;
GetStatusEffectBonuses(name2, out var _, out var _, out var _, out var extraAttackSpeedPercent2, out var _, out var _);
if (extraAttackSpeedPercent2 != 0f)
{
num += extraAttackSpeedPercent2;
string prefabName = Utils.GetPrefabName(name2);
DLogAttackSpeed($"AttackSpeed from StatusEffect: {name2} (clean={prefabName}) → {extraAttackSpeedPercent2}%");
}
}
}
}
}
catch (Exception ex)
{
LogError("Exception in ComputeTotalAttackSpeedPercentForHumanoid: " + ex);
}
return num;
}
private static void EnsureWetInit()
{
if (!_wetInit)
{
_wetInit = true;
_semanCharacterRef = AccessTools.FieldRefAccess<SEMan, Character>("m_character");
}
}
}
[HarmonyPatch]
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
public static class LivingContainerSpawner
{
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private static class DevLog
{
public const bool Enabled = false;
public const bool SpawnTimers = true;
public const bool SpawnAttempts = true;
public const bool Dedicated = true;
public const bool Lifecycle = true;
public const bool Perf = true;
public const bool Watcher = true;
public const bool WatcherVerbose = false;
public const bool StateReset = true;
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class LivingContainerDef
{
public string Prefab;
public Biome Biomes;
public float CheckInterval;
public float SpawnChance;
public int MaxInWorld;
public float MinDistance;
public float MaxDistance;
public float MinAltitude;
public float MaxAltitude;
public bool AllowInWater;
public int MaxSpawnAttempts;
public float DestroyDelay;
public string VfxPrefab;
public string PinSprite;
public string PinText;
public bool PinAnimate;
public bool PinDoubleSize;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private struct ItemsCacheEntry
{
public int ItemsLen;
public int QuickHash;
public bool IsEmpty;
public int DecodedCount;
public float LastTouchTime;
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private struct PrefabContainerTuning
{
public int Width;
public int Height;
public int LootMin;
public int LootMax;
public bool Equals(PrefabContainerTuning other)
{
if (Width == other.Width && Height == other.Height && LootMin == other.LootMin)
{
return LootMax == other.LootMax;
}
return false;
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class LivingContainerRegistryComponent : MonoBehaviour
{
private ZNetView _nview;
private ZDOID _uid;
private string _prefab;
private void Awake()
{
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
try
{
_nview = ((Component)this).GetComponent<ZNetView>();
if ((Object)(object)_nview == (Object)null || !_nview.IsValid())
{
return;
}
_prefab = Utils.GetPrefabName(((Object)((Component)this).gameObject).name);
BuildDefCache();
if (DefByPrefab.ContainsKey(_prefab))
{
ZDO val = null;
try
{
val = _nview.GetZDO();
}
catch
{
}
if (val != null)
{
_uid = val.m_uid;
RegisterLoadedRiko(_uid, _nview, _prefab);
}
}
}
catch
{
}
}
private void OnDestroy()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
try
{
if (_uid != default(ZDOID))
{
UnregisterLoadedRiko(_uid);
}
}
catch
{
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private enum GroundFail
{
None,
WorldGenNoHeight,
RaycastNoHit,
WrongBiome,
InWater,
WaterTooDeep,
OceanDepth,
Lava,
Altitude,
Overlap,
MistlandsBlocked
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
[HarmonyPatch(typeof(InventoryGui), "Hide")]
private static class InventoryGui_Hide_Patch
{
private static void Prefix(InventoryGui __instance, out Container __state)
{
__state = (((Object)(object)__instance != (Object)null) ? __instance.m_currentContainer : null);
}
private static void Postfix(Container __state)
{
if ((Object)(object)__state != (Object)null)
{
HandleContainerClosed(__state);
}
}
}
[<d174e93b-eb8b-4056-a8c2-4fa27d63ad65>Nullable(0)]
private class LivingContainerPinComponent : MonoBehaviour
{
public string PinSprite;
public string PinText;
public bool PinAnimate;
public bool PinDoubleSize;
private PinData _pin;
private const float PinUpdateInterval_Seconds = 5f;
private float _nextPinUpdateTime;
private void Update()
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
if (!LivingContainerConfig.IsRikoEnabled())
{
if (_pin != null && (Object)(object)Minimap.instance != (Object)null)
{
Minimap.instance.RemovePin(_pin);
_pin = null;
}
Object.Destroy((Object)(object)this);
return;
}
float time = Time.time;
if (_pin != null)
{
if (!(time < _nextPinUpdateTime))
{
_nextPinUpdateTime = time + 5f;
_pin.m_pos = ((Component)this).transform.position;
}
}
else
{
TryCreatePin();
}
}
private void TryCreatePin()
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
if (!string.IsNullOrEmpty(PinSprite) && !((Object)(object)Minimap.instance == (Object)null) && Magic_SupremacyPlugin.PinSprites != null && Magic_SupremacyPlugin.PinSprites.TryGetValue(PinSprite, out var value) && !((Object)(object)value == (Object)null))
{
PinData val = Minimap.instance.AddPin(((Component)this).transform.position, (PinType)8, PinText ?? "", false, false, 0L, default(PlatformUserID));
val.m_icon = value;
val.m_animate = PinAnimate;
val.m_doubleSize = PinDoubleSize;
val.m_save = false;
_pin = val;
_nextPinUpdateTime = Time.time + 5f;
}
}
private void OnDestroy()
{
if (_pin != null && (Object)(object)Minimap.instance != (Object)null)
{
Minimap.instance.RemovePin(_pin);
}
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(0)]
private class RikoSpawnStabilizer : MonoBehaviour
{
private bool _started;
public void Init()
{
if (!_started)
{
_started = true;
((MonoBehaviour)this).StartCoroutine(StabilizeRoutine());
}
}
[<89307664-be15-40a0-b8ee-c45cb43dd5ac>NullableContext(1)]
private IEnumerator StabilizeRoutine()
{
StabilizeOnce(((Component)this).gameObject);
yield return null;
StabilizeOnce(((Component)this).gameObject);
yield return (object)new WaitForSeconds(0.35f);
StabilizeOnce(((Component)this).gameObject);
}
}
private static readonly LivingContainerDef[] Defs = new LivingContainerDef[6]
{
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoBlackforestDO",
Biomes = (Biome)8,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 18f,
MaxDistance = 36f,
MinAltitude = 0f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 15,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
},
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoSwampDO",
Biomes = (Biome)2,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 18f,
MaxDistance = 36f,
MinAltitude = -1f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 18,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
},
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoMountainDO",
Biomes = (Biome)4,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 18f,
MaxDistance = 36f,
MinAltitude = 10f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 15,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
},
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoPlainsDO",
Biomes = (Biome)16,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 18f,
MaxDistance = 36f,
MinAltitude = 0f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 15,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
},
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoMistlandsDO",
Biomes = (Biome)512,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 15f,
MaxDistance = 40f,
MinAltitude = 12f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 18,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
},
new LivingContainerDef
{
Prefab = "EncounterCreatureRikoAshlandsDO",
Biomes = (Biome)32,
CheckInterval = 360f,
SpawnChance = 0.3f,
MaxInWorld = 3,
MinDistance = 18f,
MaxDistance = 36f,
MinAltitude = 0f,
MaxAltitude = 2000f,
AllowInWater = false,
MaxSpawnAttempts = 15,
DestroyDelay = 1f,
VfxPrefab = "fx_riko_disappear_DO",
PinSprite = "PinRikoDO",
PinText = "",
PinAnimate = false,
PinDoubleSize = true
}
};
private const float Riko_SeenRadius_Meters = 120f;
private const float Riko_SeenUpdateInterval_Seconds = 12f;
private const float Riko_TTL_Minutes = 20f;
private const float Riko_Grace_Minutes = 2f;
private const float Riko_TtlScanInterval_Seconds = 45f;
private const float Riko_DestroyScanInterval_Seconds = 20f;
private const float Riko_LoadedDestroyCheckInterval_Seconds = 0.2f;
private const float Riko_EmptyWatcherInterval_Seconds = 30f;
private const float ConfigRefreshInterval_Seconds = 5f;
private const int OriginMaxAttempts = 3;
private const int OriginPickGuard = 16;
private const int MaxCatchupChecksPerTick_PerDef = 2;
private const float WorldCountRescanInterval_Seconds = 90f;
private const float ServerStartupGrace_Seconds = 10f;
private const float MaxAllowedWaterDepth_Center = 0.08f;
private const float MaxAllowedWaterDepth_Side = 0.12f;
private const float DryAreaProbeRadius = 0.9f;
private const int DryAreaAllowedWetSideSamples = 1;
private const float MistlandsHeadroomHeight = 2.2f;
private const float MistlandsHeadroomRadius = 0.45f;
private const float MistlandsCeilingCheckStart = 0.15f;
private const float DedNoGroundLogInterval_Seconds = 15f;
private const string ZdoKey_Killed = "MS_Riko_Killed";
private const string ZdoKey_SpawnTime = "MS_Riko_SpawnTime";
private const string ZdoKey_LastSeenTime = "MS_Riko_LastSeenTime";
private const string ZdoKey_DestroyAt = "MS_Riko_DestroyAt";
private const string ZdoKey_VfxPlayed = "MS_Riko_VfxPlayed";
private const string ZdoKey_FinalDestroyCounted = "MS_Riko_FinalDestroyCounted";
private static readonly Dictionary<string, LivingContainerDef> DefByPrefab = new Dictionary<string, LivingContainerDef>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, int> WorldCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
private static float[] WorldChecks;
private static float LastWorldUpdate;
private static float NextSeenUpdateTime;
private static float NextDestroyScanTime;
private static float NextLoadedDestroyCheckTime;
private static float NextTtlScanTime;
private static float NextConfigRefreshTime;
private static float NextWorldCountRescanTime;
private static readonly Dictionary<string, float> NextAuthoritativeCountCheckTime = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
private const float AuthoritativeCountRefreshInterval_Seconds = 20f;
private static bool WorldCountCacheInitialized;
private static readonly List<Vector3> OriginPositions = new List<Vector3>(32);
private static readonly FieldInfo ZNetPeer_m_refPos_Field = AccessTools.Field(typeof(ZNetPeer), "m_refPos");
private static readonly Dictionary<ZDOID, ZNetView> LoadedRikos = new Dictionary<ZDOID, ZNetView>(256);
private static int LoadedRegistryInvalidSweeps;
private static List<ZDOID> _registrySweepKeys;
private static bool PurgeInProgress;
private static int PurgePrefabIndex;
private static int PurgeSectorIndex;
private static readonly List<ZDO> PurgeZdos = new List<ZDO>(256);
private static float ServerWorldEnterTime = -1f;
private static bool ServerGraceLogged;
private static float _dedNextNoGroundLogTime;
private static int _dedLastPlayers = -999;
private static int _dedLastPeers = -999;
private static int _dedLastOriginPos = -999;
private static float _dedNextHeartbeatTime;
private static bool _emptyWatcherInProgress;
private static int _emptyWatcherPrefabIndex;
private static int _emptyWatcherSectorIndex;
private static readonly List<ZDO> _emptyWatcherZdos = new List<ZDO>(256);
private static float _emptyWatcherNextRunTime;
private static int _hashInUse;
private static int _hashItems;
private static readonly Dictionary<ZDOID, ItemsCacheEntry> _itemsCache = new Dictionary<ZDOID, ItemsCacheEntry>(512);
private static float _itemsCacheNextCleanupTime;
private const float ItemsCacheCleanupInterval_Seconds = 60f;
private const float ItemsCacheKeepSeconds = 600f;
private const int ItemsCacheMaxSize = 4096;
private static List<ZDOID> _itemsCacheKeysTmp;
private static readonly Dictionary<string, PrefabContainerTuning> AppliedPrefabTuning = new Dictionary<string, PrefabContainerTuning>(StringComparer.OrdinalIgnoreCase);
private static readonly int SolidMask = LayerMask.GetMask(new string[5] { "terrain", "static_solid", "Default", "piece", "vehicle" });
private static readonly Vector2[] DryAreaProbeOffsets = (Vector2[])(object)new Vector2[5]
{
new Vector2(0f, 0f),
new Vector2(1f, 0f),
new Vector2(-1f, 0f),
new Vector2(0f, 1f),
new Vector2(0f, -1f)
};
private static void Log(string msg)
{
}
private static void LogTimers(string msg)
{
}
private static void LogSpawn(string msg)
{
}
private static void LogDed(string msg)
{
}
private static void LogLife(string msg)
{
}
private static void LogPerf(string msg)
{
}
private static void LogWatcher(string msg)
{
}
private static void LogWatcherVerbose(string msg)
{
}
private static void LogReset(string msg)
{
}
private static void RegisterLoadedRiko(ZDOID uid, ZNetView nv, string prefab)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)nv == (Object)null))
{
LoadedRikos[uid] = nv;
LogPerf($"registry ADD prefab={prefab} uid={uid} loaded={LoadedRikos.Count}");
}
}
private static void UnregisterLoadedRiko(ZDOID uid)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
if (LoadedRikos.Remove(uid))
{
LogPerf($"registry REMOVE uid={uid} loaded={LoadedRikos.Count}");
}
}
private static void EnsureRegistryComponent(GameObject go)
{
if (!((Object)(object)go == (Object)null) && !((Object)(object)go.GetComponent<LivingContainerRegistryComponent>() != (Object)null))
{
go.AddComponent<LivingContainerRegistryComponent>();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ZNet), "Awake")]
private static void ZNet_Awake_Postfix()
{
try
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
ResetRuntimeState_Server("ZNet.Awake");
}
}
catch
{
}
}
private static void ResetRuntimeState_Server(string reason)
{
WorldChecks = null;
LastWorldUpdate = 0f;
WorldCounts.Clear();
WorldCountCacheInitialized = false;
NextWorldCountRescanTime = 0f;
NextAuthoritativeCountCheckTime.Clear();
LoadedRikos.Clear();
LoadedRegistryInvalidSweeps = 0;
NextSeenUpdateTime = 0f;
NextDestroyScanTime = 0f;
NextLoadedDestroyCheckTime = 0f;
NextTtlScanTime = 0f;
NextConfigRefreshTime = 0f;
PurgeInProgress = false;
PurgePrefabIndex = 0;
PurgeSectorIndex = 0;
PurgeZdos.Clear();
_emptyWatcherInProgress = false;
_emptyWatcherPrefabIndex = 0;
_emptyWatcherSectorIndex = 0;
_emptyWatcherZdos.Clear();
_emptyWatcherNextRunTime = 0f;
_itemsCache.Clear();
_itemsCacheNextCleanupTime = 0f;
ServerWorldEnterTime = Time.time;
ServerGraceLogged = false;
OriginPositions.Clear();
_dedNextNoGroundLogTime = 0f;
_dedLastPlayers = -999;
_dedLastPeers = -999;
_dedLastOriginPos = -999;
_dedNextHeartbeatTime = 0f;
LogReset($"server RuntimeState RESET reason={reason} time={ServerWorldEnterTime:0.00} session={ZDOMan.GetSessionID()}");
}
[HarmonyPostfix]
[HarmonyPatch(typeof(ZoneSystem), "Update")]
private static void ZoneSystem_Update_Postfix()
{
if (!LivingContainerConfig.IsRikoEnabled() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ObjectDB.instance == (Object)null)
{
return;
}
if (ServerWorldEnterTime < 0f)
{
ServerWorldEnterTime = Time.time;
}
float num = Time.time - ServerWorldEnterTime;
bool flag = num < 10f;
if (flag && !ServerGraceLogged)
{
ServerGraceLogged = true;
LogTimers($"server StartupGrace ACTIVE seconds={10f:0.0} sinceEnter={num:0.00}");
}
BuildDefCache();
if (Time.time >= NextConfigRefreshTime)
{
RefreshDefsFromConfig();
NextConfigRefreshTime = Time.time + 5f;
}
List<Player> allPlayers = Player.GetAllPlayers();
BuildOriginPositions(allPlayers);
LogDedicatedStateIfNeeded(allPlayers);
UpdateLastSeenForLoadedRikos();
ProcessLoadedDestroyDueFast();
ProcessScheduledDestroyForLoadedRikos();
TryRunEmptyWatcher_Server();
TryPurgeExpiredOrKilledRikoZDOs();
if (OriginPositions.Count == 0)
{
return;
}
float time = Time.time;
if (LastWorldUpdate <= 0f)
{
LastWorldUpdate = time;
EnsureWorldChecksInitialized();
EnsureWorldCounts_Cached(initial: true);
LogTimers($"server WorldTimers INIT sinceEnter={num:0.00} note=FirstWorldTick");
return;
}
float num2 = time - LastWorldUpdate;
if (num2 < 5f)
{
return;
}
LastWorldUpdate = time;
EnsureWorldChecksInitialized();
EnsureWorldCounts_Cached(initial: false);
LogTimers($"server WorldTick sinceEnter={num:0.00} delta={num2:0.00} origins={OriginPositions.Count}");
for (int i = 0; i < Defs.Length; i++)
{
LivingContainerDef livingContainerDef = Defs[i];
if (livingContainerDef == null)
{
continue;
}
float num3 = WorldChecks[i];
WorldChecks[i] -= num2;
float num4 = WorldChecks[i];
if (num3 > 0f && (num3 < 5f || num4 <= 0f))
{
LogTimers($"server WorldTimer prefab={livingContainerDef.Prefab} before={num3:0.00} after={num4:0.00} delta={num2:0.00} interval={livingContainerDef.CheckInterval:0.0} grace={flag}");
}
if (WorldChecks[i] > 0f)
{
continue;
}
if (flag)
{
WorldChecks[i] = Mathf.Max(1f, livingContainerDef.CheckInterval);
LogTimers($"server WorldTimer BLOCKED_BY_GRACE prefab={livingContainerDef.Prefab} setNext={WorldChecks[i]:0.00}");
continue;
}
int num5 = 0;
float num6 = Mathf.Max(1f, livingContainerDef.CheckInterval);
while (WorldChecks[i] <= 0f && num5 < 2)
{
TrySpawnWithFallbackOrigins(OriginPositions, livingContainerDef, num, num5);
WorldChecks[i] += num6;
num5++;
}
if (WorldChecks[i] <= 0f)
{
WorldChecks[i] = Mathf.Min(1f, num6);
LogTimers($"server WorldTimer STILL_OVERDUE prefab={livingContainerDef.Prefab} cappedNext={WorldChecks[i]:0.00}");
}
else
{
LogTimers($"server WorldTimer ADVANCED prefab={livingContainerDef.Prefab} nextIn={WorldChecks[i]:0.00} catchupUsed={num5}");
}
}
}
private static void EnsureWorldChecksInitialized()
{
if (WorldChecks == null || WorldChecks.Length != Defs.Length)
{
WorldChecks = new float[Defs.Length];
for (int i = 0; i < WorldChecks.Length; i++)
{
LivingContainerDef livingContainerDef = Defs[i];
float num = ((livingContainerDef != null) ? Mathf.Max(1f, livingContainerDef.CheckInterval) : 60f);
float num2 = Mathf.Min(30f, num * 0.1f);
WorldChecks[i] = num + Random.Range(0f, num2);
}
LogTimers($"server WorldChecks CREATED count={WorldChecks.Length}");
}
}
private static void BuildOriginPositions(List<Player> players)
{
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: Unknown result type (might be due to invalid IL or missing references)
//IL_0152: Unknown result type (might be due to invalid IL or missing references)
//IL_0157: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
OriginPositions.Clear();
if (players != null && players.Count > 0)
{
for (int i = 0; i < players.Count; i++)
{
Player val = players[i];
if (!((Object)(object)val == (Object)null))
{
AddOriginPosition(((Component)val).transform.position);
}
}
}
else
{
if ((Object)(object)ZNet.instance == (Object)null)
{
return;
}
List<ZNetPeer> list = null;
try
{
list = ZNet.instance.GetPeers();
}
catch
{
}
if (list == null || list.Count == 0)
{
return;
}
for (int j = 0; j < list.Count; j++)
{
ZNetPeer val2 = list[j];
if (val2 == null)
{
continue;
}
Vector3 val3 = Vector3.zero;
try
{
if (ZNetPeer_m_refPos_Field != null)
{
object value = ZNetPeer_m_refPos_Field.GetValue(val2);
if (value is Vector3)
{
Vector3 val4 = (Vector3)value;
val3 = val4;
}
}
}
catch
{
}
if (!(val3 == Vector3.zero))
{
AddOriginPosition(val3);
AddOriginPosition(val3 + new Vector3(24f, 0f, 0f));
AddOriginPosition(val3 + new Vector3(-24f, 0f, 0f));
AddOriginPosition(val3 + new Vector3(0f, 0f, 24f));
AddOriginPosition(val3 + new Vector3(0f, 0f, -24f));
}
}
}
}
private static void AddOriginPosition(Vector3 pos)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
if (pos == Vector3.zero)
{
return;
}
for (int i = 0; i < OriginPositions.Count; i++)
{
Vector3 val = OriginPositions[i] - pos;
if (((Vector3)(ref val)).sqrMagnitude < 1f)
{
return;
}
}
OriginPositions.Add(pos);
}
private static void LogDedicatedStateIfNeeded(List<Player> players)
{
}
private static bool SafeIsDedicated()
{
try
{
return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated();
}
catch
{
return false;
}
}
private static bool TrySpawnWithFallbackOrigins(List<Vector3> origins, LivingContainerDef def, float sinceEnter, int catchup)
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_018d: Unknown result type (might be due to invalid IL or missing references)
//IL_019c: Unknown result type (might be due to invalid IL or missing references)
//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_0128: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_0146: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
if (origins == null || origins.Count == 0 || def == null)
{
return false;
}
int num = 0;
int num2 = -1;
int num3 = -1;
int num4 = -1;
for (int i = 0; i < 3; i++)
{
int num5 = -1;
Vector3 val = Vector3.zero;
int num6 = 0;
while (num5 < 0 && num6 < 16)
{
num6++;
int num7 = Random.Range(0, origins.Count);
if (num7 != num2 && num7 != num3 && num7 != num4)
{
Vector3 val2 = origins[num7];
if (!(val2 == Vector3.zero))
{
num5 = num7;
val = val2;
}
}
}
if (num5 < 0)
{
for (int j = 0; j < origins.Count; j++)
{
if (j != num2 && j != num3 && j != num4)
{
Vector3 val3 = origins[j];
if (!(val3 == Vector3.zero))
{
num5 = j;
val = val3;
break;
}
}
}
}
if (num5 < 0)
{
return false;
}
switch (num)
{
case 0:
num2 = num5;
break;
case 1:
num3 = num5;
break;
case 2:
num4 = num5;
break;
}
num++;
Biome biomeSafe = GetBiomeSafe(val);
if ((int)biomeSafe != 0 && (biomeSafe & def.Biomes) == 0)
{
LogSpawn("server TrySpawn SKIP originBiomeMismatch " + $"originPos=({val.x:0.0},{val.y:0.0},{val.z:0.0}) " + $"originBiome={biomeSafe} needed={def.Biomes} prefab={def.Prefab} " + $"originAttempt={i + 1}/{3}");
continue;
}
LogSpawn($"server TrySpawn CALL originPos=({val.x:0.0},{val.y:0.0},{val.z:0.0}) prefab={def.Prefab} sinceEnter={sinceEnter:0.00} catchup={catchup} originAttempt={i + 1}/{3}");
if (TrySpawnAtPosition(val, def))
{
return true;
}
}
return false;
}
private static bool TrySpawnAtPosition(Vector3 origin, LivingContainerDef def)
{
//IL_037d: Unknown result type (might be due to invalid IL or missing references)
//IL_038c: Unknown result type (might be due to invalid IL or missing references)
//IL_039b: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0201: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
//IL_0209: Unknown result type (might be due to invalid IL or missing references)
//IL_020b: Unknown result type (might be due to invalid IL or missing references)
//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
if (def == null)
{
return false;
}
float value = Random.value;
if (value > def.SpawnChance)
{
LogSpawn($"server TrySpawn FAIL chance prefab={def.Prefab} rnd={value:0.000} chance={def.SpawnChance:0.000}");
return false;
}
int authoritativeAliveCountForPrefab = GetAuthoritativeAliveCountForPrefab(def.Prefab);
if (authoritativeAliveCountForPrefab >= def.MaxInWorld)
{
LogSpawn($"server TrySpawn FAIL maxInWorld prefab={def.Prefab} count={authoritativeAliveCountForPrefab} max={def.MaxInWorld}");
return false;
}
GameObject prefab = ZNetScene.instance.GetPrefab(def.Prefab);
if ((Object)(object)prefab == (Object)null)
{
LogSpawn("server TrySpawn FAIL missingPrefab prefab=" + def.Prefab);
return false;
}
ApplyContainerConfigToPrefab(def, prefab);
int num = Mathf.Max(1, def.MaxSpawnAttempts);
int num2 = 0;
int num3 = 0;
int num4 = 0;
int num5 = 0;
int num6 = 0;
int num7 = 0;
int num8 = 0;
int num9 = 0;
int num10 = 0;
int num11 = 0;
for (int i = 0; i < num; i++)
{
Vector3 val = PickPoint(origin, def.MinDistance, def.MaxDistance);
if (!TryGetRikoSpawnPoint(val, def, out var spawnPos, out var fail))
{
switch (fail)
{
case GroundFail.WorldGenNoHeight:
case GroundFail.RaycastNoHit:
num2++;
break;
case GroundFail.WrongBiome:
num3++;
break;
case GroundFail.InWater:
num4++;
break;
case GroundFail.WaterTooDeep:
num5++;
break;
case GroundFail.OceanDepth:
num6++;
break;
case GroundFail.Lava:
num7++;
break;
case GroundFail.Altitude:
num8++;
break;
case GroundFail.Overlap:
num9++;
break;
case GroundFail.MistlandsBlocked:
num10++;
break;
}
LogSpawn($"server TrySpawn attempt={i + 1} FAIL prefab={def.Prefab} fail={fail} candidate=({val.x:0.0},{val.y:0.0},{val.z:0.0})");
continue;
}
Quaternion val2 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
GameObject val3 = Object.Instantiate<GameObject>(prefab, spawnPos, val2);
if ((Object)(object)val3 == (Object)null)
{
num11++;
LogSpawn($"server TrySpawn attempt={i + 1} FAIL instantiateNull prefab={def.Prefab}");
continue;
}
EnsureRegistryComponent(val3);
AttachPinComponent(val3, def);
Container component = val3.GetComponent<Container>();
if ((Object)(object)component != (Object)null)
{
component.m_autoDestroyEmpty = false;
}
FixSpawnInsideGeometry(val3);
ZNetView val4 = null;
ZDO val5 = null;
try
{
val4 = val3.GetComponent<ZNetView>();
if ((Object)(object)val4 != (Object)null && val4.IsValid())
{
val5 = val4.GetZDO();
}
}
catch
{
}
if (val5 != null)
{
try
{
EnsureServerOwnership(val4, val5, "SpawnInitZdo");
long nowSeconds = GetNowSeconds();
val5.Set("MS_Riko_SpawnTime", nowSeconds);
val5.Set("MS_Riko_LastSeenTime", nowSec