Add cat
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2026-02-05 20:28:47 -05:00
parent 720e147d99
commit d152047fe1
13 changed files with 263 additions and 6 deletions

View File

@ -6,7 +6,8 @@
"files" : [ "files" : [
"Data/entity/creatures/human.json", "Data/entity/creatures/human.json",
"Data/entity/creatures/skeleton.json", "Data/entity/creatures/skeleton.json",
"Data/entity/creatures/editor.json" "Data/entity/creatures/editor.json",
"Data/entity/creatures/animals.json"
] ]
} }

View File

@ -38,6 +38,80 @@
"onDamageIFrames" : 30 "onDamageIFrames" : 30
}, },
"modelPath" : "Models/deer1.fbx" "modelPath" : "Models/deer1.fbx"
},
{
"id" : "Cat",
"displayName" : "Cat",
"hitboxes" : [
{
"type": "hurt",
"bone": "Bone",
"radius": 0.04
}
],
"tokens" : [
"GRAVITY"
],
"movementSystems" : [
{
"type" : "GROUND",
"acceleration" : 300.0,
"maxVelocity" : 10.0,
"strafeMultiplier" : 1.0,
"backpedalMultiplier" : 0.5,
"footstepFirstAudioOffset" : 0.2,
"footstepSecondAudioOffset" : 0.6,
"animationStartup" : {
"nameThirdPerson" : "Walk",
"priorityCategory" : "CORE_MOVEMENT"
},
"animationLoop" : {
"nameThirdPerson" : "Walk",
"priorityCategory" : "CORE_MOVEMENT"
},
"animationWindDown" : {
"nameThirdPerson" : "Walk",
"priorityCategory" : "CORE_MOVEMENT"
}
}
],
"collidable" : {
"type" : "CAPSULE",
"dimension1" : 0.35,
"dimension2" : 0.7,
"dimension3" : 0.35,
"linearFriction": 0.001,
"mass": 0.3,
"rotX": 0,
"rotY": 0,
"rotZ": 0,
"rotW": 1,
"offsetX" : 0,
"offsetY" : 0.7,
"offsetZ" : 0,
"angularlyStatic" : true
},
"healthSystem" : {
"maxHealth" : 100,
"onDamageIFrames" : 30
},
"aiTrees" : [
{
"name" : "Wander"
}
],
"graphicsTemplate": {
"model": {
"idleData": {
"animation": {
"nameFirstPerson" : "Pose",
"nameThirdPerson" : "Pose",
"priorityCategory" : "IDLE"
}
},
"path" : "Models/creatures/cat1/cat1.glb"
}
}
} }
], ],
"files" : [] "files" : []

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

View File

@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file #maven.buildNumber.plugin properties file
#Wed Jun 04 22:45:11 EDT 2025 #Thu Jul 03 09:59:19 EDT 2025
buildNumber=645 buildNumber=646

View File

@ -43,6 +43,9 @@ public class ScriptLevelEditorUtils {
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE)); cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
} }
cursorPos = cursorPos.add(cursorVerticalOffset); cursorPos = cursorPos.add(cursorVerticalOffset);
if(cursorPos.x < 0 || cursorPos.y < 0 || cursorPos.z < 0){
return;
}
realm.getServerWorldData().clampWithinBounds(cursorPos); realm.getServerWorldData().clampWithinBounds(cursorPos);
CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.clientState.selectedSpawntype.getId(), null); CreatureUtils.serverSpawnBasicCreature(realm, cursorPos, Globals.clientState.selectedSpawntype.getId(), null);
} else if(Globals.clientState.selectedSpawntype instanceof Item){ } else if(Globals.clientState.selectedSpawntype instanceof Item){
@ -56,6 +59,9 @@ public class ScriptLevelEditorUtils {
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE)); cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
} }
cursorPos = cursorPos.add(cursorVerticalOffset); cursorPos = cursorPos.add(cursorVerticalOffset);
if(cursorPos.x < 0 || cursorPos.y < 0 || cursorPos.z < 0){
return;
}
ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.clientState.selectedSpawntype.getId()); ItemUtils.serverSpawnBasicItem(realm, cursorPos, Globals.clientState.selectedSpawntype.getId());
} else if(Globals.clientState.selectedSpawntype instanceof FoliageType){ } else if(Globals.clientState.selectedSpawntype instanceof FoliageType){
LoggerInterface.loggerEngine.INFO("spawn " + Globals.clientState.selectedSpawntype.getId() + "!"); LoggerInterface.loggerEngine.INFO("spawn " + Globals.clientState.selectedSpawntype.getId() + "!");
@ -68,6 +74,9 @@ public class ScriptLevelEditorUtils {
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE)); cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
} }
cursorPos = cursorPos.add(cursorVerticalOffset); cursorPos = cursorPos.add(cursorVerticalOffset);
if(cursorPos.x < 0 || cursorPos.y < 0 || cursorPos.z < 0){
return;
}
FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.clientState.selectedSpawntype.getId()); FoliageUtils.serverSpawnTreeFoliage(realm, cursorPos, Globals.clientState.selectedSpawntype.getId());
} else { } else {
LoggerInterface.loggerEngine.INFO("spawn " + Globals.clientState.selectedSpawntype.getId() + "!"); LoggerInterface.loggerEngine.INFO("spawn " + Globals.clientState.selectedSpawntype.getId() + "!");
@ -80,6 +89,9 @@ public class ScriptLevelEditorUtils {
cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE)); cursorPos = new Vector3d(centerPos).add(new Vector3d(eyePos).normalize().mul(-CollisionEngine.DEFAULT_INTERACT_DISTANCE));
} }
cursorPos = cursorPos.add(cursorVerticalOffset); cursorPos = cursorPos.add(cursorVerticalOffset);
if(cursorPos.x < 0 || cursorPos.y < 0 || cursorPos.z < 0){
return;
}
CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.clientState.selectedSpawntype.getId()); CommonEntityUtils.serverSpawnBasicObject(realm, cursorPos, Globals.clientState.selectedSpawntype.getId());
} }
} }

View File

@ -11,6 +11,7 @@ import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.trees.test.BlockerAITree; import electrosphere.server.ai.trees.test.BlockerAITree;
import electrosphere.server.ai.trees.character.StandardCharacterTree; import electrosphere.server.ai.trees.character.StandardCharacterTree;
import electrosphere.server.ai.trees.creature.AttackerAITree; import electrosphere.server.ai.trees.creature.AttackerAITree;
import electrosphere.server.ai.trees.creature.WanderTree;
import electrosphere.server.ai.trees.hierarchy.MaslowTree; import electrosphere.server.ai.trees.hierarchy.MaslowTree;
/** /**
@ -34,6 +35,9 @@ public class AITreeDataSerializer implements JsonDeserializer<AITreeData> {
case StandardCharacterTree.TREE_NAME: { case StandardCharacterTree.TREE_NAME: {
return context.deserialize(json, StandardCharacterTreeData.class); return context.deserialize(json, StandardCharacterTreeData.class);
} }
case WanderTree.TREE_NAME: {
return context.deserialize(json, WanderTreeData.class);
}
} }
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("JSON Object provided to AITreeDataSerializer that cannot deserialize into a tree data type cleanly " + json.getAsJsonObject().get("name").getAsString())); LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("JSON Object provided to AITreeDataSerializer that cannot deserialize into a tree data type cleanly " + json.getAsJsonObject().get("name").getAsString()));

View File

@ -0,0 +1,20 @@
package electrosphere.data.entity.creature.ai;
import electrosphere.server.ai.trees.creature.WanderTree;
/**
* Tree data for controlling a wandering creature tree
*/
public class WanderTreeData implements AITreeData {
/**
* The name of the tree
*/
String name;
@Override
public String getName() {
return WanderTree.TREE_NAME;
}
}

View File

@ -226,7 +226,7 @@ public class Actor {
this.lodLevel == Actor.LOD_LEVEL_STATIC || this.lodLevel == Actor.LOD_LEVEL_STATIC ||
//actor doesn't have anything complicated render-wise (animations, custom textures, etc) //actor doesn't have anything complicated render-wise (animations, custom textures, etc)
( (
this.animationData.isPlayingAnimation() && !this.animationData.isPlayingAnimation() &&
this.meshMask.getBlockedMeshes().size() == 0 && this.meshMask.getBlockedMeshes().size() == 0 &&
this.textureMap == null && this.textureMap == null &&
this.uniformMap.isEmpty() && this.uniformMap.isEmpty() &&

View File

@ -6,6 +6,7 @@ import electrosphere.data.entity.creature.ai.AITreeData;
import electrosphere.data.entity.creature.ai.AttackerTreeData; import electrosphere.data.entity.creature.ai.AttackerTreeData;
import electrosphere.data.entity.creature.ai.BlockerTreeData; import electrosphere.data.entity.creature.ai.BlockerTreeData;
import electrosphere.data.entity.creature.ai.StandardCharacterTreeData; import electrosphere.data.entity.creature.ai.StandardCharacterTreeData;
import electrosphere.data.entity.creature.ai.WanderTreeData;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
@ -15,6 +16,7 @@ import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.trees.character.StandardCharacterTree; import electrosphere.server.ai.trees.character.StandardCharacterTree;
import electrosphere.server.ai.trees.creature.AttackerAITree; import electrosphere.server.ai.trees.creature.AttackerAITree;
import electrosphere.server.ai.trees.creature.WanderTree;
import electrosphere.server.ai.trees.hierarchy.MaslowTree; import electrosphere.server.ai.trees.hierarchy.MaslowTree;
import electrosphere.server.ai.trees.test.BlockerAITree; import electrosphere.server.ai.trees.test.BlockerAITree;
@ -72,6 +74,9 @@ public class AI {
case StandardCharacterTree.TREE_NAME: { case StandardCharacterTree.TREE_NAME: {
rVal.rootNode = StandardCharacterTree.create((StandardCharacterTreeData) aiData); rVal.rootNode = StandardCharacterTree.create((StandardCharacterTreeData) aiData);
} break; } break;
case WanderTree.TREE_NAME: {
rVal.rootNode = WanderTree.create((WanderTreeData) aiData);
} 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;

View File

@ -0,0 +1,78 @@
package electrosphere.server.ai.nodes.plan;
import java.util.Random;
import org.joml.Vector3d;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
/**
* Sets the move-to target position to a random nearby point
*/
public class TargetRandomPositionNode implements AITreeNode {
/**
* The key to lookup the target under
*/
String targetKey;
double radius = 5;
/**
* constructor
* @param targetKey The key to lookup the target under
*/
public TargetRandomPositionNode(String targetKey){
this.targetKey = targetKey;
}
@Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
Object targetRaw = blackboard.get(this.targetKey);
if(targetRaw == null){
Vector3d targetPos = null;
Random rand = new Random();
targetPos = new Vector3d(EntityUtils.getPosition(entity));
targetPos.add(
rand.nextFloat() * (radius * 2) - radius,
0,
rand.nextFloat() * (radius * 2) - radius
);
blackboard.put(targetKey, targetPos);
TargetPositionNode.setMoveToTarget(blackboard, targetPos);
}
return AITreeNodeResult.SUCCESS;
}
/**
* Sets the move-to target of the blackboard
* @param blackboard The blackboard
* @param position The target position
*/
public static void setMoveToTarget(Blackboard blackboard, Vector3d position){
blackboard.put(BlackboardKeys.MOVE_TO_TARGET, position);
}
/**
* Gets the move-to target of the blackboard
* @param blackboard The blackboard
* @return The move-to target if it exists, null otherwise
*/
public static Vector3d getMoveToTarget(Blackboard blackboard){
return (Vector3d)blackboard.get(BlackboardKeys.MOVE_TO_TARGET);
}
/**
* Checks if the blackboard has a move to target
* @param blackboard the blackboard
* @return true if it has a move-to target, false otherwise
*/
public static boolean hasMoveToTarget(Blackboard blackboard){
return blackboard.has(BlackboardKeys.MOVE_TO_TARGET);
}
}

View File

@ -32,6 +32,9 @@ public class NearbyEntityService implements AIService {
Realm realm = Globals.serverState.realmManager.getEntityRealm(entity); Realm realm = Globals.serverState.realmManager.getEntityRealm(entity);
if(realm != null){ if(realm != null){
Vector3d position = EntityUtils.getPosition(entity); Vector3d position = EntityUtils.getPosition(entity);
if(position.x < 0 || position.y < 0 || position.z < 0){
continue;
}
Collection<Entity> nearbyEntities = realm.getDataCellManager().entityLookup(position, NearbyEntityService.SEARCH_DIST); Collection<Entity> nearbyEntities = realm.getDataCellManager().entityLookup(position, NearbyEntityService.SEARCH_DIST);
NearbyEntityService.setNearbyEntities(ai.getBlackboard(), nearbyEntities); NearbyEntityService.setNearbyEntities(ai.getBlackboard(), nearbyEntities);
} }

View File

@ -0,0 +1,60 @@
package electrosphere.server.ai.trees.creature;
import electrosphere.data.entity.creature.ai.WanderTreeData;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
import electrosphere.server.ai.blackboard.BlackboardKeys;
import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.ai.nodes.actions.move.FaceTargetNode;
import electrosphere.server.ai.nodes.actions.move.MoveStartNode;
import electrosphere.server.ai.nodes.actions.move.MoveStopNode;
import electrosphere.server.ai.nodes.meta.DataDeleteNode;
import electrosphere.server.ai.nodes.meta.collections.RandomizerNode;
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.nodes.meta.decorators.TimerNode;
import electrosphere.server.ai.nodes.plan.TargetRandomPositionNode;
/**
* Wanders around aimlessly
*/
public class WanderTree {
/**
* Name of the tree
*/
public static final String TREE_NAME = "Wander";
/**
* Creates an wander ai tree
* @return The root node of the tree
*/
public static AITreeNode create(WanderTreeData wanderTreeData){
return new SequenceNode(
"WanderTree",
//select action to perform
new RandomizerNode(
//wait
new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Waiting"),
new SucceederNode(new MoveStopNode()),
new DataDeleteNode(BlackboardKeys.POINT_TARGET),
new TimerNode(new SucceederNode(null), 1200)
),
//move towards a random position
new SequenceNode(
"WanderMove",
new PublishStatusNode("Move into wander target range"),
new TargetRandomPositionNode(BlackboardKeys.POINT_TARGET),
new FaceTargetNode(BlackboardKeys.MOVE_TO_TARGET),
new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)),
new TimerNode(new SucceederNode(null), 600)
)
)
);
}
}