package com.hypixel.hytale.builtin.npccombatactionevaluator;
import com.hypixel.hytale.builtin.npccombatactionevaluator.config.CombatBalanceAsset;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.CombatActionEvaluator;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.CombatActionEvaluatorConfig;
import com.hypixel.hytale.builtin.npccombatactionevaluator.evaluator.combatactions.CombatActionOption;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.DamageMemory;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.TargetMemory;
import com.hypixel.hytale.builtin.npccombatactionevaluator.memory.TargetMemorySystems;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.random.RandomExtra;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.protocol.GameMode;
import com.hypixel.hytale.server.core.entity.entities.Player;
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.interaction.InteractionModule;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.config.balancing.BalanceAsset;
import com.hypixel.hytale.server.npc.decisionmaker.core.EvaluationContext;
import com.hypixel.hytale.server.npc.decisionmaker.core.Evaluator;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.movement.controllers.MotionController;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.npc.role.support.PositionCache;
import com.hypixel.hytale.server.npc.role.support.StateSupport;
import com.hypixel.hytale.server.npc.systems.BalancingInitialisationSystem;
import com.hypixel.hytale.server.npc.systems.RoleSystems;
import com.hypixel.hytale.server.npc.valuestore.ValueStore;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class CombatActionEvaluatorSystems {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
public CombatActionEvaluatorSystems() {
}
public static class OnAdded extends HolderSystem<EntityStore> {
@Nullable
private final ComponentType<EntityStore, NPCEntity> componentType = NPCEntity.getComponentType();
private final ComponentType<EntityStore, CombatConstructionData> combatConstructionDataComponentType;
@Nonnull
private final Set<Dependency<EntityStore>> dependencies;
@Nonnull
private final Query<EntityStore> query;
public OnAdded(ComponentType<EntityStore, CombatConstructionData> combatConstructionDataComponentType) {
this.combatConstructionDataComponentType = combatConstructionDataComponentType;
this.dependencies = Set.of(new SystemDependency(Order.AFTER, BalancingInitialisationSystem.class));
this.query = Query.<EntityStore>and(combatConstructionDataComponentType, combatConstructionDataComponentType);
}
public void onEntityAdd(@Nonnull Holder<EntityStore> holder, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store) {
Role role = ((NPCEntity)holder.getComponent(this.componentType)).getRole();
if (role.getBalanceAsset() != null) {
BalanceAsset balancingAsset = (BalanceAsset)BalanceAsset.getAssetMap().getAsset(role.getBalanceAsset());
if (balancingAsset instanceof CombatBalanceAsset) {
CombatBalanceAsset combatBalance = (CombatBalanceAsset)balancingAsset;
CombatConstructionData constructionData = (CombatConstructionData)holder.getComponent(this.combatConstructionDataComponentType);
CombatActionEvaluator evaluator = new CombatActionEvaluator(role, combatBalance.getEvaluatorConfig(), constructionData);
evaluator.setupNPC(holder);
PositionCache var10000 = role.getPositionCache();
Objects.requireNonNull(evaluator);
var10000.addExternalPositionCacheRegistration(evaluator::setupNPC);
holder.putComponent(TargetMemory.getComponentType(), new TargetMemory(combatBalance.getTargetMemoryDuration()));
holder.putComponent(CombatActionEvaluator.getComponentType(), evaluator);
holder.ensureComponent(InteractionModule.get().getChainingDataComponent());
holder.removeComponent(this.combatConstructionDataComponentType);
}
}
}
public void onEntityRemoved(@Nonnull Holder<EntityStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store) {
}
@Nonnull
public Query<EntityStore> getQuery() {
return this.query;
}
@Nonnull
public Set<Dependency<EntityStore>> getDependencies() {
return this.dependencies;
}
}
public static class EvaluatorTick extends EntityTickingSystem<EntityStore> {
private final ComponentType<EntityStore, CombatActionEvaluator> componentType;
private final ComponentType<EntityStore, TargetMemory> targetMemoryComponentType;
private final ComponentType<EntityStore, DamageMemory> damageMemoryComponentType;
@Nullable
private final ComponentType<EntityStore, NPCEntity> npcComponentType;
@Nonnull
private final ComponentType<EntityStore, Player> playerComponentType;
private final ComponentType<EntityStore, ValueStore> valueStoreComponentType;
private final ComponentType<EntityStore, TransformComponent> transformComponentType;
private final Query<EntityStore> query;
@Nonnull
private final Set<Dependency<EntityStore>> dependencies;
public EvaluatorTick(ComponentType<EntityStore, CombatActionEvaluator> componentType, ComponentType<EntityStore, TargetMemory> targetMemoryComponentType, ComponentType<EntityStore, DamageMemory> damageMemoryComponentType) {
this.componentType = componentType;
this.targetMemoryComponentType = targetMemoryComponentType;
this.damageMemoryComponentType = damageMemoryComponentType;
this.npcComponentType = NPCEntity.getComponentType();
this.playerComponentType = Player.getComponentType();
this.valueStoreComponentType = ValueStore.getComponentType();
this.transformComponentType = TransformComponent.getComponentType();
this.query = Archetype.of(componentType, targetMemoryComponentType, this.npcComponentType, this.valueStoreComponentType, this.transformComponentType);
this.dependencies = Set.of(new SystemDependency(Order.BEFORE, RoleSystems.PreBehaviourSupportTickSystem.class), new SystemDependency(Order.AFTER, TargetMemorySystems.Ticking.class));
}
@Nonnull
public Set<Dependency<EntityStore>> getDependencies() {
return this.dependencies;
}
public Query<EntityStore> getQuery() {
return this.query;
}
public boolean isParallel(int archetypeChunkSize, int taskCount) {
return false;
}
public void tick(float dt, int index, @Nonnull ArchetypeChunk<EntityStore> archetypeChunk, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
NPCEntity npcComponent = (NPCEntity)archetypeChunk.getComponent(index, this.npcComponentType);
assert npcComponent != null;
Role role = npcComponent.getRole();
if (!archetypeChunk.getArchetype().contains(DeathComponent.getComponentType())) {
CombatActionEvaluator evaluatorComponent = (CombatActionEvaluator)archetypeChunk.getComponent(index, this.componentType);
assert evaluatorComponent != null;
evaluatorComponent.tickBasicAttackCoolDown(dt);
StateSupport stateSupport = role.getStateSupport();
int currentState = stateSupport.getStateIndex();
if (currentState != evaluatorComponent.getRunInState()) {
if (evaluatorComponent.getCurrentAction() != null) {
evaluatorComponent.completeCurrentAction(true, true);
evaluatorComponent.clearPrimaryTarget();
role.getMarkedEntitySupport().clearMarkedEntity(evaluatorComponent.getMarkedTargetSlot());
HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("%s: Leaving combat", archetypeChunk.getReferenceTo(index));
}
}
DamageMemory damageMemory = (DamageMemory)archetypeChunk.getComponent(index, this.damageMemoryComponentType);
if (damageMemory != null) {
damageMemory.clearTotalDamage();
}
} else if (!role.getCombatSupport().isExecutingAttack()) {
ValueStore valueStoreComponent = (ValueStore)archetypeChunk.getComponent(index, this.valueStoreComponentType);
assert valueStoreComponent != null;
double[] postExecutionDistanceRange = evaluatorComponent.consumePostExecutionDistanceRange();
if (postExecutionDistanceRange != null) {
valueStoreComponent.storeDouble(evaluatorComponent.getMinRangeSlot(), postExecutionDistanceRange[0]);
valueStoreComponent.storeDouble(evaluatorComponent.getMaxRangeSlot(), postExecutionDistanceRange[1]);
}
int currentSubState = stateSupport.getSubStateIndex();
CombatActionEvaluatorConfig.BasicAttacks basicAttacks = evaluatorComponent.getBasicAttacks(currentSubState);
if (basicAttacks != null) {
evaluatorComponent.setCurrentBasicAttackSet(currentSubState, basicAttacks);
String currentBasicAttack = evaluatorComponent.getCurrentBasicAttack();
if (currentBasicAttack != null) {
if (!evaluatorComponent.tickBasicAttackTimeout(dt)) {
role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), evaluatorComponent.getBasicAttackTarget());
return;
}
evaluatorComponent.clearCurrentBasicAttack();
HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("%s: Basic attack timed out", archetypeChunk.getReferenceTo(index));
}
}
if (evaluatorComponent.canUseBasicAttack(index, archetypeChunk, commandBuffer)) {
MotionController activeMotionController = role.getActiveMotionController();
TransformComponent transformComponent = (TransformComponent)archetypeChunk.getComponent(index, this.transformComponentType);
assert transformComponent != null;
Vector3d position = transformComponent.getPosition();
Ref<EntityStore> targetRef = null;
CombatActionEvaluator.CombatOptionHolder currentAction = evaluatorComponent.getCurrentAction();
if (currentAction == null || ((CombatActionOption)currentAction.getOption()).getActionTarget() != CombatActionOption.Target.Friendly) {
Ref<EntityStore> primaryTargetRef = evaluatorComponent.getPrimaryTarget();
if (primaryTargetRef != null && primaryTargetRef.isValid()) {
TransformComponent targetTransformComponent = (TransformComponent)commandBuffer.getComponent(primaryTargetRef, this.transformComponentType);
assert targetTransformComponent != null;
Vector3d targetPosition = targetTransformComponent.getPosition();
if (activeMotionController.getSquaredDistance(position, targetPosition, basicAttacks.shouldUseProjectedDistance()) < basicAttacks.getMaxRangeSquared()) {
targetRef = primaryTargetRef;
}
}
}
if (targetRef == null) {
TargetMemory targetMemoryComponent = (TargetMemory)archetypeChunk.getComponent(index, this.targetMemoryComponentType);
assert targetMemoryComponent != null;
targetRef = targetMemoryComponent.getClosestHostile();
}
if (targetRef != null) {
TransformComponent targetTransformComponent = (TransformComponent)commandBuffer.getComponent(targetRef, this.transformComponentType);
assert targetTransformComponent != null;
Vector3d targetPosition = targetTransformComponent.getPosition();
if (activeMotionController.getSquaredDistance(position, targetPosition, basicAttacks.shouldUseProjectedDistance()) < basicAttacks.getMaxRangeSquared()) {
evaluatorComponent.setBasicAttackTarget(targetRef);
role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), targetRef);
String[] basicAttackOptions = basicAttacks.getAttacks();
String attack;
if (basicAttacks.isRandom()) {
attack = basicAttackOptions[RandomExtra.randomRange(basicAttackOptions.length)];
} else {
int nextAttackIndex = evaluatorComponent.getNextBasicAttackIndex();
attack = basicAttackOptions[nextAttackIndex];
++nextAttackIndex;
if (nextAttackIndex >= basicAttackOptions.length) {
nextAttackIndex = 0;
}
evaluatorComponent.setNextBasicAttackIndex(nextAttackIndex);
}
boolean var10002 = basicAttacks.isDamageFriendlies();
Objects.requireNonNull(basicAttacks);
evaluatorComponent.setCurrentBasicAttack(attack, var10002, basicAttacks::getInteractionVars);
evaluatorComponent.setBasicAttackTimeout((double)basicAttacks.getTimeout());
HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("%s: Started basic attack %s", archetypeChunk.getReferenceTo(index), attack);
}
}
}
}
} else {
evaluatorComponent.setCurrentBasicAttackSet(currentSubState, (CombatActionEvaluatorConfig.BasicAttacks)null);
}
CombatActionEvaluator.CombatOptionHolder currentAction = evaluatorComponent.getCurrentAction();
if (currentAction != null) {
if (((CombatActionOption)currentAction.getOption()).getActionTarget() == CombatActionOption.Target.Self) {
return;
}
if (!evaluatorComponent.hasTimedOut(dt)) {
Ref<EntityStore> targetRef = evaluatorComponent.getPrimaryTarget();
if (targetRef != null && targetRef.isValid() && !commandBuffer.getArchetype(targetRef).contains(DeathComponent.getComponentType())) {
Player targetPlayerComponent = (Player)commandBuffer.getComponent(targetRef, this.playerComponentType);
if (targetPlayerComponent == null || targetPlayerComponent.getGameMode() == GameMode.Adventure) {
role.getMarkedEntitySupport().setMarkedEntity(evaluatorComponent.getMarkedTargetSlot(), targetRef);
return;
}
}
}
evaluatorComponent.terminateCurrentAction();
evaluatorComponent.clearPrimaryTarget();
role.getMarkedEntitySupport().clearMarkedEntity(evaluatorComponent.getMarkedTargetSlot());
HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("%s: Lost current action target or timed out", archetypeChunk.getReferenceTo(index));
}
}
if (evaluatorComponent.getOptionsBySubState().containsKey(currentSubState)) {
EvaluationContext evaluationContext = evaluatorComponent.getEvaluationContext();
double minRunUtility = evaluatorComponent.getMinRunUtility();
evaluationContext.setMinimumUtility(minRunUtility);
evaluationContext.setMinimumWeightCoefficient(0.0);
evaluationContext.setLastUsedNanos(evaluatorComponent.getLastRunNanos());
CombatActionEvaluator.RunOption runOption = evaluatorComponent.getRunOption();
double utility = runOption.calculateUtility(index, archetypeChunk, evaluatorComponent.getPrimaryTarget(), commandBuffer, evaluationContext);
evaluationContext.reset();
if (!(utility < minRunUtility)) {
Int2ObjectMap<List<Evaluator<CombatActionOption>.OptionHolder>> optionLists = evaluatorComponent.getOptionsBySubState();
List<Evaluator<CombatActionOption>.OptionHolder> currentStateOptions = (List)optionLists.get(currentSubState);
evaluatorComponent.setActiveOptions(currentStateOptions);
evaluatorComponent.selectNextCombatAction(index, archetypeChunk, commandBuffer, role, valueStoreComponent);
evaluatorComponent.setLastRunNanos(System.nanoTime());
DamageMemory damageMemory = (DamageMemory)archetypeChunk.getComponent(index, this.damageMemoryComponentType);
if (damageMemory != null) {
damageMemory.clearRecentDamage();
}
HytaleLogger.Api context = CombatActionEvaluatorSystems.LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("%s: Has run the combat action evaluator", archetypeChunk.getReferenceTo(index));
}
}
}
}
}
}
}
public static class CombatConstructionData implements Component<EntityStore> {
protected String combatState;
protected int markedTargetSlot;
protected int minRangeSlot;
protected int maxRangeSlot;
protected int positioningAngleSlot;
public CombatConstructionData() {
}
public static ComponentType<EntityStore, CombatConstructionData> getComponentType() {
return NPCCombatActionEvaluatorPlugin.get().getCombatConstructionDataComponentType();
}
public String getCombatState() {
return this.combatState;
}
public void setCombatState(String state) {
if (this.combatState != null && !this.combatState.equals(state)) {
throw new IllegalStateException("Cannot have more than one combat state in an NPC!");
} else {
this.combatState = state;
}
}
public int getMarkedTargetSlot() {
return this.markedTargetSlot;
}
public void setMarkedTargetSlot(int markedTargetSlot) {
this.markedTargetSlot = markedTargetSlot;
}
public int getMinRangeSlot() {
return this.minRangeSlot;
}
public void setMinRangeSlot(int minRangeSlot) {
this.minRangeSlot = minRangeSlot;
}
public int getMaxRangeSlot() {
return this.maxRangeSlot;
}
public void setMaxRangeSlot(int maxRangeSlot) {
this.maxRangeSlot = maxRangeSlot;
}
public int getPositioningAngleSlot() {
return this.positioningAngleSlot;
}
public void setPositioningAngleSlot(int positioningAngleSlot) {
this.positioningAngleSlot = positioningAngleSlot;
}
@Nonnull
public Component<EntityStore> clone() {
CombatConstructionData data = new CombatConstructionData();
data.combatState = this.combatState;
data.markedTargetSlot = this.markedTargetSlot;
data.minRangeSlot = this.minRangeSlot;
data.maxRangeSlot = this.maxRangeSlot;
data.positioningAngleSlot = this.positioningAngleSlot;
return data;
}
}
}