package com.hypixel.hytale.server.npc.role.builders;
import com.google.gson.JsonElement;
import com.hypixel.hytale.codec.validation.Validator;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.server.core.asset.type.attitude.Attitude;
import com.hypixel.hytale.server.core.asset.type.blockset.config.BlockSet;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.asset.builder.BuilderCodecObjectHelper;
import com.hypixel.hytale.server.npc.asset.builder.BuilderCombatConfig;
import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState;
import com.hypixel.hytale.server.npc.asset.builder.BuilderFactory;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import com.hypixel.hytale.server.npc.asset.builder.BuilderObjectListHelper;
import com.hypixel.hytale.server.npc.asset.builder.BuilderObjectReferenceHelper;
import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport;
import com.hypixel.hytale.server.npc.asset.builder.BuilderTemplateInteractionVars;
import com.hypixel.hytale.server.npc.asset.builder.BuilderValidationHelper;
import com.hypixel.hytale.server.npc.asset.builder.FeatureEvaluatorHelper;
import com.hypixel.hytale.server.npc.asset.builder.InstructionContextHelper;
import com.hypixel.hytale.server.npc.asset.builder.InstructionType;
import com.hypixel.hytale.server.npc.asset.builder.SpawnableWithModelBuilder;
import com.hypixel.hytale.server.npc.asset.builder.StateMappingHelper;
import com.hypixel.hytale.server.npc.asset.builder.holder.AssetArrayHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.AssetHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.BooleanHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.DoubleHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.EnumHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.FloatHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.IntHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.StringArrayHolder;
import com.hypixel.hytale.server.npc.asset.builder.holder.StringHolder;
import com.hypixel.hytale.server.npc.asset.builder.validators.ArrayValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.AssetValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleArrayValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleRangeValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleSingleValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.DoubleValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.IntRangeValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.IntSingleValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringArrayNoEmptyStringsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringNotEmptyValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringNullOrNotEmptyValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.StringValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.TagSetExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.AttitudeGroupExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.BlockSetExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemAttitudeGroupExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemDropListExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ItemExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.ModelExistsValidator;
import com.hypixel.hytale.server.npc.asset.builder.validators.asset.RootInteractionValidator;
import com.hypixel.hytale.server.npc.config.AttitudeGroup;
import com.hypixel.hytale.server.npc.config.ItemAttitudeGroup;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.server.npc.decisionmaker.stateevaluator.StateEvaluator;
import com.hypixel.hytale.server.npc.instructions.Instruction;
import com.hypixel.hytale.server.npc.movement.controllers.BuilderMotionControllerMapUtil;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.server.npc.movement.controllers.builders.BuilderMotionControllerBase;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.role.RoleDebugFlags;
import com.hypixel.hytale.server.npc.role.SpawnEffect;
import com.hypixel.hytale.server.npc.role.support.WorldSupport;
import com.hypixel.hytale.server.npc.statetransition.StateTransitionController;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import com.hypixel.hytale.server.npc.validators.NPCLoadTimeValidationHelper;
import com.hypixel.hytale.server.spawning.ISpawnable;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.spawning.SpawningContext;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class BuilderRole extends SpawnableWithModelBuilder<Role> implements SpawnEffect {
protected String[] displayNames;
protected final AssetHolder appearance = new AssetHolder();
protected final AssetHolder dropListId = new AssetHolder();
protected final IntHolder maxHealth = new IntHolder();
protected String startState;
protected String defaultSubState;
protected int startStateIndex;
protected int startSubStateIndex;
protected final EnumSet<RoleDebugFlags> parsedDebugFlags = EnumSet.noneOf(RoleDebugFlags.class);
protected String debugFlags;
protected double inertia;
protected final DoubleHolder knockbackScale = new DoubleHolder();
protected String opaqueBlockSet;
protected boolean applyAvoidance;
protected double entityAvoidanceStrength;
protected double collisionDistance;
protected double collisionForceFalloff;
protected double collisionRadius;
protected float collisionViewAngle;
protected double separationDistance;
protected double separationWeight;
protected double separationDistanceTarget;
protected double separationNearRadiusTarget;
protected double separationFarRadiusTarget;
protected final BooleanHolder applySeparation = new BooleanHolder();
protected boolean stayInEnvironment;
protected String allowedEnvironments;
protected final StringArrayHolder flockSpawnTypes = new StringArrayHolder();
protected final BooleanHolder flockSpawnTypeRandom = new BooleanHolder();
protected final StringArrayHolder flockAllowedRoles = new StringArrayHolder();
protected final BooleanHolder canLeadFlock = new BooleanHolder();
protected final FloatHolder spawnLockTime = new FloatHolder();
protected double flockWeightAlignment;
protected double flockWeightSeparation;
protected double flockWeightCohesion;
protected double flockInfluenceRange;
protected boolean corpseStaysInFlock;
protected final BooleanHolder invulnerable = new BooleanHolder();
protected final BooleanHolder breathesInAir = new BooleanHolder();
protected final BooleanHolder breathesInWater = new BooleanHolder();
protected final AssetArrayHolder hotbarItems = new AssetArrayHolder();
protected final AssetArrayHolder offHandItems = new AssetArrayHolder();
protected final AssetHolder inventoryItemsDropList = new AssetHolder();
protected final IntHolder defaultOffHandSlot = new IntHolder();
protected boolean pickupDropOnDeath;
protected String[] armor;
protected double deathAnimationTime;
protected float despawnAnimationTime;
@Nonnull
protected AssetHolder deathInteraction = new AssetHolder();
protected Role.AvoidanceMode avoidanceMode;
protected boolean disableDamageFlock;
protected final AssetArrayHolder disableDamageGroups = new AssetArrayHolder();
protected String spawnParticles;
protected double[] spawnParticleOffset;
protected double spawnViewDistance;
protected int inventorySlots;
protected int hotbarSlots;
protected int offHandSlots;
protected final EnumHolder<Attitude> defaultPlayerAttitude = new EnumHolder<Attitude>();
protected final EnumHolder<Attitude> defaultNPCAttitude = new EnumHolder<Attitude>();
protected final AssetHolder attitudeGroup = new AssetHolder();
protected final AssetHolder itemAttitudeGroup = new AssetHolder();
protected Int2ObjectMap<IntSet> busyStates;
protected final BuilderObjectReferenceHelper<Map<String, MotionController>> motionControllers;
protected final BuilderObjectListHelper<Instruction> instructionList;
protected final BuilderObjectReferenceHelper<Instruction> interactionInstruction;
protected final BuilderObjectReferenceHelper<Instruction> deathInstruction;
protected final BuilderObjectReferenceHelper<StateTransitionController> stateTransitionController;
protected final StringHolder initialMotionController;
protected final BuilderCodecObjectHelper<StateEvaluator> stateEvaluator;
protected final BuilderCombatConfig combatConfig;
protected final BuilderTemplateInteractionVars interactionVars;
protected final BooleanHolder isMemory;
protected final StringHolder memoriesCategory;
protected final StringHolder memoriesNameOverride;
protected final StringHolder nameTranslationKey;
public BuilderRole() {
this.motionControllers = new BuilderObjectReferenceHelper<Map<String, MotionController>>(BuilderMotionControllerMapUtil.CLASS_REFERENCE, this);
this.instructionList = new BuilderObjectListHelper<Instruction>(Instruction.class, this);
this.interactionInstruction = new BuilderObjectReferenceHelper<Instruction>(Instruction.class, this);
this.deathInstruction = new BuilderObjectReferenceHelper<Instruction>(Instruction.class, this);
this.stateTransitionController = new BuilderObjectReferenceHelper<StateTransitionController>(StateTransitionController.class, this);
this.initialMotionController = new StringHolder();
this.stateEvaluator = new BuilderCodecObjectHelper<StateEvaluator>(StateEvaluator.class, StateEvaluator.CODEC, (Validator)null);
this.combatConfig = new BuilderCombatConfig(BalanceAsset.CHILD_ASSET_CODEC, BalanceAsset.VALIDATOR_CACHE.getValidator());
this.interactionVars = new BuilderTemplateInteractionVars();
this.isMemory = new BooleanHolder();
this.memoriesCategory = new StringHolder();
this.memoriesNameOverride = new StringHolder();
this.nameTranslationKey = new StringHolder();
}
@Nonnull
public String getShortDescription() {
return "Generic role for NPC";
}
@Nonnull
public String getLongDescription() {
return "Generic role for NPC with a core planner and list of Motion controllers.";
}
@Nonnull
public BuilderDescriptorState getBuilderDescriptorState() {
return BuilderDescriptorState.Stable;
}
@Nonnull
public Role build(@Nonnull BuilderSupport builderSupport) {
return new Role(this, builderSupport);
}
public boolean validate(String configName, @Nonnull NPCLoadTimeValidationHelper validationHelper, @Nonnull ExecutionContext context, Scope globalScope, @Nonnull List<String> errors) {
validationHelper.setInventorySizes(this.inventorySlots, this.hotbarSlots, this.offHandSlots);
boolean hotbarValid;
try {
String[] hotbar = this.hotbarItems.get(context);
hotbarValid = hotbar == null || hotbar.length == 0 || validationHelper.validateHotbarHasSlot(hotbar.length - 1, "HotbarItems", errors);
} catch (IllegalStateException e) {
errors.add(String.format("%s: %s", configName, e.getMessage()));
hotbarValid = false;
}
boolean offHandValid;
try {
String[] offHand = this.offHandItems.get(context);
offHandValid = (offHand == null || offHand.length == 0 || validationHelper.validateOffHandHasSlot(offHand.length - 1, "OffHandItems", errors)) & validationHelper.validateOffHandHasSlot(this.defaultOffHandSlot.get(context), "DefaultOffHandSlot", errors);
} catch (IllegalStateException e) {
errors.add(String.format("%s: %s", configName, e.getMessage()));
offHandValid = false;
}
boolean validInitialMotionController = true;
String mc = this.initialMotionController.get(context);
if (mc != null) {
BuilderFactory<MotionController> factory = this.builderManager.<MotionController>getFactory(MotionController.class);
try {
BuilderMotionControllerBase builder = (BuilderMotionControllerBase)factory.createBuilder(mc);
validationHelper.requireMotionControllerType(builder.getClassType());
} catch (IllegalArgumentException var12) {
errors.add(String.format("%s: Unable to set InitialMotionController to %s as no such MotionController exists", configName, mc));
validInitialMotionController = false;
}
}
return super.validate(configName, validationHelper, context, globalScope, errors) & hotbarValid & offHandValid & validInitialMotionController & this.instructionList.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.interactionInstruction.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.deathInstruction.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.stateTransitionController.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.motionControllers.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.combatConfig.validate(configName, validationHelper, context, errors);
}
@Nonnull
public BuilderRole readConfig(@Nonnull JsonElement data) {
super.readCommonConfig(data);
this.setNotComponent();
this.requireInt(data, "MaxHealth", this.maxHealth, IntSingleValidator.greater0(), BuilderDescriptorState.Stable, "Max health", (String)null);
this.getString(data, "Debug", (e) -> this.debugFlags = e, "", (StringValidator)null, BuilderDescriptorState.WorkInProgress, "Debugging flags", (String)null);
this.requireAsset(data, "Appearance", this.appearance, ModelExistsValidator.required(), BuilderDescriptorState.Stable, "Model to use for rendering", (String)null);
this.getStringArray(data, "DisplayNames", (a) -> this.displayNames = a, (Function)null, (String[])null, StringArrayNoEmptyStringsValidator.get(), BuilderDescriptorState.Stable, "List of possible display names to choose from", (String)null);
this.requireString(data, "NameTranslationKey", this.nameTranslationKey, StringNotEmptyValidator.get(), BuilderDescriptorState.Stable, "The translation key for this NPC's name", (String)null);
this.getAsset(data, "OpaqueBlockSet", (v) -> this.opaqueBlockSet = v, "Opaque", BlockSetExistsValidator.withConfig(AssetValidator.CanBeEmpty), BuilderDescriptorState.Stable, "Blocks blocking line of sight", (String)null);
this.getDouble(data, "Inertia", (d) -> this.inertia = d, 1.0, DoubleSingleValidator.greater(0.1), BuilderDescriptorState.Experimental, "Inertia", (String)null);
this.getDouble(data, "KnockbackScale", this.knockbackScale, 1.0, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Stable, "Scale factor for knockback", "Scale factor for knockback. Values greater 1 increase knockback. Smaller values decrease it.");
this.getInt(data, "InventorySize", (v) -> this.inventorySlots = v, 0, IntRangeValidator.between(0, 36), BuilderDescriptorState.Stable, "Number of available inventory slots", (String)null);
this.getInt(data, "HotbarSize", (v) -> this.hotbarSlots = v, 3, IntRangeValidator.between(3, 8), BuilderDescriptorState.Stable, "Number of available hotbar slots", (String)null);
this.getInt(data, "OffHandSlots", (v) -> this.offHandSlots = v, 0, IntRangeValidator.between(0, 4), BuilderDescriptorState.Stable, "The number of slots for off-hand items", (String)null);
this.getAssetArray(data, "HotbarItems", this.hotbarItems, (String[])null, 0, 8, ItemExistsValidator.orDroplistWithConfig(AssetValidator.ListCanBeEmpty), BuilderDescriptorState.Stable, "Hotbar items (e.g. primary weapon, secondary weapon, etc)", (String)null);
this.getAssetArray(data, "OffHandItems", this.offHandItems, (String[])null, 0, 8, ItemExistsValidator.withConfig(AssetValidator.ListCanBeEmpty), BuilderDescriptorState.Stable, "Off-hand items (e.g. shields, torches, etc)", (String)null);
this.getAsset(data, "PossibleInventoryItems", this.inventoryItemsDropList, (String)null, ItemDropListExistsValidator.withConfig(AssetValidator.CanBeEmpty), BuilderDescriptorState.Stable, "A droplist defining the possible items the NPCs inventory could contain", (String)null);
this.getInt(data, "DefaultOffHandSlot", this.defaultOffHandSlot, -1, IntRangeValidator.between(-1, 4), BuilderDescriptorState.Stable, "The default off-hand item slot (-1 is empty)", (String)null);
this.getAssetArray(data, "Armor", (a) -> this.armor = a, (Function)null, (String[])null, ItemExistsValidator.withConfig(AssetValidator.ListCanBeEmpty), BuilderDescriptorState.WorkInProgress, "Armor items", (String)null);
this.getAsset(data, "DropList", this.dropListId, (String)null, ItemDropListExistsValidator.withConfig(AssetValidator.CanBeEmpty), BuilderDescriptorState.Stable, "Drop list to spawn when killed", (String)null);
this.getString(data, "StartState", (s) -> this.startState = s, "start", StringNotEmptyValidator.get(), BuilderDescriptorState.Stable, "Initial state", (String)null);
this.getDefaultSubState(data, "DefaultSubState", (v) -> this.defaultSubState = v, StringNotEmptyValidator.get(), BuilderDescriptorState.Stable, "The default sub state to reference when transitioning to a main state without a specified sub state", (String)null);
this.getDouble(data, "CollisionDistance", (d) -> this.collisionDistance = d, 5.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Stable, "Collision lookahead", (String)null);
this.getDouble(data, "CollisionForceFalloff", (d) -> this.collisionForceFalloff = d, 2.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Falloff rate for collision force", (String)null);
this.getDouble(data, "CollisionRadius", (d) -> this.collisionRadius = d, -1.0, (DoubleValidator)null, BuilderDescriptorState.Experimental, "Collision radius override", (String)null);
this.getFloat(data, "CollisionViewAngle", (d) -> this.collisionViewAngle = d, 320.0F, DoubleRangeValidator.between(0.0, 360.0), BuilderDescriptorState.Experimental, "Collision detection view cone", (String)null);
this.getDouble(data, "SeparationDistance", (d) -> this.separationDistance = d, 3.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Desired separation distance", (String)null);
this.getDouble(data, "SeparationWeight", (d) -> this.separationWeight = d, 1.0, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "Blend factor separation", (String)null);
this.getDouble(data, "SeparationDistanceTarget", (d) -> this.separationDistanceTarget = d, 1.0, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "Desired separation distance when close to target", (String)null);
this.getDouble(data, "SeparationNearRadiusTarget", (d) -> this.separationNearRadiusTarget = d, 1.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Distance when using SeparationDistanceTarget", (String)null);
this.getDouble(data, "SeparationFarRadiusTarget", (d) -> this.separationFarRadiusTarget = d, 5.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Use normal separation distance from further than this distance", (String)null);
this.getBoolean(data, "ApplyAvoidance", (b) -> this.applyAvoidance = b, false, BuilderDescriptorState.Experimental, "Apply avoidance steering force", (String)null);
this.getBoolean(data, "ApplySeparation", this.applySeparation, false, BuilderDescriptorState.Experimental, "Apply separation steering force", (String)null);
this.getEnum(data, "AvoidanceMode", (e) -> this.avoidanceMode = e, Role.AvoidanceMode.class, Role.AvoidanceMode.Any, BuilderDescriptorState.Experimental, "Abilities to use for avoidance", (String)null);
this.getDouble(data, "EntityAvoidanceStrength", (d) -> this.entityAvoidanceStrength = d, 1.0, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "Blending factor avoidance", (String)null);
this.getBoolean(data, "StayInEnvironment", (b) -> this.stayInEnvironment = b, false, BuilderDescriptorState.Experimental, "Stay in spawning environment", (String)null);
this.getString(data, "AllowedEnvironments", (s) -> this.allowedEnvironments = s, (String)null, StringNullOrNotEmptyValidator.get(), BuilderDescriptorState.Experimental, "Allowed environment to walk in", (String)null);
this.getStringArray(data, "FlockSpawnTypes", this.flockSpawnTypes, (String[])null, 0, 2147483647, StringArrayNoEmptyStringsValidator.get(), BuilderDescriptorState.WorkInProgress, "Types of NPC this flock should consist off", (String)null);
this.getBoolean(data, "FlockSpawnTypesRandom", this.flockSpawnTypeRandom, false, BuilderDescriptorState.WorkInProgress, "Create a randomized flock if true else spawn in order of FlockSpawnTypes", (String)null);
this.getStringArray(data, "FlockAllowedNPC", this.flockAllowedRoles, (String[])null, 0, 2147483647, StringArrayNoEmptyStringsValidator.get(), BuilderDescriptorState.Experimental, "List of NPCs allowed in flock", (String)null);
this.getBoolean(data, "FlockCanLead", this.canLeadFlock, false, BuilderDescriptorState.Experimental, "This NPC can be flock leader", (String)null);
this.getDouble(data, "FlockWeightAlignment", (v) -> this.flockWeightAlignment = v, 1.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Blending flock alignment", (String)null);
this.getDouble(data, "FlockWeightSeparation", (v) -> this.flockWeightSeparation = v, 1.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Blending flock separation", (String)null);
this.getDouble(data, "FlockWeightCohesion", (v) -> this.flockWeightCohesion = v, 1.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Blending flock cohesion", (String)null);
this.getDouble(data, "FlockInfluenceRange", (v) -> this.flockInfluenceRange = v, 10.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "Influence radius flock forces", (String)null);
this.getBoolean(data, "DisableDamageFlock", (b) -> this.disableDamageFlock = b, true, BuilderDescriptorState.WorkInProgress, "If true disables combat damage from flock members", (String)null);
this.getAssetArray(data, "DisableDamageGroups", this.disableDamageGroups, (String[])null, 0, 2147483647, TagSetExistsValidator.withConfig(AssetValidator.ListCanBeEmpty), BuilderDescriptorState.WorkInProgress, "Members in this list of group won't cause damage", (String)null);
this.getExistentStateSet(data, "BusyStates", (s) -> this.busyStates = s, this.stateHelper, BuilderDescriptorState.Stable, "States during which this NPC is busy and can't be interacted with", (String)null);
this.getCodecObject(data, "CombatConfig", this.combatConfig, BuilderDescriptorState.Stable, "The combat configuration providing optional combat action evaluator", (String)null);
this.getBoolean(data, "Invulnerable", this.invulnerable, false, BuilderDescriptorState.Stable, "Makes NPC ignore damage", (String)null);
this.getBoolean(data, "BreathesInAir", this.breathesInAir, true, BuilderDescriptorState.WorkInProgress, "Can breath in air", (String)null);
this.getBoolean(data, "BreathesInWater", this.breathesInWater, false, BuilderDescriptorState.WorkInProgress, "Can breath in fluid/water", (String)null);
this.getBoolean(data, "PickupDropOnDeath", (t) -> this.pickupDropOnDeath = t, false, BuilderDescriptorState.Stable, "Drop last picked item on death", (String)null);
this.getDouble(data, "DeathAnimationTime", (d) -> this.deathAnimationTime = d, 5.0, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "How long to let the death animation play before removing", (String)null);
this.getAsset(data, "DeathInteraction", this.deathInteraction, (String)null, RootInteractionValidator.withConfig(AssetValidator.CanBeEmpty), BuilderDescriptorState.Experimental, "Interaction to run on death", (String)null);
this.getFloat(data, "DespawnAnimationTime", (d) -> this.despawnAnimationTime = d, 0.8F, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Experimental, "How long to let the despawn animation play before removing", (String)null);
this.getString(data, "SpawnParticles", (v) -> this.spawnParticles = v, (String)null, (StringValidator)null, BuilderDescriptorState.Experimental, "Particle system when spawning", (String)null);
this.getVector3d(data, "SpawnParticlesOffset", (v) -> this.spawnParticleOffset = v, (double[])null, (DoubleArrayValidator)null, BuilderDescriptorState.Experimental, "Displacement from foot point to spawn relative to NPC heading", (String)null);
this.getDouble(data, "SpawnViewDistance", (v) -> this.spawnViewDistance = v, 75.0, DoubleSingleValidator.greater0(), BuilderDescriptorState.Experimental, "View distance for spawn particle", (String)null);
this.getEnum(data, "DefaultPlayerAttitude", this.defaultPlayerAttitude, Attitude.class, Attitude.HOSTILE, BuilderDescriptorState.Stable, "The default attitude of this NPC towards players", (String)null);
this.getEnum(data, "DefaultNPCAttitude", this.defaultNPCAttitude, Attitude.class, Attitude.NEUTRAL, BuilderDescriptorState.Stable, "The default attitude of this NPC towards other NPCs", (String)null);
this.getAsset(data, "AttitudeGroup", this.attitudeGroup, (String)null, AttitudeGroupExistsValidator.withConfig(EnumSet.of(AssetValidator.Config.NULLABLE)), BuilderDescriptorState.Stable, "The attitude group towards other NPCs this NPC belongs to (often species related)", (String)null);
this.getAsset(data, "ItemAttitudeGroup", this.itemAttitudeGroup, (String)null, ItemAttitudeGroupExistsValidator.withConfig(EnumSet.of(AssetValidator.Config.NULLABLE)), BuilderDescriptorState.Stable, "This NPC's item attitudes", (String)null);
this.getBoolean(data, "CorpseStaysInFlock", (b) -> this.corpseStaysInFlock = b, false, BuilderDescriptorState.Stable, "Whether the NPC should stay in the flock until corpse removal or be removed at the moment of death", (String)null);
this.validateAny(this.breathesInAir, this.breathesInWater);
this.registerStateSetter(this.startState, this.defaultSubState, (m, s) -> {
this.startStateIndex = m;
this.startSubStateIndex = s;
});
if (this.debugFlags != null && !this.debugFlags.isEmpty()) {
this.parsedDebugFlags.addAll(this.toDebugFlagSet("RoleDebugFlags", this.debugFlags));
}
this.requireObject(data, "MotionControllerList", this.motionControllers, BuilderDescriptorState.Stable, "Motion controllers", (String)null, new BuilderValidationHelper(this.fileName, (FeatureEvaluatorHelper)null, this.internalReferenceResolver, (StateMappingHelper)null, (InstructionContextHelper)null, this.extraInfo, this.evaluators, this.readErrors));
this.getArray(data, "Instructions", this.instructionList, (ArrayValidator)null, BuilderDescriptorState.WorkInProgress, "List of instructions", (String)null, new BuilderValidationHelper(this.fileName, (FeatureEvaluatorHelper)null, this.internalReferenceResolver, this.stateHelper, new InstructionContextHelper(InstructionType.Default), this.extraInfo, this.evaluators, this.readErrors));
this.getObject(data, "InteractionInstruction", this.interactionInstruction, BuilderDescriptorState.Stable, "Interaction instruction", "An instruction designed to evaluate and set which players can interact with an NPC, along with setting correct states upon interaction", new BuilderValidationHelper(this.fileName, (FeatureEvaluatorHelper)null, this.internalReferenceResolver, this.stateHelper, new InstructionContextHelper(InstructionType.Interaction), this.extraInfo, this.evaluators, this.readErrors));
this.getObject(data, "DeathInstruction", this.deathInstruction, BuilderDescriptorState.Stable, "Death instruction", "An instruction which will run only when the NPC is dead until they are removed", new BuilderValidationHelper(this.fileName, (FeatureEvaluatorHelper)null, this.internalReferenceResolver, this.stateHelper, new InstructionContextHelper(InstructionType.Death), this.extraInfo, this.evaluators, this.readErrors));
this.getObject(data, "StateTransitions", this.stateTransitionController, BuilderDescriptorState.Stable, "State transition actions", "A set of state transitions and the actions that will be executed during them", new BuilderValidationHelper(this.fileName, (new FeatureEvaluatorHelper()).lock(), this.internalReferenceResolver, this.stateHelper, new InstructionContextHelper(InstructionType.StateTransitions), this.extraInfo, this.evaluators, this.readErrors));
this.getCodecObject(data, "StateEvaluator", this.stateEvaluator, BuilderDescriptorState.Stable, "A state evaluator", (String)null);
this.getString(data, "InitialMotionController", this.initialMotionController, (String)null, StringNullOrNotEmptyValidator.get(), BuilderDescriptorState.Stable, "The initial motion controller to set", "The initial motion controller to set. If omitted and there are multiple, one will be chosen at random.");
this.getCodecObject(data, "InteractionVars", this.interactionVars, BuilderDescriptorState.Stable, "Interaction vars to be used in interactions.", (String)null);
this.getBoolean(data, "IsMemory", this.isMemory, false, BuilderDescriptorState.Stable, "Used to define if the NPC has a Memory to record.", (String)null);
this.getString(data, "MemoriesCategory", this.memoriesCategory, "Other", StringNullOrNotEmptyValidator.get(), BuilderDescriptorState.Stable, "Category to put the NPC in, as part of the Memories Plugin", (String)null);
this.getString(data, "MemoriesNameOverride", this.memoriesNameOverride, "", (StringValidator)null, BuilderDescriptorState.Stable, "Overrides the Memory name when set.", (String)null);
this.getFloat(data, "SpawnLockTime", this.spawnLockTime, 1.5, DoubleSingleValidator.greaterEqual0(), BuilderDescriptorState.Stable, "How long the NPC should be locked and unable to perform behavior when first spawned", (String)null);
StateEvaluator stateEvaluator = this.stateEvaluator.build();
if (stateEvaluator != null) {
this.evaluators.add(stateEvaluator);
stateEvaluator.prepareOptions(this.stateHelper);
this.stateHelper.setHasStateEvaluator();
}
return this;
}
@Nonnull
public String getIdentifier() {
BuilderInfo builderInfo = NPCPlugin.get().getBuilderInfo(this);
Objects.requireNonNull(builderInfo, "Have builder but can't get builderInfo for it");
return builderInfo.getKeyName();
}
@Nonnull
public SpawnTestResult canSpawn(@Nonnull SpawningContext context) {
Builder<Map<String, MotionController>> builder = this.motionControllers.getBuilder(this.builderManager, context.getExecutionContext(), this);
if (builder instanceof ISpawnable) {
SpawnTestResult result = ((ISpawnable)builder).canSpawn(context);
if (result != SpawnTestResult.TEST_OK) {
return result;
}
if (!context.canBreathe(this.breathesInAir.get(context.getExecutionContext()), this.breathesInWater.get(context.getExecutionContext()))) {
return SpawnTestResult.FAIL_NOT_BREATHABLE;
}
}
return SpawnTestResult.TEST_OK;
}
@Nonnull
public Class<Role> category() {
return Role.class;
}
public String getSpawnModelName(ExecutionContext context, Scope modifierScope) {
return this.appearance.get(context);
}
@Nullable
public Scope createModifierScope(ExecutionContext executionContext) {
return null;
}
@Nonnull
public Scope createExecutionScope() {
return this.getBuilderParameters().createScope();
}
public void markNeedsReload() {
NPCPlugin.get().setRoleBuilderNeedsReload(this);
}
public String getSpawnParticles() {
return this.spawnParticles;
}
public Vector3d getSpawnParticleOffset() {
double[] var10000 = this.spawnParticleOffset;
Vector3d var10001 = Vector3d.ZERO;
Objects.requireNonNull(var10001);
return createVector3d(var10000, var10001::clone);
}
public double getSpawnViewDistance() {
return this.spawnViewDistance;
}
public final boolean isEnabled(ExecutionContext context) {
return true;
}
public int getMaxHealth(@Nonnull BuilderSupport builderSupport) {
return this.maxHealth.get(builderSupport.getExecutionContext());
}
@Nullable
public String[] getDisplayNames() {
return this.displayNames;
}
public String getNameTranslationKey(BuilderSupport support) {
return this.nameTranslationKey.get(support.getExecutionContext());
}
public String getAppearance(@Nonnull BuilderSupport builderSupport) {
return this.appearance.get(builderSupport.getExecutionContext());
}
public boolean isBreathesInAir(BuilderSupport support) {
return this.breathesInAir.get(support.getExecutionContext());
}
public boolean isBreathesInWater(BuilderSupport support) {
return this.breathesInWater.get(support.getExecutionContext());
}
public int getOpaqueBlockSet() {
int index = BlockSet.getAssetMap().getIndex(this.opaqueBlockSet);
if (index == -2147483648) {
throw new IllegalArgumentException("Unknown key! " + this.opaqueBlockSet);
} else {
return index;
}
}
public double getInertia() {
return this.inertia;
}
public double getKnockbackScale(@Nonnull BuilderSupport support) {
return this.knockbackScale.get(support.getExecutionContext());
}
@Nullable
public String[] getHotbarItems(@Nonnull BuilderSupport support) {
return this.hotbarItems.get(support.getExecutionContext());
}
@Nullable
public String[] getOffHandItems(@Nonnull BuilderSupport support) {
return this.offHandItems.get(support.getExecutionContext());
}
public String getInventoryItemsDropList(@Nonnull BuilderSupport support) {
return this.inventoryItemsDropList.get(support.getExecutionContext());
}
public String[] getArmor() {
return this.armor;
}
public boolean isPickupDropOnDeath() {
return this.pickupDropOnDeath;
}
public String getDropListId(@Nonnull BuilderSupport builderSupport) {
return this.dropListId.get(builderSupport.getExecutionContext());
}
public String getStartState() {
return this.startState;
}
public int getStartStateIndex() {
return this.startStateIndex;
}
public int getStartSubStateIndex() {
return this.startSubStateIndex;
}
public double getCollisionDistance() {
return this.collisionDistance;
}
public double getCollisionForceFalloff() {
return this.collisionForceFalloff;
}
public boolean isAvoidingEntities() {
return this.applyAvoidance;
}
public Role.AvoidanceMode getAvoidanceMode() {
return this.avoidanceMode;
}
public double getCollisionRadius() {
return this.collisionRadius;
}
public double getSeparationDistance() {
return this.separationDistance;
}
public double getSeparationWeight() {
return this.separationWeight;
}
public double getSeparationDistanceTarget() {
return this.separationDistanceTarget;
}
public double getSeparationNearRadiusTarget() {
return this.separationNearRadiusTarget;
}
public double getSeparationFarRadiusTarget() {
return this.separationFarRadiusTarget;
}
public boolean isApplySeparation(BuilderSupport support) {
return this.applySeparation.get(support.getExecutionContext());
}
public boolean isStayingInEnvironment() {
return this.stayInEnvironment;
}
public String getAllowedEnvironments() {
return this.allowedEnvironments;
}
public double getEntityAvoidanceStrength() {
return this.entityAvoidanceStrength;
}
@Nullable
public String[] getFlockSpawnTypes(@Nonnull BuilderSupport support) {
return this.flockSpawnTypes.get(support.getExecutionContext());
}
public boolean isFlockSpawnTypeRandom(@Nonnull BuilderSupport support) {
return this.flockSpawnTypeRandom.get(support.getExecutionContext());
}
@Nonnull
public String[] getFlockAllowedRoles(@Nonnull BuilderSupport support) {
return this.nonNull(this.flockAllowedRoles.get(support.getExecutionContext()));
}
public boolean isCanLeadFlock(@Nonnull BuilderSupport support) {
return this.canLeadFlock.get(support.getExecutionContext());
}
public double getFlockWeightAlignment() {
return this.flockWeightAlignment;
}
public double getFlockWeightSeparation() {
return this.flockWeightSeparation;
}
public double getFlockWeightCohesion() {
return this.flockWeightCohesion;
}
public double getFlockInfluenceRange() {
return this.flockInfluenceRange;
}
@Nonnull
public EnumSet<RoleDebugFlags> getDebugFlags() {
return this.parsedDebugFlags;
}
public float getCollisionViewAngle() {
return 0.017453292F * this.collisionViewAngle;
}
@Nullable
public String getBalanceAsset(@Nonnull BuilderSupport support) {
return this.combatConfig.build(support.getExecutionContext());
}
public double getDeathAnimationTime() {
return this.deathAnimationTime;
}
@Nullable
public String getDeathInteraction(@Nonnull BuilderSupport builderSupport) {
String val = this.deathInteraction.get(builderSupport.getExecutionContext());
return val != null && !val.isEmpty() ? val : null;
}
public float getDespawnAnimationTime() {
return this.despawnAnimationTime;
}
public boolean isDisableDamageFlock() {
return this.disableDamageFlock;
}
public int[] getDisableDamageGroups(@Nonnull BuilderSupport support) {
return WorldSupport.createTagSetIndexArray(this.disableDamageGroups.get(support.getExecutionContext()));
}
public boolean isInvulnerable(BuilderSupport support) {
return this.invulnerable.get(support.getExecutionContext());
}
public int getInventorySlots() {
return this.inventorySlots;
}
public int getHotbarSlots() {
return this.hotbarSlots;
}
public int getOffHandSlots() {
return this.offHandSlots;
}
public byte getDefaultOffHandSlot(@Nonnull BuilderSupport support) {
return (byte)this.defaultOffHandSlot.get(support.getExecutionContext());
}
public Int2ObjectMap<IntSet> getBusyStates() {
return this.busyStates;
}
public Attitude getDefaultPlayerAttitude(@Nonnull BuilderSupport support) {
return this.defaultPlayerAttitude.get(support.getExecutionContext());
}
public Attitude getDefaultNPCAttitude(@Nonnull BuilderSupport support) {
return this.defaultNPCAttitude.get(support.getExecutionContext());
}
public int getAttitudeGroup(@Nonnull BuilderSupport support) {
String groupName = this.attitudeGroup.get(support.getExecutionContext());
return AttitudeGroup.getAssetMap().getIndex(groupName);
}
public int getItemAttitudeGroup(@Nonnull BuilderSupport support) {
String groupName = this.itemAttitudeGroup.get(support.getExecutionContext());
return ItemAttitudeGroup.getAssetMap().getIndex(groupName);
}
public boolean isCorpseStaysInFlock() {
return this.corpseStaysInFlock;
}
@Nullable
public Map<String, MotionController> getMotionControllerMap(@Nonnull BuilderSupport support) {
return this.motionControllers.build(support);
}
public String getInitialMotionController(@Nonnull BuilderSupport support) {
return this.initialMotionController.get(support.getExecutionContext());
}
@Nullable
public List<Instruction> getInstructionList(@Nonnull BuilderSupport support) {
support.setCurrentInstructionContext(InstructionType.Default);
return this.instructionList.build(support);
}
@Nullable
public Instruction getInteractionInstruction(@Nonnull BuilderSupport support) {
support.setCurrentInstructionContext(InstructionType.Interaction);
return this.interactionInstruction.build(support);
}
@Nullable
public Instruction getDeathInstruction(@Nonnull BuilderSupport support) {
support.setCurrentInstructionContext(InstructionType.Death);
return this.deathInstruction.build(support);
}
@Nullable
public StateTransitionController getStateTransitionController(@Nonnull BuilderSupport support) {
support.setCurrentInstructionContext(InstructionType.Default);
return this.stateTransitionController.build(support);
}
public void registerStateEvaluator(@Nonnull BuilderSupport support) {
support.setStateEvaluator(this.stateEvaluator.build());
}
@Nullable
public Map<String, String> getInteractionVars(@Nonnull BuilderSupport support) {
return this.interactionVars.build(support.getExecutionContext());
}
public boolean isMemory(ExecutionContext context) {
return this.isMemory.get(context);
}
public boolean isMemory(ExecutionContext context, Scope modifierScope) {
return this.isMemory.get(context);
}
public String getMemoriesCategory(ExecutionContext context, Scope modifierScope) {
return this.memoriesCategory.get(context);
}
public String getMemoriesNameOverride(ExecutionContext context) {
return this.memoriesNameOverride.get(context);
}
public String getMemoriesNameOverride(ExecutionContext context, @Nullable Scope modifierScope) {
return this.memoriesNameOverride.get(context);
}
@Nonnull
public String getNameTranslationKey(ExecutionContext context, @Nullable Scope modifierScope) {
return this.nameTranslationKey.get(context);
}
public float getSpawnLockTime(BuilderSupport support) {
return this.spawnLockTime.get(support.getExecutionContext());
}
protected void runLoadTimeValidationHelper0(String configName, NPCLoadTimeValidationHelper loadTimeValidationHelper, ExecutionContext context, @Nonnull List<String> errors) {
NPCPlugin npcModule = NPCPlugin.get();
String[] spawnTypes = this.flockSpawnTypes.get(context);
boolean success = true;
if (spawnTypes != null) {
for(String type : spawnTypes) {
if (!npcModule.hasRoleName(type)) {
success = false;
errors.add(String.format("%s: No such role '%s' for %s", configName, type, this.flockSpawnTypes.getName()));
}
}
}
String[] allowedTypes = this.flockAllowedRoles.get(context);
if (allowedTypes != null) {
for(String type : allowedTypes) {
if (!npcModule.hasRoleName(type)) {
success = false;
errors.add(String.format("%s: No such role '%s' for %s", configName, type, this.flockAllowedRoles.getName()));
}
}
}
if (!success) {
throw new IllegalStateException(String.format("Invalid roles referenced for %s or %s", this.flockSpawnTypes.getName(), this.flockAllowedRoles.getName()));
}
}
}