BlackMagicAPI Guide
Updated 3 months agoBlackMagicAPI Guide
A comprehensive framework for Mage Arena modding
Table of Contents
Basic Setup
Core Requirements
- BepInEx 5.4.21+
 - .NET Framework 4.8
 BlackMagicAPI.dllreference
Plugin Template
[BepInPlugin("your.mod.id", "Your Mod Name", "1.0.0")]
public class MyMod : BaseUnityPlugin
{
    private void Awake()
    {
        // Registration examples
        BlackMagicManager.RegisterSpell(this, typeof(YourSpellData), typeof(YourSpellLogic));
        BlackMagicManager.RegisterItem(this, typeof(YourItemData), typeof(YourItemBehavior));
        BlackMagicManager.RegisterCraftingRecipe(this, typeof(IngredientA), typeof(IngredientB), typeof(Result));
        BlackMagicManager.RegisterDeathIcon(this, "custom.death", "death_icon.png");
        BlackMagicManager.RegisterSoup(this, typeof(LogItem), typeof(YourSoupData), typeof(YourSoupEffect));
    }
}
Soup System
Soup Registration
/// <summary>
/// Registers a new soup with the soup management system.
/// </summary>
/// <param name="plugin">The plugin registering the soup.</param>
/// <param name="IItemInteraction">The type of the item used to create the soup (must implement IItemInteraction).</param>
/// <param name="SoupDataType">The type of the soup data (must inherit from SoupData).</param>
/// <param name="SoupEffectType">The type of the soup effect (must inherit from SoupEffect, optional if SoupData can load a prefab).</param>
/// <exception cref="InvalidCastException">Thrown if soup data cannot be created or cast to SoupData.</exception>
BlackMagicManager.RegisterSoup(
    BaseUnityPlugin plugin, 
    Type IItemInteraction, 
    Type SoupDataType, 
    Type? SoupEffectType = null
)
Soup Data Class
internal class HealthSoupData : SoupData
{
    public override string Name => "Health Soup";
    public override string ConsumeDescription => "Restores health over time";
    public override Color SoupColor => Color.red;
    
    // Optional: Load custom effect prefab
    public override Task<SoupEffect?> GetEffectPrefab()
    {
        // Load your custom prefab here
        return Task.FromResult<SoupEffect?>(null);
    }
    
    // Optional: Customize visual transform
    public override void SetObjectVisualTransform(GameObject render)
    {
        render.transform.localPosition = new Vector3(-0.0003f, 0.004f, 0.004f);
        render.transform.rotation = Quaternion.Euler(-3f, 0f, 0f);
        render.transform.localScale = render.transform.localScale * 0.03f;
    }
}
Soup Effect Class
internal class HealthSoupEffect : SoupEffect
{
    public override void ApplyEffect(PlayerMovement player)
    {
        // Implement your soup effect logic here
        // Example: Heal player over time
        StartCoroutine(HealOverTime(player));
    }
    
    private IEnumerator HealOverTime(PlayerMovement player)
    {
        float duration = 10f;
        float healPerSecond = 2f;
        float elapsed = 0f;
        
        while (elapsed < duration)
        {
            if (player != null)
            {
                // Apply healing logic
                // player.health += healPerSecond * Time.deltaTime;
            }
            elapsed += Time.deltaTime;
            yield return null;
        }
        
        DisposeEffect();
    }
    
    // Optional: Custom prefab initialization
    public override void OnPrefabCreatedAutomatically(GameObject prefab)
    {
        // Add custom components or modify the prefab
    }
}
Sprite Requirements
- Place soup UI sprites in 
Sprites/{SoupName}_Ui.pngwithin your plugin directory - Sprite filename should have spaces removed (e.g., "HealthSoup_Ui.png")
 - Fallback to default empty UI sprite if custom sprite not found
 
Complete Soup Example
// Registration
BlackMagicManager.RegisterSoup(
    this,
    typeof(SoupBowlItem), 
    typeof(HealthSoupData),
    typeof(HealthSoupEffect)
);
// Soup Data
internal class HealthSoupData : SoupData
{
    public override string Name => "Health Restoration Soup";
    public override string ConsumeDescription => "Gradually restores 20 health over 10 seconds";
    public override Color SoupColor => new Color(1f, 0.2f, 0.2f, 1f);
}
// Soup Effect
internal class HealthSoupEffect : SoupEffect
{
    public override void ApplyEffect(PlayerMovement player)
    {
        StartCoroutine(HealOverTime(player));
    }
    
    private IEnumerator HealOverTime(PlayerMovement player)
    {
        float totalHealing = 20f;
        float duration = 10f;
        float elapsed = 0f;
        
        while (elapsed < duration && player != null)
        {
            // Apply healing logic here
            elapsed += Time.deltaTime;
            yield return null;
        }
        
        DisposeEffect();
    }
}
Spell Creation
Spell Data Class
internal class FireballData : SpellData
{
    public override SpellType SpellType => SpellType.Page;
    public override string Name => "Fireball";
    public override float Cooldown => 3f;
    public override Color GlowColor => Color.red;
    
    // Additional voice command aliases
    public override string[] SubNames => ["flameball", "inferno"];
}
Spell Logic Class
internal class FireballLogic : SpellLogic
{
    public override bool CastSpell(PlayerMovement caster, PageController page, Vector3 spawnPos, Vector3 viewDirectionVector, int castingLevel);
    {
        return true; // return true to go on cooldown
    }
    // Optional page item interaction
    public override void OnPageItemUse(PlayerMovement itemOwner, PageController page)
    {
    }
}
Spell Prefab Access
// Get all active instances
var activeFireballs = Spell<FireballData>.GetLogicInstances();
// Get specific prefab component
var fireballPrefab = Spell<FireballData>.GetLogicPrefab<FireballLogic>();
// Check prefab existence
if(Spell<FireballData>.GetPagePrefab() != null) 
{
    // Prefab is loaded
}
Registration
BlackMagicManager.RegisterSpell(
    this, 
    typeof(FireballData), 
    typeof(FireballLogic)
);
Item System
Complete Item Example
internal class HealthPotionData : ItemData
{
    public override string Name => "Health Potion";
}
internal class HealthPotionBehavior : ItemBehavior
{
    protected override void OnItemUse(PlayerMovement itemOwner)
    {
    }
}
Crafting Recipes
Advanced Recipe Configuration
// Basic recipe
BlackMagicManager.RegisterCraftingRecipe(
    this,
    typeof(FireEssence),
    typeof(IceShard), 
    typeof(SteamSpirit)
);
Recipe Requirements
| Component | Requirements | 
|---|---|
| Ingredients | Must implement IItemInteraction | 
| Result | Must be a prefab of IItemInteraction | 
API Reference
SpellLogic Class
/// <summary>
/// Base class for all spell behaviors with lifecycle management
/// </summary>
public abstract class SpellLogic : MonoBehaviour, ISpell
{
    /// <summary>
    /// Core spell casting implementation
    /// </summary>
    public abstract void CastSpell(GameObject playerObj, PageController page, 
        Vector3 spawnPos, Vector3 viewDirectionVector, int castingLevel);
    /// <summary>
    /// Network data serialization for Castor
    /// </summary>
    public virtual void WriteData(DataWriter writer, PageController page,
        GameObject player, Vector3 spawnPos, Vector3 direction, int level) {}
    /// <summary>
    /// Client-side data synchronization
    /// </summary>
    public virtual void SyncData(object[] values) {}
}
Spell<SD> Utility Class
/// <summary>
/// Generic spell accessor providing runtime management utilities
/// </summary>
public class Spell<SD> where SD : SpellData
{
    /// <summary>
    /// Retrieves the PageController prefab for this spell type
    /// </summary>
    public static PageController? GetPagePrefab() { ... }
    /// <summary>
    /// Gets all active instances of this spell type
    /// </summary>
    public static SpellLogic?[] GetLogicInstances() { ... }
    /// <summary>
    /// Gets typed instances filtered by specific SpellLogic subtype
    /// </summary>
    public static SL?[] GetLogicInstances<SL>() where SL : SpellLogic { ... }
}
SoupData Class
/// <summary>
/// Abstract base class representing soup data and configuration
/// </summary>
public abstract class SoupData
{
    public abstract string Name { get; }
    public abstract string ConsumeDescription { get; }
    public abstract Color SoupColor { get; }
    public int ItemId { get; internal set; }
    public int SoupId { get; internal set; }
    public BaseUnityPlugin? Plugin { get; internal set; }
    
    public virtual Task<SoupEffect?> GetEffectPrefab() => Task.FromResult<SoupEffect?>(null);
    public virtual void SetObjectVisualTransform(GameObject render) { }
}
SoupEffect Class
/// <summary>
/// Abstract base class for soup effect behavior logic
/// </summary>
public abstract class SoupEffect : MonoBehaviour
{
    internal int Id { get; set; }
    public abstract void ApplyEffect(PlayerMovement player);
    public virtual void OnPrefabCreatedAutomatically(GameObject prefab) { }
    public void DisposeEffect() { Destroy(gameObject); }
}
Troubleshooting
Common Soup Issues
- Missing Sprites: Ensure sprite files are in 
Sprites/folder with correct naming - Registration Errors: Ensure all types implement required interfaces