252 lines
13 KiB
Java
252 lines
13 KiB
Java
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.Set;
|
|
import java.util.stream.Collectors;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class ChangeModelPage extends InteractiveCustomUIPage<ChangeModelPage.PageEventData> {
|
|
private static final String COMMON_TEXT_BUTTON_DOCUMENT = "Common/TextButton.ui";
|
|
private static final Value<String> BUTTON_LABEL_STYLE = Value.ref("Common/TextButton.ui", "LabelStyle");
|
|
private static final Value<String> BUTTON_LABEL_STYLE_SELECTED = Value.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);
|
|
}
|
|
|
|
@Override
|
|
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 ChangeModelPage.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 {
|
|
String var9 = data.type;
|
|
switch (var9) {
|
|
case "Select":
|
|
if (data.model != null) {
|
|
UICommandBuilder commandBuilder = new UICommandBuilder();
|
|
this.selectModel(ref, store, data.model, commandBuilder);
|
|
this.sendUpdate(commandBuilder, 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));
|
|
store.getComponent(ref, Player.getComponentType()).getPageManager().setPage(ref, store, Page.None);
|
|
}
|
|
break;
|
|
case "ResetModel":
|
|
PlayerSkinComponent skinComponent = store.getComponent(ref, PlayerSkinComponent.getComponentType());
|
|
if (skinComponent == null) {
|
|
return;
|
|
}
|
|
|
|
PlayerSkinComponent playerSkinComponent = store.getComponent(ref, PlayerSkinComponent.getComponentType());
|
|
Model newModel = CosmeticsModule.get().createModel(playerSkinComponent.getPlayerSkin());
|
|
store.putComponent(ref, ModelComponent.getComponentType(), new ModelComponent(newModel));
|
|
playerSkinComponent.setNetworkOutdated();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
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(roleTemplateNames.size());
|
|
|
|
for (String value : roleTemplateNames) {
|
|
int fuzzyDistance = StringCompareUtil.getFuzzyDistance(value, this.searchQuery, Locale.ENGLISH);
|
|
if (fuzzyDistance > 0) {
|
|
map.put(value, fuzzyDistance);
|
|
}
|
|
}
|
|
|
|
this.models = map.keySet().stream().sorted().sorted(Comparator.comparingInt(map::getInt).reversed()).limit(20L).collect(Collectors.toList());
|
|
} else {
|
|
this.models = roleTemplateNames.stream().sorted().sorted(String::compareTo).collect(Collectors.toList());
|
|
}
|
|
|
|
int i = 0;
|
|
|
|
for (int bound = this.models.size(); i < bound; i++) {
|
|
String id = 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, 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()) {
|
|
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
|
|
|
|
assert transformComponent != null;
|
|
|
|
HeadRotation headRotationComponent = 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(
|
|
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(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);
|
|
} else if (this.modelPreview != null && this.modelPreview.isValid()) {
|
|
store.putComponent(this.modelPreview, ModelComponent.getComponentType(), new ModelComponent(this.getModel(1.0F)));
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private Model getModel(float scale) {
|
|
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<ChangeModelPage.PageEventData> CODEC = BuilderCodec.builder(
|
|
ChangeModelPage.PageEventData.class, ChangeModelPage.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();
|
|
private String model;
|
|
private String type;
|
|
private String searchQuery;
|
|
private float scale;
|
|
}
|
|
}
|