Integration - ilikegoodfood/CommunityLib GitHub Wiki
This page contains information and sample code for ensuring that your mod, be it dependent on the Community Library or not, takes account of and interacts with the Community Library's override features. Some of these features will not operate correctly without these intergrations.
The first step of interacting with another mod is getting a reference to that mod's implementation of ModKernel
. In order to do this you will need to create a getModKernel
function, and call it from both ModKernel.beforeMapGen(Map map)
and afterLoading(Map map)
.
Code Template
The getModKernels function can be structured as a series of "else if" staements, or using a switch statement, based on your preference. The two templates below operate in exactly the same way, but the first uses a switch, and the second an "else if" construction.
// Template using a Switch statement
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
switch (kernel.GetType().Namespace)
{
case "targetModNamespaceOne"
modKernelOne = kernel;
break;
case "targetModNamespaceTwo"
modkernelTwo = kernel;
break;
default:
break;
}
}
}
}
}
// Template using an else if construction
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
if (kernel.GetType().Namespace == "targetModNamespaceOne")
{
modKernelOne = kernel;
}
else if (kernel.GetType().Namespace == "targetModNamespaceTwo")
{
modKernelTwo = kernel;
}
}
}
Dependent mods can set the variable type to CommunityLib.ModCore
when storing the ModKernel
of the Community Library, allowing for direct calls to its many functions, without needing to cast it each time. Non-dependent mods will need to use
NOTE: The game saves any class variable with public
accessability. Since the game creates a new instance of a mod kernel each time it launches the game, a saved kernel reference will result in two different mod kernels, one that is the current mod kernel of the mod, and one that was saved in the variable. To avoid this behaviour, make the mod kernel variabes private
.
Complete Example (Non-Dependent)
using Assets.Code;
using Assets.Code.Modding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ExampleMod
{
public class ExampleModKernel
{
private static ModKernel comLib;
public static ModKernel getComLib()
{
return comLib;
}
public override void beforeMapGen(Map map)
{
getModkernels(map);
}
public override void afterLoading(Map map)
{
getModKernels(map);
}
// Template using a Switch statement
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
switch (kernel.GetType().Namespace)
{
case "CommunityLib":
comLib = kernel;
break;
default:
break;
}
}
}
// Template using an else if construction
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
if (kernel.GetType().Namespace == "CommunityLib")
{
comLib = kernel;
}
}
}
// Use only one of the two options above
}
}
Complete Example (Dependent)
using Assets.Code;
using Assets.Code.Modding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace ExampleMod
{
public class ExampleModKernel
{
private static CommunityLib.ModCore comLib;
public static CommunityLib.ModCore getComLib()
{
return comLib;
}
public override void beforeMapGen(Map map)
{
getModkernels(map);
}
public override void afterLoading(Map map)
{
getModKernels(map);
}
// Template using a Switch statement
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
switch (kernel.GetType().Namespace)
{
case "CommunityLib"
if (kernel is CommunityLib.ModCore core)
{
comLib = core;
}
break;
default:
break;
}
}
}
// Template using an else if construction
private void getModKernels (Map map)
{
foreach (ModKernel kernel in map.mods)
{
if (kernel.GetType().Namespace == "CommunityLib" && kernel is CommunityLib.ModCore core)
{
comLib = core;
}
}
}
// Use only one of the two options above
}
}
The Community Library introduces the ability to allow orc tribes to build camps in locations that already contain a settlement. The settlements that they may overrite in this way are srictly controlled through the use of a settlement type whitelist and a subsettlement type blacklist.
The data is stored in a Dictionary<Type, HashSet<Type>>
. The Key is the exact type of the settlement that they can overrite (they will not overrite subtypes unless they are also registered), and the Value is a collection (HashSet) of Subsettlement Types that will block settlement.
Whenever a mod adds a new settlement type to the game that they believe orcs should be able to build camps on, thus distroying the original settlement, such as a new ruin type, the mod must register this settlemnt type to the Community Library in its getModKernels
function, using the registerSettlementTypeForOrcExpansion
function (see source code).
Function Signiture
public void registerSettlementTypeForOrcExpansion(Type t, HashSet<Type> subsettlementBlacklist = null)
A dependent mod can simply call the function, once for each settlement type.
Code Example (Dependent)
// Inside if-statement or switch-case of "getModKernel" function for "CommunityLib" namespace
CommunityLib.ModCore comLib = kernel as CommunityLib.ModCore;
comLib.registerSettlementTypeForOrcExpansion(typeof(Set_ExampleRuin), new HashSet<Type> { typeof(Sub_HauntedRuin) });
// Each settlement type must be registered separately
comLib.registerSettlementTypeForOrcExpansion(typeof(Set_ExampleWildSettlement));
A non-dependent mod must use reflection to dynamically aquire a reference to the function that is going to be called before it can call the function.
Code Example (Non-Dependent)
// Inside if-statement or switch-case of "getModKernel" function for "CommunityLib" namespace
// This line dynamically gets a reference to the function that you are about to call (MethodInfo), based on it's name and parameters.
MethodInfo MI_registerSettlementTypeForOrcExpansion = kernel.GetType().GetMethod("tryGetSettlementTypeForOrcExpansion", new Type[] { typeof(Type), typeof(HashSet<Type>) });
// The parameters object array contains the parameters that will be passed into the function.
object[] parameters = new object[] { typeof(Set_ExampleRuin), new HashSet<Type> { typeof(Sub_HauntedRuin) } };
// This line calls the function by its MethodInfo, on the class instance kernel", with the parameters "parameters".
MI_registerSettlementTypeForOrcExpansion.Invoke(kernel, parameters);
// Each settlement type must be registered seperately.
parameters = new object[] { typeof(Set_ExampleWildSettlement), null };
MI_registerSettlementTypeForOrcExpansion.Invoke(kernel, parameters);
The function will only fail if the Type you passed in is not a subtype of Settlement
.
If the settlement type has already been registered, and you pass in a blacklist, it will automatically add the new items to the existing blacklist without duplicates.
The Community Library has introduced a REVIVE_PERSON
event outcome that can be used to prevent a person from dying. When using this event outcome, there are several hoops that need to be jumped through, so check the Community Library's own test-implementation and documentation before proceeding.
The revivePerson function (see source code) will restore all of the values that need to be reset when a person dies, to the best of it's ability. This includes making a new agent for the person if they don't have one, and their current location (settlement or military unit) is destroyed, using hard-coded defaults.
Dependent mods can, when needed, provide a different agent for the person to inhabit using the onRevivePerson_CreateAgent
hook (see hooks).
Function Signiture
public void registerSettlementTypeForOrcExpansion(Type t, HashSet<Type> subsettlementBlacklist = null)
A dependent mod can simply call the function, once for each settlement type.
Code Example (Dependent)
// Inside if-statement or switch-case of "getModKernel" function for "CommunityLib" namespace
CommunityLib.ModCore comLib = kernel as CommunityLib.ModCore;
comLib.registerSettlementTypeForOrcExpansion(typeof(Set_ExampleRuin), new HashSet<Type> { typeof(Sub_HauntedRuin) });
// Each settlement type must be registered separately
comLib.registerSettlementTypeForOrcExpansion(typeof(Set_ExampleWildSettlement));
A non-dependent mod must use reflection to dynamically aquire a reference to the function that is going to be called before it can call the function.
Code Example (Non-Dependent)
// Inside if-statement or switch-case of "getModKernel" function for "CommunityLib" namespace
// This line dynamically gets a reference to the function that you are about to call (MethodInfo), based on it's name and parameters.
MethodInfo MI_registerSettlementTypeForOrcExpansion = kernel.GetType().GetMethod("tryGetSettlementTypeForOrcExpansion", new Type[] { typeof(Type), typeof(HashSet<Type>) });
// The parameters object array contains the parameters that will be passed into the function.
object[] parameters = new object[] { typeof(Set_ExampleRuin), new HashSet<Type> { typeof(Sub_HauntedRuin) } };
// This line calls the function by its MethodInfo, on the class instance kernel", with the parameters "parameters".
MI_registerSettlementTypeForOrcExpansion.Invoke(kernel, parameters);
// Each settlement type must be registered seperately.
parameters = new object[] { typeof(Set_ExampleWildSettlement), null };
MI_registerSettlementTypeForOrcExpansion.Invoke(kernel, parameters);
The Community Library implements a bulk harmony patch to all isntances of UA and it's subclasses.
This patch forces the game to check if an agent type has been assignd to the Community Library's Universal Agent AI, and if it has been, it uses the Universal Agent AI's pipeline for getting a challenge's utility (AgentAI.getChallengeUtility
) for that agent instead of the base game's pipeline.
This change is dynamic and does not require any action.