ai tool equipping
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-05-01 22:21:12 -04:00
parent f0ff713413
commit af0e30dd95
11 changed files with 291 additions and 55 deletions

View File

@ -200,7 +200,8 @@
"GRAVITY",
"MELEE",
"TARGETABLE",
"OUTLINE"
"OUTLINE",
"AXE"
],
"graphicsTemplate": {
"model": {

View File

@ -1636,6 +1636,8 @@ Many new AI trees
- AI can harvest entities
- AI can pick up items
- AI can craft
- AI can equip tools
- AI can fell trees

View File

@ -358,13 +358,15 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(true);
if(this.currentMove != null){
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(true);
}
}
}
}
@ -382,13 +384,15 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
if(this.currentMove != null){
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}
@ -406,13 +410,15 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
if(this.currentMove != null){
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}

View File

@ -0,0 +1,95 @@
package electrosphere.server.ai.nodes.actions.inventory;
import electrosphere.entity.Entity;
import electrosphere.entity.state.equip.ServerToolbarState;
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.nodes.AITreeNode;
/**
* Tries to equip a type of item to the toolbar and select that item in the toolbar
*/
public class EquipToolbarNode implements AITreeNode {
/**
* Key to lookup the item type under
*/
String itemTypeKey;
/**
* Constructor
* @param itemTypeKey The blackboard key to lookup the item type under
*/
public EquipToolbarNode(String itemTypeKey){
this.itemTypeKey = itemTypeKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
String targetItemType = (String)blackboard.get(itemTypeKey);
//check if that item type is already equipped
ServerToolbarState serverToolbarState = ServerToolbarState.getServerToolbarState(entity);
if(serverToolbarState.getRealWorldItem() != null){
Entity realWorldItem = serverToolbarState.getRealWorldItem();
String type = CommonEntityUtils.getEntitySubtype(realWorldItem);
if(type.equals(targetItemType)){
return AITreeNodeResult.SUCCESS;
}
}
//check if this item type is already in toolbar and we can just swap to it
if(InventoryUtils.hasToolbarInventory(entity)){
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(entity);
for(Entity itemEnt : toolbarInventory.getItems()){
if(itemEnt == null){
continue;
}
String type = CommonEntityUtils.getEntitySubtype(itemEnt);
if(type.equals(targetItemType)){
String slotId = toolbarInventory.getItemSlot(itemEnt);
serverToolbarState.attemptChangeSelection(Integer.parseInt(slotId));
return AITreeNodeResult.SUCCESS;
}
}
}
//make sure we have a free toolbar slot
int freeToolbarSlot = 0;
if(InventoryUtils.hasToolbarInventory(entity)){
RelationalInventoryState toolbarInventory = InventoryUtils.getToolbarInventory(entity);
int i = 0;
for(Entity itemEnt : toolbarInventory.getItems()){
if(itemEnt == null){
freeToolbarSlot = i;
break;
}
i++;
}
}
//check if this item type is in the natural inventory, if it is, try to equip it
if(InventoryUtils.hasNaturalInventory(entity)){
UnrelationalInventoryState naturalInventory = InventoryUtils.getNaturalInventory(entity);
//find matching natural item
Entity naturalItem = null;
for(Entity itemEnt : naturalInventory.getItems()){
String type = CommonEntityUtils.getEntitySubtype(itemEnt);
if(type.equals(targetItemType)){
naturalItem = itemEnt;
break;
}
}
if(naturalItem != null){
serverToolbarState.attemptEquip(naturalItem, freeToolbarSlot);
return AITreeNodeResult.SUCCESS;
}
}
return AITreeNodeResult.FAILURE;
}
}

View File

@ -5,8 +5,6 @@ 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;
@ -33,11 +31,6 @@ public class InventoryContainsNode implements AITreeNode {
@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;
@ -46,31 +39,11 @@ public class InventoryContainsNode implements AITreeNode {
//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;
}
List<Entity> items = InventoryUtils.getAllInventoryItems(entity);
List<String> itemIds = items.stream().map((Entity itemEnt) -> {return CommonEntityUtils.getEntitySubtype(itemEnt);}).collect(Collectors.toList());
if(itemIds.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;
}

View File

@ -37,7 +37,7 @@ public class TargetRangeCheckNode implements AITreeNode {
Object targetRaw = blackboard.get(this.targetKey);
Vector3d targetPos = null;
if(targetRaw == null){
throw new Error("Target undefined!");
return AITreeNodeResult.FAILURE;
}
if(targetRaw instanceof Vector3d){
targetPos = (Vector3d)targetRaw;

View File

@ -0,0 +1,38 @@
package electrosphere.server.ai.nodes.meta;
import electrosphere.entity.Entity;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Stores a piece of data into a blackboard
*/
public class DataStorageNode implements AITreeNode {
/**
* The data to store in the key
*/
Object data;
/**
* The key to push data into
*/
String destinationKey;
/**
* Constructor
* @param destinationKey The key to push data into
* @param data The data to store at the key
*/
public DataStorageNode(String destinationKey, Object data){
this.data = data;
this.destinationKey = destinationKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
blackboard.put(this.destinationKey, this.data);
return AITreeNodeResult.SUCCESS;
}
}

View File

@ -44,6 +44,8 @@ public class SolveSourcingTreeNode implements AITreeNode {
//set the type to harvest if this is a harvest type
if(sourcingData.getHarvestTargets().size() > 0){
HarvestNode.setHarvestTargetType(blackboard, sourcingData.getHarvestTargets().get(0).getId());
} else if(sourcingData.getTrees().size() > 0){
HarvestNode.setHarvestTargetType(blackboard, sourcingData.getTrees().get(0).getId());
}
SolveSourcingTreeNode.setItemSourcingData(blackboard, sourcingData);
SolveSourcingTreeNode.setItemTargetCategory(blackboard, sourcingData.getGoalItem());

View File

@ -15,6 +15,7 @@ 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;
import electrosphere.server.ai.trees.creature.melee.FellTree;
/**
* A tree to acquire an item
@ -67,7 +68,8 @@ public class AcquireItemTree {
new PublishStatusNode("Fell a tree"),
//check if we should be sourcing this from felling a tree
new SourcingTypeNode(SourcingType.TREE, blackboardKey),
//TODO: logic to fell a tree
new TargetEntityCategoryNode(BlackboardKeys.HARVEST_TARGET_TYPE),
FellTree.create(BlackboardKeys.ENTITY_TARGET),
new RunnerNode(null)
)
),

View File

@ -0,0 +1,38 @@
package electrosphere.server.ai.trees.creature.inventory;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.inventory.EquipToolbarNode;
import electrosphere.server.ai.nodes.checks.inventory.InventoryContainsNode;
import electrosphere.server.ai.nodes.meta.DataTransferNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
/**
* Tries to equip an item into the toolbar
*/
public class EquipToolbarTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "EquipToolbar";
/**
* Creates an equip toolbar tree
* @param itemType The type of item to try to equip in the toolbar
* @return The root node of the tree
*/
public static AITreeNode create(String targetKey){
return new SequenceNode(
new PublishStatusNode("Equip an item"),
//check that we have this type of item
new DataTransferNode(targetKey, BlackboardKeys.INVENTORY_CHECK_TYPE),
new InventoryContainsNode(BlackboardKeys.INVENTORY_CHECK_TYPE),
//try to equip the item to the toolbar
new EquipToolbarNode(BlackboardKeys.INVENTORY_CHECK_TYPE)
);
}
}

View File

@ -0,0 +1,79 @@
package electrosphere.server.ai.trees.creature.melee;
import electrosphere.game.data.item.ItemIdStrings;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.AITreeNode.AITreeNodeResult;
import electrosphere.server.ai.nodes.actions.combat.AttackStartNode;
import electrosphere.server.ai.nodes.actions.move.FaceTargetNode;
import electrosphere.server.ai.nodes.actions.move.MoveStopNode;
import electrosphere.server.ai.nodes.checks.IsMovingNode;
import electrosphere.server.ai.nodes.checks.spatial.TargetRangeCheckNode;
import electrosphere.server.ai.nodes.meta.DataStorageNode;
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.TimerNode;
import electrosphere.server.ai.nodes.meta.decorators.UntilNode;
import electrosphere.server.ai.trees.creature.MoveToTarget;
import electrosphere.server.ai.trees.creature.inventory.EquipToolbarTree;
/**
* A behavior tree to fell a tree entity
*/
public class FellTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "Fell";
/**
* Distance to start attacking at
*/
static final float FELL_RANGE = 0.5f;
/**
* Creates a fell tree
* @param targetKey The key to lookup the target entity under
* @return The root node of the tree
*/
public static AITreeNode create(String targetKey){
return new SequenceNode(
//preconditions here
new DataStorageNode(BlackboardKeys.INVENTORY_CHECK_TYPE, ItemIdStrings.ITEM_STONE_AXE),
EquipToolbarTree.create(BlackboardKeys.INVENTORY_CHECK_TYPE),
//perform different actions based on distance to target
new SelectorNode(
//in attack range
new SequenceNode(
//check if in range of target
new TargetRangeCheckNode(FellTree.FELL_RANGE, targetKey),
//stop walking now that we're in range
new PublishStatusNode("Slowing down"),
new MoveStopNode(),
new UntilNode(AITreeNodeResult.FAILURE, new IsMovingNode()),
//attack
new SequenceNode(
new PublishStatusNode("Attacking"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new AttackStartNode(),
new TimerNode(new RunnerNode(null), 300)
)
),
//move to target
MoveToTarget.create(FellTree.FELL_RANGE, targetKey),
//movement succeeded, but failed to attack -- tree is currently running
new RunnerNode(null)
)
);
}
}