ai work
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-04-30 19:23:00 -04:00
parent 4b8aa104b5
commit 4b25c110ee
13 changed files with 279 additions and 4 deletions

View File

@ -596,9 +596,7 @@
},
"aiTrees" : [
{
"name" : "Attacker",
"aggroRange" : 10,
"attackRange" : 0.8
"name" : "Maslow"
}
],
"cameraData" : {

View File

@ -1627,6 +1627,7 @@ Blocks stack
Item tag adjustments
Pine tree loot pool update
DB characters store toolbar items
Scaffolding for new macro-cognizating ai approach

View File

@ -1,9 +1,11 @@
package electrosphere.client.ui.menu.debug;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.renderer.ui.imgui.ImGuiWindow;
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
import electrosphere.server.ai.AI;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import imgui.ImGui;
/**
@ -38,6 +40,15 @@ public class ImGuiAI {
Globals.aiManager.setActive(!Globals.aiManager.isActive());
}
int serverIdForClientEntity = Globals.clientSceneWrapper.mapClientToServerId(Globals.playerEntity.getId());
Entity serverPlayerEntity = EntityLookupUtils.getEntityById(serverIdForClientEntity);
AI playerAi = AI.getAI(serverPlayerEntity);
ImGui.text("AI applied to player entity: " + playerAi.isApplyToPlayer());
if(ImGui.button("Toggle AI on player entity")){
playerAi.setApplyToPlayer(!playerAi.isApplyToPlayer());
}
if(ImGui.collapsingHeader("Statuses")){
for(AI ai : Globals.aiManager.getAIList()){
ImGui.text(ai.getParent().getId() + " - " + ai.getStatus());

View File

@ -14,6 +14,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.trees.creature.AttackerAITree;
import electrosphere.server.ai.trees.hierarchy.MaslowTree;
import electrosphere.server.ai.trees.test.BlockerAITree;
/**
@ -47,6 +48,11 @@ public class AI {
*/
List<AITreeNode> evaluatedNodes = new LinkedList<AITreeNode>();
/**
* Tracks whether this should apply even if there is a controlling player
*/
boolean applyToPlayer = false;
/**
* The status of the ai
*/
@ -69,6 +75,9 @@ public class AI {
case AttackerAITree.TREE_NAME: {
rVal.rootNode = AttackerAITree.create((AttackerTreeData) aiData);
} break;
case MaslowTree.TREE_NAME: {
rVal.rootNode = MaslowTree.create();
} break;
default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
} break;
@ -137,7 +146,7 @@ public class AI {
* @return true if should simulate, false otherwise
*/
private boolean shouldExecute(){
return !CreatureUtils.hasControllerPlayerId(this.parent);
return this.applyToPlayer || !CreatureUtils.hasControllerPlayerId(this.parent);
}
/**
@ -164,5 +173,23 @@ public class AI {
ServerGroundMovementTree.getServerGroundMovementTree(this.parent).slowdown();
}
}
/**
* Checks if this should apply ai behavior even if the entity is controlled by a player
* @return true if it should always apply ai behavior, false if it should defer to player
*/
public boolean isApplyToPlayer() {
return applyToPlayer;
}
/**
* Sets whether this should apply ai behavior even if the entity is controlled by a player
* @param applyToPlayer true if it sohuld always apply ai behavior, false if it should defer to player
*/
public void setApplyToPlayer(boolean applyToPlayer) {
this.applyToPlayer = applyToPlayer;
}
}

View File

@ -0,0 +1,28 @@
package electrosphere.server.ai.nodes.checks.macro;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.server.ServerCharacterData;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.macro.character.Character;
import electrosphere.server.macro.character.CharacterUtils;
import electrosphere.server.macro.structure.Structure;
/**
* Checks if the character has shelter
*/
public class HasShelter implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
ServerCharacterData serverCharacterData = ServerCharacterData.getServerCharacterData(entity);
Character character = Globals.macroData.getCharacter(serverCharacterData.getCharacterId());
Structure shelter = CharacterUtils.getShelter(character);
if(shelter == null){
return AITreeNodeResult.FAILURE;
}
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -0,0 +1,21 @@
package electrosphere.server.ai.nodes.checks.macro;
import electrosphere.entity.Entity;
import electrosphere.entity.state.server.ServerCharacterData;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Checks if this entity is a character
*/
public class IsCharacterNode implements AITreeNode {
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
if(ServerCharacterData.hasServerCharacterDataTree(entity)){
return AITreeNodeResult.SUCCESS;
}
return AITreeNodeResult.FAILURE;
}
}

View File

@ -0,0 +1,27 @@
package electrosphere.server.ai.trees.hierarchy;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.trees.hierarchy.safety.MaslowSafetyTree;
/**
* Arranges goals based on an approximation of Maslow's hierarchy of needs
*/
public class MaslowTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "Maslow";
/**
* Creates an attacker ai tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SequenceNode(
MaslowSafetyTree.create()
);
}
}

View File

@ -0,0 +1,24 @@
package electrosphere.server.ai.trees.hierarchy.safety;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
/**
* A tree that causes the entity to engage in combat when it determines it needs to in order to maintain its safety
*/
public class CombatTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "CombatTree";
/**
* Creates a combat tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SucceederNode(null);
}
}

View File

@ -0,0 +1,24 @@
package electrosphere.server.ai.trees.hierarchy.safety;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
/**
* A tree that causes the entity to flee in terror if it deems it needs to
*/
public class FleeTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "FleeTree";
/**
* Creates an flee tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SucceederNode(null);
}
}

View File

@ -0,0 +1,29 @@
package electrosphere.server.ai.trees.hierarchy.safety;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.trees.hierarchy.safety.shelter.ShelterTree;
/**
* A tree that runs all of the tier 1 maslow need trees
*/
public class MaslowSafetyTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "MaslowSafetyTree";
/**
* Creates a tier 1 maslow tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SequenceNode(
FleeTree.create(),
CombatTree.create(),
ShelterTree.create()
);
}
}

View File

@ -0,0 +1,23 @@
package electrosphere.server.ai.trees.hierarchy.safety.shelter;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
/**
* Tree for constructing shelter
*/
public class ConstructShelterTree {
/**
* 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 SucceederNode(null);
}
}

View File

@ -0,0 +1,48 @@
package electrosphere.server.ai.trees.hierarchy.safety.shelter;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.checks.macro.HasShelter;
import electrosphere.server.ai.nodes.checks.macro.IsCharacterNode;
import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
/**
* A tree that causes the entity to try to secure shelter
*/
public class ShelterTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "ShelterTree";
/**
* Creates a shelter tree
* @return The root node of the tree
*/
public static AITreeNode create(){
return new SequenceNode(
//make sure that this entity actually cares about shelter
new SequenceNode(
//if this is a character
new IsCharacterNode()
),
//now that we know the entity cares about shelter, check if they have shelter
new SequenceNode(
//if has shelter..
new SelectorNode(
new SequenceNode(
new HasShelter(),
//does not have shelter
ConstructShelterTree.create()
),
new SequenceNode(
//already has shelter
//TODO: check environment (ie time of day) to see if we should return to shelter
)
)
)
);
}
}

View File

@ -301,5 +301,19 @@ public class MacroData {
blockers.addAll(this.structures);
return blockers;
}
/**
* Gets a character by its id
* @param id The id
* @return The character if it exists, null otherwise
*/
public Character getCharacter(int id){
for(Character character : this.characters){
if(character.getId() == id){
return character;
}
}
return null;
}
}