entity spawning palette item
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-09-30 17:35:52 -04:00
parent 8e2f357101
commit 5f54e5034c
16 changed files with 291 additions and 4 deletions

View File

@ -373,7 +373,8 @@
"path" : "Models/basic/geometry/unitcapsule.glb"
}
},
"clientSidePrimary": "OPEN_SPAWN_PALETTE",
"clientSidePrimary": "LEVEL_EDIT_SPAWN",
"clientSideSecondary": "OPEN_SPAWN_SELECT",
"collidable": {
"type" : "CUBE",
"dimension1" : 0.1,

View File

@ -16,5 +16,17 @@ export const clientHooks: Hook[] = [
callback: (engine: Engine) => {
engine.classes.voxelUtils.static.applyEdit()
}
},
{
signal: "OPEN_SPAWN_SELECT",
callback: (engine: Engine) => {
engine.classes.menuUtils.static.openSpawnSelection()
}
},
{
signal: "LEVEL_EDIT_SPAWN",
callback: (engine: Engine) => {
engine.classes.levelEditorUtils.static.spawnEntity()
}
}
]

View File

@ -0,0 +1,11 @@
/**
* Utilities for editing levels
*/
export interface ClientLevelEditorUtils {
/**
* Spawns the selected entity
*/
readonly spawnEntity: () => void
}

View File

@ -8,4 +8,9 @@ export interface MenuUtils {
*/
readonly openVoxel: () => void
/**
* Opens the menu to select what to spawn
*/
readonly openSpawnSelection: () => void
}

View File

@ -1,3 +1,4 @@
import { ClientLevelEditorUtils } from "/Scripts/types/host/client/client-level-editor-utils";
import { ClientVoxelUtils } from "/Scripts/types/host/client/client-voxel-utils";
import { Entity } from "/Scripts/types/host/entity/entity";
import { MenuUtils } from "/Scripts/types/host/renderer/ui/menus";
@ -40,6 +41,11 @@ export interface StaticClasses {
*/
readonly voxelUtils?: Class<ClientVoxelUtils>,
/**
* Utilities for level editing
*/
readonly levelEditorUtils?: Class<ClientLevelEditorUtils>,
}
/**

View File

@ -860,7 +860,7 @@ Toolbar scrolling
Items executing script engine hooks on usage
Fix server attack tree regressions
Editing voxels hook and extensions for voxel palette item
Entity spawning palette item actually working via hooks
# TODO

View File

@ -35,6 +35,9 @@ public class WindowStrings {
//the voxel type selection menu
public static final String VOXEL_TYPE_SELECTION = "voxelTypeSelection";
//the voxel type selection menu
public static final String SPAWN_TYPE_SELECTION = "spawnTypeSelection";
//the tutorial popup
public static final String TUTORIAL_POPUP = "tutorialPopup";

View File

@ -288,9 +288,9 @@ public class MenuGeneratorsLevelEditor {
fillInDefaultContent(scrollable);
}));
//button for spawning all foliage types
//button for spawning all object types
for(CommonEntityType object : Globals.gameConfigCurrent.getObjectTypeMap().getTypes()){
//spawn foliage button
//spawn object button
scrollable.addChild(Button.createButton("Spawn " + object.getId(), () -> {
LoggerInterface.loggerEngine.INFO("spawn " + object.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));

View File

@ -8,8 +8,10 @@ import electrosphere.client.ui.menu.WindowUtils;
import electrosphere.controls.ControlHandler.ControlsState;
import electrosphere.engine.Globals;
import electrosphere.engine.signal.Signal.SignalType;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.voxel.VoxelData;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.renderer.ui.components.SpawnSelectionPanel;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.ImagePanel;
@ -33,6 +35,7 @@ import electrosphere.renderer.ui.events.NavigationEvent;
public class MenuGeneratorsTerrainEditing {
static Window terrainEditingSidePanelWindow;
static Window entitySelectionWindow;
//text input
static final int TEXT_INPUT_HEIGHT = 50;
@ -190,4 +193,37 @@ public class MenuGeneratorsTerrainEditing {
}
}
/**
* Creates the entity type selection window
* @return
*/
public static Window createEntityTypeSelectionPanel(){
//setup window
entitySelectionWindow = Window.create(Globals.renderingEngine.getOpenGLState(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, true);
entitySelectionWindow.setParentAlignContent(YogaAlignment.Center);
entitySelectionWindow.setParentJustifyContent(YogaJustification.Center);
entitySelectionWindow.setParentAlignItem(YogaAlignment.Center);
entitySelectionWindow.setAlignContent(YogaAlignment.Center);
entitySelectionWindow.setAlignItems(YogaAlignment.Center);
entitySelectionWindow.setJustifyContent(YogaJustification.Center);
entitySelectionWindow.setFlexDirection(YogaFlexDirection.Column);
//nav logic
entitySelectionWindow.setOnNavigationCallback(new NavigationEventCallback() {public boolean execute(NavigationEvent event){
WindowUtils.closeWindow(WindowStrings.SPAWN_TYPE_SELECTION);
Globals.controlHandler.hintUpdateControlState(ControlsState.MAIN_GAME);
MenuGeneratorsLevelEditor.voxelWindowOpen = false;
return false;
}});
//attach scrollable after search input for organzation purposes
entitySelectionWindow.addChild(SpawnSelectionPanel.createEntityTypeSelectionPanel((CommonEntityType type) -> {
Globals.selectedSpawntype = type;
}));
Globals.signalSystem.post(SignalType.YOGA_APPLY,entitySelectionWindow);
return entitySelectionWindow;
}
}

View File

@ -16,6 +16,7 @@ import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.renderer.actor.ActorUtils;
@ -23,6 +24,7 @@ import electrosphere.renderer.ui.components.CharacterCustomizer;
import electrosphere.renderer.ui.components.EquipmentInventoryPanel;
import electrosphere.renderer.ui.components.InputMacros;
import electrosphere.renderer.ui.components.NaturalInventoryPanel;
import electrosphere.renderer.ui.components.SpawnSelectionPanel;
import electrosphere.renderer.ui.elements.ActorPanel;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.FormElement;
@ -60,6 +62,7 @@ public class MenuGeneratorsUITesting {
"NaturalInventoryPanel",
"EquipInventoryPanel",
"VoxelPicker",
"EntitySpawnPicker",
}),
(ValueChangeEvent event) -> {
attachComponent(rVal,event.getAsString());
@ -140,6 +143,12 @@ public class MenuGeneratorsUITesting {
} break;
case "VoxelPicker": {
formEl.addChild(MenuGeneratorsTerrainEditing.createVoxelTypeSelectionPanel((VoxelType voxelType) -> {
System.out.println(voxelType.getName());
}));
} break;
case "EntitySpawnPicker": {
formEl.addChild(SpawnSelectionPanel.createEntityTypeSelectionPanel((CommonEntityType entType) -> {
System.out.println(entType.getId());
}));
} break;
}

View File

@ -0,0 +1,69 @@
package electrosphere.client.ui.menu.script;
import java.util.Random;
import org.graalvm.polyglot.HostAccess.Export;
import org.joml.Vector3d;
import electrosphere.client.entity.camera.CameraEntityUtils;
import electrosphere.collision.CollisionEngine;
import electrosphere.engine.Globals;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.foliage.FoliageUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.creature.type.CreatureData;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.game.data.item.type.Item;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.datacell.Realm;
/**
* Utilities for editing levels
*/
public class ScriptLevelEditorUtils {
//vertical offset from cursor position to spawn things at
static final Vector3d cursorVerticalOffset = new Vector3d(0,0.05,0);
/**
* Spawns the selected entity
*/
@Export
public static void spawnEntity(){
if(Globals.selectedSpawntype instanceof CreatureData){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.selectedSpawntype.getId(), null);
} else if(Globals.selectedSpawntype instanceof Item){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.selectedSpawntype.getId());
} else if(Globals.selectedSpawntype instanceof FoliageType){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.selectedSpawntype.getId(), new Random().nextLong());
} else {
LoggerInterface.loggerEngine.INFO("spawn " + Globals.selectedSpawntype.getId() + "!");
Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(Globals.playerCamera));
Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(Globals.playerCamera));
Realm realm = Globals.realmManager.getRealms().iterator().next();
CollisionEngine clientCollisionEngine = Globals.clientSceneWrapper.getCollisionEngine(); //using client collision engine so ray doesn't collide with player entity
Vector3d cursorPos = clientCollisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0).add(cursorVerticalOffset);
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.selectedSpawntype.getId());
}
}
}

View File

@ -22,4 +22,13 @@ public class ScriptMenuUtils {
Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU);
}
/**
* Opens the menu to select what to spawn
*/
@Export
public static void openSpawnSelection(){
WindowUtils.replaceWindow(WindowStrings.SPAWN_TYPE_SELECTION,MenuGeneratorsTerrainEditing.createEntityTypeSelectionPanel());
Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU);
}
}

View File

@ -258,6 +258,7 @@ public class ControlHandler {
static String[] controlBlockingWindows = new String[]{
WindowStrings.LEVEL_EDTIOR_SIDE_PANEL,
WindowStrings.VOXEL_TYPE_SELECTION,
WindowStrings.SPAWN_TYPE_SELECTION,
WindowStrings.WINDOW_CHARACTER,
WindowStrings.WINDOW_DEBUG,
WindowStrings.WINDOW_MENU_INGAME_MAIN,

View File

@ -43,6 +43,7 @@ import electrosphere.engine.time.Timekeeper;
import electrosphere.entity.Entity;
import electrosphere.entity.scene.Scene;
import electrosphere.game.config.UserSettings;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.particle.ParticleDefinition;
import electrosphere.game.data.voxel.VoxelType;
import electrosphere.game.server.structure.virtual.StructureManager;
@ -389,6 +390,8 @@ public class Globals {
//client current selected voxel type
public static VoxelType clientSelectedVoxelType = null;
//the selected type of entity to spawn
public static CommonEntityType selectedSpawntype = null;
//skybox entity
public static Entity skybox;

View File

@ -0,0 +1,120 @@
package electrosphere.renderer.ui.components;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Consumer;
import electrosphere.engine.Globals;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.renderer.ui.elements.Button;
import electrosphere.renderer.ui.elements.Div;
import electrosphere.renderer.ui.elements.Label;
import electrosphere.renderer.ui.elements.TextInput;
import electrosphere.renderer.ui.elements.VirtualScrollable;
import electrosphere.renderer.ui.elementtypes.ClickableElement.ClickEventCallback;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaAlignment;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaFlexDirection;
import electrosphere.renderer.ui.elementtypes.ContainerElement.YogaJustification;
import electrosphere.renderer.ui.elementtypes.KeyEventElement.KeyboardEventCallback;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.KeyboardEvent;
/**
* A panel for selecting an entity to spawn
*/
public class SpawnSelectionPanel {
//text input
static final int TEXT_INPUT_HEIGHT = 50;
static final int TEXT_INPUT_WIDTH = 200;
//type selection
static final int SELECTION_SCROLLABLE_WIDTH = 500;
static final int SELECTION_SCROLLABLE_HEIGHT = 500;
//single listing
static final int MARGIN_EACH_SIDE = 5;
/**
* Creates the spawning menu selection panel
* @return
*/
public static Div createEntityTypeSelectionPanel(Consumer<CommonEntityType> onSelectType){
//setup window
Div rVal = Div.createDiv();
rVal.setAlignContent(YogaAlignment.Center);
rVal.setAlignItems(YogaAlignment.Center);
rVal.setJustifyContent(YogaJustification.Center);
rVal.setFlexDirection(YogaFlexDirection.Column);
//scrollable that contains all the voxel types
VirtualScrollable scrollable = new VirtualScrollable(SELECTION_SCROLLABLE_WIDTH, SELECTION_SCROLLABLE_HEIGHT);
scrollable.setFlexDirection(YogaFlexDirection.Column);
scrollable.setAlignItems(YogaAlignment.Start);
//search input
TextInput searchInput = TextInput.createTextInput();
searchInput.setWidth(TEXT_INPUT_WIDTH);
searchInput.setMinWidth(TEXT_INPUT_WIDTH);
searchInput.setMinHeight(20);
searchInput.setOnPress(new KeyboardEventCallback() {public boolean execute(KeyboardEvent event){
boolean rVal = searchInput.defaultKeyHandling(event);
fillInEntitySelectors(scrollable, searchInput.getText(), onSelectType);
return rVal;
}});
rVal.addChild(searchInput);
//attach scrollable after search input for organzation purposes
rVal.addChild(scrollable);
//final step
fillInEntitySelectors(scrollable, searchInput.getText(), onSelectType);
return rVal;
}
/**
* Fills in the types to display based on the contents of the search string
* @param scrollable the scrollable to drop selection buttons in to
* @param searchString the string to search based on
*/
static void fillInEntitySelectors(VirtualScrollable scrollable, String searchString, Consumer<CommonEntityType> onSelectType){
scrollable.clearChildren();
//get relevant types
List<CommonEntityType> types = new LinkedList<CommonEntityType>();
types.addAll(Globals.gameConfigCurrent.getCreatureTypeLoader().getTypes());
types.addAll(Globals.gameConfigCurrent.getFoliageMap().getFoliageList());
types.addAll(Globals.gameConfigCurrent.getItemMap().getItems());
types.addAll(Globals.gameConfigCurrent.getObjectTypeMap().getTypes());
types = types.stream().filter((type)->type.getId().toLowerCase().contains(searchString.toLowerCase())).toList();
//generate ui elements
for(CommonEntityType type : types){
Div containerDiv = Div.createDiv();
containerDiv.setMinWidthPercent(25.0f);
scrollable.addChild(containerDiv);
Button newButton = new Button();
newButton.setAlignItems(YogaAlignment.Center);
//margin
newButton.setMarginBottom(MARGIN_EACH_SIDE);
newButton.setMarginLeft(MARGIN_EACH_SIDE);
newButton.setMarginRight(MARGIN_EACH_SIDE);
newButton.setMarginTop(MARGIN_EACH_SIDE);
//label
Label voxelLabel = Label.createLabel(type.getId());
//button handling
newButton.addChild(voxelLabel);
newButton.setOnClick(new ClickEventCallback() {public boolean execute(ClickEvent event){
//set voxel type to this type
onSelectType.accept(type);
Globals.selectedSpawntype = type;
return false;
}});
containerDiv.addChild(newButton);
}
}
}

View File

@ -14,6 +14,7 @@ import org.graalvm.polyglot.Source.Builder;
import org.graalvm.polyglot.Value;
import electrosphere.client.script.ScriptClientVoxelUtils;
import electrosphere.client.ui.menu.script.ScriptLevelEditorUtils;
import electrosphere.client.ui.menu.script.ScriptMenuUtils;
import electrosphere.client.ui.menu.tutorial.TutorialMenus;
import electrosphere.engine.Globals;
@ -96,6 +97,7 @@ public class ScriptEngine {
{"serverUtils",JSServerUtils.class},
{"menuUtils",ScriptMenuUtils.class},
{"voxelUtils",ScriptClientVoxelUtils.class},
{"levelEditorUtils",ScriptLevelEditorUtils.class},
};
//singletons from the host that are provided to the javascript context