package com.hypixel.hytale.server.npc.statetransition.builders; import com.google.gson.JsonElement; import com.hypixel.hytale.server.npc.asset.builder.Builder; import com.hypixel.hytale.server.npc.asset.builder.BuilderBase; import com.hypixel.hytale.server.npc.asset.builder.BuilderDescriptorState; import com.hypixel.hytale.server.npc.asset.builder.BuilderObjectReferenceHelper; import com.hypixel.hytale.server.npc.asset.builder.BuilderObjectStaticListHelper; import com.hypixel.hytale.server.npc.asset.builder.BuilderSupport; import com.hypixel.hytale.server.npc.asset.builder.holder.BooleanHolder; import com.hypixel.hytale.server.npc.instructions.ActionList; 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 java.util.BitSet; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BuilderStateTransition extends BuilderBase { protected final BuilderObjectStaticListHelper stateTransitionEdges = new BuilderObjectStaticListHelper<>( BuilderStateTransitionEdges.StateTransitionEdges.class, this ); protected final BuilderObjectReferenceHelper actions = new BuilderObjectReferenceHelper<>(ActionList.class, this); protected final BooleanHolder enabled = new BooleanHolder(); @Nonnull @Override public String getShortDescription() { return "An entry containing a list of actions to execute when moving from one state to another"; } @Nonnull @Override public String getLongDescription() { return this.getShortDescription(); } @Nonnull public BuilderStateTransition.StateTransition build(@Nonnull BuilderSupport builderSupport) { return new BuilderStateTransition.StateTransition(this, builderSupport); } @Nonnull @Override public Class category() { return BuilderStateTransition.StateTransition.class; } @Nonnull @Override public BuilderDescriptorState getBuilderDescriptorState() { return BuilderDescriptorState.Stable; } @Override public boolean isEnabled(ExecutionContext context) { return this.enabled.get(context); } @Nonnull @Override public Builder readConfig(@Nonnull JsonElement data) { this.requireArray( data, "States", this.stateTransitionEdges, null, BuilderDescriptorState.Stable, "List of state transitions", null, this.validationHelper ); this.requireObject(data, "Actions", this.actions, BuilderDescriptorState.Stable, "List of actions", null, this.validationHelper); this.getBoolean(data, "Enabled", this.enabled, true, BuilderDescriptorState.Stable, "Whether this sensor should be enabled on the NPC", null); return this; } @Override public boolean validate( String configName, @Nonnull NPCLoadTimeValidationHelper validationHelper, @Nonnull ExecutionContext context, Scope globalScope, @Nonnull List errors ) { boolean valid = super.validate(configName, validationHelper, context, globalScope, errors) & this.stateTransitionEdges.validate(configName, validationHelper, this.builderManager, context, globalScope, errors) & this.actions.validate(configName, validationHelper, this.builderManager, context, globalScope, errors); List edges = this.stateTransitionEdges.staticBuild(this.builderManager); BitSet seenEdges = new BitSet(); int[] allMainStates = this.stateHelper.getAllMainStates(); for (BuilderStateTransitionEdges.StateTransitionEdges edge : edges) { for (int from : edge.getFromStateIndices() != null ? edge.getFromStateIndices() : allMainStates) { for (int to : edge.getToStateIndices() != null ? edge.getToStateIndices() : allMainStates) { int combinedValue = StateTransitionController.indexStateTransitionEdge(from, to); if (seenEdges.get(combinedValue)) { errors.add( String.format( "Cannot define the same edge twice in state transitions: %s -> %s!", this.stateHelper.getStateName(from), this.stateHelper.getStateName(to) ) ); valid = false; } else { seenEdges.set(combinedValue); } } } } return valid; } @Nullable public List getStateTransitionEdges(@Nonnull BuilderSupport support) { return this.stateTransitionEdges.build(support); } @Nonnull public ActionList getActionList(@Nonnull BuilderSupport builderSupport) { ActionList actions = this.actions.build(builderSupport); if (actions != ActionList.EMPTY_ACTION_LIST) { actions.setAtomic(false); actions.setBlocking(true); } return actions; } public static class StateTransition { @Nullable private final List stateTransitionEdges; @Nonnull private final ActionList actions; private StateTransition(@Nonnull BuilderStateTransition builder, @Nonnull BuilderSupport support) { this.stateTransitionEdges = builder.getStateTransitionEdges(support); this.actions = builder.getActionList(support); } @Nullable public List getStateTransitionEdges() { return this.stateTransitionEdges; } @Nonnull public ActionList getActions() { return this.actions; } } }