Loot Manager
Adds loot library for mod creators. This tool allows user to assign loots to enemies using Mods Communicator.
| Last updated | 2 days ago |
| Total downloads | 23 |
| Total rating | 0 |
| Categories | Libraries Utility |
| Dependency string | GymMed-Loot_Manager-0.0.3 |
| Dependants | 0 other packages depend on this package |
This mod requires the following mods to function
GymMed-Mods_Communicator
Outward Mods Communicator enables seamless communication between mods through shared events and configuration syncing. It also lets users override any changes made by other mods, giving them full control over their settings.
Preferred version: 1.2.0README
Outward Loot Manager
Outward mod that allows you to assign loots to enemies using Mods Communicator to send events.
Deeper Explanation
This mod hooks into theLootable.OnDeath method and evaluates the
provided LootRules. These rules determine whether your custom loot
should be applied to a dying enemy.
The mod acts as a central Loot Manager that stores simple, abstracted logic and handles more complex game mechanics internally. Its state can be saved or loaded using XML.
Other mods can inspect, modify, or react to the LootManager’s
dynamic state and see how different mods interact with it.
How to use it
Firstly, install Mods
Communicator
After that, you can publish and subscribe to LootManager events.
All events are registered and visible in Mods Communicator’s logging system. However, it is still recommended to read the event descriptions below and review the examples. Many event fields are optional and give you extra control, but they are not required.
Percentages vs Weights
If the total drop chance of your items exceeds 100, the system automatically switches to weight-based loot instead of percentages. Providing
maxDiceValue also forces weight mode.In weight mode, Outward uses its original loot system, where each item is
assigned a dice-roll range based on its dropChance and its position
in the list. The system builds contiguous ranges as follows:
minDiceRollValueis the sum of all previous items’dropChance.maxDiceRollValueisminDiceRollValue + dropChance.
When the game rolls a number, the item whose range contains that number is the one that drops.
Example: If each item has a dropChance of 10, the ranges will be
0–10, 10–20, 20–30, etc.
The seventh item will therefore have
minDiceRollValue = 70 and maxDiceRollValue = 80.
You can adjust the behavior further using emptyDropChance and
maxDiceValue.
Events
Check the parameter tables to understand which values are required. Event descriptions below are intentionally abstract — always refer to the tables for the exact field requirements.
Publish
Mod Namespace:gymmed.loot_manager_*
Make sure to publish only after LootManager has initialized. I
recommend adding it as a dependency in your mod, or publishing it before/after
ResourcesPrefabManager.Load has finished.
Add Loot
Event Name:AddLoot
Requires at least one of the following:
-
item -
itemDropChance -
ListItemDropChance
Also requires one or more of the filtering fields: enemy / faction / area / uniqueness.
This event is the most flexible one — you can do everything with it. Other events exist only for convenience.
Examples
Guaranteed Loot
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
var payload = new EventPayload
{
["itemId"] = myItemId,
["enemyName"] = "Hyena", //Character.Name
};
EventBus.Publish("gymmed.loot_manager_*", "AddLoot", payload);
Chance Loot
Providing enemy id ignores other enemy requirements.This only works for enemy id, other requirements can be combined for flexibility. By providing
dropChance to item it will internally
make emptyDropChance
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
string myEnemyId = "eCz766tEIEOWfK81om19wg"; // Calixa Boss
var payload = new EventPayload
{
["itemId"] = myItemId,
["dropChance"] = 50,
["enemyId"] = myEnemyId,
};
EventBus.Publish("gymmed.loot_manager_*", "AddLoot", payload);
Faction Loot
Additionally added how many items should be dropped(LootManager
tries to build ItemDropChance internally).
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
var payload = new EventPayload
{
["itemId"] = myItemId,
["minDropCount"] = 1,
["maxDropCount"] = 3,
["faction"] = Character.Factions.Bandits,
};
EventBus.Publish("gymmed.loot_manager_*", "AddLoot", payload);
Area Loot
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
var payload = new EventPayload
{
["itemId"] = myItemId,
["area"] = AreaManager.AreaEnum.Abrassar,
};
EventBus.Publish("gymmed.loot_manager_*", "AddLoot", payload);
Area Family Loot
I recommend to make a helper method of finding yourAreaFamily by enum/name.You can find them in
AreaManager.AreaFamilies.
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
var payload = new EventPayload
{
["itemId"] = myItemId,
["areaFamily"] = AreaManager.AreaFamilies[2], //Levant Region includes abrassar and all dungeons
// you can combine requirements
["faction"] = Character.Factions.Bandits,
["listExceptNames"] = new List<string>() { "Hyena" },
};
EventBus.Publish("gymmed.loot_manager_*", "AddLoot", payload);
Add Loot By Enemy Name
Event Name:AddLootByEnemyNameCompares Character.Name to your provided string and if they match provides loot
Required one of the item/itemDropChance/ListItemDropChance information.
Required enemyName.
Only listExceptIds work.
Example
using OutwardModsCommunicator;
...
ItemDropChance myItem = new ItemDropChance();
myItem.ItemID = 4000360; // Dreamers root
myItem.DropChance = 30;
var payload = new EventPayload
{
["itemDropChance"] = myItem,
["enemyName"] = "Hyena", //Character.Name
["listExceptIds"] = new List<string>() { "yourEnemyId" },
};
EventBus.Publish("gymmed.loot_manager_*", "AddLootByEnemyName", payload);
Add Loot By Enemy Id
Event Name:AddLootByEnemyIdRequired one of the item/itemDropChance/ListItemDropChance information.
Required enemyId.
Example
using OutwardModsCommunicator;
...
List<ItemDropChance> drops = new();
ItemDropChance myFirstItem = new ItemDropChance();
myFirstItem.ItemID = 4000360; // Dreamers root
myFirstItem.DropChance = 30;
ItemDropChance mySecondItem = new ItemDropChance();
mySecondItem.ItemID = 6000170; // Purifying quartz
mySecondItem.DropChance = 30;
drops.Add(myFirstItem);
drops.Add(mySecondItem);
var payload = new EventPayload
{
["listOfItemDropChances"] = drops,
["enemyId"] = "JmeufMpL_E6eYnqCYP2r3w", // Elite Burning Man
};
EventBus.Publish("gymmed.loot_manager_*", "AddLootByEnemyId", payload);
Add Loot For Uniques
Makes bosses lootable.Event Name:
AddLootForUniquesRequired one of the item/itemDropChance/ListItemDropChance information.
Required one of the
isForBosses/isForBossPawns/
isForStoryBosses/isForUniqueArenaBosses/
isForUniqueEnemies parameters.
Example
using OutwardModsCommunicator;
...
int myItemId = 4000360; // Dreamers root
var payload = new EventPayload
{
["itemId"] = myItemId,
["isForBosses"] = true, // is for all bosses
};
EventBus.Publish("gymmed.loot_manager_*", "AddLootForUniques", payload);
Load Loots XML
Loads your custom loot-rules XML document into the mod.Event Name:
LootRulesSerializer@LoadCustomLootsRequires the full path to the XML file, provided as the
filePath parameter.Example
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
["filePath"] = "assemblyLocation/filePath.xml",
};
EventBus.Publish("gymmed.loot_manager", "LootRulesSerializer@SaveLootRulesToXml", payload);
Store Loots to XML
Stores the currentLootManager loot rules into an XML document.Event Name:
LootRulesSerializer@SaveLootRulesToXmlOptional: you may provide a full XML file path using the
filePath
parameter. If omitted, the file will be stored at:
BepInEx/config/gymmed.Mods_Communicator/Loot_Manager/LootRules-date.xml
Example
using OutwardModsCommunicator;
...
var payload = new EventPayload
{
//["filePath"] = "",
};
EventBus.Publish("gymmed.loot_manager", "LootRulesSerializer@SaveLootRulesToXml", payload);
Subscribe
Mod Namespace:gymmed.loot_managerYou can listen then mod methods a triggered.
Append Loot Rule
Event Name:LootRuleRegistryManager@AppendLootRuleOn published and added loot rule gets triggered.
Example
using OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.loot_manager",
"LootRuleRegistryManager@AppendLootRule",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
int lootRuleId = payload.Get<string>("lootRuleId", null);
// You would compare it with your registered loot rule id that you can provide
// Your code...
}
Remove Loot Rule
Event Name:LootRuleRegistryManager@RemoveLootRuleOn published and removed loot rule gets triggered.
Example
using OutwardModsCommunicator;
...
public awake()
{
...
EventBus.Subscribe(
"gymmed.loot_manager",
"LootRuleRegistryManager@RemoveLootRule",
YourMethod
);
}
...
public static void YourMethod(EventPayload payload)
{
if (payload == null) return;
int lootRuleId = payload.Get<string>("lootRuleId", null);
// You would compare it with your registered loot rule id that you can provide
// Your code...
}
Unique Enemies
If you going to provide onlyenemyName and that enemy is unique
make sure to provide one of isForBosses,
isForBossPawns, isForStoryBosses,
isForUniqueArenaBosses, isForUniqueEnemies parameters.Data collected using Outward Scene Tester
Bosses
Below are all included inisForBosses parameter.
Story Bosses
All enemies included inisForStoryBosses. They are all compared by UID. Djinn has dummy data and is not tested, but he never enters the state to be looted?
Boss Pawns
All enemies included inisForBossPawns. They are all compared by UID.
Unique Arena Bosses
All enemies included inisForUniqueArenaBosses. They are all compared by UID.
Definitive Edition Unique Enemies
All enemies included inisForUniqueEnemies. They are all compared by UID.
All Parameters
| Required atleast one of type | Parameter | Type | Description | |
|---|---|---|---|---|
| Item | Builds ItemDropChance Internally | itemId | int | Required if itemDropChance/listOfItemDropChances is not provided. Loot item ID. |
| dropChance | int | Optional. Default is 10. Determines chance of dropping item. You can provide ItemDropChance instead if you like. | ||
| minDropCount | int | Optional. Default is 1. Provides minimum amount of items could be dropped. You can provide ItemDropChance instead if you like. | ||
| maxDropCount | int | Optional. Default is 1. Provides maximum amount of items could be dropped. You can provide ItemDropChance instead if you like. | ||
| minDiceRollValue | int | Optional. Default is 0. Sets the lowest dice roll value at which item drop chances begin to count. Use together with 'maxDiceRollValue' and 'maxDiceValue'. You can provide ItemDropChance instead if you like. | ||
| maxDiceRollValue | int | Optional. Default is 0. Sets the highest dice roll value considered when calculating item drop chances. Use together with 'minDiceRollValue' and 'maxDiceValue'. You can provide ItemDropChance instead if you like. | ||
| Choice to provide instead | listOfItemDropChances | List<string> | Optional. Default null. Provide your created list of your ItemDropChance instances to be dropped. | |
| itemDropChance | ItemDropChance | Optional. Default null. Provide your created ItemDropChance instance to be dropped. | ||
| Enemy | enemyId | string | Default null. Determines if drop should be appliead for enemy. You can get this from UnityExplorer mod or logs. | |
| enemyName | string | Default null. Determines if drop should be appliead for enemy. You can get this from UnityExplorer mod or logs. | ||
| area | AreaManager.AreaEnum? | Optional. Default nullable. Determines if drop should be appliead for specific area. You can get this from AreaManager.AreaEnum enum. | ||
| areaFamily | AreaFamily | Optional. Default null. Determines if drop should be appliead for specific area family(region). You can get this from AreaManager.AreaFamilies variable. | ||
| faction | Character.Factions? | Optional. Default nullable. Determines if drop should be appliead for specific faction. You can get this from Character.Factions enum. | ||
| isForBosses | bool | Optional. Default false. Determines if drop should be appliead for all game bosses and pawns. | ||
| isForBossPawns | bool | Optional. Default false. Should drop be applied for bosses pawns? | ||
| isForStoryBosses | bool | Optional. Default false. Should drop be applied for story bosses? | ||
| isForUniqueArenaBosses | bool | Optional. Default false. Should drop be applied for unique arena bosses? | ||
| isForUniqueEnemies | bool | Optional. Default false. Should drop be applied for unique enemies? | ||
| Not Required | lootId | string | Optional. You will need loot id if you planning to remove loot later. | |
| listExceptIds | List<string> | Optional. Default null. List of enemy ids that will not receive loot. You can get this from Character.UID.Value . | ||
| listExceptNames | List<string> | Optional. Default null. List of enemy names that will not receive loot. You can get this from Character.Name . | ||
| minNumberOfDrops | int | Optional. Default is 1. Determines minimum amout of drops for same provided items(ItemDropChance). | ||
| maxNumberOfDrops | int | Optional. Default is 1. Determines maximum amout of drops for same provided items(ItemDropChance). | ||
| emptyDropChance | int | Optional. Default is 0. Defines the percentage chance for a drop to be empty. Used together with 'maxDiceValue'. | ||
| Parameters used for rules control | ||
|---|---|---|
| Parameter | Type | Description |
| filePath | string | Required. Used for loading custom loots from xml file. |
| filePath | string | Required. Used for loading custom loots from xml file. |
| lootRuleId | string | Provides loot rule id. |
Known Bugs
Unique Enemies
1.The unique arena boss Grandmother and its pawn enemies, the Kryptia Warriors, do not drop loot because they never trigger theLootable.OnDeath event. Not a priority for me right now to fix it.
Can I find full project example how to use this?
You can view outward enchantments balancer pack here.
How to set up
To manually set up, do the following
- Create the directory:
Outward\BepInEx\plugins\OutwardLootManager\. - Extract the archive into any directory(recommend empty).
- Move the contents of the plugins\ directory from the archive into the
BepInEx\plugins\OutwardLootManager\directory you created. - It should look like
Outward\BepInEx\plugins\OutwardSceneTester\OutwardLootManager.dllLaunch the game.