Understanding Error CS1061 in Game Modding

When decompiling and modding games built on Unity and C# like From The Depths, you will often find yourself copying existing classes to reverse-engineer functionality. However, a common stumbling block for modders is Compiler Error CS1061:

'IBlockToConstructBlockTypeStorage' does not contain a definition for 'HeavyPlasmaMantletStore' and no accessible extension method 'HeavyPlasmaMantletStore' accepting a first argument of type 'IBlockToConstructBlockTypeStorage' could be found...

This error indicates that the compiler cannot find the member you are calling on a specific type. If you tried resolving this by creating a duplicate interface in your own project and adding HeavyPlasmaMantletStore, you likely noticed that the error persists. Here is why that happens and how to properly structure your mod code to solve it.

Why Duplicating the Interface Does Not Work

In C#, types are defined by their fully qualified name and the assembly they reside in. Even if you recreate an interface with the exact same name and namespace inside your mod's project:

  • The property base.MainConstruct.iBlockTypeStorage returns an instance typed to the game's compiled assembly (e.g., Assembly-CSharp.dll).
  • Your mod contains a different interface that happens to share the same name, but the two are incompatible at compile-time and runtime.
  • You cannot modify an already-compiled interface from an external assembly without low-level assembly rewriting (such as MonoMod or IL weaving).

Solution 1: Inherit from the Base Class and Reuse the Existing Store

In most game architectures, specialized block types share the same pipeline as the original block. If your custom SuperHeavyPlasmaMantlet or HeavyPlasmaMantlet derives from the game's base PlasmaMantlet class, you can often register it directly into the existing store.

Step 1: Inherit from the Original Class

Ensure your class inherits from PlasmaMantlet:

public class HeavyPlasmaMantlet : PlasmaMantlet
{
    // Your custom implementation and overrides
}

Step 2: Register in the Original PlasmaMantletStore

Instead of creating a non-existent store property, register your block directly into the original store:

public override void StateChanged(IBlockStateChange change)
{
    base.StateChanged(change);

    if (change.IsAvailableToConstruct)
    {
        // Use the existing PlasmaMantletStore if polymorphism is supported
        base.MainConstruct.iBlockTypeStorage.PlasmaMantletStore.Add(this);
        base.MainConstruct.SchedulerRestricted.RegisterForFixedUpdate(FixedUpdate);
        base.MainConstruct.SchedulerRestricted.RegisterFor20PerSecond(RunCooling);
        base.MainConstruct.SchedulerRestricted.RegisterForLateUpdate(LateVisualise);
    }
    else if (change.IsLostToConstructOrConstructLost)
    {
        base.MainConstruct.iBlockTypeStorage.PlasmaMantletStore.Remove(this);
        base.MainConstruct.SchedulerRestricted.UnregisterForFixedUpdate(FixedUpdate);
        base.MainConstruct.SchedulerRestricted.UnregisterFor20PerSecond(RunCooling);
        base.MainConstruct.SchedulerRestricted.UnregisterForLateUpdate(LateVisualise);
        MeshMaker?.TemporarilyDisable();
    }
}

If the game iterates over PlasmaMantletStore to update targeting, fire inputs, or cooling, your block will automatically participate in those updates without needing engine-level alterations.

Solution 2: Maintain an Independent Store in Your Mod

If the game code does not need to know about your mantlets through iBlockTypeStorage, you should avoid modifying MainConstruct altogether. Instead, maintain your own static or construct-bound storage system.

You can create a standalone manager to track your custom blocks:

using System.Collections.Generic;

public static class HeavyPlasmaModManager
{
    // Map constructs to their custom mantlet stores
    private static readonly Dictionary<Constructable, List<HeavyPlasmaMantlet>> ConstructStores 
        = new Dictionary<Constructable, List<HeavyPlasmaMantlet>>();

    public static void RegisterMantlet(Constructable construct, HeavyPlasmaMantlet mantlet)
    {
        if (!ConstructStores.TryGetValue(construct, out var list))
        {
            list = new List<HeavyPlasmaMantlet>();
            ConstructStores[construct] = list;
        }
        list.Add(mantlet);
    }

    public static void UnregisterMantlet(Constructable construct, HeavyPlasmaMantlet mantlet)
    {
        if (ConstructStores.TryGetValue(construct, out var list))
        {
            list.Remove(mantlet);
            if (list.Count == 0)
            {
                ConstructStores.Remove(construct);
            }
        }
    }
}

Then, update your StateChanged method to register with your custom registry:

if (change.IsAvailableToConstruct)
{
    HeavyPlasmaModManager.RegisterMantlet(base.MainConstruct, this);
    // Register scheduler updates...
}
else if (change.IsLostToConstructOrConstructLost)
{
    HeavyPlasmaModManager.UnregisterMantlet(base.MainConstruct, this);
    // Unregister scheduler updates...
}

Solution 3: When You Must Inject Logic (Harmony)

If the game engine contains hardcoded checks specifically querying iBlockTypeStorage for mantlet logic, you cannot simply add a property to the interface. Instead, use Harmony to patch the methods that read from iBlockTypeStorage.

  • Locate the system that uses PlasmaMantletStore (such as weapon controllers or AI aiming systems).
  • Apply a [HarmonyPostfix] or [HarmonyPrefix] patch to append your custom mantlets to those logic checks.

By leveraging inheritance or external mod stores rather than redefining game-internal interfaces, you eliminate CS1061 while keeping your mod stable across future game updates.