architecture work, blocker ai tree
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-08-10 14:43:00 -04:00
parent 9349b35596
commit 07a708edfc
10 changed files with 191 additions and 16 deletions

4
Jenkinsfile vendored
View File

@ -23,6 +23,10 @@ pipeline {
stage('Build (Documentation)') {
steps {
sh 'cd ./docs && doxygen ./Doxyfile'
sh 'rm -rf /docs/*'
sh 'cd ./docs-dist/html && rm -f ./docs.tar.gz'
sh 'cd ./docs-dist/html && tar -czvf ./docs.tar.gz ./*'
sh 'cp ./docs-dist/html/docs.tar.gz /docs/docs.tar.gz && cd /docs/ && tar -xzvf ./docs.tar.gz'
}
}
stage('Test') {

View File

@ -514,10 +514,7 @@
},
"aiTrees" : [
{
"name" : "Attacker",
"aggroRange" : 10,
"attackRange" : 2,
"stateChangeTimeout" : "240"
"name" : "Blocker"
}
],
"boneGroups" : [

View File

@ -6,17 +6,19 @@
enemy ai
review effects
review combat code (lifestate, damage calculation, etc)
- Allow block hotboxxes to block damage
audio fx for everything
- Block SFX
+ rearchitecture
Ability to synchronize state of synchronized variables on client joining scene
ie, send currentBlockVariant when client connects
this is probably going to involve massive netcode arch to send all synchronized variables for each entity
+ fix the vibes
Attack animation feels slow
Hitboxes between server and client feel wayyyy off
+ bug fixes
Things that feel bad:
Attack animation feels slow
Short movement bursts feel jittery
Part of this may be cylinder collidable instead of capsule
Hitboxes between server and client feel wayyyy off
Sound effect on block
Allow block hotboxes to block damage

View File

@ -362,7 +362,9 @@ public class HitboxCollectionState {
//calculate rotation from old position to new position
//the second quaternion is a rotation along the x axis. This is used to put the hitbox rotation into ode's space
//ode is Z-axis-up
rotation = MathUtils.calculateRotationFromPointToPoint(previousWorldPos,worldPosition).mul(new Quaterniond(0,0,0.707,0.707));
if(previousWorldPos.distance(worldPosition) > 0.0){
rotation = MathUtils.calculateRotationFromPointToPoint(previousWorldPos,worldPosition).mul(new Quaterniond(0,0,0.707,0.707));
}
//create new capsule
length = previousWorldPos.distance(worldPosition) / 2.0;

View File

@ -9,6 +9,7 @@ import com.google.gson.JsonParseException;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.creature.Attacker;
import electrosphere.server.ai.creature.Blocker;
/**
* Deserializes ai tree data types
@ -21,6 +22,8 @@ public class AITreeDataSerializer implements JsonDeserializer<AITreeData> {
switch(json.getAsJsonObject().get("name").getAsString()){
case Attacker.TREE_NAME:
return context.deserialize(json, AttackerTreeData.class);
case Blocker.TREE_NAME:
return context.deserialize(json, BlockerTreeData.class);
}
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("JSON Object provided to AITreeDataSerializer that cannot deserialize into a tree data type cleanly"));

View File

@ -0,0 +1,13 @@
package electrosphere.game.data.creature.type.ai;
/**
* Data for a blocker ai tree
*/
public class BlockerTreeData implements AITreeData {
@Override
public String getName() {
return "Blocker";
}
}

View File

@ -5,6 +5,7 @@ import org.joml.Vector3d;
import electrosphere.client.collision.ClientHitboxCollision;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.CombatMessage;
import electrosphere.net.template.ClientProtocolTemplate;
@ -25,7 +26,11 @@ public class CombatProtocol implements ClientProtocolTemplate<CombatMessage> {
Vector3d position = new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ());
Entity senderEntity = Globals.clientSceneWrapper.getEntityFromServerId(message.getentityID());
Entity receiverEntity = Globals.clientSceneWrapper.getEntityFromServerId(message.getreceiverEntityID());
ClientHitboxCollision.handleHitboxCollision(senderEntity, receiverEntity, position, message.gethitboxType(), message.gethurtboxType());
if(senderEntity != null && receiverEntity != null){
ClientHitboxCollision.handleHitboxCollision(senderEntity, receiverEntity, position, message.gethitboxType(), message.gethurtboxType());
} else {
LoggerInterface.loggerEngine.WARNING("Received collision event for entities that are undefined! " + senderEntity + " " + receiverEntity);
}
} break;
}
}

View File

@ -12,8 +12,10 @@ import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.creature.type.ai.AITreeData;
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
import electrosphere.logger.LoggerInterface;
import electrosphere.server.ai.creature.Attacker;
import electrosphere.server.ai.creature.Blocker;
/**
* A collection of AITrees that are attached to a single entity.
@ -55,6 +57,12 @@ public class AI {
case Attacker.TREE_NAME:
rVal.trees.add(Attacker.construct(parent, (AttackerTreeData) aiData));
break;
case Blocker.TREE_NAME: {
rVal.trees.add(Blocker.construct(parent, (BlockerTreeData) aiData));
} break;
default: {
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
} break;
}
}

View File

@ -0,0 +1,125 @@
package electrosphere.server.ai.creature;
import java.util.Random;
import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
import electrosphere.server.ai.AITree;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.util.math.MathUtils;
/**
* AI that just continuously blocks
*/
public class Blocker implements AITree {
/**
* The name of the tree type
*/
public static final String TREE_NAME = "Blocker";
/**
* The configuring tree data for this tree
*/
BlockerTreeData treeData;
/**
* The parent to this tree
*/
Entity parent;
/**
* The current target of this tree
*/
Entity target = null;
/**
* The number of frames the tree has been in its current state
*/
int currentStateFrameCount = 0;
/**
* The random used for rolls on behavior transitions
*/
Random random = new Random();
/**
* Private constructor
* @param parent The parent entity
*/
private Blocker(Entity parent){
this.parent = parent;
}
/**
* Constructs an attacker tree
* @param parent The parent entity
* @param treeData The tree data
* @return The Attacker tree
*/
public static AITree construct(Entity parent, BlockerTreeData treeData) {
Blocker rVal = new Blocker(parent);
rVal.treeData = treeData;
return rVal;
}
@Override
public void simulate() {
if(target == null){
searchForTarget();
}
if(ServerBlockTree.getServerBlockTree(parent) != null){
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
if(!serverBlockTree.isBlocking()){
serverBlockTree.start();
}
}
if(target != null){
Vector3d targetPos = EntityUtils.getPosition(target);
EntityUtils.getRotation(parent).set(MathUtils.calculateRotationFromPointToPoint(EntityUtils.getPosition(parent), targetPos));
}
}
@Override
public String getTreeName() {
return TREE_NAME;
}
@Override
public void updateStateAndPriority() {
}
@Override
public String getCurrentStateName() {
return "BLCOKING";
}
@Override
public int getCurrentStatePriority() {
return 1;
}
/**
* Searches for a valid target
*/
private void searchForTarget(){
Vector3d position = EntityUtils.getPosition(parent);
Realm realm = Globals.realmManager.getEntityRealm(parent);
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
if(current != parent && CreatureUtils.isCreature(current)){
target = current;
break;
}
}
}
}

View File

@ -5,6 +5,8 @@ import org.joml.Vector3d;
import org.joml.Vector3f;
import org.joml.Vector4d;
import electrosphere.logger.LoggerInterface;
/**
* Utility functions for doing math
*/
@ -91,7 +93,21 @@ public class MathUtils {
* @return The quaternion
*/
public static Quaterniond calculateRotationFromPointToPoint(Vector3d originPoint, Vector3d destinationPoint){
return getOriginVector().rotationTo(new Vector3d(destinationPoint).sub(originPoint).normalize(), new Quaterniond());
if(originPoint == destinationPoint || originPoint.distance(destinationPoint) == 0.0){
String message = "Trying to find rotation between same point!";
LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
return new Quaterniond();
}
Quaterniond rVal = getOriginVector().rotationTo(new Vector3d(destinationPoint).sub(originPoint).normalize(), new Quaterniond());
if(!Double.isFinite(rVal.w) || !Double.isFinite(rVal.x) || !Double.isFinite(rVal.y) || !Double.isFinite(rVal.z)){
String message = "Rotation is NaN!\n" +
"originPoint: " + originPoint + "\n" +
"destinationPoint: " + destinationPoint
;
LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
rVal = new Quaterniond();
}
return rVal;
}