huge ai work
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-01 20:11:29 -04:00
parent 4908ea0f44
commit 48622bb08f
52 changed files with 1543 additions and 171 deletions

View File

@ -3,7 +3,8 @@
{ {
"id" : "bush4", "id" : "bush4",
"tokens" : [ "tokens" : [
"FLAMMABLE" "FLAMMABLE",
"HARVESTABLE"
], ],
"hitboxes" : [ "hitboxes" : [
{ {

View File

@ -3,6 +3,7 @@
{ {
"id" : "rock_static", "id" : "rock_static",
"tokens" : [ "tokens" : [
"HARVESTABLE"
], ],
"buttonInteraction" : { "buttonInteraction" : {
"onInteract" : "harvest", "onInteract" : "harvest",

View File

@ -5,7 +5,8 @@
"maxStack" : 100, "maxStack" : 100,
"tokens" : [ "tokens" : [
"GRAVITY", "GRAVITY",
"TARGETABLE" "TARGETABLE",
"HARVESTABLE"
], ],
"itemAudio": { "itemAudio": {
"uiGrabAudio" : "Audio/ui/items/specific/Pick Up Stone A.wav", "uiGrabAudio" : "Audio/ui/items/specific/Pick Up Stone A.wav",

View File

@ -5,7 +5,8 @@
"maxStack" : 100, "maxStack" : 100,
"tokens" : [ "tokens" : [
"GRAVITY", "GRAVITY",
"TARGETABLE" "TARGETABLE",
"HARVESTABLE"
], ],
"itemAudio": { "itemAudio": {
"uiGrabAudio" : "Audio/ui/items/specific/Pick Up Wood A.wav", "uiGrabAudio" : "Audio/ui/items/specific/Pick Up Wood A.wav",

View File

@ -20,6 +20,7 @@
"files": [ "files": [
"Data/game/recipes/weapons.json", "Data/game/recipes/weapons.json",
"Data/game/recipes/tools.json", "Data/game/recipes/tools.json",
"Data/game/recipes/fabs.json" "Data/game/recipes/fabs.json",
"Data/game/recipes/voxelrecipes.json"
] ]
} }

View File

@ -0,0 +1,23 @@
{
"recipes": [
{
"displayName": "Refiend Wood",
"craftingTag" : "HAND",
"ingredients": [
{
"itemType": "mat:Log",
"count": 1
}
],
"products": [
{
"itemType": "block:refined_wood",
"count": 1
}
]
}
],
"files": [
]
}

View File

@ -1607,7 +1607,7 @@ Items keep charge state
UI renders charge state UI renders charge state
Item stacking Item stacking
(04/20/2025) (04/30/2025)
Voxel placement improvements Voxel placement improvements
Smaller wall section Smaller wall section
First proper house~! First proper house~!
@ -1630,6 +1630,10 @@ DB characters store toolbar items
Scaffolding for new macro-cognizating ai approach Scaffolding for new macro-cognizating ai approach
AI work AI work
(05/01/2025)
Many new AI trees
- AI can seek out items
- AI can harvest entities

View File

@ -349,7 +349,8 @@ public class CameraEntityUtils {
*/ */
public static Vector3d getFacingVec(Quaterniond rotation){ public static Vector3d getFacingVec(Quaterniond rotation){
//quaternion is multiplied by pi because we want to point away from the eye of the camera, NOT towards it //quaternion is multiplied by pi because we want to point away from the eye of the camera, NOT towards it
Matrix4d rotationMat = new Matrix4d().rotate(rotation); Quaterniond quatd = new Quaterniond(rotation).normalize();
Matrix4d rotationMat = new Matrix4d().rotate(quatd);
Vector4d rotationVecRaw = SpatialMathUtils.getOriginVector4(); Vector4d rotationVecRaw = SpatialMathUtils.getOriginVector4();
rotationVecRaw = rotationMat.transform(rotationVecRaw); rotationVecRaw = rotationMat.transform(rotationVecRaw);
if(rotationVecRaw.length() < 0.001){ if(rotationVecRaw.length() < 0.001){

View File

@ -747,4 +747,21 @@ public class InventoryUtils {
return rVal; return rVal;
} }
/**
* Checks if the enity has a given type of tool
* @param entity The entity
* @param toolType The type of tool
* @return true if it has the type of tool, false otherwise
*/
public static boolean serverHasTool(Entity entity, String toolType){
List<Entity> items = InventoryUtils.getAllInventoryItems(entity);
for(Entity itemEnt : items){
Item itemData = Globals.gameConfigCurrent.getItemMap().getItem(itemEnt);
if(itemData.getTokens().contains(toolType)){
return true;
}
}
return false;
}
} }

View File

@ -124,8 +124,10 @@ public class ServerLifeTree implements BehaviorTree {
* Kills the entity * Kills the entity
*/ */
public void kill(){ public void kill(){
lifeCurrent = 0; if(this.getState() == LifeStateEnum.ALIVE){
this.setState(LifeStateEnum.DYING); lifeCurrent = 0;
this.setState(LifeStateEnum.DYING);
}
} }
/** /**

View File

@ -31,6 +31,7 @@ import electrosphere.net.synchronization.enums.BehaviorTreeIdEnums;
import electrosphere.net.synchronization.enums.FieldIdEnums; import electrosphere.net.synchronization.enums.FieldIdEnums;
import electrosphere.renderer.anim.Animation; import electrosphere.renderer.anim.Animation;
import electrosphere.script.utils.AccessTransforms; import electrosphere.script.utils.AccessTransforms;
import electrosphere.server.ai.AI;
import electrosphere.server.datacell.utils.DataCellSearchUtils; import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.utils.ServerScriptUtils; import electrosphere.server.utils.ServerScriptUtils;
import electrosphere.util.math.SpatialMathUtils; import electrosphere.util.math.SpatialMathUtils;
@ -175,8 +176,10 @@ public class ServerGroundMovementTree implements BehaviorTree {
Vector3d position = EntityUtils.getPosition(parent); Vector3d position = EntityUtils.getPosition(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent);
if(ServerPlayerViewDirTree.hasTree(parent)){ if(ServerPlayerViewDirTree.hasTree(parent)){
ServerPlayerViewDirTree serverViewTree =ServerPlayerViewDirTree.getTree(parent); if(AI.getAI(parent) == null || !AI.getAI(parent).isApplyToPlayer()){
facingVector = CameraEntityUtils.getFacingVec(serverViewTree.getYaw(), serverViewTree.getPitch()); ServerPlayerViewDirTree serverViewTree =ServerPlayerViewDirTree.getTree(parent);
facingVector = CameraEntityUtils.getFacingVec(serverViewTree.getYaw(), serverViewTree.getPitch());
}
} }
DBody body = PhysicsEntityUtils.getDBody(parent); DBody body = PhysicsEntityUtils.getDBody(parent);
DVector3C linearVelocity = body.getLinearVel(); DVector3C linearVelocity = body.getLinearVel();

View File

@ -19,7 +19,9 @@ import electrosphere.entity.Entity;
import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityCreationUtils;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.EntityTypes.EntityType;
import electrosphere.entity.types.collision.CollisionObjUtils; import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.renderer.meshgen.BlockMeshgen; import electrosphere.renderer.meshgen.BlockMeshgen;
import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData; import electrosphere.renderer.meshgen.BlockMeshgen.BlockMeshData;
@ -119,6 +121,7 @@ public class BlockChunkEntity {
Quaterniond entityRot = EntityUtils.getRotation(entity); Quaterniond entityRot = EntityUtils.getRotation(entity);
PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody); PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody);
entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
CommonEntityUtils.setEntityType(entity, EntityType.ENGINE);
} }
} }

View File

@ -136,6 +136,7 @@ public class TerrainChunk {
Quaterniond entityRot = EntityUtils.getRotation(entity); Quaterniond entityRot = EntityUtils.getRotation(entity);
PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody); PhysicsUtils.setRigidBodyTransform(realm.getCollisionEngine(), entityPos, entityRot, terrainBody);
entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true); entity.putData(EntityDataStrings.TERRAIN_IS_TERRAIN, true);
CommonEntityUtils.setEntityType(entity, EntityType.ENGINE);
} }
// ServerEntityUtils.initiallyPositionEntity(realm, rVal, position); // ServerEntityUtils.initiallyPositionEntity(realm, rVal, position);
// physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true); // physicsObject = PhysicsUtils.attachTerrainRigidBody(physicsEntity,heightmap,true);

View File

@ -18,6 +18,7 @@ import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.game.data.foliage.type.FoliageTypeLoader; import electrosphere.game.data.foliage.type.FoliageTypeLoader;
import electrosphere.game.data.foliage.type.model.FoliageTypeMap; import electrosphere.game.data.foliage.type.model.FoliageTypeMap;
import electrosphere.game.data.item.ItemDataMap; import electrosphere.game.data.item.ItemDataMap;
import electrosphere.game.data.item.source.ItemSourcingMap;
import electrosphere.game.data.projectile.ProjectileTypeHolder; import electrosphere.game.data.projectile.ProjectileTypeHolder;
import electrosphere.game.data.struct.StructureDataLoader; import electrosphere.game.data.struct.StructureDataLoader;
import electrosphere.game.data.tutorial.HintDefinition; import electrosphere.game.data.tutorial.HintDefinition;
@ -96,6 +97,11 @@ public class Config {
*/ */
StructureDataLoader structureData; StructureDataLoader structureData;
/**
* The item sourcing map for items
*/
ItemSourcingMap itemSourcingMap;
/** /**
* Loads the default data * Loads the default data
* @return The config * @return The config
@ -124,6 +130,9 @@ public class Config {
ItemDataMap.loadSpawnItems(config.itemMap, config.objectTypeLoader); ItemDataMap.loadSpawnItems(config.itemMap, config.objectTypeLoader);
ItemDataMap.generateBlockItems(config.itemMap, config.blockData); ItemDataMap.generateBlockItems(config.itemMap, config.blockData);
//construct the sourcing map
config.itemSourcingMap = ItemSourcingMap.parse(config);
//validate //validate
ConfigValidator.valdiate(config); ConfigValidator.valdiate(config);
@ -376,4 +385,12 @@ public class Config {
return this.structureData; return this.structureData;
} }
/**
* Gets the item sourcing map of the config
* @return The item sourcing map
*/
public ItemSourcingMap getItemSourcingMap(){
return this.itemSourcingMap;
}
} }

View File

@ -0,0 +1,23 @@
package electrosphere.game.data.common;
/**
* All common entity tokens
*/
public class CommonEntityTokens {
/**
* This entity is a tree foliage type
*/
public static final String TOKEN_TREE = "TREE";
/**
* An axe
*/
public static final String TOKEN_AXE = "AXE";
/**
* Flags this object as harvestable
*/
public static final String TOKEN_HARVESTABLE = "HARVESTABLE";
}

View File

@ -124,6 +124,15 @@ public class Item extends CommonEntityType {
return rVal; return rVal;
} }
/**
* Gets the id of the item type for a given block type
* @param blockType The block type
* @return The id of the corresponding item data
*/
public static String getBlockTypeId(BlockType blockType){
return "block:" + blockType.getName();
}
/** /**
* Creates item data from a block type * Creates item data from a block type
* @param description The block type * @param description The block type
@ -131,7 +140,7 @@ public class Item extends CommonEntityType {
*/ */
public static Item createBlockItem(BlockType blockType){ public static Item createBlockItem(BlockType blockType){
Item rVal = new Item(); Item rVal = new Item();
rVal.setId("block:" + blockType.getName()); rVal.setId(Item.getBlockTypeId(blockType));
if(blockType.getTexture() != null){ if(blockType.getTexture() != null){

View File

@ -0,0 +1,13 @@
package electrosphere.game.data.item;
/**
* Hardcoded item ids for items
*/
public class ItemIdStrings {
/**
* The basic stone axe
*/
public static final String ITEM_STONE_AXE = "Stone Axe";
}

View File

@ -0,0 +1,85 @@
package electrosphere.game.data.item.source;
import java.util.List;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.foliage.type.FoliageType;
/**
* Data that stores how an item can be sources
*/
public class ItemSourcingData {
/**
* Types of sources for items
*/
public static enum SourcingType {
/**
* Craft a recipe
*/
RECIPE,
/**
* Harvest from an item
*/
HARVEST,
/**
* Fell a tree
*/
TREE,
}
/**
* The list of recipes that create this item
*/
List<RecipeData> recipes;
/**
* The list of harvesting targets that can drop this item
*/
List<CommonEntityType> harvestTargets;
/**
* The list of trees that can drop this item
*/
List<FoliageType> trees;
/**
* Constructor
* @param recipes The recipes to source from
* @param harvestTargets The objects to harvest from
* @param trees The trees to drop from
*/
public ItemSourcingData(List<RecipeData> recipes, List<CommonEntityType> harvestTargets, List<FoliageType> trees){
this.recipes = recipes;
this.harvestTargets = harvestTargets;
this.trees = trees;
}
/**
* Gets the list of recipes that can produce this item
* @return The list of recipes
*/
public List<RecipeData> getRecipes() {
return recipes;
}
/**
* Gets the list of harvest targets that can drop this item
* @return The list of harvest targets
*/
public List<CommonEntityType> getHarvestTargets() {
return harvestTargets;
}
/**
* Gets the list of trees that can drop this item
* @return The list of trees
*/
public List<FoliageType> getTrees() {
return trees;
}
}

View File

@ -0,0 +1,129 @@
package electrosphere.game.data.item.source;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import electrosphere.game.data.Config;
import electrosphere.game.data.common.CommonEntityTokens;
import electrosphere.game.data.common.CommonEntityType;
import electrosphere.game.data.common.life.loot.LootPool;
import electrosphere.game.data.common.life.loot.LootTicket;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.crafting.RecipeDataMap;
import electrosphere.game.data.crafting.RecipeIngredientData;
import electrosphere.game.data.foliage.type.FoliageType;
import electrosphere.game.data.foliage.type.FoliageTypeLoader;
import electrosphere.game.data.item.Item;
import electrosphere.game.data.item.ItemDataMap;
/**
* Map of items to the methods to source them
*/
public class ItemSourcingMap {
/**
* Map of item id -> sourcing data
*/
private Map<String,ItemSourcingData> itemSourcingDataMap = new HashMap<String,ItemSourcingData>();
/**
* Parses a sourcing map from a given config
* @param config The config
* @return The sourcing map
*/
public static ItemSourcingMap parse(Config config){
ItemSourcingMap sourcingMap = new ItemSourcingMap();
RecipeDataMap recipeMap = config.getRecipeMap();
ItemDataMap itemMap = config.getItemMap();
FoliageTypeLoader foliageMap = config.getFoliageMap();
//structures that store sources
List<RecipeData> recipes;
List<CommonEntityType> harvestTargets;
List<FoliageType> trees;
//iterate through each item and find where they can be sources
for(Item item : itemMap.getTypes()){
//construct new lists for each item type
recipes = new LinkedList<RecipeData>();
harvestTargets = new LinkedList<CommonEntityType>();
trees = new LinkedList<FoliageType>();
//check if any recipes can create this item
for(RecipeData recipe : recipeMap.getTypes()){
for(RecipeIngredientData product : recipe.getProducts()){
if(product.getItemType().equals(item.getId())){
recipes.add(recipe);
break;
}
}
}
//check if any common objects can from this item as loot
for(CommonEntityType foliageEnt : foliageMap.getTypes()){
if(foliageEnt.getTokens() == null){
continue;
}
if(!foliageEnt.getTokens().contains(CommonEntityTokens.TOKEN_HARVESTABLE)){
continue;
}
if(foliageEnt.getHealthSystem() != null){
if(foliageEnt.getHealthSystem().getLootPool() != null){
LootPool lootPool = foliageEnt.getHealthSystem().getLootPool();
for(LootTicket ticket : lootPool.getTickets()){
if(ticket.getItemId().equals(item.getId())){
harvestTargets.add(foliageEnt);
break;
}
}
}
}
}
//check if any trees can drop the item
for(FoliageType foliage : foliageMap.getTypes()){
if(foliage.getTokens() != null && foliage.getTokens().contains(CommonEntityTokens.TOKEN_TREE)){
if(foliage.getHealthSystem() != null){
if(foliage.getHealthSystem().getLootPool() != null){
LootPool lootPool = foliage.getHealthSystem().getLootPool();
for(LootTicket ticket : lootPool.getTickets()){
if(ticket.getItemId().equals(item.getId())){
trees.add(foliage);
break;
}
}
}
}
}
}
//store the sourcing data
ItemSourcingData sourcingData = new ItemSourcingData(recipes, harvestTargets, trees);
sourcingMap.itemSourcingDataMap.put(item.getId(),sourcingData);
}
return sourcingMap;
}
/**
* Gets the sourcing data for a given item type
* @param itemId The item type
* @return The sourcing data for that type of item
*/
public ItemSourcingData getSourcingData(String itemId){
return itemSourcingDataMap.get(itemId);
}
/**
* Gets the sourcing data for a given item type
* @param item The item data
* @return The sourcing data for that type of item
*/
public ItemSourcingData getSourcingData(Item item){
return itemSourcingDataMap.get(item.getId());
}
}

View File

@ -0,0 +1,138 @@
package electrosphere.game.data.item.source;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.common.CommonEntityTokens;
import electrosphere.game.data.crafting.RecipeData;
import electrosphere.game.data.crafting.RecipeIngredientData;
import electrosphere.game.data.item.ItemIdStrings;
/**
* A tree of dependencies to acquire a final item
*/
public class ItemSourcingTree {
/**
* The root item to search for
*/
String rootItemId;
/**
* The map of item id -> specific
*/
Map<String,ItemSourcingData> itemSourceMap = new HashMap<String,ItemSourcingData>();
/**
* Creates an item sourcing tree
* @param itemType The type of item to source
* @return The tree of dependencies to get the item
*/
public static ItemSourcingTree create(String itemType){
ItemSourcingTree rVal = new ItemSourcingTree();
rVal.rootItemId = itemType;
rVal.evaluate();
return rVal;
}
/**
* Evaluates what items are still depended upon
*/
public void evaluate(){
//all items that haven't had sources solved for yet
List<String> unsolvedItems = new LinkedList<String>();
unsolvedItems.add(this.rootItemId);
while(unsolvedItems.size() > 0){
String currentId = unsolvedItems.remove(0);
ItemSourcingData sourcingData = Globals.gameConfigCurrent.getItemSourcingMap().getSourcingData(currentId);
if(sourcingData.recipes.size() > 0){
for(RecipeData recipeData : sourcingData.recipes){
for(RecipeIngredientData reagent : recipeData.getIngredients()){
if(!unsolvedItems.contains(reagent.getItemType()) && !itemSourceMap.containsKey(reagent.getItemType())){
unsolvedItems.add(reagent.getItemType());
}
}
}
}
if(sourcingData.trees.size() > 0){
unsolvedItems.add(ItemIdStrings.ITEM_STONE_AXE);
}
this.itemSourceMap.put(currentId,sourcingData);
}
}
/**
* Gets the sourcing data for the current dependency
* @param entity The entity to check
* @return The sourcing data for the current dependency
*/
public ItemSourcingData getCurrentDependency(Entity entity){
List<Entity> items = InventoryUtils.getAllInventoryItems(entity);
List<String> itemIds = items.stream().map((Entity item) -> {return ItemUtils.getType(item);}).collect(Collectors.toList());
if(itemIds.contains(this.rootItemId)){
return null;
}
String currentRootId = this.rootItemId;
while(currentRootId != null){
ItemSourcingData sourcingData = this.itemSourceMap.get(currentRootId);
if(sourcingData == null){
throw new Error("Failed to find sourcing data for " + currentRootId);
}
if(sourcingData.harvestTargets.size() > 0){
return sourcingData;
}
if(sourcingData.trees.size() > 0){
//if we don't have an axe in inventory, consider it a dependency
if(!InventoryUtils.serverHasTool(entity, CommonEntityTokens.TOKEN_AXE)){
currentRootId = ItemIdStrings.ITEM_STONE_AXE;
continue;
}
//we have an axe, return this sourcing data
return sourcingData;
}
currentRootId = null;
if(sourcingData.recipes.size() > 0){
boolean foundAllIngredients = true;
RecipeData craftableRecipe = null;
for(RecipeData recipeData : sourcingData.recipes){
//check if we have all the ingredients to craft this item
foundAllIngredients = true;
for(RecipeIngredientData ingredient : recipeData.getIngredients()){
if(!itemIds.contains(ingredient.getItemType())){
//ingredient is not in inventory
foundAllIngredients = false;
currentRootId = ingredient.getItemType();
break;
}
}
if(foundAllIngredients){
craftableRecipe = recipeData;
break;
}
}
if(craftableRecipe != null){
return sourcingData;
}
}
}
return null;
}
/**
* Gets the item id of the root item
* @return The id
*/
public String getRootItem(){
return this.rootItemId;
}
}

View File

@ -25,4 +25,34 @@ public class BlackboardKeys {
*/ */
public static final String MOVE_TO_TARGET = "moveToTarget"; public static final String MOVE_TO_TARGET = "moveToTarget";
/**
* The structure that is being targeted
*/
public static final String STRUCTURE_TARGET = "structureTarget";
/**
* The material currently needed for building the targeted structure
*/
public static final String BUILDING_MATERIAL_CURRENT = "buildingMaterialCurrent";
/**
* The type of item to scan the inventory for
*/
public static final String INVENTORY_CHECK_TYPE = "inventoryCheckType";
/**
* Tree that stores the item sourcing
*/
public static final String ITEM_SOURCING_TREE = "itemSourcingTree";
/**
* Sourcing data for the item that is currently being sought after
*/
public static final String ITEM_SOURCING_DATA = "itemSourcingData";
/**
* The type of entity to try to harvest
*/
public static final String HARVEST_TARGET_TYPE = "harvestTargetType";
} }

View File

@ -8,7 +8,7 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.server.ai.blackboard.Blackboard; import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.plan.FindEntityTargetNode; import electrosphere.server.ai.nodes.plan.TargetEntityCategoryNode;
/** /**
* Tries to collect an item * Tries to collect an item
@ -17,17 +17,17 @@ public class CollectItemNode implements AITreeNode {
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){ public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
if(!FindEntityTargetNode.hasTarget(blackboard)){ if(!TargetEntityCategoryNode.hasTarget(blackboard)){
return AITreeNodeResult.FAILURE; return AITreeNodeResult.FAILURE;
} }
Entity target = FindEntityTargetNode.getTarget(blackboard); Entity target = TargetEntityCategoryNode.getTarget(blackboard);
Vector3d parentPos = EntityUtils.getPosition(entity); Vector3d parentPos = EntityUtils.getPosition(entity);
Vector3d targetPos = EntityUtils.getPosition(target); Vector3d targetPos = EntityUtils.getPosition(target);
if(parentPos.distance(targetPos) > CollisionEngine.DEFAULT_INTERACT_DISTANCE){ if(parentPos.distance(targetPos) > CollisionEngine.DEFAULT_INTERACT_DISTANCE){
return AITreeNodeResult.FAILURE; return AITreeNodeResult.FAILURE;
} }
InventoryUtils.serverAttemptStoreItemTransform(entity, target); InventoryUtils.serverAttemptStoreItemTransform(entity, target);
FindEntityTargetNode.setTarget(blackboard, null); TargetEntityCategoryNode.setTarget(blackboard, null);
return AITreeNodeResult.SUCCESS; return AITreeNodeResult.SUCCESS;
} }

View File

@ -6,8 +6,9 @@ import electrosphere.collision.CollisionEngine;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.server.ai.blackboard.Blackboard; import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.plan.FindEntityTargetNode; import electrosphere.server.ai.nodes.plan.TargetEntityCategoryNode;
import electrosphere.server.player.PlayerActions; import electrosphere.server.player.PlayerActions;
/** /**
@ -17,17 +18,45 @@ public class HarvestNode implements AITreeNode {
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){ public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
if(!FindEntityTargetNode.hasTarget(blackboard)){ if(!TargetEntityCategoryNode.hasTarget(blackboard)){
return AITreeNodeResult.FAILURE; return AITreeNodeResult.FAILURE;
} }
Entity target = FindEntityTargetNode.getTarget(blackboard); Entity target = TargetEntityCategoryNode.getTarget(blackboard);
Vector3d parentPos = EntityUtils.getPosition(entity); Vector3d parentPos = EntityUtils.getPosition(entity);
Vector3d targetPos = EntityUtils.getPosition(target); Vector3d targetPos = EntityUtils.getPosition(target);
if(parentPos.distance(targetPos) > CollisionEngine.DEFAULT_INTERACT_DISTANCE){ if(parentPos.distance(targetPos) > CollisionEngine.DEFAULT_INTERACT_DISTANCE){
return AITreeNodeResult.FAILURE; return AITreeNodeResult.FAILURE;
} }
PlayerActions.harvest(entity, target); PlayerActions.harvest(entity, target);
FindEntityTargetNode.setTarget(blackboard, null); TargetEntityCategoryNode.setTarget(blackboard, null);
return AITreeNodeResult.SUCCESS; return AITreeNodeResult.SUCCESS;
} }
/**
* sets the type of entity to try to harvest
* @param blackboard The blackboard
* @param type The type of entity to try to harvest
*/
public static void setHarvestTargetType(Blackboard blackboard, String type){
blackboard.put(BlackboardKeys.HARVEST_TARGET_TYPE, type);
}
/**
* checks if this blackboard has a type of entity it wants to try to harvest
* @param blackboard The blackboard
* @return true if the type is defined, false otherwise
*/
public static boolean hasHarvestTargetType(Blackboard blackboard){
return blackboard.has(BlackboardKeys.HARVEST_TARGET_TYPE);
}
/**
* Gets the type of entity to try to harvest
* @param blackboard The blackboard
* @return The type of entity to try to harvest
*/
public static String getHarvestTargetType(Blackboard blackboard){
return (String)blackboard.get(BlackboardKeys.HARVEST_TARGET_TYPE);
}
} }

View File

@ -0,0 +1,18 @@
package electrosphere.server.ai.nodes.actions.interact;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Places a block
*/
public class PlaceBlockNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'evaluate'");
}
}

View File

@ -9,7 +9,6 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.server.ai.blackboard.Blackboard; import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.plan.TargetPositionNode;
import electrosphere.util.math.SpatialMathUtils; import electrosphere.util.math.SpatialMathUtils;
/** /**
@ -17,17 +16,37 @@ import electrosphere.util.math.SpatialMathUtils;
*/ */
public class FaceTargetNode implements AITreeNode { public class FaceTargetNode implements AITreeNode {
/**
* The key to lookup the target under
*/
String targetKey;
/**
* Constructor
* @param targetKey The key to lookup the target under
*/
public FaceTargetNode(String targetKey){
this.targetKey = targetKey;
}
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) { public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(TargetPositionNode.hasMoveToTarget(blackboard)){ Object targetRaw = blackboard.get(this.targetKey);
Vector3d parentPos = EntityUtils.getPosition(entity); Vector3d targetPos = null;
Vector3d targetPos = TargetPositionNode.getMoveToTarget(blackboard); if(targetRaw == null){
Quaterniond rotation = SpatialMathUtils.calculateRotationFromPointToPoint(parentPos, targetPos); throw new Error("Target undefined!");
EntityUtils.getRotation(entity).set(rotation);
CreatureUtils.setFacingVector(entity, CameraEntityUtils.getFacingVec(rotation));
return AITreeNodeResult.SUCCESS;
} }
return AITreeNodeResult.FAILURE; if(targetRaw instanceof Vector3d){
targetPos = (Vector3d)targetRaw;
}
if(targetRaw instanceof Entity){
targetPos = EntityUtils.getPosition((Entity)targetRaw);
}
Vector3d parentPos = EntityUtils.getPosition(entity);
Quaterniond rotation = SpatialMathUtils.calculateRotationFromPointToPoint(parentPos, targetPos);
EntityUtils.getRotation(entity).set(rotation);
CreatureUtils.setFacingVector(entity, CameraEntityUtils.getFacingVec(rotation));
return AITreeNodeResult.SUCCESS;
} }
} }

View File

@ -0,0 +1,33 @@
package electrosphere.server.ai.nodes.checks;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Checks if a blackboard key exists
*/
public class BlackboardKeyCheckNode implements AITreeNode {
/**
* The key to check for
*/
String key;
/**
* Constructor
* @param key The key to check for
*/
public BlackboardKeyCheckNode(String key){
this.key = key;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(blackboard.has(this.key)){
return AITreeNodeResult.SUCCESS;
}
return AITreeNodeResult.FAILURE;
}
}

View File

@ -0,0 +1,104 @@
package electrosphere.server.ai.nodes.checks.inventory;
import java.util.List;
import java.util.stream.Collectors;
import electrosphere.entity.Entity;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Checks if any of the inventories on a given entity contain the target item type
*/
public class InventoryContainsNode implements AITreeNode {
/**
* The key to look for the item type in
*/
String key = BlackboardKeys.INVENTORY_CHECK_TYPE;
/**
* Constructor
* @param key The key to lookup the entity type under
*/
public InventoryContainsNode(String key){
this.key = key;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
//error checking
if(!InventoryContainsNode.hasInventoryCheckType(blackboard)){
return AITreeNodeResult.FAILURE;
}
//key isn't defined
if(!blackboard.has(key)){
return AITreeNodeResult.FAILURE;
}
//type to look for
String type = (String)blackboard.get(this.key);
//check equip inventory if it exists
if(InventoryUtils.hasEquipInventory(entity)){
RelationalInventoryState equipInventory = InventoryUtils.getEquipInventory(entity);
if(equipInventory.getSlots().contains(type)){
return AITreeNodeResult.SUCCESS;
}
}
//check natural inventory if it exists
if(InventoryUtils.hasNaturalInventory(entity)){
UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(entity);
List<String> itemIds = naturalInventory.getItems().stream().map((Entity itemEnt) -> {return CommonEntityUtils.getEntitySubtype(entity);}).collect(Collectors.toList());
if(itemIds.contains(type)){
return AITreeNodeResult.SUCCESS;
}
}
//check toolbar inventory if it exists
if(InventoryUtils.hasToolbarInventory(entity)){
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(entity);
if(toolbarInventory.getSlots().contains(type)){
return AITreeNodeResult.SUCCESS;
}
}
return AITreeNodeResult.FAILURE;
}
/**
* Sets the type of item to check for
* @param blackboard The blackboard
* @param type The type of item to check for
*/
public static void setInventoryCheckType(Blackboard blackboard, String type){
blackboard.put(BlackboardKeys.INVENTORY_CHECK_TYPE, type);
}
/**
* Checks if this has an item type to search for
* @param blackboard The blackboard
* @return true if there is an item type to check for, false otherwise
*/
public static boolean hasInventoryCheckType(Blackboard blackboard){
return blackboard.has(BlackboardKeys.INVENTORY_CHECK_TYPE);
}
/**
* Gets the item type to check for
* @param blackboard The blackboard
* @return The item type to check for if it exists, null otherwise
*/
public static String getInventoryCheckType(Blackboard blackboard){
return (String)blackboard.get(BlackboardKeys.INVENTORY_CHECK_TYPE);
}
}

View File

@ -0,0 +1,62 @@
package electrosphere.server.ai.nodes.checks.inventory;
import electrosphere.entity.Entity;
import electrosphere.game.data.item.source.ItemSourcingData;
import electrosphere.game.data.item.source.ItemSourcingData.SourcingType;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.plan.SolveSourcingTreeNode;
/**
* Checks if the supplied type of sourcing is the path for the current target item to acquire
*/
public class SourcingTypeNode implements AITreeNode {
/**
* The type of sourcing
*/
SourcingType sourcingType;
/**
* Constructor
* @param sourcingType The type of sourcing to check
*/
public SourcingTypeNode(SourcingType sourcingType){
this.sourcingType = sourcingType;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(!SolveSourcingTreeNode.hasItemSourcingData(blackboard)){
return AITreeNodeResult.FAILURE;
}
ItemSourcingData sourcingData = SolveSourcingTreeNode.getItemSourcingData(blackboard);
if(sourcingData == null){
throw new Error("Sourcing data is null!");
}
//succeed based on the type of sourcing that this node is set for
switch(this.sourcingType){
case RECIPE: {
if(sourcingData.getRecipes().size() > 0){
return AITreeNodeResult.SUCCESS;
}
} break;
case HARVEST: {
if(sourcingData.getHarvestTargets().size() > 0){
return AITreeNodeResult.SUCCESS;
}
} break;
case TREE: {
if(sourcingData.getTrees().size() > 0){
return AITreeNodeResult.SUCCESS;
}
} break;
}
return AITreeNodeResult.FAILURE;
}
}

View File

@ -22,6 +22,9 @@ public class HasShelter implements AITreeNode {
MacroData macroData = entityRealm.getServerContentManager().getMacroData(); MacroData macroData = entityRealm.getServerContentManager().getMacroData();
ServerCharacterData serverCharacterData = ServerCharacterData.getServerCharacterData(entity); ServerCharacterData serverCharacterData = ServerCharacterData.getServerCharacterData(entity);
Character character = macroData.getCharacter(serverCharacterData.getCharacterId()); Character character = macroData.getCharacter(serverCharacterData.getCharacterId());
if(character == null){
throw new Error("Character is null");
}
Structure shelter = CharacterUtils.getShelter(character); Structure shelter = CharacterUtils.getShelter(character);
if(shelter == null){ if(shelter == null){
return AITreeNodeResult.FAILURE; return AITreeNodeResult.FAILURE;

View File

@ -0,0 +1,85 @@
package electrosphere.server.ai.nodes.checks.spatial;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.game.data.block.BlockFab;
import electrosphere.game.data.struct.StructureData;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.MacroData;
import electrosphere.server.macro.structure.Structure;
import electrosphere.server.macro.utils.StructurePlacementUtils;
import electrosphere.util.FileUtils;
/**
* Tries to begin building a structure
*/
public class BeginStructureNode implements AITreeNode {
/**
* The data for the structure to place
*/
StructureData structureData;
/**
* Constructor
* @param structureData The type of structure to place
*/
public BeginStructureNode(StructureData structureData){
this.structureData = structureData;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(!BeginStructureNode.hasStructureTarget(blackboard)){
//requisite data
Realm realm = Globals.realmManager.getEntityRealm(entity);
MacroData macroData = realm.getServerContentManager().getMacroData();
Vector3d position = EntityUtils.getPosition(entity);
//solve where to place
Vector3d placementPos = StructurePlacementUtils.getPlacementPosition(macroData, structureData, position);
//add to macro data
Structure struct = Structure.createStructure(structureData, placementPos);
struct.setRepairable(true);
struct.setFab(BlockFab.read(FileUtils.getAssetFile(struct.getFabPath())));
// macroData.getStructures().add(struct);
BeginStructureNode.setStructureTarget(blackboard, struct);
}
return AITreeNodeResult.SUCCESS;
}
/**
* Sets the structure target for the entity
* @param blackboard The blackboard
* @param structure The structure to target
*/
public static void setStructureTarget(Blackboard blackboard, Structure structure){
blackboard.put(BlackboardKeys.STRUCTURE_TARGET, structure);
}
/**
* Checks if the blackboard has a structure target
* @param blackboard The blackboard
*/
public static boolean hasStructureTarget(Blackboard blackboard){
return blackboard.has(BlackboardKeys.STRUCTURE_TARGET);
}
/**
* Gets the structure target in the blackboard
* @param blackboard The blackboard
* @return The structure if it exists, null otherwise
*/
public static Structure getStructureTarget(Blackboard blackboard){
return (Structure)blackboard.get(BlackboardKeys.STRUCTURE_TARGET);
}
}

View File

@ -6,33 +6,45 @@ import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.server.ai.blackboard.Blackboard; import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.plan.FindEntityTargetNode;
/** /**
* Checks if the target is inside a given range of the entity * Checks if the target is inside a given range of the entity
*/ */
public class TargetRangeCheck implements AITreeNode { public class TargetRangeCheckNode implements AITreeNode {
/** /**
* The distance to succeed within * The distance to succeed within
*/ */
double dist; double dist;
/**
* The key to lookup the target under
*/
String targetKey;
/** /**
* Constructor * Constructor
* @param dist The distance outside of which the node will fail * @param dist The distance outside of which the node will fail
* @param targetKey The key to lookup the target under
*/ */
public TargetRangeCheck(double dist){ public TargetRangeCheckNode(double dist, String targetKey){
this.dist = dist; this.dist = dist;
this.targetKey = targetKey;
} }
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) { public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(!FindEntityTargetNode.hasTarget(blackboard)){ Object targetRaw = blackboard.get(this.targetKey);
return AITreeNodeResult.FAILURE; Vector3d targetPos = null;
if(targetRaw == null){
throw new Error("Target undefined!");
}
if(targetRaw instanceof Vector3d){
targetPos = (Vector3d)targetRaw;
}
if(targetRaw instanceof Entity){
targetPos = EntityUtils.getPosition((Entity)targetRaw);
} }
Entity target = FindEntityTargetNode.getTarget(blackboard);
Vector3d targetPos = EntityUtils.getPosition(target);
Vector3d entPos = EntityUtils.getPosition(entity); Vector3d entPos = EntityUtils.getPosition(entity);
if(targetPos.distance(entPos) < this.dist){ if(targetPos.distance(entPos) < this.dist){

View File

@ -0,0 +1,39 @@
package electrosphere.server.ai.nodes.meta;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Transfers data from one blackboard key to another
*/
public class DataTransferNode implements AITreeNode {
/**
* The key to pull data from
*/
String sourceKey;
/**
* The key to push data into
*/
String destinationKey;
/**
* Constructor
* @param sourceKey The key to pull data from
* @param destinationKey The key to push data into
*/
public DataTransferNode(String sourceKey, String destinationKey){
this.sourceKey = sourceKey;
this.destinationKey = destinationKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
Object data = blackboard.get(this.sourceKey);
blackboard.put(this.destinationKey, data);
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -1,16 +0,0 @@
package electrosphere.server.ai.nodes.plan;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* A node that performs functions to try to build a structure
*/
public class BuildStructureNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -1,56 +0,0 @@
package electrosphere.server.ai.nodes.plan;
import electrosphere.entity.Entity;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Finds a target given some criteria
*/
public class FindEntityTargetNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
if(ServerBlockTree.getServerBlockTree(entity) != null){
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(entity);
if(serverBlockTree.isIdle()){
serverBlockTree.start();
return AITreeNodeResult.SUCCESS;
} else {
return AITreeNodeResult.RUNNING;
}
} else {
return AITreeNodeResult.FAILURE;
}
}
/**
* Sets the target in the blackboard
* @param blackboard The blackboard
* @param target The target
*/
public static void setTarget(Blackboard blackboard, Entity target){
blackboard.put(BlackboardKeys.ENTITY_TARGET, target);
}
/**
* Gets the currently targeted entity
* @param blackboard The blackboard
* @return The entity target if it exists, null otherwise
*/
public static Entity getTarget(Blackboard blackboard){
return (Entity)blackboard.get(BlackboardKeys.ENTITY_TARGET);
}
/**
* Checks if the blackboard has a currently targeted entity
* @param blackboard The blackboard
* @return true if it has a currently targeted entity, false otherwise
*/
public static boolean hasTarget(Blackboard blackboard){
return blackboard.has(BlackboardKeys.ENTITY_TARGET);
}
}

View File

@ -1,35 +0,0 @@
package electrosphere.server.ai.nodes.plan;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Sets the type of structure to build
*/
public class SetBuildGoalNode implements AITreeNode {
/**
* A shelter structure type
*/
public static final String STRUCTURE_TYPE_SHELTER = "shelter";
/**
* The type of structure
*/
String type;
/**
* Constructor
* @param type The type to set the build goal to
*/
public SetBuildGoalNode(String type){
this.type = type;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -0,0 +1,106 @@
package electrosphere.server.ai.nodes.plan;
import electrosphere.entity.Entity;
import electrosphere.game.data.item.source.ItemSourcingData;
import electrosphere.game.data.item.source.ItemSourcingTree;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.interact.HarvestNode;
/**
* Solves a dependency tree for how to acquire a given item
*/
public class SolveSourcingTreeNode implements AITreeNode {
/**
* Blackboard key that stores the id of the item to source
*/
String itemIdKey;
/**
* Constructor
* @param itemIdKey The blackboard key that stores the id of the item to calculate sourcing for
*/
public SolveSourcingTreeNode(String itemIdKey){
this.itemIdKey = itemIdKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(!blackboard.has(itemIdKey)){
return AITreeNodeResult.FAILURE;
}
if(!SolveSourcingTreeNode.hasItemSourcingTree(blackboard) || !SolveSourcingTreeNode.getItemSourcingTree(blackboard).getRootItem().equals(this.itemIdKey)){
String itemId = (String)blackboard.get(itemIdKey);
ItemSourcingTree sourcingTree = ItemSourcingTree.create(itemId);
SolveSourcingTreeNode.setItemSourcingTree(blackboard, sourcingTree);
}
ItemSourcingTree sourcingTree = SolveSourcingTreeNode.getItemSourcingTree(blackboard);
ItemSourcingData sourcingData = sourcingTree.getCurrentDependency(entity);
if(sourcingData == null){
throw new Error("Source data is null!");
}
//set the type to harvest if this is a harvest type
if(sourcingData.getHarvestTargets().size() > 0){
HarvestNode.setHarvestTargetType(blackboard, sourcingData.getHarvestTargets().get(0).getId());
}
SolveSourcingTreeNode.setItemSourcingData(blackboard, sourcingData);
return AITreeNodeResult.SUCCESS;
}
/**
* Sets the item sourcing tree of the blackboard
* @param blackboard The blackboard
* @param tree The tree
*/
public static void setItemSourcingTree(Blackboard blackboard, ItemSourcingTree tree){
blackboard.put(BlackboardKeys.ITEM_SOURCING_TREE, tree);
}
/**
* Checks if the blackboard has an item sourcing tree
* @param blackboard The blackboard
* @return The item sourcing tree
*/
public static boolean hasItemSourcingTree(Blackboard blackboard){
return blackboard.has(BlackboardKeys.ITEM_SOURCING_TREE);
}
/**
* Gets the item sourcing tree of the blackboard
* @param blackboard The blackboard
* @return The item sourcing tree
*/
public static ItemSourcingTree getItemSourcingTree(Blackboard blackboard){
return (ItemSourcingTree)blackboard.get(BlackboardKeys.ITEM_SOURCING_TREE);
}
/**
* Sets the item sourcing data of the blackboard
* @param blackboard The blackboard
* @param tree The data
*/
public static void setItemSourcingData(Blackboard blackboard, ItemSourcingData data){
blackboard.put(BlackboardKeys.ITEM_SOURCING_DATA, data);
}
/**
* Checks if the blackboard has an item sourcing data
* @param blackboard The blackboard
* @return The item sourcing data
*/
public static boolean hasItemSourcingData(Blackboard blackboard){
return blackboard.has(BlackboardKeys.ITEM_SOURCING_DATA);
}
/**
* Gets the item sourcing data of the blackboard
* @param blackboard The blackboard
* @return The item sourcing data
*/
public static ItemSourcingData getItemSourcingData(Blackboard blackboard){
return (ItemSourcingData)blackboard.get(BlackboardKeys.ITEM_SOURCING_DATA);
}
}

View File

@ -0,0 +1,106 @@
package electrosphere.server.ai.nodes.plan;
import java.util.Collection;
import electrosphere.entity.Entity;
import electrosphere.entity.types.common.CommonEntityUtils;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.services.NearbyEntityService;
/**
* Targets a nearby entity of a specific type
*/
public class TargetEntityCategoryNode implements AITreeNode {
/**
* The blackboard key to pull the entity type from
*/
String sourceKey;
/**
* Constructor
* @param sourceKey The blackboard key to pull the entity type from
*/
public TargetEntityCategoryNode(String sourceKey){
this.sourceKey = sourceKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
String goalEntityId = (String)blackboard.get(sourceKey);
if(goalEntityId == null){
throw new Error("Entity id to search for is null!");
}
if(TargetEntityCategoryNode.hasTarget(blackboard)){
Entity currentTarget = TargetEntityCategoryNode.getTarget(blackboard);
if(currentTarget == null){
TargetEntityCategoryNode.clearTarget(blackboard);
} else {
String typeId = CommonEntityUtils.getEntitySubtype(currentTarget);
if(!typeId.equals(goalEntityId)){
TargetEntityCategoryNode.clearTarget(blackboard);
}
}
}
if(!TargetEntityCategoryNode.hasTarget(blackboard)){
Collection<Entity> nearbyEntities = NearbyEntityService.getNearbyEntities(blackboard);
for(Entity potential : nearbyEntities){
//get id -- skip empty ids
String potentialId = CommonEntityUtils.getEntitySubtype(potential);
if(potentialId == null){
continue;
}
//set to target if id match
if(potentialId.equals(goalEntityId)){
TargetEntityCategoryNode.setTarget(blackboard, potential);
break;
}
}
}
if(!TargetEntityCategoryNode.hasTarget(blackboard)){
return AITreeNodeResult.FAILURE;
}
return AITreeNodeResult.SUCCESS;
}
/**
* Sets the target in the blackboard
* @param blackboard The blackboard
* @param target The target
*/
public static void setTarget(Blackboard blackboard, Entity target){
blackboard.put(BlackboardKeys.ENTITY_TARGET, target);
}
/**
* Gets the currently targeted entity
* @param blackboard The blackboard
* @return The entity target if it exists, null otherwise
*/
public static Entity getTarget(Blackboard blackboard){
return (Entity)blackboard.get(BlackboardKeys.ENTITY_TARGET);
}
/**
* Checks if the blackboard has a currently targeted entity
* @param blackboard The blackboard
* @return true if it has a currently targeted entity, false otherwise
*/
public static boolean hasTarget(Blackboard blackboard){
return blackboard.has(BlackboardKeys.ENTITY_TARGET);
}
/**
* Clears the target
* @param blackboard The target
*/
public static void clearTarget(Blackboard blackboard){
blackboard.delete(BlackboardKeys.ENTITY_TARGET);
}
}

View File

@ -13,13 +13,32 @@ import electrosphere.server.ai.nodes.AITreeNode;
*/ */
public class TargetPositionNode implements AITreeNode { public class TargetPositionNode implements AITreeNode {
/**
* The key to lookup the target under
*/
String targetKey;
/**
* constructor
* @param targetKey The key to lookup the target under
*/
public TargetPositionNode(String targetKey){
this.targetKey = targetKey;
}
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){ public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
if(!FindEntityTargetNode.hasTarget(blackboard)){ Object targetRaw = blackboard.get(this.targetKey);
return AITreeNodeResult.FAILURE; Vector3d targetPos = null;
if(targetRaw == null){
throw new Error("Target undefined!");
}
if(targetRaw instanceof Vector3d){
targetPos = (Vector3d)targetRaw;
}
if(targetRaw instanceof Entity){
targetPos = EntityUtils.getPosition((Entity)targetRaw);
} }
Entity target = FindEntityTargetNode.getTarget(blackboard);
Vector3d targetPos = EntityUtils.getPosition(target);
TargetPositionNode.setMoveToTarget(blackboard, targetPos); TargetPositionNode.setMoveToTarget(blackboard, targetPos);
return AITreeNodeResult.SUCCESS; return AITreeNodeResult.SUCCESS;
} }

View File

@ -0,0 +1,28 @@
package electrosphere.server.ai.nodes.solvers;
import electrosphere.entity.Entity;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Finds a target given some criteria
*/
public class FindEntityTargetNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
if(ServerBlockTree.getServerBlockTree(entity) != null){
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(entity);
if(serverBlockTree.isIdle()){
serverBlockTree.start();
return AITreeNodeResult.SUCCESS;
} else {
return AITreeNodeResult.RUNNING;
}
} else {
return AITreeNodeResult.FAILURE;
}
}
}

View File

@ -0,0 +1,47 @@
package electrosphere.server.ai.nodes.solvers;
import org.joml.Vector3i;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.game.data.block.BlockFab;
import electrosphere.game.data.block.BlockType;
import electrosphere.game.data.item.Item;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.checks.spatial.BeginStructureNode;
import electrosphere.server.ai.trees.struct.BuildStructureTree;
import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.structure.Structure;
import electrosphere.server.macro.utils.StructureRepairUtils;
/**
* Solves for the current build material
*/
public class SolveBuildMaterialNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(!BuildStructureTree.hasCurrentMaterial(blackboard)){
Structure struct = BeginStructureNode.getStructureTarget(blackboard);
if(!struct.isRepairable()){
return AITreeNodeResult.FAILURE;
}
//solve for repairable block
Realm realm = Globals.realmManager.getEntityRealm(entity);
Vector3i repairPos = StructureRepairUtils.getRepairablePosition(realm, struct);
//get the id of item entity type for the block we need
BlockFab fab = struct.getFab();
short blockTypeId = fab.getType(repairPos.x, repairPos.y, repairPos.z);
BlockType blockType = Globals.gameConfigCurrent.getBlockData().getTypeFromId(blockTypeId);
String itemId = Item.getBlockTypeId(blockType);
//store
BuildStructureTree.setCurrentMaterial(blackboard, itemId);
}
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -0,0 +1,66 @@
package electrosphere.server.ai.trees.creature;
import electrosphere.collision.CollisionEngine;
import electrosphere.game.data.item.source.ItemSourcingData.SourcingType;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.interact.HarvestNode;
import electrosphere.server.ai.nodes.checks.inventory.SourcingTypeNode;
import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.nodes.plan.SolveSourcingTreeNode;
import electrosphere.server.ai.nodes.plan.TargetEntityCategoryNode;
/**
* A tree to acquire an item
*/
public class AcquireItemTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "AcquireItem";
/**
* Creates a acquire-item tree
* @param key The blackboard key to search for the item name under
* @return The root node of the acquire-item tree
*/
public static AITreeNode create(String blackboardKey){
return new SequenceNode(
new PublishStatusNode("Acquire an item"),
//solve how we're going to get this top level item
new SolveSourcingTreeNode(blackboardKey),
new SelectorNode(
new SequenceNode(
new PublishStatusNode("Craft an item"),
//check if we should be sourcing this from a recipe
new SourcingTypeNode(SourcingType.RECIPE),
//TODO: logic to craft a recipe
new RunnerNode(null)
),
new SequenceNode(
new PublishStatusNode("Harvest an item"),
//check if we should be sourcing this from harvesting foliage
new SourcingTypeNode(SourcingType.HARVEST),
new TargetEntityCategoryNode(BlackboardKeys.HARVEST_TARGET_TYPE),
MoveToTarget.create(CollisionEngine.DEFAULT_INTERACT_DISTANCE, BlackboardKeys.ENTITY_TARGET),
new HarvestNode(),
new RunnerNode(null)
),
new SequenceNode(
new PublishStatusNode("Fell a tree"),
//check if we should be sourcing this from felling a tree
new SourcingTypeNode(SourcingType.TREE),
//TODO: logic to fell a tree
new RunnerNode(null)
)
),
new SucceederNode(null)
);
}
}

View File

@ -5,12 +5,11 @@ import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.move.FaceTargetNode; import electrosphere.server.ai.nodes.actions.move.FaceTargetNode;
import electrosphere.server.ai.nodes.actions.move.MoveStartNode; import electrosphere.server.ai.nodes.actions.move.MoveStartNode;
import electrosphere.server.ai.nodes.actions.move.MoveStopNode; import electrosphere.server.ai.nodes.actions.move.MoveStopNode;
import electrosphere.server.ai.nodes.checks.spatial.TargetRangeCheck; import electrosphere.server.ai.nodes.checks.spatial.TargetRangeCheckNode;
import electrosphere.server.ai.nodes.meta.collections.SelectorNode; import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode; import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode; import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode; import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.nodes.plan.TargetPositionNode;
/** /**
* Moves to a target * Moves to a target
@ -25,22 +24,22 @@ public class MoveToTarget {
/** /**
* Creates a move-to-target tree * Creates a move-to-target tree
* @param dist The target distance to be within * @param dist The target distance to be within
* @param targetKey The key to lookup the target under
* @return The root node of the move-to-target tree * @return The root node of the move-to-target tree
*/ */
public static AITreeNode create(double dist){ public static AITreeNode create(double dist, String targetKey){
return new SelectorNode( return new SelectorNode(
new SequenceNode( new SequenceNode(
//check if in range of target //check if in range of target
new TargetRangeCheck(0), new TargetRangeCheckNode(dist, targetKey),
//if in range, stop moving fowards and return SUCCESS //if in range, stop moving fowards and return SUCCESS
new SucceederNode(new MoveStopNode()) new SucceederNode(new MoveStopNode())
), ),
//not in range of target, keep moving towards it //not in range of target, keep moving towards it
new SequenceNode( new SequenceNode(
new TargetPositionNode(),
//check that dependencies exist //check that dependencies exist
new FaceTargetNode(), new FaceTargetNode(targetKey),
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD)) new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
) )
); );

View File

@ -2,6 +2,7 @@ package electrosphere.server.ai.trees.creature.melee;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
import electrosphere.game.data.creature.type.ai.AttackerTreeData; import electrosphere.game.data.creature.type.ai.AttackerTreeData;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.AITreeNode.AITreeNodeResult; import electrosphere.server.ai.nodes.AITreeNode.AITreeNodeResult;
import electrosphere.server.ai.nodes.actions.combat.AttackStartNode; import electrosphere.server.ai.nodes.actions.combat.AttackStartNode;
@ -66,19 +67,19 @@ public class MeleeAITree {
//wait //wait
new SequenceNode( new SequenceNode(
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 600) new TimerNode(new SucceederNode(null), 600)
), ),
//wait //wait
new SequenceNode( new SequenceNode(
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 300) new TimerNode(new SucceederNode(null), 300)
), ),
//attack //attack
new SequenceNode( new SequenceNode(
new PublishStatusNode("Attacking"), new PublishStatusNode("Attacking"),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new AttackStartNode(), new AttackStartNode(),
new TimerNode(new SucceederNode(null), 300) new TimerNode(new SucceederNode(null), 300)
) )
@ -96,7 +97,7 @@ public class MeleeAITree {
//wait //wait
new SequenceNode( new SequenceNode(
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 1200) new TimerNode(new SucceederNode(null), 1200)
), ),
@ -108,7 +109,7 @@ public class MeleeAITree {
new MoveStartNode(MovementRelativeFacing.RIGHT), new MoveStartNode(MovementRelativeFacing.RIGHT),
new FailerNode(null) new FailerNode(null)
)), )),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 600), new TimerNode(new SucceederNode(null), 600),
new SucceederNode(new WalkStopNode()), new SucceederNode(new WalkStopNode()),
new SucceederNode(new MoveStopNode()) new SucceederNode(new MoveStopNode())
@ -122,7 +123,7 @@ public class MeleeAITree {
new MoveStartNode(MovementRelativeFacing.LEFT), new MoveStartNode(MovementRelativeFacing.LEFT),
new FailerNode(null) new FailerNode(null)
)), )),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 600), new TimerNode(new SucceederNode(null), 600),
new SucceederNode(new WalkStopNode()), new SucceederNode(new WalkStopNode()),
new SucceederNode(new MoveStopNode()) new SucceederNode(new MoveStopNode())
@ -132,7 +133,7 @@ public class MeleeAITree {
//move towards target and attack //move towards target and attack
new SequenceNode( new SequenceNode(
new PublishStatusNode("Move into attack range"), new PublishStatusNode("Move into attack range"),
new FaceTargetNode(), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)), new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)),
new TimerNode(new SucceederNode(null), 600) new TimerNode(new SucceederNode(null), 600)
) )

View File

@ -1,9 +1,12 @@
package electrosphere.server.ai.trees.hierarchy.safety.shelter; package electrosphere.server.ai.trees.hierarchy.safety.shelter;
import electrosphere.engine.Globals;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.checks.spatial.BeginStructureNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode; import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode; import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode; import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.trees.struct.BuildStructureTree;
/** /**
* Tree for constructing shelter * Tree for constructing shelter
@ -22,6 +25,8 @@ public class ConstructShelterTree {
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
new PublishStatusNode("Construct a shelter"), new PublishStatusNode("Construct a shelter"),
new BeginStructureNode(Globals.gameConfigCurrent.getStructureData().getTypes().iterator().next()),
BuildStructureTree.create(),
new SucceederNode(null) new SucceederNode(null)
); );
} }

View File

@ -0,0 +1,93 @@
package electrosphere.server.ai.trees.struct;
import electrosphere.collision.CollisionEngine;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.interact.PlaceBlockNode;
import electrosphere.server.ai.nodes.checks.inventory.InventoryContainsNode;
import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
import electrosphere.server.ai.nodes.solvers.SolveBuildMaterialNode;
import electrosphere.server.ai.trees.creature.AcquireItemTree;
import electrosphere.server.ai.trees.creature.MoveToTarget;
/**
* A tree to build whatever the current structure target is
*/
public class BuildStructureTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "ConstructShelterTree";
/**
* Creates a construct shelter tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SequenceNode(
new PublishStatusNode("Construct a structure"),
//figure out current task
new SelectorNode(
//make sure we know what material we need to build with currently
new SequenceNode(
new SolveBuildMaterialNode(),
new PublishStatusNode("Trying to place block in structure"),
//if has building materials
new SequenceNode(
new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT),
//if we're within range to place the material
new SelectorNode(
//in range, place block
new PlaceBlockNode(),
//not in range, move to within range
new SequenceNode(
//TODO: Solve for where to move towards
MoveToTarget.create(CollisionEngine.DEFAULT_INTERACT_DISTANCE, BlackboardKeys.ENTITY_TARGET)
)
)
)
),
//does not have building materials
new SequenceNode(
new PublishStatusNode("Acquire building material"),
//try to find building materials
AcquireItemTree.create(BlackboardKeys.BUILDING_MATERIAL_CURRENT),
new RunnerNode(null)
)
)
);
}
/**
* Sets the current needed building material
* @param blackboard The blackboard
* @param entityTypeId The id of the material
*/
public static void setCurrentMaterial(Blackboard blackboard, String entityTypeId){
blackboard.put(BlackboardKeys.BUILDING_MATERIAL_CURRENT, entityTypeId);
}
/**
* Checks if the blackboard stores the currently sought after material
* @param blackboard The blackboard
* @return true if there is a currently desired material, false otherwise
*/
public static boolean hasCurrentMaterial(Blackboard blackboard){
return blackboard.has(BlackboardKeys.BUILDING_MATERIAL_CURRENT);
}
/**
* Gets the currently sought after material
* @param blackboard The blackboard
* @return The id of the entity type of the sought after material if it exists, null otherwise
*/
public static String getCurrentMaterial(Blackboard blackboard){
return (String)blackboard.get(BlackboardKeys.BUILDING_MATERIAL_CURRENT);
}
}

View File

@ -1,6 +1,7 @@
package electrosphere.server.ai.trees.test; package electrosphere.server.ai.trees.test;
import electrosphere.game.data.creature.type.ai.BlockerTreeData; import electrosphere.game.data.creature.type.ai.BlockerTreeData;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.BlockStartNode; import electrosphere.server.ai.nodes.actions.BlockStartNode;
import electrosphere.server.ai.nodes.actions.combat.MeleeTargetingNode; import electrosphere.server.ai.nodes.actions.combat.MeleeTargetingNode;
@ -25,7 +26,7 @@ public class BlockerAITree {
return new SequenceNode( return new SequenceNode(
new BlockStartNode(), new BlockStartNode(),
new MeleeTargetingNode(5.0f), new MeleeTargetingNode(5.0f),
new FaceTargetNode() new FaceTargetNode(BlackboardKeys.ENTITY_TARGET)
); );
} }

View File

@ -248,7 +248,7 @@ public class ServerWorldData {
* @param real The real position * @param real The real position
* @return The local block grid position * @return The local block grid position
*/ */
public int convertRealToLocalBlockSpace(double real){ public static int convertRealToLocalBlockSpace(double real){
return (int)Math.floor(real * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE % BlockChunkData.CHUNK_DATA_WIDTH); return (int)Math.floor(real * BlockChunkData.BLOCKS_PER_UNIT_DISTANCE % BlockChunkData.CHUNK_DATA_WIDTH);
} }
@ -350,7 +350,7 @@ public class ServerWorldData {
* @param position The real coordinate * @param position The real coordinate
* @return The local block grid space coordinate * @return The local block grid space coordinate
*/ */
public Vector3i convertRealToLocalBlockSpace(Vector3d position){ public static Vector3i convertRealToLocalBlockSpace(Vector3d position){
return new Vector3i( return new Vector3i(
convertRealToLocalBlockSpace(position.x), convertRealToLocalBlockSpace(position.x),
convertRealToLocalBlockSpace(position.y), convertRealToLocalBlockSpace(position.y),

View File

@ -43,6 +43,10 @@ public class ContentSerialization {
for(Entity entity : entities){ for(Entity entity : entities){
if(!CreatureUtils.hasControllerPlayerId(entity) && !ServerCharacterData.hasServerCharacterDataTree(entity)){ if(!CreatureUtils.hasControllerPlayerId(entity) && !ServerCharacterData.hasServerCharacterDataTree(entity)){
EntityType type = CommonEntityUtils.getEntityType(entity); EntityType type = CommonEntityUtils.getEntityType(entity);
if(type == EntityType.ENGINE){
//do not serialize engine entities
continue;
}
if(type != null){ if(type != null){
EntitySerialization serializedEntity = constructEntitySerialization(entity); EntitySerialization serializedEntity = constructEntitySerialization(entity);
rVal.serializedEntities.add(serializedEntity); rVal.serializedEntities.add(serializedEntity);
@ -114,6 +118,9 @@ public class ContentSerialization {
*/ */
public static Entity serverHydrateEntitySerialization(Realm realm, EntitySerialization serializedEntity){ public static Entity serverHydrateEntitySerialization(Realm realm, EntitySerialization serializedEntity){
Entity rVal = null; Entity rVal = null;
if(serializedEntity.getSubtype() == null){
throw new Error("Subtype undefined!");
}
switch(EntityTypes.fromInt(serializedEntity.getType())){ switch(EntityTypes.fromInt(serializedEntity.getType())){
case CREATURE: { case CREATURE: {
CreatureTemplate template = null; CreatureTemplate template = null;

View File

@ -104,17 +104,17 @@ public class MacroData {
} }
//add a test character //add a test character
Character testChar = new Character(); // Character testChar = new Character();
testChar.setPos(new Vector3d(ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 3, 32769)))); // testChar.setPos(new Vector3d(ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 3, 32769))));
Race.setRace(testChar, Race.create("human", "human")); // Race.setRace(testChar, Race.create("human", "human"));
rVal.characters.add(testChar); // rVal.characters.add(testChar);
//add a test character //add a test character
Vector3d structPos = ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 0, 32770)); // Vector3d structPos = ServerWorldData.convertChunkToRealSpace(new Vector3i(32774, 0, 32770));
double elevationAtStruct = serverWorldData.getServerTerrainManager().getElevation(32774, 32770, 0, 0); // double elevationAtStruct = serverWorldData.getServerTerrainManager().getElevation(32774, 32770, 0, 0);
structPos.y = elevationAtStruct; // structPos.y = elevationAtStruct;
Structure struct = Structure.createStructure(Globals.gameConfigCurrent.getStructureData().getType("test1"),structPos); // Structure struct = Structure.createStructure(Globals.gameConfigCurrent.getStructureData().getType("test1"),structPos);
rVal.structures.add(struct); // rVal.structures.add(struct);
//spawn initial characters in each race //spawn initial characters in each race
//find initial positions to place characters at per race //find initial positions to place characters at per race

View File

@ -41,6 +41,11 @@ public class Structure extends CharacterData implements MacroAreaObject {
*/ */
String type; String type;
/**
* Tracks whether this structure needs repairs or not
*/
boolean repairable = false;
/** /**
* Constructor * Constructor
* @param dataType The data type of the structure * @param dataType The data type of the structure
@ -126,5 +131,21 @@ public class Structure extends CharacterData implements MacroAreaObject {
this.fab = fab; this.fab = fab;
} }
/**
* Checks if the structure is repairable
* @return true if it is repairable, false otherwise
*/
public boolean isRepairable() {
return repairable;
}
/**
* Sets whether this structure is repairable or not
* @param repairable true if it is repairable, false otherwise
*/
public void setRepairable(boolean repairable) {
this.repairable = repairable;
}
} }

View File

@ -0,0 +1,24 @@
package electrosphere.server.macro.utils;
import org.joml.Vector3d;
import electrosphere.game.data.struct.StructureData;
import electrosphere.server.macro.MacroData;
/**
* Utilities for placing structures
*/
public class StructurePlacementUtils {
/**
* Gets an optimal position to place a structure
* @param macroData The macro data
* @param structureData The data for the structure
* @param approxLocation The location to start searching from
* @return The position
*/
public static Vector3d getPlacementPosition(MacroData macroData, StructureData structureData, Vector3d approxLocation){
return approxLocation;
}
}

View File

@ -0,0 +1,49 @@
package electrosphere.server.macro.utils;
import org.joml.Vector3d;
import org.joml.Vector3i;
import electrosphere.client.block.BlockChunkData;
import electrosphere.game.data.block.BlockFab;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.ServerWorldData;
import electrosphere.server.datacell.gridded.GriddedDataCellManager;
import electrosphere.server.macro.structure.Structure;
/**
* Utilities for repairing a structure
*/
public class StructureRepairUtils {
/**
* Solves for the next position in the structure's fab that can be repaired
* @param realm The realm the structure is within
* @param struct The structure
* @return The next position that can be repaired if it exists, null otherwise
*/
public static Vector3i getRepairablePosition(Realm realm, Structure struct){
//error checking
if(!(realm.getDataCellManager() instanceof GriddedDataCellManager)){
throw new Error("Realm is not a gridded realm!");
}
BlockFab fab = struct.getFab();
Vector3d structStartPos = struct.getStartPos();
GriddedDataCellManager griddedDataCellManager = (GriddedDataCellManager)realm.getDataCellManager();
for(int x = 0; x < fab.getDimensions().x; x++){
for(int y = 0; y < fab.getDimensions().y; y++){
for(int z = 0; z < fab.getDimensions().z; z++){
Vector3d offsetPos = new Vector3d(structStartPos).add(x,y,z);
Vector3i chunkPos = ServerWorldData.convertRealToChunkSpace(offsetPos);
Vector3i blockPos = ServerWorldData.convertRealToLocalBlockSpace(offsetPos);
BlockChunkData blockChunkData = griddedDataCellManager.getBlocksAtPosition(chunkPos);
if(blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z) != fab.getType(x, y, z)){
return new Vector3i(x,y,z);
}
}
}
}
return null;
}
}