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" : [ "aiTrees" : [
{ {
"name" : "Attacker", "name" : "Maslow"
"aggroRange" : 10,
"attackRange" : 0.8
} }
], ],
"cameraData" : { "cameraData" : {

View File

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

View File

@ -1,9 +1,11 @@
package electrosphere.client.ui.menu.debug; package electrosphere.client.ui.menu.debug;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.renderer.ui.imgui.ImGuiWindow; import electrosphere.renderer.ui.imgui.ImGuiWindow;
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback; import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
import electrosphere.server.ai.AI; import electrosphere.server.ai.AI;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import imgui.ImGui; import imgui.ImGui;
/** /**
@ -38,6 +40,15 @@ public class ImGuiAI {
Globals.aiManager.setActive(!Globals.aiManager.isActive()); 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")){ if(ImGui.collapsingHeader("Statuses")){
for(AI ai : Globals.aiManager.getAIList()){ for(AI ai : Globals.aiManager.getAIList()){
ImGui.text(ai.getParent().getId() + " - " + ai.getStatus()); 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.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.trees.creature.AttackerAITree; import electrosphere.server.ai.trees.creature.AttackerAITree;
import electrosphere.server.ai.trees.hierarchy.MaslowTree;
import electrosphere.server.ai.trees.test.BlockerAITree; import electrosphere.server.ai.trees.test.BlockerAITree;
/** /**
@ -47,6 +48,11 @@ public class AI {
*/ */
List<AITreeNode> evaluatedNodes = new LinkedList<AITreeNode>(); 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 * The status of the ai
*/ */
@ -69,6 +75,9 @@ public class AI {
case AttackerAITree.TREE_NAME: { case AttackerAITree.TREE_NAME: {
rVal.rootNode = AttackerAITree.create((AttackerTreeData) aiData); rVal.rootNode = AttackerAITree.create((AttackerTreeData) aiData);
} break; } break;
case MaslowTree.TREE_NAME: {
rVal.rootNode = MaslowTree.create();
} break;
default: { default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName())); LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
} break; } break;
@ -137,7 +146,7 @@ public class AI {
* @return true if should simulate, false otherwise * @return true if should simulate, false otherwise
*/ */
private boolean shouldExecute(){ 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(); 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); blockers.addAll(this.structures);
return blockers; 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;
}
} }