Jump to content

[1.16.3]I want to change the AI of an existing mob


KGJP

Recommended Posts

Assumptions and what we want to achieve


If you can change or delete the goals added to the goalSelector and targetSelector, you can solve this problem.
(See below for targetSelector only)

What I tried below is an example: "Erase the movement of existing zombies from trying to target the player

 

What I've tried

1. reassign the new instance to targetSelector

   public final GoalSelector goalSelector;
   public final GoalSelector targetSelector;

→ Can't do it for final.

 

 

2. writing code to eliminate a specific goal

  @SubscribeEvent
  public static void mobEvent(LivingSpawnEvent event) {

    if (!(event.getEntity() instanceof ZombieEntity)) {
      return;
    }
    ZombieEntity zombie = (ZombieEntity) event.getEntity();
    Stream<PrioritizedGoal> pgStream = zombie.targetSelector.getRunningGoals()
        .filter(prioritizedGoal -> prioritizedGoal.getPriority() == 2);
    Optional<PrioritizedGoal> prioritizedGoal = pgStream.findAny();
    if (prioritizedGoal.isPresent()) {
      zombie.targetSelector.removeGoal(prioritizedGoal.get().getGoal());
    }

targetSelector.removeGoal is provided, but it has to be an instance of the target.
The goal the zombie targets the player with is set in PrioritizedGoal's Priority 2.
The only way to get the existing goal is with targetSelector.getRunningGoals(), so the moment RunningGoal becomes Priority 2 (the moment it tries to target the player), it's forced to remove the Priority 2 goal. code

 

supplementary information

minecraft version 1.16.3
minecraft forge version 1.16.3

 

I have looked into various things and tried, but I can't seem to solve the problem and I am having trouble.
Please help me out.

What is the best practice coding for overwriting or changing the AI of an existing MOB?

Edited by KGJP
Link to comment
Share on other sites

31 minutes ago, KGJP said:

but it has to be an instance of the target

Yes, if you want to edit an entity's AI, you should add or remove goals within EntityJoinWorldEvent. This will need to be done for every instance a zombie spawns, so this is necessary. However, you do need to grab all goals.

31 minutes ago, KGJP said:

Can't do it for final.

31 minutes ago, KGJP said:

The only way to get the existing goal is with targetSelector.getRunningGoals()

Both of these are incorrect statements. You should take a look into Reflection or Access Transformers and apply what you know to what you need.

Link to comment
Share on other sites

1 hour ago, ChampionAsh5357 said:

Yes, if you want to edit an entity's AI, you should add or remove goals within EntityJoinWorldEvent. This will need to be done for every instance a zombie spawns, so this is necessary. However, you do need to grab all goals.

Both of these are incorrect statements. You should take a look into Reflection or Access Transformers and apply what you know to what you need.

jar {
    manifest {
        attributes([
                "Specification-Title"     : "examplemod",
                "Specification-Vendor"    : "examplemodsareus",
                "Specification-Version"   : "1", // We are version 1 of ourselves
                "Implementation-Title"    : project.name,
                "Implementation-Version"  : "${version}",
                "Implementation-Vendor"   : "examplemodsareus",
                "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
        ])
        accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
    }
}
public-f net.minecraft.entity.ai.goal.GoalSelector goals

I decided to use Access Transformer.

But it doesn't take a gradle build.

 

org.gradle.api.GradleScriptException: A problem occurred evaluating root project 'MyMod'.
Caused by: groovy.lang.MissingPropertyException: Could not set unknown property 'accessTransformer' for object of type org.gradle.api.java.archives.internal.DefaultManifest.
    at org.gradle.internal.metaobject.AbstractDynamicObject.setMissingProperty(AbstractDynamicObject.java:118)
    at org.gradle.internal.metaobject.ConfigureDelegate.setProperty(ConfigureDelegate.java:105)
 

Link to comment
Share on other sites

http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html

You can do this kind of hack-like coding with Java features.
I'd like to try it with this.

 

  private static Field privateFieldGoals;

  static {
    try {
      privateFieldGoals = GoalSelector.class.getDeclaredField("goals");
      privateFieldGoals.setAccessible(true);
    } catch (NoSuchFieldException e) {
      System.err.println("Error");
    }
  }

 

Link to comment
Share on other sites

7 minutes ago, ChampionAsh5357 said:

Nope, you moved the line where that was originally written.

You mean you have to describe it at the top?

7 minutes ago, ChampionAsh5357 said:

Using reflection requires you to make use of ObfuscationReflectionHelper as you need to supply the srg name.

      Set<PrioritizedGoal> goals = (Set) privateFieldGoals.get(zombie.targetSelector);

      goals.removeAll(goals.stream()
          .filter(prioritizedGoal -> prioritizedGoal.getPriority() == 2)
          .collect(Collectors.toList()));

It worked with the code I wrote.
What is the benefit of using the ObfuscationReflectionHelper that Forge provides?

Edited by KGJP
Link to comment
Share on other sites

1 minute ago, KGJP said:

What is the benefit of using the ObfuscationReflectionHelper that Forge provides?

It will not work outside the dev workspace since the field name is obfuscated. This means that if you try and call "goal", it will not exist at runtime. Therefore, you need to grab the associated srg name. Since the name is mapped in the dev workspace however, this will also throw an error. Enter ObfuscationReflectionHelper. This takes in a srg name and maps it to the current environment you are running on. This way it will work in and out of dev.

Link to comment
Share on other sites

3 minutes ago, ChampionAsh5357 said:

It will not work outside the dev workspace since the field name is obfuscated. This means that if you try and call "goal", it will not exist at runtime. Therefore, you need to grab the associated srg name. Since the name is mapped in the dev workspace however, this will also throw an error. Enter ObfuscationReflectionHelper. This takes in a srg name and maps it to the current environment you are running on. This way it will work in and out of dev.

I see, so you're saying that what you're running now is the runClient development environment?
So when you write it out to a jar file, the current code won't work?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.