BlackMagicAPI Guide
Updated 2 weeks 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.dll
reference
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.png
within 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