EvenMoreModifiers’s documentation¶
Introductory¶
Welcome to the official documentation of Even More Modifiers! Please refer to the sidebar menu to navigate this documentation site.
This documentation is generously hosted by readthedocs.io Docs are written in rst (reStructuredText) and is processed with Sphinx python. (Markdown docs supported using MkDocs) You can view the docs on Github here Syntax highlighting is done by Pygments which internally uses ‘lexers’ to generate highlighting.
Navigate using the links below or use the sidebar.
Getting started¶
It is completely possible to use EMM in your own mod and start adding your own modifiers! On this page, we will cover how te set yourself up in order to do this.
Extracting the mod¶
Firstly, download EMM’s .tmod file and place it in your mods folder. Now go in game, navigate to the mods menu and press the extra info button for EMM. From there, press the extract button.
Getting the .dll file¶
Now you will have extracted EMM, with that comes the .dll file you’ll need for your project. The extraction process unpacks the mod and places its files in the Mod Reader folder. This is usually located in %userprofile%\Documents\My Games\Terraria\ModLoader\Mod Reader
. In there, look for a folder named Loot
. Inside that folder, grab the Windows.dll file and take it to some place you can easily navigate to. It is recommended to place it inside a lib/
folder inside your mod root. Make sure to rename the file to Loot.dll
.
FAQ: Why call it ‘Loot’? A: EMM is internally named as ‘Loot’ by its previous owner, which cannot be changed.
Referencing the file¶
Now inside Visual Studio you can add the .dll as a reference which will give you access to everything EMM has to offer you. To do this, navigate to the references subject in your solution explorer, right click it and press ‘Add Reference’
Usage requirements¶
Setting the dependency¶
Note: it is very important to consider if you want your mod to be an optional reference or forced. If you want EMM to be required for your mod, add EMM to your build.txt as dependency: modReferences = Loot
. If you want EMM to be optional, add it to the weakReferences in build.txt: weakReferences = Loot
. Additionally, if you make EMM a weak reference, you must make 100% sure to not call any code referencing the EMM assembly if EMM is not loaded. If you don’t, the game will crash for people using your mod.
Registering your mod¶
In order to have content in your mod added to the EMM system, you need to actually register your mod and then your content. You do this inside your mod’s Load() hook. If you made EMM a weakReference, make sure to only call this if EMM is loaded.
public override void Load()
{
EMMLoader.RegisterMod(this);
EMMLoader.SetupContent(this);
}
Or as a weakReference:
public override void Load()
{
Mod mod = ModLoader.GetMod("Loot");
if (mod != null)
{
EMMLoader.RegisterMod(this);
EMMLoader.SetupContent(this);
}
}
Note: if you want your mod to work on Mono (mac), you must put code that references the EMM assembly in a separate class. It could look something like this:
public static class EMMHandler
{
public static void RegisterMod(Mod mod)
{
EMMLoader.RegisterMod(mod);
}
public static void SetupContent(Mod mod)
{
EMMLoader.SetupContent(mod);
}
}
Which would change your Load() to:
public override void Load()
{
EMMHandler.RegisterMod(this);
EMMHandler.SetupContent(this);
}
Or if as a weakReference:
public override void Load()
{
Mod mod = ModLoader.GetMod("Loot");
if (mod != null)
{
EMMHandler.RegisterMod(this);
EMMHandler.SetupContent(this);
}
}
Note that, it is best practice to do this for any weakReference. You only have to do this for this part though if you can make sure to not reference anything in your assembly yourself that references EMM’s assembly, such as a modifier.
After this, you’re set! You can now start adding modifiers. To learn how to add modifiers, see the particular section that describes it.
Autoloading glowmask and shader assets¶
If you want to autoload your own assets, you’ll need to add the following to your Load():
if (!Main.dedServ)
{
EMMLoader.RegisterAssets(this, "MyFolder");
}
Note that again, it is best practice to add a new method to EMMHandler and call that if you want this to work on Mono.
Make sure to edit “MyFolder” to the folder you want it to register. More details can be found in the specific section for this subject.
Modifier¶
To make a new Modifier, simply make a new class that inherits the class Modifier
in the Loot.System
namespace.
Properties¶
Brief properties explanation¶
Modifiers have the following features:
Power rolls: modifiers can roll its power by ‘magnitudes’. This happens when the modifier is rolled on the item, and these values are saved.
Weighted roll chances: modifiers can have different chances of being rolling (weighted rolling). The default weight is 1f. If you increase this number, your modifier will show up more often. Putting it to 2f will make it show up twice as often. Something like 10f is a really high roll chance. For very powerful modifiers, you want the roll chance to be low.
Rarity levels: the total rarity is decided by the sum of the rarity level of all modifiers on the item. The default is 1f. If you have more powerful modifiers, you will want to increase this number to signify the power of it.
Modifiers function like GlobalItems. This means you have access to pretty much all hooks available in GlobalItem (a few exceptions). You can assume the item in the context of a hook (the item parameter) is an item being influenced by that modifier. This is handled by EMM and out of your way, mostly consisting of boilerplate code.
Below I will detail all the properties of ModifierProperties
public float MinMagnitude { get; private set; }
public float MaxMagnitude { get; private set; }
public float MagnitudeStrength { get; private set; }
public float BasePower { get; private set; }
public float RarityLevel { get; private set; }
public float RollChance { get; private set; }
public int RoundPrecision { get; private set; }
public float Magnitude { get; private set; }
private float _power;
public float Power
{
get { return _power; }
private set
{
_power = value;
RoundedPower = (float)Math.Round(value, RoundPrecision);
}
}
public float RoundedPower
{
get;
private set;
}
Properties explanation in context¶
In short:
- roll around a certain number (BasePower)
- control the range in which the power will roll (MinMagnitude and MaxMagnitude)
- influence the end result of the power (MagnitudeStrength)
The luck stat is something you can roll on gear which influences some of this
In depth:
MinMagnitude and MaxMagnitude define the range that Magnitude will roll in. For example 10f - 15f will make Magnitude roll between 10f and 15f. MagnitudeStrength influences the final result of that roll as a multiplier. For example setting this to 2 and rolling the upper bound of the previous example (15f) would result in a Magnitude of 30. Power is then calculated by BasePower*Magnitude, so a BasePower of 10 in the previous context would result in a Power of 300. When Power is set, it automatically sets RoundedPower as well. This property is primarily useful for tooltips, where you’ll likely want to display 230% instead of something like 230.568893%. The precision of the rounding can be adjusted with RoundPrecision, which defaults to 0. So by default it will prune all decimals. If you set it to 1, it will leave 1 decimal, at 2 it will leave 2, and so forth…
These Properties (and all other hooks) support inheritance. A good example of that can be found here and here.
Modifying properties¶
To modify the Properties, you override the GetModifierProperties method and return a new object. All properties have a default value, to set specific ones you can specify the parameter names like so:
public virtual ModifierProperties GetModifierProperties(Item item)
{
return new ModifierProperties(roundPrecision: 1, minMagnitude: 15f, maxMagnitude: 30f);
}
Please note that you need to be careful when using inheritance! Always call base first and then modify whatever properties it returns:
A base class:
public abstract class EquipModifier : Modifier
{
public override ModifierProperties GetModifierProperties(Item item)
{
return new ModifierProperties(magnitudeStrength: item.IsAccessory() ? .6f : 1f);
}
}
If a class inherits from this base class, the properties should be handled like so:
public override ModifierProperties GetModifierProperties(Item item)
{
return base.GetModifierProperties(item).Set(maxMagnitude: 15f);
}
Saving and loading data per modifier¶
You can save and load your own data per modifier, an example can be seen here. Essentially, saving and loading works exactly the same as you are used to. The handling of it is managed by EMM itself and out of your way.
Saving data¶
Please note that, inside the Save method the TagCompound already contains necessary save data for the modifier (which is automatically handled by EMM). Make sure that you don’t clear the TC or make a new instance!
public override void Save(Item item, TagCompound tag)
{
// If you use inheritance, always call base first
base.Save(item, tag);
// Any saving here!
}
Loading data¶
public override void Load(Item item, TagCompound tag)
{
// If you use inheritance, always call base first
base.Load(item, tag);
// Any loading here!
}
Tooltips¶
Tooltips by modifiers are automatically managed by EMM, and it does the following:
- Item name automatically modified if rarity overrides color and/or adds prefix/suffix
- Adds a rarity tag by name [Modifier:Name]
- Adds a modifier line (description) per line by name [Modifier:Description:i]
However, you can still modify the tooltips per modifier if you want to. The ModifyTooltips method is called after this automatic behaviour.
ModifierRarity¶
To make a new ModifierRarity, simply make a new class that inherits the class ModifierRarity
in the Loot.System
namespace.
Primary functions¶
The primary use of a rarity is to provide some representation of how strong a rolled item’s modifiers are. For most rarities, you’ll just want to be overriding these properties: (example is Common Rarity)
public class CommonRarity : ModifierRarity
{
public override string Name => "Common";
public override Color Color => Color.White;
public override float RequiredRarityLevel => 0f;
}
A rarity is always rolled by its RequiredRarityLevel, and the highest matching rarity is always selected. A higher required level indicates a more powerful item, so the rarity should signify that.
Extra functions¶
A rarity can add a prefix and suffix to an item name, as well as change the color of it in the tooltip.
public virtual Color? OverrideNameColor => null;
public virtual string ItemPrefix => null;
public virtual string ItemSuffix => null;
Saving and loading data¶
You can save and load data exactly like how you can do it for a Modifier (see above)
ModifierPool¶
To make a new ModifierPool, simply make a new class that inherits the class ModifierPool
in the Loot.System
namespace.
Concept¶
The idea of a pool is that it consists of a ‘pool’ of modifiers of which up to 4 can be rolled on an item. EMM itself uses a single pool that consists of all modifiers in the system.
Also note that, the modifiers on an item are actually saved as a ModifierPool: the Modifiers are inside this ModifierPool.
Primary use¶
The primary use of a pool should be to roll a ‘themed’ item. You could also make pools that are rare that consist only of modifiers that work well together, making it more likely the item becomes very potent with the rolled modifiers.
You’ll want to populate the Modifiers
array in your contructor. An example can be seen here
ModifierEffect¶
A ModifierEffect signifies the effect of a modifier on a player It should house the implementation, and delegation of such an effect Methods on effects can be delegated from ModPlayer
How to get a ModifierEffect¶
You can get an effect as follows:
ModifierPlayer.Player(player).GetEffect<MyModifierEffect>()
UsesEffectAttribute¶
The UsesEffect attribute is linking a Modifier to a ModifierEffect. This will make the ModifierEffect become activated if we have an item with this modifier.
Usage:
[UsesEffect(typeof(MyEffect))]
public class MyModifier : Modifier
[UsesEffect(typeof(FirstEffect), typeof(SecondEffect), typeof(ThirdEffect)]
public class MyModifier : Modifier
DelegationPrioritizationAttribute¶
This attribute is used to set a custom prioritization for a delegation It allows you to customize at which point your delegation is called in the chain The end result is a prioritization list as follows:
- First part: all delegations prioritized as DelegationPrioritization.Early, order by their level
- Second part: all delegations with no custom prioritization
- Third part: all delegations prioritized as DelegationPrioritization.Late, order by their level
To increase the force put into your prioritization, increase the delegation level
Example use-case¶
Consider a modifier adding a flat +50 damage, and another modifier giving +100% damage to the item. Without the prioritization options, it sometimes happens that the latter modifier is delegated before the former one. This means the multiplication happens before addition. Example:
Base 10 damage. * 100% = 20 damage, + 50 damage = 70 damage
Base 10 damage. + 50 = 60 damage * 100% = 120 damage
You can see how big of a difference this can make. So this is what this functionality is used for: to prioritize when your delegation is executed in the chain
How to use¶
You have two options (technically three): early in the chain or later in the chain.
To be in the middle, simply don’t use this attribute.
Examples:
// a multiplicative effect wants to be last in the chain, hence 999
[AutoDelegation("OnModifyHitNPC")]
[DelegationPrioritization(DelegationPrioritization.Late, 999)]
// this particular example wants to happen early, for example mitigating damage entirely
[AutoDelegation("OnPreHurt")]
[DelegationPrioritization(DelegationPrioritization.Early, 100)]
Utilities¶
EMM provides necessary utility functions for modifiers, rarities and pools. These can be called on a mod instance. The utils can be found in EMMUtils. The utils work primarily the same as the ones in tML. An example call to get one of your modifiers:
myMod.GetModifier<MyModifier>();
Advanced content creation¶
In the following section we will go over advanced content creation, which includes an explanation of the more complex systems that are in place that make this possible.
Automated event-based delegation¶
This mod features what I call ‘automated event-based delegation’. The purpose of this system is simple: it allows you to run ModPlayer code, through a Modifier. What does this mean? Just imagine that you need to run ModPlayer code for some Modifier, this system makes that possible in an easy-to-use yet efficient manner.
To use this, you do not need to understand how the system works internally: you just need to know how to call the necessary features to have it work. This system is used with ModifierEffects. This is a class not yet covered, so we will do so now.
ModifierEffect & delegation¶
ModifierEffects live on the player, specifically a ModPlayer, part of EMM called the ‘ModifierPlayer’. Do not make these more complicated as they are: they give you access to virtually all ModPlayer hooks similarly to how Modifier exposes most GlobalItem hooks to you. Instead, it does it in a different way. It does not inherit from ModPlayer, it is it’s own class instead. This is where the event-based delegation comes into play, as it allows those hooks to actually be ‘attached’ to an existing ModPlayer hook. Here is what that looks like:
public class CursedEffect : ModifierEffect
{
public int CurseCount;
public override void ResetEffects(ModifierPlayer player)
{
CurseCount = 0;
}
[AutoDelegation("OnUpdateBadLifeRegen")]
private void Curse(ModifierPlayer player)
{
if (CurseCount > 0 && !player.player.buffImmune[BuffID.Cursed])
{
if (player.player.lifeRegen > 0)
{
player.player.lifeRegen = 0;
}
player.player.lifeRegen -= 2 * CurseCount;
player.player.lifeRegenTime = 0;
}
}
}
The above example showcases a ‘CursedEffect’, that inherits from ModifierEffect. Our Curse method is being delegated to the ModPlayer.OnUpdateBadLifeRegen
, notice how the AutoDelegation attribute makes that possible. All you need to make sure is that your method signature matches the one of the hook you want to attach to, where the first parameter is always of type ModifierPlayer. Even if the hook normally has no parameters.
You are now correct if you say that the Curse method is being ran from within the OnUpdateBadLifeRegen hook.
The effect is ‘attached’ to a Modifier in the following way:
[UsesEffect(typeof(CursedEffect))]
public class CursedDamage : Modifier
Notice the UsesEffect attribute, in which you specify which effect should become active when an item has this modifier. Note that you can specify multiple effects, if you want:
[UsesEffect(typeof(EffectOne), typeof(EffectTwo), typeof(EffectThree))]
public class MyModifier : Modifier
The glowmask- and shader-entities system¶
EMM features a system called entities for glowmasks and shaders. This allows you to apply glowmasks and shaders to items based on its modifiers. (You quite literally attach one to the Modifier itself!)
How does it work?¶
In the background, EMM handles everything for you. An ‘entity’ lives on the modifier of an item, which can alter its appearance. Examples:
How to use it?¶
Shader entity¶
Inside your modifier class, simply override GetShaderEntity()
and return an entity:
public override ShaderEntity GetShaderEntity(Item item)
{
return new ShaderEntity(item,
GameShaders.Armor.GetShaderIdFromItemId(ItemID.MirageDye),
drawLayer: ShaderDrawLayer.Front,
drawOffsetStyle: ShaderDrawOffsetStyle.Alternate,
shaderDrawColor: Color.IndianRed);
}
Note that custom shaders are not supported at this time.
Read below to learn how to use a custom sprite.
Glowmask entity¶
Inside your modifier class, simply override GetGlowmaskEntity()
and return an entity:
public override GlowmaskEntity GetGlowmaskEntity(Item item)
{
return new GlowmaskEntity(item);
}
Read below to learn how to use a custom sprite.
Using custom assets¶
EMM has a custom system built in that can automagically load assets that entities can use. This means you can control which parts of the item is being applied a glowmask or shader. An example where only a specific part of the item is being applied a shader: https://streamable.com/ezkm5
How to load my own assets?¶
All you have to do is specify to register your assets in Load():
if (!Main.dedServ)
{
EMMLoader.RegisterAssets(this, "MyFolder");
}
How does it work?¶
The above code will make it so that assets from the folder <YourMod>/GraphicAssets
are added to the system. This path is relative to your mod, so if you enter MyFolder/SubFolder/DeeperFolder
it will look in there. Currently only a single folder is supported, and you should put all assets in there.
Keynote: the assets will be removed from your mod’s internal texture array. You can disable this by passing: clearOwnTextures: false
Naming rules¶
All assets can be named by either the item name or its ID. The ID (or type) is recommended for vanilla items. Currently you can only add assets for your own mod or vanilla. Example asset names:
- MyModItem_Glowmask.png –or– MyModItem_Glow.png
- MyModItem_Shader.png –or– MyModItem_Shad.png
As shown, names can be suffixed with either the full variant or shorthand of the asset type. This suffix is important or else the asset will not be added.
For vanilla items, it is recommended to name them by their type, it is however still possible to name them by their name. (EMM will try to resolve its type manually)
Indices and tables¶
Brief overview of common classes¶
Below is given a brief description of common classes from EMM. To learn more about these classes, visit their respective guides.
Modifier¶
Defines a modifier, has access to virtually any hook for items that can be used to affect the item. (this goes into effect when an item has that modifier on it)
ModifierEffect¶
Effects live on the player, and they allow running code in ModPlayer hooks coming from a modifier. There is an auto delegation system behind the scenes that makes this possible.
ModifierPool¶
So a pool is what modifiers are drawn from when an item rolls it’s modifiers. Just consider it a collection of modifiers, because that’s all it is. On the item itself, all modifiers are also stored as a ModifierPool
ModifierRarity¶
The combined strength of modifiers on an item form a numeric value that can be matched with a rarity, which can affect the item’s name, change color etc. If you understand these bits, you understand most core stuff of the mod besides a lot of back-end stuff making stuff possible