package com.hypixel.hytale.builtin.model.pages;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.common.util.StringCompareUtil;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.NonSerialized;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.protocol.packets.interface_.Page;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
import com.hypixel.hytale.server.core.cosmetics.CosmeticsModule;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;
import com.hypixel.hytale.server.core.modules.entity.component.HeadRotation;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.server.core.ui.Value;
import com.hypixel.hytale.server.core.ui.builder.EventData;
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.TargetUtil;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class ChangeModelPage extends InteractiveCustomUIPage<PageEventData> {
private static final String COMMON_TEXT_BUTTON_DOCUMENT = "Common/TextButton.ui";
private static final Value<String> BUTTON_LABEL_STYLE = Value.<String>ref("Common/TextButton.ui", "LabelStyle");
private static final Value<String> BUTTON_LABEL_STYLE_SELECTED = Value.<String>ref("Common/TextButton.ui", "SelectedLabelStyle");
@Nonnull
private String searchQuery = "";
private List<String> models;
@Nullable
private String selectedModel;
@Nullable
private Ref<EntityStore> modelPreview;
private Vector3d position;
private Vector3f rotation;
private float scale = 1.0F;
public ChangeModelPage(@Nonnull PlayerRef playerRef) {
super(playerRef, CustomPageLifetime.CanDismiss, ChangeModelPage.PageEventData.CODEC);
}
public void build(@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store<EntityStore> store) {
commandBuilder.append("Pages/ChangeModelPage.ui");
eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, "#SearchInput", EventData.of("@SearchQuery", "#SearchInput.Value"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, "#Scale", (new EventData()).append("Type", "UpdateScale").append("@Scale", "#Scale.Value"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#ChangeModel", (new EventData()).append("Type", "ChangeModel"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#ResetModel", (new EventData()).append("Type", "ResetModel"), false);
this.buildModelList(ref, store, commandBuilder, eventBuilder);
}
public void handleDataEvent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull PageEventData data) {
if (data.searchQuery != null) {
this.searchQuery = data.searchQuery.trim().toLowerCase();
UICommandBuilder commandBuilder = new UICommandBuilder();
UIEventBuilder eventBuilder = new UIEventBuilder();
this.buildModelList(ref, store, commandBuilder, eventBuilder);
this.sendUpdate(commandBuilder, eventBuilder, false);
} else {
switch (data.type) {
case "Select":
if (data.model != null) {
UICommandBuilder commandBuilder = new UICommandBuilder();
this.selectModel(ref, store, data.model, commandBuilder);
this.sendUpdate(commandBuilder, (UIEventBuilder)null, false);
}
break;
case "UpdateScale":
this.scale = data.scale;
if (this.modelPreview.isValid()) {
store.putComponent(this.modelPreview, ModelComponent.getComponentType(), new ModelComponent(this.getModel(this.scale)));
}
break;
case "ChangeModel":
if (this.selectedModel != null) {
if (this.modelPreview.isValid()) {
store.removeEntity(this.modelPreview, RemoveReason.REMOVE);
}
Model model = this.getModel(this.scale);
store.putComponent(ref, ModelComponent.getComponentType(), new ModelComponent(model));
((Player)store.getComponent(ref, Player.getComponentType())).getPageManager().setPage(ref, store, Page.None);
}
break;
case "ResetModel":
PlayerSkinComponent skinComponent = (PlayerSkinComponent)store.getComponent(ref, PlayerSkinComponent.getComponentType());
if (skinComponent == null) {
return;
}
PlayerSkinComponent playerSkinComponent = (PlayerSkinComponent)store.getComponent(ref, PlayerSkinComponent.getComponentType());
Model newModel = CosmeticsModule.get().createModel(playerSkinComponent.getPlayerSkin());
store.putComponent(ref, ModelComponent.getComponentType(), new ModelComponent(newModel));
playerSkinComponent.setNetworkOutdated();
}
}
}
public void onDismiss(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
if (this.modelPreview != null && this.modelPreview.isValid()) {
store.removeEntity(this.modelPreview, RemoveReason.REMOVE);
}
}
private void buildModelList(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder) {
commandBuilder.clear("#ModelList");
Set<String> roleTemplateNames = ModelAsset.getAssetMap().getAssetMap().keySet();
if (!this.searchQuery.isEmpty()) {
Object2IntMap<String> map = new Object2IntOpenHashMap<String>(roleTemplateNames.size());
for(String value : roleTemplateNames) {
int fuzzyDistance = StringCompareUtil.getFuzzyDistance(value, this.searchQuery, Locale.ENGLISH);
if (fuzzyDistance > 0) {
map.put(value, fuzzyDistance);
}
}
Stream var10001 = map.keySet().stream().sorted();
Objects.requireNonNull(map);
this.models = (List)var10001.sorted(Comparator.comparingInt(map::getInt).reversed()).limit(20L).collect(Collectors.toList());
} else {
this.models = (List)roleTemplateNames.stream().sorted().sorted(String::compareTo).collect(Collectors.toList());
}
int i = 0;
for(int bound = this.models.size(); i < bound; ++i) {
String id = (String)this.models.get(i);
String selector = "#ModelList[" + i + "]";
commandBuilder.append("#ModelList", "Common/TextButton.ui");
commandBuilder.set(selector + " #Button.Text", id);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, selector + " #Button", (new EventData()).append("Type", "Select").append("Model", id), false);
}
if (!this.models.isEmpty()) {
if (!this.models.contains(this.selectedModel)) {
this.selectModel(ref, store, (String)this.models.getFirst(), commandBuilder);
} else if (this.selectedModel != null) {
this.selectModel(ref, store, this.selectedModel, commandBuilder);
}
}
}
private void selectModel(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String modelId, @Nonnull UICommandBuilder commandBuilder) {
if (this.selectedModel != null && this.models.contains(this.selectedModel)) {
commandBuilder.set("#ModelList[" + this.models.indexOf(this.selectedModel) + "] #Button.Style", BUTTON_LABEL_STYLE);
}
commandBuilder.set("#ModelList[" + this.models.indexOf(modelId) + "] #Button.Style", BUTTON_LABEL_STYLE_SELECTED);
commandBuilder.set("#ModelName.Text", modelId);
this.selectedModel = modelId;
if (this.modelPreview != null && this.modelPreview.isValid()) {
if (this.modelPreview != null && this.modelPreview.isValid()) {
store.putComponent(this.modelPreview, ModelComponent.getComponentType(), new ModelComponent(this.getModel(1.0F)));
}
} else {
TransformComponent transformComponent = (TransformComponent)store.getComponent(ref, TransformComponent.getComponentType());
assert transformComponent != null;
HeadRotation headRotationComponent = (HeadRotation)store.getComponent(ref, HeadRotation.getComponentType());
assert headRotationComponent != null;
Vector3d playerPosition = transformComponent.getPosition();
Vector3f headRotation = headRotationComponent.getRotation();
Vector3d previewPosition = TargetUtil.getTargetLocation(ref, 8.0, store);
if (previewPosition == null) {
previewPosition = playerPosition.clone().add(Transform.getDirection(headRotation.getPitch(), headRotation.getYaw()).scale(4.0));
}
Vector3d targetGround = TargetUtil.getTargetLocation(((EntityStore)store.getExternalData()).getWorld(), (blockId) -> blockId != 0, previewPosition.x, previewPosition.y, previewPosition.z, 0.0, -1.0, 0.0, 8.0);
if (targetGround != null) {
previewPosition = targetGround;
}
Vector3d relativePos = playerPosition.clone().subtract(previewPosition);
relativePos.setY(0.0);
Vector3f previewRotation = Vector3f.lookAt(relativePos);
this.position = previewPosition;
this.rotation = previewRotation;
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(((EntityStore)store.getExternalData()).takeNextNetworkId()));
holder.addComponent(EntityStore.REGISTRY.getNonSerializedComponentType(), NonSerialized.get());
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(previewPosition, previewRotation));
holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(this.getModel(this.scale)));
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(previewRotation));
this.modelPreview = store.addEntity(holder, AddReason.SPAWN);
}
}
@Nullable
private Model getModel(float scale) {
ModelAsset modelAsset = (ModelAsset)ModelAsset.getAssetMap().getAsset(this.selectedModel);
return Model.createScaledModel(modelAsset, scale);
}
public static class PageEventData {
static final String KEY_MODEL = "Model";
static final String KEY_TYPE = "Type";
static final String KEY_SEARCH_QUERY = "@SearchQuery";
static final String KEY_SCALE = "@Scale";
public static final BuilderCodec<PageEventData> CODEC;
private String model;
private String type;
private String searchQuery;
private float scale;
public PageEventData() {
}
static {
CODEC = ((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)((BuilderCodec.Builder)BuilderCodec.builder(PageEventData.class, PageEventData::new).append(new KeyedCodec("Model", Codec.STRING), (entry, s) -> entry.model = s, (entry) -> entry.model).add()).append(new KeyedCodec("Type", Codec.STRING), (entry, s) -> entry.type = s, (entry) -> entry.type).add()).append(new KeyedCodec("@SearchQuery", Codec.STRING), (entry, s) -> entry.searchQuery = s, (entry) -> entry.searchQuery).add()).append(new KeyedCodec("@Scale", Codec.FLOAT), (entry, s) -> entry.scale = s, (entry) -> entry.scale).add()).build();
}
}
}