1149 lines
43 KiB
Java
1149 lines
43 KiB
Java
package com.hypixel.hytale.server.npc.role;
|
|
|
|
import com.hypixel.hytale.common.util.ArrayUtil;
|
|
import com.hypixel.hytale.component.CommandBuffer;
|
|
import com.hypixel.hytale.component.ComponentAccessor;
|
|
import com.hypixel.hytale.component.ComponentType;
|
|
import com.hypixel.hytale.component.Holder;
|
|
import com.hypixel.hytale.component.Ref;
|
|
import com.hypixel.hytale.component.Store;
|
|
import com.hypixel.hytale.math.random.RandomExtra;
|
|
import com.hypixel.hytale.math.shape.Box;
|
|
import com.hypixel.hytale.math.util.TrigMathUtil;
|
|
import com.hypixel.hytale.math.vector.Vector3d;
|
|
import com.hypixel.hytale.protocol.BlockMaterial;
|
|
import com.hypixel.hytale.protocol.MovementStates;
|
|
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
|
|
import com.hypixel.hytale.server.core.inventory.Inventory;
|
|
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
|
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
|
|
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
|
|
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
|
|
import com.hypixel.hytale.server.core.modules.item.ItemModule;
|
|
import com.hypixel.hytale.server.core.modules.splitvelocity.VelocityConfig;
|
|
import com.hypixel.hytale.server.core.universe.world.World;
|
|
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
|
import com.hypixel.hytale.server.npc.NPCPlugin;
|
|
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
|
|
import com.hypixel.hytale.server.npc.entities.NPCEntity;
|
|
import com.hypixel.hytale.server.npc.instructions.BodyMotion;
|
|
import com.hypixel.hytale.server.npc.instructions.Instruction;
|
|
import com.hypixel.hytale.server.npc.movement.GroupSteeringAccumulator;
|
|
import com.hypixel.hytale.server.npc.movement.Steering;
|
|
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
|
|
import com.hypixel.hytale.server.npc.movement.steeringforces.SteeringForceAvoidCollision;
|
|
import com.hypixel.hytale.server.npc.role.builders.BuilderRole;
|
|
import com.hypixel.hytale.server.npc.role.support.CombatSupport;
|
|
import com.hypixel.hytale.server.npc.role.support.DebugSupport;
|
|
import com.hypixel.hytale.server.npc.role.support.EntitySupport;
|
|
import com.hypixel.hytale.server.npc.role.support.MarkedEntitySupport;
|
|
import com.hypixel.hytale.server.npc.role.support.PositionCache;
|
|
import com.hypixel.hytale.server.npc.role.support.RoleStats;
|
|
import com.hypixel.hytale.server.npc.role.support.StateSupport;
|
|
import com.hypixel.hytale.server.npc.role.support.WorldSupport;
|
|
import com.hypixel.hytale.server.npc.statetransition.StateTransitionController;
|
|
import com.hypixel.hytale.server.npc.util.ComponentInfo;
|
|
import com.hypixel.hytale.server.npc.util.IAnnotatedComponent;
|
|
import com.hypixel.hytale.server.npc.util.IAnnotatedComponentCollection;
|
|
import com.hypixel.hytale.server.npc.util.InventoryHelper;
|
|
import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.function.Supplier;
|
|
import java.util.logging.Level;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class Role implements IAnnotatedComponentCollection {
|
|
public static final double INTERACTION_PLAYER_DISTANCE = 10.0;
|
|
public static final boolean DEBUG_APPLIED_FORCES = false;
|
|
@Nonnull
|
|
protected final CombatSupport combatSupport;
|
|
@Nonnull
|
|
protected final StateSupport stateSupport;
|
|
@Nonnull
|
|
protected final MarkedEntitySupport markedEntitySupport;
|
|
@Nonnull
|
|
protected final WorldSupport worldSupport;
|
|
@Nonnull
|
|
protected final EntitySupport entitySupport;
|
|
@Nonnull
|
|
protected final PositionCache positionCache;
|
|
@Nonnull
|
|
protected final DebugSupport debugSupport;
|
|
protected final int initialMaxHealth;
|
|
protected final double collisionProbeDistance;
|
|
protected final double collisionRadius;
|
|
protected final double collisionForceFalloff;
|
|
protected final float collisionViewAngle;
|
|
protected final float collisionViewHalfAngleCosine;
|
|
protected final Steering bodySteering = new Steering();
|
|
protected final Steering headSteering = new Steering();
|
|
protected final SteeringForceAvoidCollision steeringForceAvoidCollision = new SteeringForceAvoidCollision();
|
|
protected final GroupSteeringAccumulator groupSteeringAccumulator = new GroupSteeringAccumulator();
|
|
protected final Vector3d separation = new Vector3d();
|
|
protected final Set<Ref<EntityStore>> ignoredEntitiesForAvoidance = new HashSet<>();
|
|
protected final double entityAvoidanceStrength;
|
|
protected final Role.AvoidanceMode avoidanceMode;
|
|
protected final boolean isAvoidingEntities;
|
|
protected final double separationDistance;
|
|
protected final double separationWeight;
|
|
protected final double separationDistanceTarget;
|
|
protected final double separationNearRadiusTarget;
|
|
protected final double separationFarRadiusTarget;
|
|
protected final boolean applySeparation;
|
|
protected final Vector3d lastSeparationSteering = new Vector3d();
|
|
protected final boolean stayInEnvironment;
|
|
protected final String allowedEnvironments;
|
|
@Nullable
|
|
protected final String[] flockSpawnTypes;
|
|
protected final boolean flockSpawnTypesRandom;
|
|
@Nonnull
|
|
protected final String[] flockAllowedRoles;
|
|
protected final boolean canLeadFlock;
|
|
protected final double flockWeightAlignment;
|
|
protected final double flockWeightSeparation;
|
|
protected final double flockWeightCohesion;
|
|
protected final double flockInfluenceRange;
|
|
protected final boolean corpseStaysInFlock;
|
|
protected final double inertia;
|
|
protected final double knockbackScale;
|
|
protected final boolean breathesInAir;
|
|
protected final boolean breathesInWater;
|
|
protected final boolean pickupDropOnDeath;
|
|
@Nullable
|
|
protected final String[] hotbarItems;
|
|
@Nullable
|
|
protected final String[] offHandItems;
|
|
protected final double deathAnimationTime;
|
|
protected final float despawnAnimationTime;
|
|
protected final String dropListId;
|
|
@Nullable
|
|
protected final String deathInteraction;
|
|
protected final boolean invulnerable;
|
|
protected final int inventorySlots;
|
|
protected final String inventoryContentsDropList;
|
|
protected final int hotbarSlots;
|
|
protected final int offHandSlots;
|
|
protected final byte defaultOffHandSlot;
|
|
protected final List<Role.DeferredAction> deferredActions = new ObjectArrayList();
|
|
protected final RoleStats roleStats;
|
|
@Nullable
|
|
protected final String balanceAsset;
|
|
@Nullable
|
|
protected final Map<String, String> interactionVars;
|
|
protected int roleIndex;
|
|
protected String roleName;
|
|
protected String appearance;
|
|
protected boolean isActivated;
|
|
@Nonnull
|
|
protected Map<String, MotionController> motionControllers = new HashMap<>();
|
|
protected MotionController activeMotionController;
|
|
protected int[] flockSpawnTypeIndices;
|
|
protected boolean requiresLeashPosition;
|
|
protected boolean hasReachedTerminalAction;
|
|
@Nullable
|
|
protected String[] armor;
|
|
protected boolean[] flags;
|
|
protected Instruction rootInstruction;
|
|
@Nullable
|
|
protected Instruction lastBodyMotionStep;
|
|
@Nullable
|
|
protected Instruction lastHeadMotionStep;
|
|
protected Instruction[] indexedInstructions;
|
|
@Nullable
|
|
protected Instruction interactionInstruction;
|
|
@Nullable
|
|
protected Instruction deathInstruction;
|
|
protected Instruction currentTreeModeStep;
|
|
protected boolean roleChangeRequested;
|
|
protected final boolean isMemory;
|
|
protected final String memoriesNameOverride;
|
|
protected final boolean isMemoriesNameOverriden;
|
|
protected final float spawnLockTime;
|
|
protected final String nameTranslationKey;
|
|
protected boolean backingAway;
|
|
|
|
public Role(@Nonnull BuilderRole builder, @Nonnull BuilderSupport builderSupport) {
|
|
NPCEntity npcComponent = builderSupport.getEntity();
|
|
this.combatSupport = new CombatSupport(npcComponent, builder, builderSupport);
|
|
this.stateSupport = new StateSupport(builder, builderSupport);
|
|
this.markedEntitySupport = new MarkedEntitySupport(npcComponent);
|
|
this.worldSupport = new WorldSupport(npcComponent, builder, builderSupport);
|
|
this.entitySupport = new EntitySupport(npcComponent, builder);
|
|
this.positionCache = new PositionCache(this);
|
|
this.debugSupport = new DebugSupport(npcComponent, builder);
|
|
this.initialMaxHealth = builder.getMaxHealth(builderSupport);
|
|
this.nameTranslationKey = builder.getNameTranslationKey(builderSupport);
|
|
this.appearance = builder.getAppearance(builderSupport);
|
|
this.hotbarItems = builder.getHotbarItems(builderSupport);
|
|
this.offHandItems = builder.getOffHandItems(builderSupport);
|
|
this.defaultOffHandSlot = builder.getDefaultOffHandSlot(builderSupport);
|
|
this.inventoryContentsDropList = builder.getInventoryItemsDropList(builderSupport);
|
|
this.armor = builder.getArmor();
|
|
this.inertia = builder.getInertia();
|
|
|
|
for (MotionController motionController : this.motionControllers.values()) {
|
|
motionController.setInertia(this.inertia);
|
|
}
|
|
|
|
this.knockbackScale = builder.getKnockbackScale();
|
|
|
|
for (MotionController motionController : this.motionControllers.values()) {
|
|
motionController.setKnockbackScale(this.knockbackScale);
|
|
}
|
|
|
|
this.positionCache.setOpaqueBlockSet(builder.getOpaqueBlockSet());
|
|
this.dropListId = builder.getDropListId(builderSupport);
|
|
this.isAvoidingEntities = builder.isAvoidingEntities();
|
|
this.avoidanceMode = builder.getAvoidanceMode();
|
|
this.collisionProbeDistance = builder.getCollisionDistance();
|
|
this.collisionForceFalloff = builder.getCollisionForceFalloff();
|
|
this.collisionRadius = builder.getCollisionRadius();
|
|
this.collisionViewAngle = builder.getCollisionViewAngle();
|
|
this.collisionViewHalfAngleCosine = TrigMathUtil.cos(this.collisionViewAngle / 2.0F);
|
|
this.separationDistance = builder.getSeparationDistance();
|
|
this.separationWeight = builder.getSeparationWeight();
|
|
this.separationDistanceTarget = builder.getSeparationDistanceTarget();
|
|
this.separationNearRadiusTarget = builder.getSeparationNearRadiusTarget();
|
|
this.separationFarRadiusTarget = builder.getSeparationFarRadiusTarget();
|
|
this.applySeparation = builder.isApplySeparation(builderSupport);
|
|
this.stayInEnvironment = builder.isStayingInEnvironment();
|
|
this.allowedEnvironments = builder.getAllowedEnvironments();
|
|
this.entityAvoidanceStrength = builder.getEntityAvoidanceStrength();
|
|
this.flockSpawnTypes = builder.getFlockSpawnTypes(builderSupport);
|
|
this.flockSpawnTypesRandom = builder.isFlockSpawnTypeRandom(builderSupport);
|
|
this.flockAllowedRoles = builder.getFlockAllowedRoles(builderSupport);
|
|
this.canLeadFlock = builder.isCanLeadFlock(builderSupport);
|
|
this.flockWeightAlignment = builder.getFlockWeightAlignment();
|
|
this.flockWeightSeparation = builder.getFlockWeightSeparation();
|
|
this.flockWeightCohesion = builder.getFlockWeightCohesion();
|
|
this.flockInfluenceRange = builder.getFlockInfluenceRange();
|
|
this.invulnerable = builder.isInvulnerable(builderSupport);
|
|
this.breathesInAir = builder.isBreathesInAir(builderSupport);
|
|
this.breathesInWater = builder.isBreathesInWater(builderSupport);
|
|
this.pickupDropOnDeath = builder.isPickupDropOnDeath();
|
|
this.deathAnimationTime = builder.getDeathAnimationTime();
|
|
this.deathInteraction = builder.getDeathInteraction(builderSupport);
|
|
this.despawnAnimationTime = builder.getDespawnAnimationTime();
|
|
this.inventorySlots = builder.getInventorySlots();
|
|
this.hotbarSlots = builder.getHotbarSlots();
|
|
this.offHandSlots = builder.getOffHandSlots();
|
|
this.corpseStaysInFlock = builder.isCorpseStaysInFlock();
|
|
this.roleStats = builderSupport.getRoleStats();
|
|
this.balanceAsset = builder.getBalanceAsset(builderSupport);
|
|
this.interactionVars = builder.getInteractionVars(builderSupport);
|
|
this.isMemory = builder.isMemory(builderSupport.getExecutionContext());
|
|
this.memoriesNameOverride = builder.getMemoriesNameOverride(builderSupport.getExecutionContext());
|
|
this.isMemoriesNameOverriden = this.memoriesNameOverride != null && !this.memoriesNameOverride.isEmpty();
|
|
this.spawnLockTime = builder.getSpawnLockTime(builderSupport);
|
|
this.entitySupport.pickRandomDisplayName(builderSupport.getHolder(), false);
|
|
List<Instruction> instructionList = builder.getInstructionList(builderSupport);
|
|
if (instructionList == null) {
|
|
instructionList = new ObjectArrayList();
|
|
}
|
|
|
|
Instruction[] instructions = instructionList.toArray(Instruction[]::new);
|
|
this.rootInstruction = Instruction.createRootInstruction(instructions, builderSupport);
|
|
this.interactionInstruction = builder.getInteractionInstruction(builderSupport);
|
|
this.deathInstruction = builder.getDeathInstruction(builderSupport);
|
|
builder.registerStateEvaluator(builderSupport);
|
|
this.setMotionControllers(builderSupport.getEntity(), builder.getMotionControllerMap(builderSupport), builder.getInitialMotionController(builderSupport));
|
|
if (this.interactionInstruction != null) {
|
|
builderSupport.trackInteractions();
|
|
}
|
|
}
|
|
|
|
public int getInitialMaxHealth() {
|
|
return this.initialMaxHealth;
|
|
}
|
|
|
|
public boolean isAvoidingEntities() {
|
|
return this.isAvoidingEntities;
|
|
}
|
|
|
|
public double getCollisionProbeDistance() {
|
|
return this.collisionProbeDistance;
|
|
}
|
|
|
|
public boolean isApplySeparation() {
|
|
return this.applySeparation;
|
|
}
|
|
|
|
public double getSeparationDistance() {
|
|
return this.separationDistance;
|
|
}
|
|
|
|
public Instruction getRootInstruction() {
|
|
return this.rootInstruction;
|
|
}
|
|
|
|
@Nullable
|
|
public Instruction getInteractionInstruction() {
|
|
return this.interactionInstruction;
|
|
}
|
|
|
|
@Nullable
|
|
public Instruction getDeathInstruction() {
|
|
return this.deathInstruction;
|
|
}
|
|
|
|
@Nonnull
|
|
public Steering getBodySteering() {
|
|
return this.bodySteering;
|
|
}
|
|
|
|
@Nonnull
|
|
public Steering getHeadSteering() {
|
|
return this.headSteering;
|
|
}
|
|
|
|
@Nonnull
|
|
public Set<Ref<EntityStore>> getIgnoredEntitiesForAvoidance() {
|
|
return this.ignoredEntitiesForAvoidance;
|
|
}
|
|
|
|
public String getDropListId() {
|
|
return this.dropListId;
|
|
}
|
|
|
|
@Nullable
|
|
public String getBalanceAsset() {
|
|
return this.balanceAsset;
|
|
}
|
|
|
|
@Nullable
|
|
public Map<String, String> getInteractionVars() {
|
|
return this.interactionVars;
|
|
}
|
|
|
|
public boolean isMemory() {
|
|
return this.isMemory;
|
|
}
|
|
|
|
public String getMemoriesNameOverride() {
|
|
return this.memoriesNameOverride;
|
|
}
|
|
|
|
public String getNameTranslationKey() {
|
|
return this.nameTranslationKey;
|
|
}
|
|
|
|
public boolean isMemoriesNameOverriden() {
|
|
return this.isMemoriesNameOverriden;
|
|
}
|
|
|
|
public float getSpawnLockTime() {
|
|
return this.spawnLockTime;
|
|
}
|
|
|
|
public void postRoleBuilt(@Nonnull BuilderSupport builderSupport) {
|
|
this.requiresLeashPosition = builderSupport.requiresLeashPosition();
|
|
this.flags = builderSupport.allocateFlags();
|
|
this.indexedInstructions = builderSupport.getInstructionSlotMappings();
|
|
this.stateSupport.postRoleBuilt(builderSupport);
|
|
this.worldSupport.postRoleBuilt(builderSupport);
|
|
this.entitySupport.postRoleBuilt(builderSupport);
|
|
this.markedEntitySupport.postRoleBuilder(builderSupport);
|
|
this.rootInstruction.setContext(this, 0);
|
|
}
|
|
|
|
public void loaded() {
|
|
this.rootInstruction.loaded(this);
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.loaded(this);
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.loaded(this);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.loaded(this);
|
|
}
|
|
}
|
|
|
|
public void spawned(@Nonnull Holder<EntityStore> holder, @Nonnull NPCEntity npcComponent) {
|
|
MotionController activeMotionController = this.getActiveMotionController();
|
|
if (activeMotionController != null) {
|
|
activeMotionController.spawned();
|
|
}
|
|
|
|
this.entitySupport.pickRandomDisplayName(holder, true);
|
|
this.rootInstruction.spawned(this);
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.spawned(this);
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.spawned(this);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.spawned(this);
|
|
}
|
|
|
|
this.initialiseInventories(npcComponent);
|
|
}
|
|
|
|
public void unloaded() {
|
|
this.worldSupport.unloaded();
|
|
this.markedEntitySupport.unloaded();
|
|
this.deferredActions.clear();
|
|
this.rootInstruction.unloaded(this);
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.unloaded(this);
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.unloaded(this);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.unloaded(this);
|
|
}
|
|
}
|
|
|
|
public void removed() {
|
|
this.worldSupport.resetAllBlockSensors();
|
|
this.rootInstruction.removed(this);
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.removed(this);
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.removed(this);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.removed(this);
|
|
}
|
|
}
|
|
|
|
public void teleported(@Nonnull World from, @Nonnull World to) {
|
|
this.rootInstruction.teleported(this, from, to);
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.teleported(this, from, to);
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.teleported(this, from, to);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.teleported(this, from, to);
|
|
}
|
|
}
|
|
|
|
public String getAppearanceName() {
|
|
return this.appearance;
|
|
}
|
|
|
|
public MotionController getActiveMotionController() {
|
|
return this.activeMotionController;
|
|
}
|
|
|
|
@Nonnull
|
|
public CombatSupport getCombatSupport() {
|
|
return this.combatSupport;
|
|
}
|
|
|
|
@Nonnull
|
|
public StateSupport getStateSupport() {
|
|
return this.stateSupport;
|
|
}
|
|
|
|
@Nonnull
|
|
public WorldSupport getWorldSupport() {
|
|
return this.worldSupport;
|
|
}
|
|
|
|
@Nonnull
|
|
public MarkedEntitySupport getMarkedEntitySupport() {
|
|
return this.markedEntitySupport;
|
|
}
|
|
|
|
@Nonnull
|
|
public PositionCache getPositionCache() {
|
|
return this.positionCache;
|
|
}
|
|
|
|
@Nonnull
|
|
public EntitySupport getEntitySupport() {
|
|
return this.entitySupport;
|
|
}
|
|
|
|
@Nonnull
|
|
public DebugSupport getDebugSupport() {
|
|
return this.debugSupport;
|
|
}
|
|
|
|
public boolean isRoleChangeRequested() {
|
|
return this.roleChangeRequested;
|
|
}
|
|
|
|
public void setRoleChangeRequested() {
|
|
this.roleChangeRequested = true;
|
|
}
|
|
|
|
public boolean setActiveMotionController(
|
|
@Nullable Ref<EntityStore> ref, @Nonnull NPCEntity npcComponent, @Nonnull String name, @Nullable ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
MotionController motionController = this.motionControllers.get(name);
|
|
if (motionController == null) {
|
|
NPCPlugin.get()
|
|
.getLogger()
|
|
.at(Level.SEVERE)
|
|
.log("Failed to set MotionController for NPC of type '%s': MotionController '%s' not found! ", this.getRoleName(), name);
|
|
return false;
|
|
} else {
|
|
this.setActiveMotionController(ref, npcComponent, motionController, componentAccessor);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public void setActiveMotionController(
|
|
@Nullable Ref<EntityStore> ref,
|
|
@Nonnull NPCEntity npcComponent,
|
|
@Nonnull MotionController motionController,
|
|
@Nullable ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
if (this.activeMotionController != motionController) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.deactivate();
|
|
}
|
|
|
|
this.activeMotionController = motionController;
|
|
this.activeMotionController.activate();
|
|
this.motionControllerChanged(ref, npcComponent, this.activeMotionController, componentAccessor);
|
|
}
|
|
}
|
|
|
|
protected void motionControllerChanged(
|
|
@Nullable Ref<EntityStore> ref,
|
|
@Nonnull NPCEntity npcComponent,
|
|
@Nullable MotionController motionController,
|
|
@Nullable ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
this.rootInstruction.motionControllerChanged(ref, npcComponent, motionController, componentAccessor);
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.motionControllerChanged(ref, npcComponent, motionController, componentAccessor);
|
|
}
|
|
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.motionControllerChanged(ref, npcComponent, motionController, componentAccessor);
|
|
}
|
|
|
|
StateTransitionController stateTransitions = this.stateSupport.getStateTransitionController();
|
|
if (stateTransitions != null) {
|
|
stateTransitions.motionControllerChanged(ref, npcComponent, motionController, componentAccessor);
|
|
}
|
|
}
|
|
|
|
public void setMotionControllers(
|
|
@Nonnull NPCEntity npcComponent, @Nonnull Map<String, MotionController> motionControllers, @Nullable String initialMotionController
|
|
) {
|
|
this.motionControllers = motionControllers;
|
|
this.updateMotionControllers(null, null, null, null);
|
|
if (!this.motionControllers.isEmpty()) {
|
|
if (initialMotionController != null && this.setActiveMotionController(null, npcComponent, initialMotionController, null)) {
|
|
return;
|
|
}
|
|
|
|
this.setActiveMotionController(null, npcComponent, RandomExtra.randomElement(new ObjectArrayList(motionControllers.values())), null);
|
|
}
|
|
}
|
|
|
|
public void updateMotionControllers(
|
|
@Nullable Ref<EntityStore> ref, @Nullable Model model, @Nullable Box boundingBox, @Nullable ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
for (MotionController motionController : this.motionControllers.values()) {
|
|
motionController.setRole(this);
|
|
motionController.setInertia(this.inertia);
|
|
motionController.setKnockbackScale(this.knockbackScale);
|
|
if (boundingBox != null && model != null) {
|
|
motionController.updateModelParameters(ref, model, boundingBox, componentAccessor);
|
|
motionController.updatePhysicsValues(model.getPhysicsValues());
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updateMovementState(
|
|
@Nonnull Ref<EntityStore> ref,
|
|
@Nonnull MovementStates movementStates,
|
|
@Nonnull Vector3d velocity,
|
|
@Nonnull ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.updateMovementState(ref, movementStates, this.bodySteering, velocity, componentAccessor);
|
|
}
|
|
}
|
|
|
|
public void tick(@Nonnull Ref<EntityStore> ref, float tickTime, @Nonnull Store<EntityStore> store) {
|
|
int i = 0;
|
|
|
|
while (i < this.deferredActions.size()) {
|
|
Role.DeferredAction action = this.deferredActions.get(i);
|
|
if (action.tick(ref, this, tickTime, store)) {
|
|
this.deferredActions.remove(i);
|
|
} else {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
this.computeActionsAndSteering(ref, tickTime, this.bodySteering, this.headSteering, store);
|
|
}
|
|
|
|
public void addDeferredAction(@Nonnull Role.DeferredAction handler) {
|
|
this.deferredActions.add(handler);
|
|
}
|
|
|
|
protected void computeActionsAndSteering(
|
|
@Nonnull Ref<EntityStore> ref, double tickTime, @Nonnull Steering bodySteering, @Nonnull Steering headSteering, @Nonnull Store<EntityStore> store
|
|
) {
|
|
boolean isDead = store.getArchetype(ref).contains(DeathComponent.getComponentType());
|
|
if (isDead) {
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.execute(ref, this, tickTime, store);
|
|
}
|
|
} else {
|
|
if (this.interactionInstruction != null) {
|
|
this.positionCache.forEachPlayer((d, _playerRef, _this, _selfRef, _store) -> {
|
|
_this.stateSupport.setInteractionIterationTarget(_playerRef);
|
|
_this.interactionInstruction.execute(_selfRef, _this, d, _store);
|
|
}, this, ref, store, tickTime, store);
|
|
this.stateSupport.setInteractionIterationTarget(null);
|
|
this.entitySupport.clearTargetPlayerActiveTasks();
|
|
}
|
|
|
|
this.getActiveMotionController().beforeInstructionSensorsAndActions(tickTime);
|
|
this.entitySupport.clearNextBodyMotionStep();
|
|
this.entitySupport.clearNextHeadMotionStep();
|
|
if (!this.stateSupport.runTransitionActions(ref, this, tickTime, store)) {
|
|
this.rootInstruction.execute(ref, this, tickTime, store);
|
|
}
|
|
|
|
NPCEntity npcComponent = store.getComponent(ref, NPCEntity.getComponentType());
|
|
|
|
assert npcComponent != null;
|
|
|
|
if (!npcComponent.isPlayingDespawnAnim()) {
|
|
this.getActiveMotionController().beforeInstructionMotion(tickTime);
|
|
Instruction nextBodyMotionStep = this.entitySupport.getNextBodyMotionStep();
|
|
if (nextBodyMotionStep != this.lastBodyMotionStep) {
|
|
if (this.lastBodyMotionStep != null) {
|
|
this.lastBodyMotionStep.getBodyMotion().deactivate(ref, this, store);
|
|
this.lastBodyMotionStep.onEndMotion();
|
|
}
|
|
|
|
if (nextBodyMotionStep != null) {
|
|
nextBodyMotionStep.getBodyMotion().activate(ref, this, store);
|
|
}
|
|
}
|
|
|
|
this.lastBodyMotionStep = nextBodyMotionStep;
|
|
Instruction nextHeadMotionStep = this.entitySupport.getNextHeadMotionStep();
|
|
if (nextHeadMotionStep != this.lastHeadMotionStep) {
|
|
if (this.lastHeadMotionStep != null) {
|
|
this.lastHeadMotionStep.getHeadMotion().deactivate(ref, this, store);
|
|
this.lastHeadMotionStep.onEndMotion();
|
|
}
|
|
|
|
if (nextHeadMotionStep != null) {
|
|
nextHeadMotionStep.getHeadMotion().activate(ref, this, store);
|
|
}
|
|
}
|
|
|
|
this.lastHeadMotionStep = nextHeadMotionStep;
|
|
if (nextBodyMotionStep != null) {
|
|
nextBodyMotionStep.getBodyMotion().computeSteering(ref, this, nextBodyMotionStep.getSensor().getSensorInfo(), tickTime, bodySteering, store);
|
|
}
|
|
|
|
if (nextHeadMotionStep != null) {
|
|
nextHeadMotionStep.getHeadMotion().computeSteering(ref, this, nextHeadMotionStep.getSensor().getSensorInfo(), tickTime, headSteering, store);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void blendSeparation(
|
|
@Nonnull Ref<EntityStore> selfRef,
|
|
@Nonnull Vector3d position,
|
|
@Nonnull Steering steering,
|
|
@Nonnull ComponentType<EntityStore, TransformComponent> transformComponentType,
|
|
@Nonnull CommandBuffer<EntityStore> commandBuffer
|
|
) {
|
|
this.lastSeparationSteering.assign(Vector3d.ZERO);
|
|
double maxRange = this.separationDistance;
|
|
Ref<EntityStore> targetRef = this.markedEntitySupport.getTargetReferenceToIgnoreForAvoidance();
|
|
if (targetRef != null && targetRef.isValid()) {
|
|
TransformComponent targetTransformComponent = commandBuffer.getComponent(targetRef, transformComponentType);
|
|
|
|
assert targetTransformComponent != null;
|
|
|
|
double distance = targetTransformComponent.getPosition().distanceSquaredTo(position);
|
|
if (distance <= this.separationNearRadiusTarget * this.separationNearRadiusTarget) {
|
|
maxRange = this.separationDistanceTarget;
|
|
} else if (distance < this.separationFarRadiusTarget * this.separationFarRadiusTarget) {
|
|
double s = (Math.sqrt(distance) - this.separationNearRadiusTarget) / (this.separationFarRadiusTarget - this.separationNearRadiusTarget);
|
|
maxRange = NPCPhysicsMath.lerp(this.separationDistanceTarget, this.separationDistance, s);
|
|
}
|
|
}
|
|
|
|
this.groupSteeringAccumulator.setComponentSelector(this.activeMotionController.getComponentSelector());
|
|
this.groupSteeringAccumulator.setMaxRange(maxRange);
|
|
this.groupSteeringAccumulator.setViewConeHalfAngleCosine(this.collisionViewHalfAngleCosine);
|
|
this.groupSteeringAccumulator.begin(selfRef, commandBuffer);
|
|
this.positionCache
|
|
.forEachEntityInAvoidanceRange(
|
|
this.ignoredEntitiesForAvoidance,
|
|
(ref, _groupSteeringAccumulator, _role, _buffer) -> _groupSteeringAccumulator.processEntity(ref, this.separationWeight, 1.0, 1.0, _buffer),
|
|
this.groupSteeringAccumulator,
|
|
this,
|
|
commandBuffer
|
|
);
|
|
this.groupSteeringAccumulator.end();
|
|
if (this.groupSteeringAccumulator.getCount() > 0) {
|
|
Vector3d sumOfDistances = this.groupSteeringAccumulator.getSumOfDistances();
|
|
if (sumOfDistances.squaredLength() > 1.0000000000000002E-10) {
|
|
double speed = steering.getSpeed();
|
|
this.separation.assign(sumOfDistances).setLength(-0.5);
|
|
this.lastSeparationSteering.assign(this.separation);
|
|
if (speed > 0.0) {
|
|
this.separation.add(steering.getTranslation());
|
|
this.separation.setLength(speed);
|
|
}
|
|
|
|
steering.setTranslation(this.separation);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
public Vector3d getLastSeparationSteering() {
|
|
return this.lastSeparationSteering;
|
|
}
|
|
|
|
public void blendAvoidance(
|
|
@Nonnull Ref<EntityStore> ref, @Nonnull Vector3d position, @Nonnull Steering steering, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
|
) {
|
|
this.steeringForceAvoidCollision.setDebug(this.debugSupport.isDebugRoleSteering());
|
|
this.steeringForceAvoidCollision.setAvoidanceMode(this.getAvoidanceMode());
|
|
this.steeringForceAvoidCollision.setSelf(ref, position, commandBuffer);
|
|
if (!this.activeMotionController.estimateVelocity(steering, this.steeringForceAvoidCollision.getSelfVelocity())) {
|
|
this.steeringForceAvoidCollision.setVelocityFromEntity(ref, commandBuffer);
|
|
}
|
|
|
|
if (this.collisionRadius >= 0.0) {
|
|
this.steeringForceAvoidCollision.setSelfRadius(this.collisionRadius);
|
|
}
|
|
|
|
this.steeringForceAvoidCollision.setMaxDistance(this.collisionProbeDistance);
|
|
this.steeringForceAvoidCollision.setFalloff(this.collisionForceFalloff);
|
|
this.steeringForceAvoidCollision.setComponentSelector(this.activeMotionController.getComponentSelector());
|
|
this.steeringForceAvoidCollision.reset();
|
|
this.positionCache
|
|
.forEachEntityInAvoidanceRange(
|
|
this.ignoredEntitiesForAvoidance,
|
|
(_ref, _steeringForceAvoidCollision, _buffer) -> _steeringForceAvoidCollision.add(_ref, _buffer),
|
|
this.steeringForceAvoidCollision,
|
|
commandBuffer
|
|
);
|
|
this.steeringForceAvoidCollision.compute(steering);
|
|
}
|
|
|
|
@Nonnull
|
|
public Vector3d getLastAvoidanceSteering() {
|
|
return this.steeringForceAvoidCollision.getLastSteeringDirection();
|
|
}
|
|
|
|
public void resetInstruction(int instruction) {
|
|
this.indexedInstructions[instruction].reset();
|
|
}
|
|
|
|
public String getRoleName() {
|
|
return this.roleName;
|
|
}
|
|
|
|
public int getRoleIndex() {
|
|
return this.roleIndex;
|
|
}
|
|
|
|
public void setRoleIndex(int roleIndex, @Nonnull String roleName) {
|
|
this.roleIndex = roleIndex;
|
|
this.roleName = roleName;
|
|
}
|
|
|
|
public boolean isInvulnerable() {
|
|
return this.invulnerable;
|
|
}
|
|
|
|
public boolean isBreathesInAir() {
|
|
return this.breathesInAir;
|
|
}
|
|
|
|
public boolean isBreathesInWater() {
|
|
return this.breathesInWater;
|
|
}
|
|
|
|
public double getInertia() {
|
|
return this.inertia;
|
|
}
|
|
|
|
public double getKnockbackScale() {
|
|
return this.knockbackScale;
|
|
}
|
|
|
|
public boolean canBreathe(@Nonnull BlockMaterial breathingMaterial, int fluidId) {
|
|
return this.isInvulnerable() ? true : this.couldBreathe(breathingMaterial, fluidId);
|
|
}
|
|
|
|
public boolean couldBreathe(@Nonnull BlockMaterial breathingMaterial, int fluidId) {
|
|
if (fluidId != 0) {
|
|
return this.breathesInWater;
|
|
} else {
|
|
return breathingMaterial == BlockMaterial.Empty ? this.breathesInAir : false;
|
|
}
|
|
}
|
|
|
|
public boolean couldBreatheCached() {
|
|
return this.positionCache.couldBreatheCached();
|
|
}
|
|
|
|
public void addForce(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.addForce(velocity, velocityConfig);
|
|
}
|
|
}
|
|
|
|
public void forceVelocity(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig, boolean ignoreDamping) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.forceVelocity(velocity, velocityConfig, ignoreDamping);
|
|
}
|
|
}
|
|
|
|
public void processAddVelocityInstruction(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.addForce(velocity, velocityConfig);
|
|
}
|
|
}
|
|
|
|
public void processSetVelocityInstruction(@Nonnull Vector3d velocity, @Nullable VelocityConfig velocityConfig) {
|
|
if (this.activeMotionController != null) {
|
|
this.activeMotionController.forceVelocity(Vector3d.ZERO, null, false);
|
|
this.activeMotionController.addForce(velocity, velocityConfig);
|
|
}
|
|
}
|
|
|
|
public boolean isOnGround() {
|
|
return this.getActiveMotionController() != null && this.getActiveMotionController().onGround();
|
|
}
|
|
|
|
public void setArmor(@Nonnull NPCEntity npcComponent, @Nullable String[] armor) {
|
|
this.armor = armor;
|
|
if (armor != null) {
|
|
for (String s : armor) {
|
|
RoleUtils.setArmor(npcComponent, s);
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isPickupDropOnDeath() {
|
|
return this.pickupDropOnDeath;
|
|
}
|
|
|
|
public boolean requiresLeashPosition() {
|
|
return this.requiresLeashPosition;
|
|
}
|
|
|
|
public void clearOnce() {
|
|
this.rootInstruction.clearOnce();
|
|
if (this.interactionInstruction != null) {
|
|
this.interactionInstruction.clearOnce();
|
|
}
|
|
|
|
if (this.deathInstruction != null) {
|
|
this.deathInstruction.clearOnce();
|
|
}
|
|
|
|
this.stateSupport.pollNeedClearOnce();
|
|
}
|
|
|
|
public void clearOnceIfNeeded() {
|
|
if (this.stateSupport.pollNeedClearOnce()) {
|
|
this.clearOnce();
|
|
this.stateSupport.resetLocalStateMachines();
|
|
}
|
|
}
|
|
|
|
public void setMarkedTarget(@Nonnull String targetSlot, @Nonnull Ref<EntityStore> target) {
|
|
this.markedEntitySupport.setMarkedEntity(targetSlot, target);
|
|
}
|
|
|
|
public boolean isFriendly(@Nonnull Ref<EntityStore> ref, @Nonnull ComponentAccessor<EntityStore> componentAccessor) {
|
|
return !this.combatSupport.getCanCauseDamage(ref, componentAccessor);
|
|
}
|
|
|
|
public boolean isIgnoredForAvoidance(@Nonnull Ref<EntityStore> entityReference) {
|
|
return this.ignoredEntitiesForAvoidance.contains(entityReference);
|
|
}
|
|
|
|
public Role.AvoidanceMode getAvoidanceMode() {
|
|
return this.avoidanceMode;
|
|
}
|
|
|
|
public double getCollisionRadius() {
|
|
return this.collisionRadius;
|
|
}
|
|
|
|
public int[] getFlockSpawnTypes() {
|
|
if (this.flockSpawnTypeIndices != null) {
|
|
return this.flockSpawnTypeIndices;
|
|
} else {
|
|
int length = this.flockSpawnTypes == null ? 0 : this.flockSpawnTypes.length;
|
|
this.flockSpawnTypeIndices = new int[length];
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
int index = NPCPlugin.get().getIndex(this.flockSpawnTypes[i]);
|
|
if (index == Integer.MIN_VALUE) {
|
|
throw new IllegalStateException(String.format("Role %s contains unknown FlockSpawnTypes NPC %s", this.roleName, this.flockSpawnTypes[i]));
|
|
}
|
|
|
|
this.flockSpawnTypeIndices[i] = index;
|
|
}
|
|
|
|
return this.flockSpawnTypeIndices;
|
|
}
|
|
}
|
|
|
|
@Nonnull
|
|
public String[] getFlockAllowedRoles() {
|
|
return this.flockAllowedRoles != null ? Arrays.copyOf(this.flockAllowedRoles, this.flockAllowedRoles.length) : ArrayUtil.EMPTY_STRING_ARRAY;
|
|
}
|
|
|
|
public boolean isFlockSpawnTypesRandom() {
|
|
return this.flockSpawnTypesRandom;
|
|
}
|
|
|
|
public boolean isCanLeadFlock() {
|
|
return this.canLeadFlock;
|
|
}
|
|
|
|
public double getFlockInfluenceRange() {
|
|
return this.flockInfluenceRange;
|
|
}
|
|
|
|
public double getDeathAnimationTime() {
|
|
return this.deathAnimationTime;
|
|
}
|
|
|
|
@Nullable
|
|
public String getDeathInteraction() {
|
|
return this.deathInteraction;
|
|
}
|
|
|
|
public float getDespawnAnimationTime() {
|
|
return this.despawnAnimationTime;
|
|
}
|
|
|
|
public void setReachedTerminalAction(boolean hasReached) {
|
|
this.hasReachedTerminalAction = hasReached;
|
|
}
|
|
|
|
public boolean hasReachedTerminalAction() {
|
|
return this.hasReachedTerminalAction;
|
|
}
|
|
|
|
public void setFlag(int index, boolean value) {
|
|
if (this.flags == null) {
|
|
throw new NullPointerException(String.format("Trying to set a flag in role %s but flags are null", this.getRoleName()));
|
|
} else if (index >= 0 && index < this.flags.length) {
|
|
this.flags[index] = value;
|
|
} else {
|
|
throw new IllegalArgumentException(
|
|
String.format("Flag value cannot be less than 0 and must be less than array length %s. Value was %s", this.flags.length, index)
|
|
);
|
|
}
|
|
}
|
|
|
|
public boolean isFlagSet(int index) {
|
|
return this.flags != null && index >= 0 && index < this.flags.length ? this.flags[index] : false;
|
|
}
|
|
|
|
public boolean isBackingAway() {
|
|
return this.backingAway;
|
|
}
|
|
|
|
public void setBackingAway(boolean backingAway) {
|
|
this.backingAway = backingAway;
|
|
}
|
|
|
|
public Instruction swapTreeModeSteps(Instruction newStep) {
|
|
Instruction old = this.currentTreeModeStep;
|
|
this.currentTreeModeStep = newStep;
|
|
return old;
|
|
}
|
|
|
|
public void notifySensorMatch() {
|
|
if (this.currentTreeModeStep != null) {
|
|
this.currentTreeModeStep.notifyChildSensorMatch();
|
|
}
|
|
}
|
|
|
|
public void resetAllInstructions() {
|
|
for (Instruction instruction : this.indexedInstructions) {
|
|
instruction.reset();
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public String getSteeringMotionName() {
|
|
if (this.lastBodyMotionStep == null) {
|
|
return null;
|
|
} else {
|
|
BodyMotion motion = this.lastBodyMotionStep.getBodyMotion();
|
|
if (motion != null) {
|
|
motion = motion.getSteeringMotion();
|
|
}
|
|
|
|
return motion == null ? null : motion.getClass().getSimpleName();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int componentCount() {
|
|
return 1;
|
|
}
|
|
|
|
@Override
|
|
public IAnnotatedComponent getComponent(int index) {
|
|
return this.rootInstruction;
|
|
}
|
|
|
|
@Override
|
|
public void getInfo(Role role, ComponentInfo holder) {
|
|
}
|
|
|
|
@Override
|
|
public int getIndex() {
|
|
throw new UnsupportedOperationException("Roles do not have component indexes!");
|
|
}
|
|
|
|
@Override
|
|
public void setContext(IAnnotatedComponent parent, int index) {
|
|
throw new UnsupportedOperationException("Roles do not have parent contexts!");
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public IAnnotatedComponent getParent() {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public String getLabel() {
|
|
return this.roleName;
|
|
}
|
|
|
|
private void initialiseInventories(@Nonnull NPCEntity npcComponent) {
|
|
List<ItemStack> inventoryItems = null;
|
|
if (this.inventoryContentsDropList != null) {
|
|
ItemModule itemModule = ItemModule.get();
|
|
if (itemModule.isEnabled()) {
|
|
inventoryItems = itemModule.getRandomItemDrops(this.inventoryContentsDropList);
|
|
}
|
|
}
|
|
|
|
int inventorySlots = inventoryItems != null && inventoryItems.size() > this.inventorySlots ? inventoryItems.size() : this.inventorySlots;
|
|
if (inventorySlots > 0 || this.hotbarSlots > 3 || this.offHandSlots > 0) {
|
|
npcComponent.setInventorySize(this.hotbarSlots, inventorySlots, this.offHandSlots);
|
|
}
|
|
|
|
if (inventoryItems != null) {
|
|
ItemContainer inventory = npcComponent.getInventory().getStorage();
|
|
|
|
for (ItemStack item : inventoryItems) {
|
|
inventory.addItemStack(item);
|
|
}
|
|
}
|
|
|
|
if (this.hotbarItems != null && this.hotbarItems.length > 0 && npcComponent.getInventory().getHotbar().isEmpty()) {
|
|
Inventory inventory = npcComponent.getInventory();
|
|
ItemContainer hotbar = inventory.getHotbar();
|
|
|
|
for (byte i = 0; i < this.hotbarItems.length; i++) {
|
|
if (this.hotbarItems[i] != null) {
|
|
if (this.hotbarItems[i].startsWith("Droplist:")) {
|
|
if (!InventoryHelper.checkHotbarSlot(inventory, i)) {
|
|
continue;
|
|
}
|
|
|
|
List<ItemStack> items = ItemModule.get().getRandomItemDrops(this.hotbarItems[i].substring("Droplist:".length()));
|
|
hotbar.setItemStackForSlot(i, items.get(RandomExtra.randomRange(items.size())));
|
|
}
|
|
|
|
InventoryHelper.setHotbarItem(inventory, this.hotbarItems[i], i);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.offHandItems != null && this.offHandItems.length > 0) {
|
|
RoleUtils.setOffHandItems(npcComponent, this.offHandItems);
|
|
}
|
|
|
|
if (this.defaultOffHandSlot >= 0) {
|
|
InventoryHelper.setOffHandSlot(npcComponent.getInventory(), this.defaultOffHandSlot);
|
|
}
|
|
|
|
this.setArmor(npcComponent, this.armor);
|
|
}
|
|
|
|
public boolean isCorpseStaysInFlock() {
|
|
return this.corpseStaysInFlock;
|
|
}
|
|
|
|
public void onLoadFromWorldGenOrPrefab(
|
|
@Nonnull Ref<EntityStore> ref, @Nonnull NPCEntity npcComponent, @Nonnull ComponentAccessor<EntityStore> componentAccessor
|
|
) {
|
|
this.entitySupport.pickRandomDisplayName(ref, true, componentAccessor);
|
|
this.initialiseInventories(npcComponent);
|
|
}
|
|
|
|
public RoleStats getRoleStats() {
|
|
return this.roleStats;
|
|
}
|
|
|
|
public static enum AvoidanceMode implements Supplier<String> {
|
|
Slowdown("Only slow down NPC"),
|
|
Evade("Only evade"),
|
|
Any("Any avoidance allowed");
|
|
|
|
@Nonnull
|
|
private final String description;
|
|
|
|
private AvoidanceMode(@Nonnull final String description) {
|
|
this.description = description;
|
|
}
|
|
|
|
@Nonnull
|
|
public String get() {
|
|
return this.description;
|
|
}
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface DeferredAction {
|
|
boolean tick(@Nonnull Ref<EntityStore> var1, @Nonnull Role var2, double var3, @Nonnull Store<EntityStore> var5);
|
|
}
|
|
}
|