Jump to content

Recommended Posts

Posted

I'm trying to modify the 'fleeentity' AI task to change how the move speed works. At first I considered patching AiTaskFleeEntity with Harmony. Is this a good solution? If I wanted to associate additional data with the AiTaskFleeEntity instance beyond its existing fields, how should I do that?

Secondly I considered extending AiTaskFleeEntity and registering the new AI task. This works fine, but I'd like to affect entities which already use the existing fleeentity. There are too many entities in survival than I'm comfortable with to patch with JSON patching, and it would also cause a discrepancy in behavior with entities originating from player mods. I considered Asset Patching in Code but this looks complicated to do in code. I think I would have to remove the AiTaskFleeEntity and add my descendant AI task from the AiTaskManager from the EntityBehaviorTaskAI in each entity type, but I'm not sure how I would LoadConfig without the aiconfig JsonObject.

Posted (edited)

Hi, 
I'm freshly learning the modding and haven't touched entities so far yet, but wanted to join discussion if you or someone figures this out.

I haven't used Harmony (and honestly I'm trying to avoid it as it seems it's quite delicate and would require higher level of maintenance between releases) - but perhaps using it would be the easiest way of doing this...

It depends on what you mean by 

Quote

 change how the move speed works

If there is additional logic to be added I think there is no way other than modifying AiTaskFleeEntity functions (as the logic of other AI stuff calls the flee behavior), but if just changing default parameters would be enough I did some digging:

The behavior/aitasks/fleeentity from json has some params. Unfortunately it seems that these are loaded as JSon objects per entity type (and that is read-only) and can't be modified on the fly. With this code in ModSystem I was able to get the list of all entities with flee behavior:
 

public override void AssetsFinalize(ICoreAPI api)
{
    if (api.Side != EnumAppSide.Server)return;
    foreach (EntityProperties ep in api.World.EntityTypes)
    {
        if(ep.Server.BehaviorsAsJsonObj != null)
        {
            for (int i = 0; i < ep.Server.BehaviorsAsJsonObj.Length; i++)
            {
                JsonObject jobj = ep.Server.BehaviorsAsJsonObj[i];
                string code = jobj["code"].AsString();
                if("taskai" == code)
                {
                    if (null == jobj["aitasks"]) break; // I don't think this should happen, but guard just in case
                    JsonObject[] aitasks = jobj["aitasks"].AsArray();
                    Mod.Logger.Notification("Entity: {0}", ep.Code.ToString());
                    foreach (JsonObject _j in aitasks)
                    {
                        if ("fleeentity" == _j["code"].AsString())
                        {
                            Mod.Logger.Notification("Entity {0}", ep.Code.ToString());
                            Mod.Logger.Notification("Flee move speed: {0}", _j["movespeed"].AsFloat());
                            // _j is read-only and we can't "patch" it's default value :(
                        }
                    }
                }
            }
        }
    }
}

It seems that each the entity behavior has JSON-loaded default config. When entity spawns, this config is loaded and behavior list gets populated. This is problematic for modding without interference with original class, as it'd mean that we'd have to overwrite these values per entity per spawn in real time in game. The performance loss might be inconsequential, but it seems fishy. Patching every single json as you mentioned also feels like a wrong approach.

I'll continue to look at different angles for it, feel free to share any findings.

EDIT: Following on the just-existing-attributes topic, it seems quite simple with harmony to do the following:
 

[HarmonyPostfix]
[HarmonyPatch(typeof(AiTaskFleeEntity), nameof(AiTaskFleeEntity.LoadConfig))]
public static void AfterLoadConfig(ref float ___moveSpeed)
{
___moveSpeed = 2.0f;
}

This does not solve the issue of saving additional data within the class (not sure if this is possible), but it really depends on what you want to do. Existing fields seem to be easily accessible through postfix onLoadConfig harmony patch, and any change to behavior can be probably done by pre/postfixing appropriate methods within the behavior class.

Also, another approach that might be possible to experiment with is to just add additional custom behavior to entity from your own class, and harmony-patch functions that actually call the Flee behavior, to instead call your class instead.

Edited by Orjanek
Posted

I decided to use Harmony and store additional data in a class and associate instances of the AiTaskFleeEntity with instances of the class using a ConditionalWeakTable, like this:

using System.Runtime.CompilerServices;
using Vintagestory.GameContent;

public class ExtendedAiTaskFleeEntity
{
  private static readonly ConditionalWeakTable<AiTaskFleeEntity, ExtendedAiTaskFleeEntity> ExtendedAiTasks = new();
  
  public readonly AiTaskFleeEntity AiTask;
  
  // additional fields...
  
  public ExtendedAiTaskFleeEntity(AiTaskFleeEntity aiTask)
  {
    AiTask = aiTask;
  }
  
  public static ExtendedAiTaskFleeEntity FromOriginal(AiTaskFleeEntity aiTask)
  {
    return ExtendedAiTasks.GetValue(aiTask, key => new ExtendedAiTaskFleeEntity(key));
  }
}

I am then able to use this data in Harmony patches of different methods of AiTaskFleeEntity.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.