classpublicPriority 3
EntitySpawnPage
com.hypixel.hytale.server.npc.pages.EntitySpawnPage
extends InteractiveCustomUIPage
2
Methods
2
Public Methods
14
Fields
1
Constructors
Constants
floatBLOCK_ENTITY_BASE_SCALE= 2.0F
Value<String>BUTTON_LABEL_STYLE= Value.ref("Common/TextButton.ui", "LabelStyle")
Value<String>BUTTON_LABEL_STYLE_SELECTED= Value.ref("Common/TextButton.ui", "SelectedLabelStyle")
BuilderCodec<EntitySpawnPage.EntitySpawnPageEventData>CODEC= BuilderCodec.builder(
EntitySpawnPage.EntitySpawnPageEventData.class, EntitySpawnPage...
StringCOMMON_TEXT_BUTTON_DOCUMENT= "Common/TextButton.ui"
StringCOMMON_UI_DOCUMENT= "Common.ui"
intFALLBACK_RAYCAST_DOWN_DISTANCE= 3
doubleFALLBACK_RAYCAST_Y_OFFSET= 0.5
StringKEY_COUNT= "@Count"
StringKEY_ITEM_ID= "ItemId"
StringKEY_ITEM_STACK_ID= "ItemStackId"
StringKEY_MODEL_ID= "ModelId"
StringKEY_NPC_ROLE= "NPCRole"
StringKEY_ROTATION_OFFSET= "@RotationOffset"
StringKEY_SCALE= "@Scale"
StringKEY_SEARCH_QUERY= "@SearchQuery"
StringKEY_SELECT_AN_ITEM= "server.customUI.entitySpawnPage.selectAnItem"
StringKEY_SELECT_AN_NPC= "server.customUI.entitySpawnPage.selectAnNpc"
StringKEY_SELECT_A_MODEL= "server.customUI.entitySpawnPage.selectAModel"
StringKEY_TAB= "Tab"
StringKEY_TYPE= "Type"
intLOOK_RAYCAST_DISTANCE= 4
intMAX_SPAWN_COUNT= 100
StringTAB_ITEMS= "Items"
StringTAB_MODEL= "Model"
StringTAB_NPC= "NPC"
Value<String>TAB_STYLE_ACTIVE= Value.ref("Common.ui", "DefaultTextButtonStyle")
Value<String>TAB_STYLE_INACTIVE= Value.ref("Common.ui", "SecondaryTextButtonStyle")
Constructors
public
EntitySpawnPage(PlayerRef playerRef)Methods
Public Methods (2)
public
void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, EntitySpawnPage.EntitySpawnPageEventData data)public
void onDismiss(Ref<EntityStore> ref, Store<EntityStore> store)@Override
Fields
Private/Package Fields (14)
private
String activeTabprivate
float currentRotationOffsetprivate
float currentScaleprivate
float lastPreviewScaleprivate
long lastScaleUpdateTimeprivate
List<String> modelIdsprivate
Ref<EntityStore> modelPreviewprivate
List<String> npcRolesprivate
Vector3d positionprivate
Vector3f rotationprivate
String searchQueryprivate
String selectedItemIdprivate
String selectedModelIdprivate
String selectedNpcRoleInheritance
Parent
Current
Interface
Child
Use mouse wheel to zoom, drag to pan. Click nodes to navigate.
Related Classes
Used By
CodecKeyedCodecBuilderCodecStringCompareUtilAddReasonHolderNonSerializedRefRemoveReasonStoreTransformVector3dVector3fVector3iCustomPageLifetimeCustomUIEventBindingTypePageMessageBlockTypeItemModelModelAssetGeneralCommandExceptionUUIDComponentBlockEntityPlayerInteractiveCustomUIPageItemStackEntityScaleComponentHeadRotationModelComponentPersistentModelPropComponentTransformComponentItemComponentPreventItemMergingPreventPickupNetworkIdItemGridSlotValueEventDataUICommandBuilderUIEventBuilderPlayerRefWorldEntityStoreTargetUtilNPCPluginBuilderBuilderInfoRoleISpawnableWithModelSpawningContext
Source Code
package com.hypixel.hytale.server.npc.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.math.vector.Vector3i;
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.Message;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
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.command.system.exceptions.GeneralCommandException;
import com.hypixel.hytale.server.core.entity.UUIDComponent;
import com.hypixel.hytale.server.core.entity.entities.BlockEntity;
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.inventory.ItemStack;
import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent;
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.PersistentModel;
import com.hypixel.hytale.server.core.modules.entity.component.PropComponent;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
import com.hypixel.hytale.server.core.modules.entity.item.PreventItemMerging;
import com.hypixel.hytale.server.core.modules.entity.item.PreventPickup;
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
import com.hypixel.hytale.server.core.ui.ItemGridSlot;
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.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.TargetUtil;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.spawning.SpawningContext;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class EntitySpawnPage extends InteractiveCustomUIPage<EntitySpawnPage.EntitySpawnPageEventData> {
private static final String COMMON_TEXT_BUTTON_DOCUMENT = "Common/TextButton.ui";
private static final String COMMON_UI_DOCUMENT = "Common.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");
private static final Value<String> TAB_STYLE_ACTIVE = Value.ref("Common.ui", "DefaultTextButtonStyle");
private static final Value<String> TAB_STYLE_INACTIVE = Value.ref("Common.ui", "SecondaryTextButtonStyle");
private static final String TAB_NPC = "NPC";
private static final String TAB_ITEMS = "Items";
private static final String TAB_MODEL = "Model";
private static final String KEY_SELECT_AN_ITEM = "server.customUI.entitySpawnPage.selectAnItem";
private static final String KEY_SELECT_AN_NPC = "server.customUI.entitySpawnPage.selectAnNpc";
private static final String KEY_SELECT_A_MODEL = "server.customUI.entitySpawnPage.selectAModel";
private static final int MAX_SPAWN_COUNT = 100;
private static final float BLOCK_ENTITY_BASE_SCALE = 2.0F;
private static final int LOOK_RAYCAST_DISTANCE = 4;
private static final int FALLBACK_RAYCAST_DOWN_DISTANCE = 3;
private static final double FALLBACK_RAYCAST_Y_OFFSET = 0.5;
@Nonnull
private String activeTab = "NPC";
@Nonnull
private String searchQuery = "";
private List<String> npcRoles;
@Nullable
private String selectedNpcRole;
private List<String> modelIds;
@Nullable
private String selectedModelId;
@Nullable
private String selectedItemId;
@Nullable
private Ref<EntityStore> modelPreview;
private Vector3d position;
private Vector3f rotation;
private float currentRotationOffset = 0.0F;
private float currentScale = 1.0F;
private float lastPreviewScale = 1.0F;
private long lastScaleUpdateTime = 0L;
public EntitySpawnPage(@Nonnull PlayerRef playerRef) {
super(playerRef, CustomPageLifetime.CanDismiss, EntitySpawnPage.EntitySpawnPageEventData.CODEC);
}
@Override
public void build(
@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store<EntityStore> store
) {
commandBuilder.append("Pages/EntitySpawnPage.ui");
eventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, "#SearchInput", EventData.of("@SearchQuery", "#SearchInput.Value"), false);
eventBuilder.addEventBinding(
CustomUIEventBindingType.ValueChanged,
"#RotationOffset",
new EventData().append("Type", "UpdateRotationOffset").append("@RotationOffset", "#RotationOffset.Value"),
false
);
eventBuilder.addEventBinding(
CustomUIEventBindingType.ValueChanged, "#ScaleSlider", new EventData().append("Type", "UpdateScale").append("@Scale", "#ScaleSlider.Value"), false
);
eventBuilder.addEventBinding(
CustomUIEventBindingType.MouseButtonReleased,
"#ScaleSlider",
new EventData().append("Type", "ScaleReleased").append("@Scale", "#ScaleSlider.Value"),
false
);
eventBuilder.addEventBinding(
CustomUIEventBindingType.Activating,
"#Spawn",
new EventData().append("Type", "Spawn").append("@Count", "#Count.Value").append("@Scale", "#ScaleSlider.Value"),
false
);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#TabNPC", new EventData().append("Type", "TabSwitch").append("Tab", "NPC"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#TabItems", new EventData().append("Type", "TabSwitch").append("Tab", "Items"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#TabModel", new EventData().append("Type", "TabSwitch").append("Tab", "Model"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Dropped, "#ItemMaterialSlot", new EventData().append("Type", "SetItemMaterial"), false);
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#ClearMaterial", new EventData().append("Type", "ClearMaterial"), false);
this.buildList(ref, store, commandBuilder, eventBuilder);
}
public void handleDataEvent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull EntitySpawnPage.EntitySpawnPageEventData data) {
if (data.searchQuery != null) {
this.searchQuery = data.searchQuery.trim().toLowerCase();
UICommandBuilder commandBuilder = new UICommandBuilder();
UIEventBuilder eventBuilder = new UIEventBuilder();
this.buildList(ref, store, commandBuilder, eventBuilder);
this.sendUpdate(commandBuilder, eventBuilder, false);
} else {
String var9 = data.type;
switch (var9) {
case "TabSwitch":
if (data.tab != null && !data.tab.equals(this.activeTab)) {
this.activeTab = data.tab;
this.searchQuery = "";
this.clearPreview(store);
UICommandBuilder commandBuilder = new UICommandBuilder();
UIEventBuilder eventBuilder = new UIEventBuilder();
this.updateTabVisibility(commandBuilder);
commandBuilder.set("#SearchInput.Value", "");
this.buildList(ref, store, commandBuilder, eventBuilder);
this.sendUpdate(commandBuilder, eventBuilder, false);
}
break;
case "Select":
this.handleSelect(ref, store, data);
break;
case "SetItemMaterial":
this.handleSetItemMaterial(ref, store, data);
break;
case "ClearMaterial":
this.clearSelectedItem(ref, store);
break;
case "UpdateRotationOffset":
this.currentRotationOffset = (float)Math.toRadians((double)data.rotationOffset);
if (this.modelPreview != null && this.modelPreview.isValid()) {
TransformComponent transform = store.getComponent(this.modelPreview, TransformComponent.getComponentType());
transform.getRotation().setYaw(this.rotation.getYaw() + this.currentRotationOffset);
HeadRotation headRotation = store.getComponent(this.modelPreview, HeadRotation.getComponentType());
if (headRotation != null) {
headRotation.getRotation().setYaw(this.rotation.getYaw() + this.currentRotationOffset);
}
}
break;
case "UpdateScale":
if (data.scale != null && data.scale >= 0.1F) {
this.currentScale = data.scale;
UICommandBuilder commandBuilder = new UICommandBuilder();
commandBuilder.set("#ScaleValue.Text", String.format("%.1f", this.currentScale));
this.sendUpdate(commandBuilder, null, false);
long now = System.currentTimeMillis();
if (this.modelPreview != null
&& this.modelPreview.isValid()
&& this.currentScale != this.lastPreviewScale
&& now - this.lastScaleUpdateTime >= 200L) {
this.lastScaleUpdateTime = now;
this.lastPreviewScale = this.currentScale;
this.updatePreviewScale(ref, store);
}
}
break;
case "ScaleReleased":
if (data.scale != null && data.scale >= 0.1F) {
this.currentScale = data.scale;
if ("Model".equals(this.activeTab) && this.selectedModelId != null) {
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(this.selectedModelId);
if (modelAsset != null) {
UICommandBuilder commandBuilder = new UICommandBuilder();
this.createOrUpdatePreview(ref, store, commandBuilder, Model.createStaticScaledModel(modelAsset, this.currentScale));
}
} else if ("Items".equals(this.activeTab) && this.selectedItemId != null) {
Item item = Item.getAssetMap().getAsset(this.selectedItemId);
if (item != null) {
Model model = this.getItemModel(item);
if (model != null) {
UICommandBuilder commandBuilder = new UICommandBuilder();
this.createOrUpdatePreview(ref, store, commandBuilder, model);
} else if (item.hasBlockType()) {
this.createOrUpdateBlockPreview(ref, store, this.selectedItemId);
} else {
this.createOrUpdateItemPreview(ref, store, this.selectedItemId);
}
}
}
}
break;
case "Spawn":
this.handleSpawn(ref, store, data);
}
}
}
private void handleSelect(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull EntitySpawnPage.EntitySpawnPageEventData data) {
UICommandBuilder commandBuilder = new UICommandBuilder();
String var5 = this.activeTab;
switch (var5) {
case "NPC":
if (data.npcRole != null) {
this.selectNPCRole(ref, store, data.npcRole, commandBuilder);
}
break;
case "Items":
if (data.itemId != null) {
this.selectItem(ref, store, data.itemId, commandBuilder);
}
break;
case "Model":
if (data.modelId != null) {
this.selectModel(ref, store, data.modelId, commandBuilder);
}
}
this.sendUpdate(commandBuilder, null, false);
}
private void handleSetItemMaterial(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull EntitySpawnPage.EntitySpawnPageEventData data) {
if (data.itemStackId != null) {
UICommandBuilder commandBuilder = new UICommandBuilder();
this.selectItem(ref, store, data.itemStackId, commandBuilder);
this.sendUpdate(commandBuilder, null, false);
}
}
private void clearSelectedItem(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
this.selectedItemId = null;
this.clearPreview(store);
UICommandBuilder commandBuilder = new UICommandBuilder();
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAnItem"));
commandBuilder.set("#ItemMaterialSlot.Slots", new ItemGridSlot[]{new ItemGridSlot()});
commandBuilder.set("#ClearMaterial.Visible", false);
commandBuilder.set("#DropIndicator.Visible", true);
this.sendUpdate(commandBuilder, null, false);
}
private void handleSpawn(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull EntitySpawnPage.EntitySpawnPageEventData data) {
String var4 = this.activeTab;
switch (var4) {
case "NPC":
this.spawnNPC(ref, store, data.count);
break;
case "Items":
this.spawnItem(ref, store, data.count);
break;
case "Model":
this.spawnModel(ref, store, data.count);
}
}
private void spawnNPC(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, int count) {
if (this.selectedNpcRole != null && this.position != null && this.rotation != null) {
if (count >= 1 && count <= 100) {
this.clearPreview(store);
Vector3f spawnRotation = this.rotation.clone();
spawnRotation.setYaw(this.rotation.getYaw() + this.currentRotationOffset);
for (int i = 0; i < count; i++) {
NPCPlugin.get().spawnNPC(store, this.selectedNpcRole, null, this.position, spawnRotation);
}
store.getComponent(ref, Player.getComponentType()).getPageManager().setPage(ref, store, Page.None);
this.playerRef
.sendMessage(
Message.translation(count == 1 ? "server.npc.spawn.spawnedOne" : "server.npc.spawn.spawnedMany")
.param("quantity", count)
.param("type", this.selectedNpcRole)
);
}
}
}
private void spawnModel(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, int count) {
if (this.selectedModelId != null && this.position != null && this.rotation != null) {
if (count >= 1 && count <= 100) {
this.clearPreview(store);
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(this.selectedModelId);
if (modelAsset != null) {
Model model = Model.createStaticScaledModel(modelAsset, this.currentScale);
Vector3f spawnRotation = this.rotation.clone();
spawnRotation.setYaw(this.rotation.getYaw() + this.currentRotationOffset);
for (int i = 0; i < count; i++) {
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, spawnRotation));
holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model));
holder.addComponent(
PersistentModel.getComponentType(), new PersistentModel(new Model.ModelReference(this.selectedModelId, this.currentScale, null, true))
);
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(spawnRotation));
holder.addComponent(PropComponent.getComponentType(), PropComponent.get());
holder.ensureComponent(UUIDComponent.getComponentType());
store.addEntity(holder, AddReason.SPAWN);
}
store.getComponent(ref, Player.getComponentType()).getPageManager().setPage(ref, store, Page.None);
this.playerRef
.sendMessage(
Message.translation("server.customUI.entitySpawnPage.spawnedModel").param("quantity", count).param("model", this.selectedModelId)
);
}
}
}
}
@Override
public void onDismiss(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
this.clearPreview(store);
}
private void clearPreview(@Nonnull Store<EntityStore> store) {
if (this.modelPreview != null && this.modelPreview.isValid()) {
store.removeEntity(this.modelPreview, RemoveReason.REMOVE);
}
this.modelPreview = null;
}
private void updateTabVisibility(@Nonnull UICommandBuilder commandBuilder) {
commandBuilder.set("#NPCContent.Visible", this.activeTab.equals("NPC"));
commandBuilder.set("#ItemsContent.Visible", this.activeTab.equals("Items"));
commandBuilder.set("#ModelContent.Visible", this.activeTab.equals("Model"));
commandBuilder.set("#TabNPC.Style", this.activeTab.equals("NPC") ? TAB_STYLE_ACTIVE : TAB_STYLE_INACTIVE);
commandBuilder.set("#TabItems.Style", this.activeTab.equals("Items") ? TAB_STYLE_ACTIVE : TAB_STYLE_INACTIVE);
commandBuilder.set("#TabModel.Style", this.activeTab.equals("Model") ? TAB_STYLE_ACTIVE : TAB_STYLE_INACTIVE);
commandBuilder.set("#RotationGroup.Visible", this.activeTab.equals("Model") || this.activeTab.equals("NPC") || this.activeTab.equals("Items"));
commandBuilder.set("#ScaleGroup.Visible", this.activeTab.equals("Model") || this.activeTab.equals("Items"));
if (this.activeTab.equals("Model") || this.activeTab.equals("Items")) {
commandBuilder.set("#ScaleSlider.Value", this.currentScale);
commandBuilder.set("#ScaleValue.Text", String.format("%.1f", this.currentScale));
}
}
private void buildList(
@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder
) {
this.updateTabVisibility(commandBuilder);
String var5 = this.activeTab;
switch (var5) {
case "NPC":
this.buildNPCList(ref, store, commandBuilder, eventBuilder);
break;
case "Items":
this.buildItemsContent(ref, store, commandBuilder);
break;
case "Model":
this.buildModelList(ref, store, commandBuilder, eventBuilder);
}
}
private void buildNPCList(
@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder
) {
commandBuilder.clear("#NPCList");
List<String> roleTemplateNames = NPCPlugin.get().getRoleTemplateNames(true);
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.npcRoles = map.keySet().stream().sorted().sorted(Comparator.comparingInt(map::getInt).reversed()).limit(20L).collect(Collectors.toList());
} else {
roleTemplateNames.sort(String::compareTo);
this.npcRoles = roleTemplateNames;
}
int i = 0;
for (int bound = this.npcRoles.size(); i < bound; i++) {
String id = this.npcRoles.get(i);
String selector = "#NPCList[" + i + "]";
commandBuilder.append("#NPCList", "Common/TextButton.ui");
commandBuilder.set(selector + " #Button.Text", id);
eventBuilder.addEventBinding(
CustomUIEventBindingType.Activating, selector + " #Button", new EventData().append("Type", "Select").append("NPCRole", id), false
);
}
if (!this.npcRoles.isEmpty() && this.selectedNpcRole != null) {
if (this.npcRoles.contains(this.selectedNpcRole)) {
this.selectNPCRole(ref, store, this.selectedNpcRole, commandBuilder);
} else {
this.selectedNpcRole = null;
this.clearPreview(store);
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAnNpc"));
}
} else if (this.selectedNpcRole == null) {
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAnNpc"));
}
}
private void buildModelList(
@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder
) {
commandBuilder.clear("#ModelList");
Set<String> itemAndBlockModelPaths = new HashSet<>();
for (Item item : Item.getAssetMap().getAssetMap().values()) {
if (item.getModel() != null) {
itemAndBlockModelPaths.add(item.getModel());
}
}
for (BlockType blockType : BlockType.getAssetMap().getAssetMap().values()) {
if (blockType.getCustomModel() != null) {
itemAndBlockModelPaths.add(blockType.getCustomModel());
}
}
Set<String> allModels = new HashSet<>();
for (Entry<String, ModelAsset> entry : ModelAsset.getAssetMap().getAssetMap().entrySet()) {
String modelPath = entry.getValue().getModel();
if (modelPath != null && !itemAndBlockModelPaths.contains(modelPath) && !modelPath.startsWith("Items/Projectiles/")) {
allModels.add(entry.getKey());
}
}
if (!this.searchQuery.isEmpty()) {
Object2IntMap<String> map = new Object2IntOpenHashMap(allModels.size());
for (String value : allModels) {
int fuzzyDistance = StringCompareUtil.getFuzzyDistance(value, this.searchQuery, Locale.ENGLISH);
if (fuzzyDistance > 0) {
map.put(value, fuzzyDistance);
}
}
this.modelIds = map.keySet().stream().sorted().sorted(Comparator.comparingInt(map::getInt).reversed()).limit(20L).collect(Collectors.toList());
} else {
this.modelIds = allModels.stream().sorted().collect(Collectors.toList());
}
int i = 0;
for (int bound = this.modelIds.size(); i < bound; i++) {
String id = this.modelIds.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("ModelId", id), false
);
}
if (!this.modelIds.isEmpty() && this.selectedModelId != null) {
if (this.modelIds.contains(this.selectedModelId)) {
this.selectModel(ref, store, this.selectedModelId, commandBuilder);
} else {
this.selectedModelId = null;
this.clearPreview(store);
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAModel"));
}
} else if (this.selectedModelId == null) {
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAModel"));
}
}
private void buildItemsContent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder) {
if (this.selectedItemId != null) {
Item item = Item.getAssetMap().getAsset(this.selectedItemId);
if (item != null) {
commandBuilder.set("#SelectedName.Text", this.selectedItemId);
commandBuilder.set("#ItemMaterialSlot.Slots", new ItemGridSlot[]{new ItemGridSlot(new ItemStack(this.selectedItemId, 1))});
commandBuilder.set("#ClearMaterial.Visible", true);
commandBuilder.set("#DropIndicator.Visible", false);
}
} else {
commandBuilder.set("#SelectedName.Text", Message.translation("server.customUI.entitySpawnPage.selectAnItem"));
commandBuilder.set("#ItemMaterialSlot.Slots", new ItemGridSlot[]{new ItemGridSlot()});
commandBuilder.set("#ClearMaterial.Visible", false);
commandBuilder.set("#DropIndicator.Visible", true);
}
}
private void selectItem(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String itemId, @Nonnull UICommandBuilder commandBuilder) {
Item item = Item.getAssetMap().getAsset(itemId);
if (item != null) {
this.selectedItemId = itemId;
commandBuilder.set("#SelectedName.Text", itemId);
commandBuilder.set("#ItemMaterialSlot.Slots", new ItemGridSlot[]{new ItemGridSlot(new ItemStack(itemId, 1))});
commandBuilder.set("#ClearMaterial.Visible", true);
commandBuilder.set("#DropIndicator.Visible", false);
Model model = this.getItemModel(item);
if (model != null) {
this.createOrUpdatePreview(ref, store, commandBuilder, model);
} else if (item.hasBlockType()) {
this.createOrUpdateBlockPreview(ref, store, itemId);
} else {
this.createOrUpdateItemPreview(ref, store, itemId);
}
}
}
private void spawnItem(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, int count) {
if (this.selectedItemId != null && this.position != null && this.rotation != null) {
if (count >= 1 && count <= 100) {
Item item = Item.getAssetMap().getAsset(this.selectedItemId);
if (item != null) {
this.clearPreview(store);
Vector3f spawnRotation = this.rotation.clone();
spawnRotation.setYaw(this.rotation.getYaw() + this.currentRotationOffset);
Model model = this.getItemModel(item);
if (model != null) {
String modelId = this.getItemModelId(item);
for (int i = 0; i < count; i++) {
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, spawnRotation));
holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model));
holder.addComponent(
PersistentModel.getComponentType(), new PersistentModel(new Model.ModelReference(modelId, this.currentScale, null, true))
);
ItemStack itemStack = new ItemStack(this.selectedItemId, 1);
itemStack.setOverrideDroppedItemAnimation(true);
holder.addComponent(ItemComponent.getComponentType(), new ItemComponent(itemStack));
holder.addComponent(PreventPickup.getComponentType(), PreventPickup.INSTANCE);
holder.addComponent(PreventItemMerging.getComponentType(), PreventItemMerging.INSTANCE);
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(spawnRotation));
holder.addComponent(PropComponent.getComponentType(), PropComponent.get());
holder.ensureComponent(UUIDComponent.getComponentType());
store.addEntity(holder, AddReason.SPAWN);
}
} else if (item.hasBlockType()) {
for (int i = 0; i < count; i++) {
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(BlockEntity.getComponentType(), new BlockEntity(this.selectedItemId));
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, spawnRotation));
holder.addComponent(EntityScaleComponent.getComponentType(), new EntityScaleComponent(this.currentScale * 2.0F));
ItemStack itemStack = new ItemStack(this.selectedItemId, 1);
itemStack.setOverrideDroppedItemAnimation(true);
holder.addComponent(ItemComponent.getComponentType(), new ItemComponent(itemStack));
holder.addComponent(PreventPickup.getComponentType(), PreventPickup.INSTANCE);
holder.addComponent(PreventItemMerging.getComponentType(), PreventItemMerging.INSTANCE);
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(spawnRotation));
holder.addComponent(PropComponent.getComponentType(), PropComponent.get());
holder.ensureComponent(UUIDComponent.getComponentType());
store.addEntity(holder, AddReason.SPAWN);
}
} else {
for (int i = 0; i < count; i++) {
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, spawnRotation));
ItemStack itemStack = new ItemStack(this.selectedItemId, 1);
itemStack.setOverrideDroppedItemAnimation(true);
holder.addComponent(ItemComponent.getComponentType(), new ItemComponent(itemStack));
holder.addComponent(EntityScaleComponent.getComponentType(), new EntityScaleComponent(this.currentScale));
holder.addComponent(PreventPickup.getComponentType(), PreventPickup.INSTANCE);
holder.addComponent(PreventItemMerging.getComponentType(), PreventItemMerging.INSTANCE);
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(spawnRotation));
holder.addComponent(PropComponent.getComponentType(), PropComponent.get());
store.addEntity(holder, AddReason.SPAWN);
}
}
store.getComponent(ref, Player.getComponentType()).getPageManager().setPage(ref, store, Page.None);
this.playerRef
.sendMessage(Message.translation("server.customUI.entitySpawnPage.spawnedItem").param("quantity", count).param("item", this.selectedItemId));
}
}
}
}
@Nullable
private String getItemModelId(@Nonnull Item item) {
String modelId = item.getModel();
if (modelId == null && item.hasBlockType()) {
BlockType blockType = BlockType.getAssetMap().getAsset(item.getId());
if (blockType != null && blockType.getCustomModel() != null) {
modelId = blockType.getCustomModel();
}
}
return modelId;
}
@Nullable
private Model getItemModel(@Nonnull Item item) {
String modelId = this.getItemModelId(item);
if (modelId == null) {
return null;
} else {
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(modelId);
return modelAsset != null ? Model.createStaticScaledModel(modelAsset, this.currentScale) : null;
}
}
private void selectNPCRole(
@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String npcRole, @Nonnull UICommandBuilder commandBuilder
) {
if (this.selectedNpcRole != null && this.npcRoles.contains(this.selectedNpcRole)) {
commandBuilder.set("#NPCList[" + this.npcRoles.indexOf(this.selectedNpcRole) + "] #Button.Style", BUTTON_LABEL_STYLE);
}
commandBuilder.set("#NPCList[" + this.npcRoles.indexOf(npcRole) + "] #Button.Style", BUTTON_LABEL_STYLE_SELECTED);
commandBuilder.set("#SelectedName.Text", npcRole);
this.selectedNpcRole = npcRole;
this.createOrUpdatePreview(ref, store, commandBuilder, this.getNPCModel());
}
private void selectModel(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String modelId, @Nonnull UICommandBuilder commandBuilder) {
if (this.selectedModelId != null && this.modelIds.contains(this.selectedModelId)) {
commandBuilder.set("#ModelList[" + this.modelIds.indexOf(this.selectedModelId) + "] #Button.Style", BUTTON_LABEL_STYLE);
}
commandBuilder.set("#ModelList[" + this.modelIds.indexOf(modelId) + "] #Button.Style", BUTTON_LABEL_STYLE_SELECTED);
commandBuilder.set("#SelectedName.Text", modelId);
this.selectedModelId = modelId;
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(modelId);
if (modelAsset != null) {
this.createOrUpdatePreview(ref, store, commandBuilder, Model.createStaticScaledModel(modelAsset, this.currentScale));
}
}
private void initPosition(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
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 direction = Transform.getDirection(headRotation.getPitch(), headRotation.getYaw());
Vector3d lookTarget = TargetUtil.getTargetLocation(ref, 4.0, store);
Vector3d previewPosition;
if (lookTarget != null) {
previewPosition = lookTarget;
} else {
Vector3d aheadPosition = playerPosition.clone().add(direction.clone().scale(4.0));
World world = store.getExternalData().getWorld();
Vector3i groundTarget = TargetUtil.getTargetBlock(
world, (blockId, fluidId) -> blockId != 0, aheadPosition.x, aheadPosition.y + 0.5, aheadPosition.z, 0.0, -1.0, 0.0, 3.0
);
if (groundTarget != null) {
previewPosition = new Vector3d((double)groundTarget.x + 0.5, (double)(groundTarget.y + 1), (double)groundTarget.z + 0.5);
} else {
previewPosition = aheadPosition;
}
}
Vector3d relativePos = playerPosition.clone().subtract(previewPosition);
relativePos.setY(0.0);
Vector3f previewRotation = Vector3f.lookAt(relativePos);
this.position = previewPosition;
this.rotation = previewRotation;
this.currentRotationOffset = 0.0F;
}
private void createOrUpdatePreview(
@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull UICommandBuilder commandBuilder, @Nullable Model model
) {
if (model != null) {
if (this.modelPreview != null && this.modelPreview.isValid()) {
store.putComponent(this.modelPreview, ModelComponent.getComponentType(), new ModelComponent(model));
} else {
this.initPosition(ref, store);
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(this.position, this.rotation));
holder.addComponent(ModelComponent.getComponentType(), new ModelComponent(model));
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(this.rotation));
this.modelPreview = store.addEntity(holder, AddReason.SPAWN);
this.lastPreviewScale = this.currentScale;
}
}
}
private void updatePreviewScale(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store) {
if (this.modelPreview != null && this.modelPreview.isValid()) {
EntityScaleComponent existingScale = store.getComponent(this.modelPreview, EntityScaleComponent.getComponentType());
if (existingScale != null) {
boolean hasBlock = store.getComponent(this.modelPreview, BlockEntity.getComponentType()) != null;
existingScale.setScale(hasBlock ? this.currentScale * 2.0F : this.currentScale);
} else if ("Model".equals(this.activeTab) && this.selectedModelId != null) {
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(this.selectedModelId);
if (modelAsset != null) {
store.putComponent(
this.modelPreview, ModelComponent.getComponentType(), new ModelComponent(Model.createStaticScaledModel(modelAsset, this.currentScale))
);
}
}
}
}
private void createOrUpdateBlockPreview(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String blockTypeKey) {
this.clearPreview(store);
this.initPosition(ref, store);
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(EntityStore.REGISTRY.getNonSerializedComponentType(), NonSerialized.get());
holder.addComponent(BlockEntity.getComponentType(), new BlockEntity(blockTypeKey));
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, this.rotation));
holder.addComponent(EntityScaleComponent.getComponentType(), new EntityScaleComponent(this.currentScale * 2.0F));
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(this.rotation));
this.modelPreview = store.addEntity(holder, AddReason.SPAWN);
this.lastPreviewScale = this.currentScale;
}
private void createOrUpdateItemPreview(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull String itemId) {
this.clearPreview(store);
this.initPosition(ref, store);
Holder<EntityStore> holder = store.getRegistry().newHolder();
holder.addComponent(NetworkId.getComponentType(), new NetworkId(store.getExternalData().takeNextNetworkId()));
holder.addComponent(EntityStore.REGISTRY.getNonSerializedComponentType(), NonSerialized.get());
ItemStack itemStack = new ItemStack(itemId, 1);
itemStack.setOverrideDroppedItemAnimation(true);
holder.addComponent(ItemComponent.getComponentType(), new ItemComponent(itemStack));
holder.addComponent(EntityScaleComponent.getComponentType(), new EntityScaleComponent(this.currentScale));
holder.addComponent(PreventPickup.getComponentType(), PreventPickup.INSTANCE);
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(this.position, this.rotation));
holder.addComponent(HeadRotation.getComponentType(), new HeadRotation(this.rotation));
this.modelPreview = store.addEntity(holder, AddReason.SPAWN);
this.lastPreviewScale = this.currentScale;
}
@Nullable
private Model getNPCModel() {
NPCPlugin npcPlugin = NPCPlugin.get();
int roleIndex = npcPlugin.getIndex(this.selectedNpcRole);
npcPlugin.forceValidation(roleIndex);
BuilderInfo roleBuilderInfo = npcPlugin.getRoleBuilderInfo(roleIndex);
if (roleBuilderInfo == null) {
throw new IllegalStateException("Can't find a matching role builder");
} else if (!npcPlugin.testAndValidateRole(roleBuilderInfo)) {
throw new GeneralCommandException(Message.translation("server.commands.npc.spawn.validation_failed"));
} else {
Builder<Role> roleBuilder = npcPlugin.tryGetCachedValidRole(roleIndex);
if (roleBuilder == null) {
throw new IllegalArgumentException("Can't find a matching role builder");
} else if (roleBuilder instanceof ISpawnableWithModel spawnable) {
if (!roleBuilder.isSpawnable()) {
throw new IllegalArgumentException("Abstract role templates cannot be spawned directly - a variant needs to be created!");
} else {
SpawningContext spawningContext = new SpawningContext();
if (!spawningContext.setSpawnable(spawnable)) {
throw new GeneralCommandException(Message.translation("server.commands.npc.spawn.cantSetRolebuilder"));
} else {
return spawningContext.getModel();
}
}
} else {
throw new IllegalArgumentException("Role builder must support ISpawnableWithModel interface");
}
}
}
public static class EntitySpawnPageEventData {
static final String KEY_NPC_ROLE = "NPCRole";
static final String KEY_MODEL_ID = "ModelId";
static final String KEY_ITEM_ID = "ItemId";
static final String KEY_ITEM_STACK_ID = "ItemStackId";
static final String KEY_TYPE = "Type";
static final String KEY_TAB = "Tab";
static final String KEY_SEARCH_QUERY = "@SearchQuery";
static final String KEY_COUNT = "@Count";
static final String KEY_ROTATION_OFFSET = "@RotationOffset";
static final String KEY_SCALE = "@Scale";
public static final BuilderCodec<EntitySpawnPage.EntitySpawnPageEventData> CODEC = BuilderCodec.builder(
EntitySpawnPage.EntitySpawnPageEventData.class, EntitySpawnPage.EntitySpawnPageEventData::new
)
.append(new KeyedCodec<>("NPCRole", Codec.STRING), (entry, s) -> entry.npcRole = s, entry -> entry.npcRole)
.add()
.append(new KeyedCodec<>("ModelId", Codec.STRING), (entry, s) -> entry.modelId = s, entry -> entry.modelId)
.add()
.append(new KeyedCodec<>("ItemId", Codec.STRING), (entry, s) -> entry.itemId = s, entry -> entry.itemId)
.add()
.append(new KeyedCodec<>("ItemStackId", Codec.STRING), (entry, s) -> entry.itemStackId = s, entry -> entry.itemStackId)
.add()
.append(new KeyedCodec<>("Type", Codec.STRING), (entry, s) -> entry.type = s, entry -> entry.type)
.add()
.append(new KeyedCodec<>("Tab", Codec.STRING), (entry, s) -> entry.tab = s, entry -> entry.tab)
.add()
.append(new KeyedCodec<>("@SearchQuery", Codec.STRING), (entry, s) -> entry.searchQuery = s, entry -> entry.searchQuery)
.add()
.append(new KeyedCodec<>("@Count", Codec.INTEGER), (entry, s) -> entry.count = s, entry -> entry.count)
.add()
.append(new KeyedCodec<>("@RotationOffset", Codec.FLOAT), (entry, s) -> entry.rotationOffset = s, entry -> entry.rotationOffset)
.add()
.append(new KeyedCodec<>("@Scale", Codec.FLOAT), (entry, s) -> entry.scale = s, entry -> entry.scale)
.add()
.build();
private String npcRole;
private String modelId;
private String itemId;
private String itemStackId;
private String type;
private String tab;
private String searchQuery;
private int count;
private float rotationOffset;
private Float scale;
public EntitySpawnPageEventData() {
}
}
}