mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-13 00:37:52 +00:00
Added LivingMakeBrainEvent, to allow a consistent way for modders to manipulate entity Brains. #9292
This commit is contained in:
1 parent
00ad61b4ae
commit
30d0a630d6
6 files changed
+317
-4
No files matched your search
@@ -35,6 +35,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
@@ -77,6 +78,7 @@ import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.ExperienceOrb;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.ai.attributes.Attribute;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
@@ -128,6 +130,7 @@ import net.minecraftforge.common.loot.IGlobalLootModifier;
|
||||
import net.minecraftforge.common.loot.LootModifierManager;
|
||||
import net.minecraftforge.common.loot.LootTableIdCondition;
|
||||
import net.minecraftforge.common.util.BlockSnapshot;
|
||||
import net.minecraftforge.common.util.BrainBuilder;
|
||||
import net.minecraftforge.common.util.Lazy;
|
||||
import net.minecraftforge.common.util.MavenVersionStringHelper;
|
||||
import net.minecraftforge.event.AnvilUpdateEvent;
|
||||
@@ -160,6 +163,7 @@ import net.minecraftforge.event.entity.living.LivingChangeTargetEvent.ILivingTar
|
||||
import net.minecraftforge.event.entity.living.LivingFallEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingKnockBackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingMakeBrainEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSwapItemsEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingUseTotemEvent;
|
||||
import net.minecraftforge.event.entity.living.LootingLevelEvent;
|
||||
@@ -244,6 +248,14 @@ public class ForgeHooks
|
||||
return event;
|
||||
}
|
||||
|
||||
public static Brain<?> onLivingMakeBrain(LivingEntity entity, Brain<?> originalBrain, Dynamic<?> dynamic)
|
||||
{
|
||||
BrainBuilder<?> brainBuilder = originalBrain.createBuilder();
|
||||
LivingMakeBrainEvent event = new LivingMakeBrainEvent(entity, brainBuilder);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return brainBuilder.makeBrain(dynamic);
|
||||
}
|
||||
|
||||
public static boolean onLivingTick(LivingEntity entity)
|
||||
{
|
||||
return MinecraftForge.EVENT_BUS.post(new LivingTickEvent(entity));
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.util;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus;
|
||||
import net.minecraft.world.entity.ai.sensing.Sensor;
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType;
|
||||
import net.minecraft.world.entity.schedule.Activity;
|
||||
import net.minecraft.world.entity.schedule.Schedule;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* This object is used to encapsulate state found inside a {@link Brain} instance,
|
||||
* to make it easily accessible for modders to manipulate during {@link net.minecraftforge.event.entity.living.LivingMakeBrainEvent}.<br>
|
||||
* <br>
|
||||
* Provided are a variety of getter/setter methods to access and manipulate the encapsulated state.<br>
|
||||
* <br>
|
||||
* Methods marked with "INTENDED FOR INTERNAL USE" are only meant to be used inside:
|
||||
* <ul>
|
||||
* <li>{@link net.minecraftforge.common.ForgeHooks#onLivingMakeBrain(LivingEntity, Brain, Dynamic)}
|
||||
* <li>{@link Brain#createBuilder()}
|
||||
* <li>{@link Brain#copyFromBuilder(BrainBuilder)}
|
||||
* </ul>
|
||||
* Of course, nothing egregious will happen should a modder choose to use them for their own purposes.<br>
|
||||
*/
|
||||
public class BrainBuilder<E extends LivingEntity> {
|
||||
private final Collection<MemoryModuleType<?>> memoryTypes = new HashSet<>();
|
||||
private final Collection<SensorType<? extends Sensor<? super E>>> sensorTypes = new HashSet<>();
|
||||
private final Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> availableBehaviorsByPriority = Maps.newTreeMap();
|
||||
private Schedule schedule = Schedule.EMPTY;
|
||||
private final Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements = Maps.newHashMap();
|
||||
private final Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped = Maps.newHashMap();
|
||||
private final Set<Activity> coreActivities = Sets.newHashSet();
|
||||
private Activity defaultActivity = Activity.IDLE;
|
||||
|
||||
public BrainBuilder(Brain<E> ignoredBrain) {}
|
||||
|
||||
public Brain.Provider<E> provider() {
|
||||
return Brain.provider(this.memoryTypes, this.sensorTypes);
|
||||
}
|
||||
|
||||
public Collection<MemoryModuleType<?>> getMemoryTypes() {
|
||||
return this.memoryTypes;
|
||||
}
|
||||
|
||||
public Collection<SensorType<? extends Sensor<? super E>>> getSensorTypes() {
|
||||
return this.sensorTypes;
|
||||
}
|
||||
|
||||
public Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> getAvailableBehaviorsByPriority() {
|
||||
return this.availableBehaviorsByPriority;
|
||||
}
|
||||
|
||||
public Schedule getSchedule() {
|
||||
return this.schedule;
|
||||
}
|
||||
|
||||
public void setSchedule(Schedule schedule) {
|
||||
this.schedule = schedule;
|
||||
}
|
||||
|
||||
public Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> getActivityRequirements() {
|
||||
return this.activityRequirements;
|
||||
}
|
||||
|
||||
public Map<Activity, Set<MemoryModuleType<?>>> getActivityMemoriesToEraseWhenStopped() {
|
||||
return this.activityMemoriesToEraseWhenStopped;
|
||||
}
|
||||
|
||||
public Set<Activity> getCoreActivities() {
|
||||
return this.coreActivities;
|
||||
}
|
||||
|
||||
public Activity getDefaultActivity() {
|
||||
return this.defaultActivity;
|
||||
}
|
||||
|
||||
public void setDefaultActivity(Activity defaultActivity) {
|
||||
this.defaultActivity = defaultActivity;
|
||||
}
|
||||
|
||||
/** You may use this as a helper method for adding a behavior to an Activity by priority to an entity's brain. */
|
||||
public void addBehaviorToActivityByPriority(Integer priority, Activity activity, BehaviorControl<? super E> behaviorControl) {
|
||||
this.availableBehaviorsByPriority.computeIfAbsent(priority, (i) -> Maps.newHashMap()).computeIfAbsent(activity, (a) -> Sets.newLinkedHashSet()).add(behaviorControl);
|
||||
}
|
||||
|
||||
/** You may use this as a helper method for adding memory requirements for an Activity to an entity's brain. */
|
||||
public void addRequirementsToActivity(Activity activity, Collection<Pair<MemoryModuleType<?>, MemoryStatus>> requirements) {
|
||||
addRequirementsToActivityInternal(this.activityRequirements, activity, requirements);
|
||||
}
|
||||
|
||||
/** You may use this as a helper method for adding a collection of memories to erase when an Activity is stopped to entity's brain. */
|
||||
public void addMemoriesToEraseWhenActivityStopped(Activity activity, Collection<MemoryModuleType<?>> memories) {
|
||||
addMemoriesToEraseWhenActivityStoppedInternal(this.activityMemoriesToEraseWhenStopped, activity, memories);
|
||||
}
|
||||
|
||||
//====================================================================================================================//
|
||||
// Everything below this is just to implement Brain.createBuilder and Brain.copyFromBuilder, hence marked as internal //
|
||||
//====================================================================================================================//
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addAvailableBehaviorsByPriorityFrom(Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> addFrom) {
|
||||
addFrom.forEach(((priority, activitySetMap) -> activitySetMap.forEach(((activity, behaviorControls) -> this.availableBehaviorsByPriority.computeIfAbsent(priority, (p) -> Maps.newHashMap()).computeIfAbsent(activity, (a) -> Sets.newLinkedHashSet()).addAll(behaviorControls)))));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addAvailableBehaviorsByPriorityTo(Map<Integer, Map<Activity, Set<BehaviorControl<? super E>>>> addTo){
|
||||
this.availableBehaviorsByPriority.forEach(((priority, activitySetMap) -> activitySetMap.forEach(((activity, behaviorControls) -> addTo.computeIfAbsent(priority, (p) -> Maps.newHashMap()).computeIfAbsent(activity, (a) -> Sets.newLinkedHashSet()).addAll(behaviorControls)))));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addActivityRequirementsFrom(Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> addFrom) {
|
||||
addFrom.forEach(this::addRequirementsToActivity);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addActivityRequirementsTo(Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> addTo) {
|
||||
this.activityRequirements.forEach(((activity, requirements) -> addRequirementsToActivityInternal(addTo, activity, requirements)));
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addActivityMemoriesToEraseWhenStoppedFrom(Map<Activity, Set<MemoryModuleType<?>>> addFrom) {
|
||||
addFrom.forEach(this::addMemoriesToEraseWhenActivityStopped);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void addActivityMemoriesToEraseWhenStoppedTo(Map<Activity, Set<MemoryModuleType<?>>> addTo) {
|
||||
this.activityMemoriesToEraseWhenStopped.forEach(((activity, memories) -> addMemoriesToEraseWhenActivityStoppedInternal(addTo, activity, memories)));
|
||||
}
|
||||
|
||||
private static void addMemoriesToEraseWhenActivityStoppedInternal(Map<Activity, Set<MemoryModuleType<?>>> activityMemoriesToEraseWhenStopped, Activity activity, Collection<MemoryModuleType<?>> memories) {
|
||||
activityMemoriesToEraseWhenStopped.computeIfAbsent(activity, (a) -> Sets.newHashSet()).addAll(memories);
|
||||
}
|
||||
|
||||
private static void addRequirementsToActivityInternal(Map<Activity, Set<Pair<MemoryModuleType<?>, MemoryStatus>>> activityRequirements, Activity activity, Collection<Pair<MemoryModuleType<?>, MemoryStatus>> requirements) {
|
||||
activityRequirements.computeIfAbsent(activity, (a) -> Sets.newHashSet()).addAll(requirements);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public Brain<E> makeBrain(Dynamic<?> dynamic) {
|
||||
Brain<E> brain = Brain.provider(this.memoryTypes, this.sensorTypes).makeBrain(dynamic);
|
||||
brain.copyFromBuilder(this);
|
||||
return brain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.event.entity.living;
|
||||
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.Brain;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.BrainBuilder;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
|
||||
/**
|
||||
* LivingMakeBrainEvent is fired whenever a new {@link net.minecraft.world.entity.ai.Brain} instance is created using {@link LivingEntity#makeBrain(Dynamic)}.<br>
|
||||
* <br>
|
||||
* To access the internal {@link BrainBuilder}, call {@link LivingMakeBrainEvent#getTypedBrainBuilder(LivingEntity)} using the downcasted {@link LivingEntity} obtained from {@link LivingEvent#getEntity()}.<br>
|
||||
* <br>
|
||||
* The BrainBuilder will initially contain all the state found in the original Brain instance.<br>
|
||||
* <br>
|
||||
* After this event is posted, a fresh Brain instance will be created using the encapsulated state found in the BrainBuilder
|
||||
* and replace the previously created Brain instance for the entity.<br>
|
||||
* <br>
|
||||
* This event is fired via the {@link ForgeHooks#onLivingMakeBrain(LivingEntity, Brain, Dynamic)}.<br>
|
||||
* <br>
|
||||
* This event is not {@link Cancelable}.<br>
|
||||
* <br>
|
||||
* This event does not have a result. {@link HasResult}<br>
|
||||
* <br>
|
||||
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
|
||||
**/
|
||||
public class LivingMakeBrainEvent extends LivingEvent
|
||||
{
|
||||
private final BrainBuilder<?> brainBuilder;
|
||||
|
||||
public LivingMakeBrainEvent(LivingEntity entity, BrainBuilder<?> brainBuilder)
|
||||
{
|
||||
super(entity);
|
||||
this.brainBuilder = brainBuilder;
|
||||
}
|
||||
|
||||
public <E extends LivingEntity> BrainBuilder<E> getTypedBrainBuilder(E ignoredEntity)
|
||||
{
|
||||
return (BrainBuilder<E>) this.brainBuilder;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user