API Documentation

The Mana and Artifice API allows mod makers to interface with the mod in several ways, from creating new shapes/components/modifiers to new rituals, to interfacing with the player's mana capabilities and more.

Setting Up Your Environment


  1. Install Forge and get your IDE set up with a basic mod.
  2. At the root of your project, create a folder called "libs"
  3. Put the Mana and Artifice main jar as well as the API jar in that folder
  4. Add the following to your build.gradle:
repositories {
    flatDir {
        dirs 'libs'
    }
     maven { url 'https://maven.theillusivec4.top/' } 
}

dependencies {
    minecraft 'net.minecraftforge:forge:1.16.2-33.0.61'

    compileOnly 'blank:mana-and-artifice-1.0.7.1:api' //api, compilation only
    runtimeOnly fg.deobf('blank:mana-and-artifice:1.0.7.1') //mod itself, deobf for runtime
    
    runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:1.16.2-4.0.0.1")
    compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:1.16.2-4.0.0.1:api")    

}

Adding a new Ritual

Adding a ritual is comprised of 3 main pieces: the ritual recipe JSON file, the ritual effect handler, and your ritual registration class. We'll go over all three of them here.

1.) The recipe JSON file

The JSON file describes many things about the ritual, including where the runes need to go, what reagents are required, and what the beam/light pattern looks like.  It's comprised of many different sections.  Let's take a look at an example and break it down piece by piece.
{
	"type": "mana-and-artifice:ritual",
	"tier": 4,
	"pattern":
	[
		[ 1, 2, 3, 4, 5 ],
		[ 0,-1, 0,-1, 0 ],
		[-1, 0, 6, 0,-1 ],
		[ 0,-1, 0,-1, 0 ],
		[-1, 0,-1, 0,-1 ]		
	],
	"displayPattern":
	[
		[ 1, 2, 3, 4, 5 ],
		[ 0,-1, 0,-1, 0 ],
		[-1, 0,-1, 0,-1 ],
		[ 0,-1, 0,-1, 0 ],
		[-1, 0,-1, 0,-1 ]		
	],
	"reagents":
	[
		"WWWWW",
		"     ",
		"  F  ",
		"     ",
		"     "
	],
	"keys":
	{
		"F": 
		{ 
			"item": "mana-and-artifice:enchantment_focus_water",
			"optional": false,
			"consume": false
		},
		"W":
		{
			"item": "minecraft:gray_wool",
			"optional": true,
			"consume": false
		}
	},
	"manaweave":
	[
		"mana-and-artifice:manaweave_patterns/inverted_triangle",
		"mana-and-artifice:manaweave_patterns/hourglass",
		"mana-and-artifice:manaweave_patterns/inverted_triangle"
	],
	"parameters":
	{
		"innerColor": "0xffffff",
		"outerColor": "0x2fb4e0",
		"displayIndexes": false,
		"connectBeam": true,
		"beamColor": "0x2fb4e0"
	}
}

First, we have the type.  It tells the recipe parser that this is a ritual recipe.

Next, we have the tier.  This ties directly into M&A's progression system, and indicates where in progression a player should be in order to be able to perform this ritual.

After that is pattern.  The pattern dictates where runes need to go, as well as the size of the ritual.   Your pattern array must be an odd size!  This is because the ritual needs a center point.  In the pattern, a value less than zero indiciates that a rune must be placed there, but there is no index assigned to that position.  More on that later.  A value of zero indicates that there is no rune at that position.  A value greater than zero indicates a rune as well as an index value.  This is for when the order of reagents matters, such as with the Ritual of Return.

Next is displayPattern.  This controls how the light points and beam will display.  It must be the same size as pattern, and must require points/indices at the same offset.  It works in much the same way as pattern does.  A value less than zero means that there is a rune there but no point of light.  A value of zero means that there is no rune there.  A value greater than zero means that there is a point of light at that spot.  Points will show up in the order defined, and the beam will connect points in order.  You can create a gap in the beam simply by skipping a number in order, for example (1-2-3) (6-7-8) will draw two separate beams.  This is referred to as beam grouping.

Following that is reagents.  This also must be the same size as pattern.  A space indicates no reagent, and a letter indicates a reagent.  The key here is that wherever you put a letter, the matching point in pattern must be non-zero!  This makes sense as you can't have a reagent where there is no rune.

Keys works closely with reagents.  Every letter in reagents must have an associated key.  The item field can be an item id or a tag id.  Optional will allow the ritual to start if that item is not present (you will need to handle how this changes the effect in your ritual handler).  Default false.  Consume if false will return the item to the player when the ritual is complete instead of consuming it.  Default true.

Manaweave is an array of patterns needed, in order, to complete the ritual.  It can be omitted entirely if manaweaving isn't required for your ritual.

Lastly, parameters are a set of miscellaneous configuration options that control various display properties. 
InnerColor and OuterColor control the inner and outer colors of the light points, respectively.  They are in RGB format: 0x(red)(green)(blue).
DisplayIndexes controls whether non-zero indices are rendered (like on the ritual of return).  This should be set to true when order matters.
connectBeam will loop the beam back to the original point on every beam group, so you can have closed shapes in the pattern drawn.
beamColor will set the beam's color.

All items in the parameters block are optional.

One extremely important thing:  When your pattern is drawn in game, it will be flipped on the horizontal axis!  This doesn't matter if it's symmetrical, but keep that in mind.

2.) The ritual handler

After defining your recipe, you need to define your handler. This controls what effects occur when your ritual is performed. Mana and Artifice core will handle the pattern matching, reagent collection, and the actual ritual itself, and your handler will simply be called when the ritual is completed.

Below is a commented example of a ritual effect.

//need to extend RitualEffect, a base class from the API
public class RitualEffectMonsoon extends RitualEffect{

	//default constructor takes in the ID of the ritual recipe this handles
	public RitualEffectMonsoon(ResourceLocation ritualName) {
		super(ritualName);
	}

	//actually applies the effect.  This will be called when the player completes the ritual.
	@Override
	protected boolean applyRitualEffect(PlayerEntity ritualCaster, ServerWorld world, BlockPos ritualCenter, 
		RitualRecipe completedRecipe, NonNullList<ItemStack> reagents) {
		
		boolean thunder = false;
		
		//here we are handling processing the reagents.  We remove any air and water focus items, leaving only the 
		//ones we want to handle
		reagents.removeIf(p -> p.getItem() == Items.AIR || p.getItem() == ItemInit.ENCHANTMENT_FOCUS_WATER.get());
		
		//look through the remaining reagents and set a flag if they match what we need them to
		if (reagents.size() == 5) {
			boolean allGrayWool = true;
			for (ItemStack stack : reagents) {
				if (stack.getItem() != Items.GRAY_WOOL) {
					allGrayWool = false;
					break;
				}				
			}
			thunder = allGrayWool;
		}
		
		//apply the actual effect taking into account our flags
		if (thunder) {
			world.func_241113_a_(0, 6000, true, true);
		}else {			
			world.func_241113_a_(0, 6000, true, false);
		}
		
		//return true to indicate that the ritual was completed successfully
		return true;
	}

	//this method will keep the runes, lights, beams, and particles around for the number of ticks specified.  
	//Useful if you're using an entity as a timed effect.
	//You cannot return less than 1 from this.  If you do, it will default to 60.
	@Override
	protected int getApplicationTicks(ServerWorld world, BlockPos ritualCenter, IRitualRecipe completedrecipe, 
		NonNullList<ItemStack> reagents) {
		return 10;
	}
}

3.) Regsitering your ritual

Now that you have everything defined, you need to register your handler.  To do this, you use the Minecraft Forge event bus, and subscribe to the RegistryEvent of type RitualEffect.

Doing so matches your ritual handler to its recipe.  You can have multiple handlers per recipe if  you register them that way.  Each one will be called when the ritual is completed.

An example is below.

@Mod.EventBusSubscriber(modid="your_mod_id", bus = Mod.EventBusSubscriber.Bus.MOD)
public class RitualInit {
  @SubscribeEvent
  public static void registerRitualEffects(RegistryEvent.Register<RitualEffect> event) {
    event.getRegistry().registerAll(
      new YourRitualHandler(new ResourceLocation("your_mod_id", "the_recipe_location"))
		.setRegistryName(new ResourceLocation("your_mod_id", "your_handler_unique_id"))
    );
    //put some kind of log line here so that you know it's being called at runtime (in dev breakpoint obv is fine)
  }
}

The first ResourceLocation is matching the recipe.  By default it will look in data/your_mod_id/recipes.  If you have subfolders in there to organize your recipes, you will need to specify them here.

The second ResourceLocation is the registered ID for your ritual handler.

Adding a Codex Entry

Codex entries can easily be added by creating a new json file with the entries in question and registering it with the mod.

Your file name should be prefix_xx_xx.json, where _xx_xx is the language code.  This helps to handle translations.  Every translation of your json file should have the same prefix.  For example, guidebook_en_us.json.

The below example is an entry for a new ritual called Porcine.

{
	"Porcine": {
		"index": 0,
		"category": "rituals",
		"sections": [
			{
				"type": "title",
				"value": "Ritual of Porcine"
			},
			{
				"type": "text",
				"value": "This ritual will spawn a pig.\n\nOink oink."
			}
		],
		"related_recipes": [
			{
				"type": "ritual",
				"location": "examplemod:rituals/porcine"
			}
		]
	}
}

Index is a field that helps to handle ordering within the codes.  Entries are sorted first by index, then alphabetically.

Category is the group within the codex.  Categories include:

  • artifice
  • basics
  • constructs
  • enchantments
  • manaweaving
  • rituals
  • runesmithing
  • sorcery

Sections define the actual content.  You need to specify a type, then subsequent properties are derived from that type.

Types are:

  • Image
    • Displays an image
    • Parameters:
      • location: A ResourceLocation that will point to an image.  It's relative to the assets folder and must be namespaced.
      • width: The width of the image to render
      • height: The height of the image to render
  • Item
    • Renders an item
    • Parameters:
      • location:  A ResourceLocation that will resolve to an item.
      • scale: The size of the item when rendered.  Maximum 3.0.
  • Text
    • Displays text
    • Parameters:
      • value: The text to display. Use \n for newlines, or split the text into multiple sections.
  • Title
    • Displays a title
    • Parameters:
      • value: The title to display.

Wrapping is handled automatically.

related_recipes are the recipe tabs that will be displayed on the side.

The type must be one of:

  • arcane_furnace
  • crafting
  • manaweaving_altar
  • manaweaving_pattern
  • ritual
  • runeforging
  • runescribing
  • spell_part

The location must be a valid ResourceLocation pointing at your recipe.

Once you've created your file, you register it into the mod using the RegisterGuidebooksEvent, like so:

@SubscribeEvent
    public void onRegisterGuidebooks(RegisterGuidebooksEvent event) {
    	event.getRegistry().AddGuidebook(new ResourceLocation("examplemod", "guide/guidebook"));
    }

Notice that the .json extension nor the language key is added.  This will be appended by the mod automatically based on the user's current language.  The only one that must exist is _en_us as that is the default language and will be used as a fallback when translations do not exist.