Renderer/src/main/java/electrosphere/entity/state/movement/ServerGroundMovementTree.java
2023-07-14 22:10:38 -04:00

638 lines
25 KiB
Java

package electrosphere.entity.state.movement;
import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.state.gravity.GravityUtils;
import electrosphere.collision.CollisionEngine;
import electrosphere.collision.PhysicsUtils;
import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.BehaviorTree;
import electrosphere.entity.state.attack.ServerAttackTree;
import electrosphere.entity.state.attack.ServerAttackTree.AttackTreeState;
import electrosphere.entity.state.movement.ServerSprintTree.SprintTreeState;
import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.renderer.anim.Animation;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.server.poseactor.PoseActor;
import electrosphere.renderer.Model;
import electrosphere.renderer.actor.Actor;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3d;
import org.joml.Vector3f;
/*
Behavior tree for movement in an entity
*/
public class ServerGroundMovementTree implements BehaviorTree {
public static enum MovementTreeState {
STARTUP,
MOVE,
SLOWDOWN,
IDLE,
}
public static enum MovementRelativeFacing {
FORWARD,
LEFT,
RIGHT,
BACKWARD,
FORWARD_LEFT,
FORWARD_RIGHT,
BACKWARD_LEFT,
BACKWARD_RIGHT,
}
static final double STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD = 1.0;
static final double STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD = 0.2;
static final double SOFT_UPDATE_MULTIPLIER = 0.1;
String animationStartUp = Animation.ANIMATION_MOVEMENT_STARTUP;
String animationMain = Animation.ANIMATION_MOVEMENT_MOVE;
String animationSlowDown = Animation.ANIMATION_MOVEMENT_MOVE;
String animationSprintStart = Animation.ANIMATION_SPRINT_STARTUP;
String animationSprint = Animation.ANIMATION_SPRINT;
String animationSprintWindDown = Animation.ANIMATION_SPRINT_WINDDOWN;
MovementTreeState state;
MovementRelativeFacing facing;
ServerSprintTree sprintTree;
ServerJumpTree jumpTree;
ServerFallTree fallTree;
Entity parent;
Collidable collidable;
CopyOnWriteArrayList<EntityMessage> networkMessageQueue = new CopyOnWriteArrayList<EntityMessage>();
long lastUpdateTime = 0;
public ServerGroundMovementTree(Entity e, Collidable collidable){
state = MovementTreeState.IDLE;
facing = MovementRelativeFacing.FORWARD;
parent = e;
this.collidable = collidable;
}
public MovementTreeState getState(){
return state;
}
public void start(MovementRelativeFacing facing){
if(canStartMoving()){
this.facing = facing;
state = MovementTreeState.STARTUP;
//if we aren't the server, alert the server we intend to walk forward
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
0 //magic number corresponding to state startup
)
);
}
}
public void interrupt(){
state = MovementTreeState.IDLE;
CreatureUtils.setVelocity(parent, 0);
}
public void slowdown(){
state = MovementTreeState.SLOWDOWN;
//if we aren't the server, alert the server we intend to slow down
Vector3d position = EntityUtils.getPosition(parent);
Quaterniond rotation = EntityUtils.getRotation(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
float velocity = CreatureUtils.getVelocity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
2 //magic number corresponding to state slowdown
)
);
}
public void simulate(float deltaTime){
float velocity = CreatureUtils.getVelocity(parent);
float acceleration = CreatureUtils.getAcceleration(parent);
float maxNaturalVelocity = sprintTree != null && sprintTree.state == SprintTreeState.SPRINTING ? sprintTree.maxVelocity : CreatureUtils.getMaxNaturalVelocity(parent);
PoseActor entityPoseActor = EntityUtils.getPoseActor(parent);
// Model entityModel = Globals.assetManager.fetchModel(EntityUtils.getEntityModelPath(parent));
Vector3d position = EntityUtils.getPosition(parent);
Vector3d facingVector = CreatureUtils.getFacingVector(parent);
Vector3d movementVector = new Vector3d(facingVector);
switch(facing){
case FORWARD:
movementVector.normalize();
break;
case LEFT:
movementVector.rotateY((float)(90 * Math.PI / 180)).normalize();
break;
case RIGHT:
movementVector.rotateY((float)(-90 * Math.PI / 180)).normalize();
break;
case BACKWARD:
movementVector.x = -movementVector.x;
movementVector.z = -movementVector.z;
movementVector.normalize();
break;
case FORWARD_LEFT:
movementVector.rotateY((float)(45 * Math.PI / 180)).normalize();
break;
case FORWARD_RIGHT:
movementVector.rotateY((float)(-45 * Math.PI / 180)).normalize();
break;
case BACKWARD_LEFT:
movementVector.rotateY((float)(135 * Math.PI / 180)).normalize();
break;
case BACKWARD_RIGHT:
movementVector.rotateY((float)(-135 * Math.PI / 180)).normalize();
break;
}
// float movementYaw = CameraEntityUtils.getCameraYaw(Globals.playerCamera);
Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize();
Quaterniond rotation = EntityUtils.getRotation(parent);
//parse attached network messages
for(EntityMessage message : networkMessageQueue){
networkMessageQueue.remove(message);
long updateTime = message.gettime();
// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ());
switch(message.getMessageSubtype()){
case MOVE: {
} break;
case SETFACING:
break;
case MOVEUPDATE: {
if(updateTime > lastUpdateTime){
lastUpdateTime = updateTime;
switch(message.gettreeState()){
case 0:
state = MovementTreeState.STARTUP;
// System.out.println("Set state STARTUP");
GravityUtils.serverAttemptActivateGravity(parent);
break;
case 1:
state = MovementTreeState.MOVE;
// System.out.println("Set state MOVE");
GravityUtils.serverAttemptActivateGravity(parent);
break;
case 2:
state = MovementTreeState.SLOWDOWN;
// System.out.println("Set state SLOWDOWN");
GravityUtils.serverAttemptActivateGravity(parent);
break;
case 3:
state = MovementTreeState.IDLE;
// System.out.println("Set state IDLE");
break;
}
//we want to always update the server facing vector with where the client says they're facing
EntityUtils.getRotation(parent).set(message.getrotationX(),message.getrotationY(),message.getrotationZ(),message.getrotationW());
CreatureUtils.setFacingVector(parent, new Vector3d(0,0,1).rotate(EntityUtils.getRotation(parent)));
break;
}
} break;
default:
break;
}
}
// System.out.println(movementVector + " " + velocity * Main.deltaTime);
//state machine
switch(state){
case STARTUP:
if(entityPoseActor != null){
String animationToPlay = determineCorrectAnimation();
if(
!entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) &&
(jumpTree == null || !jumpTree.isJumping()) &&
(fallTree == null || !fallTree.isFalling())
){
entityPoseActor.playAnimation(animationToPlay,1);
entityPoseActor.incrementAnimationTime(0.0001);
}
}
//run startup code
velocity = velocity + acceleration * Main.deltaFrames;
//check if can transition state
if(velocity >= maxNaturalVelocity){
velocity = maxNaturalVelocity;
state = MovementTreeState.MOVE;
}
CreatureUtils.setVelocity(parent, velocity);
// //actually update
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
GravityUtils.serverAttemptActivateGravity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
0
)
);
break;
case MOVE:
//check if can restart animation
//if yes, restart animation
if(entityPoseActor != null){
String animationToPlay = determineCorrectAnimation();
if(
!entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) &&
(jumpTree == null || !jumpTree.isJumping()) &&
(fallTree == null || !fallTree.isFalling())
){
entityPoseActor.playAnimation(animationToPlay,1);
entityPoseActor.incrementAnimationTime(0.0001);
}
}
if(velocity != maxNaturalVelocity){
velocity = maxNaturalVelocity;
CreatureUtils.setVelocity(parent, velocity);
}
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
GravityUtils.serverAttemptActivateGravity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
1
)
);
break;
case SLOWDOWN:
//run slowdown code
if(entityPoseActor != null){
String animationToPlay = determineCorrectAnimation();
if(
!entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationToPlay) &&
(jumpTree == null || !jumpTree.isJumping()) &&
(fallTree == null || !fallTree.isFalling())
){
entityPoseActor.playAnimation(animationToPlay,1);
entityPoseActor.incrementAnimationTime(0.0001);
}
}
//velocity stuff
velocity = velocity - acceleration * Main.deltaFrames;
//check if can transition state
if(velocity <= 0){
velocity = 0;
state = MovementTreeState.IDLE;
if(entityPoseActor != null){
String animationToPlay = determineCorrectAnimation();
if(entityPoseActor.isPlayingAnimation() && entityPoseActor.isPlayingAnimation(animationToPlay)){
entityPoseActor.stopAnimation(animationToPlay);
}
}
}
CreatureUtils.setVelocity(parent, velocity);
collidable.addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Main.deltaFrames, "movement"));
// position.set(newPosition);
rotation.set(movementQuaternion);
GravityUtils.serverAttemptActivateGravity(parent);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(
EntityMessage.constructmoveUpdateMessage(
parent.getId(),
Main.getCurrentFrame(),
position.x,
position.y,
position.z,
rotation.x,
rotation.y,
rotation.z,
rotation.w,
velocity,
2
)
);
break;
case IDLE:
break;
}
}
public void addNetworkMessage(EntityMessage networkMessage) {
networkMessageQueue.add(networkMessage);
}
public boolean canStartMoving(){
boolean rVal = true;
if(parent.containsKey(EntityDataStrings.SERVER_ATTACK_TREE) && ((ServerAttackTree)parent.getData(EntityDataStrings.SERVER_ATTACK_TREE)).getState() != AttackTreeState.IDLE){
rVal = false;
}
return rVal;
}
public void setAnimationStartUp(String animationStartUp) {
this.animationStartUp = animationStartUp;
}
public void setAnimationMain(String animationMain) {
this.animationMain = animationMain;
}
public void setAnimationSlowDown(String animationSlowDown) {
this.animationSlowDown = animationSlowDown;
}
public void setAnimationSprintStartUp(String animationSprintStartUp){
this.animationSprintStart = animationSprintStartUp;
}
public void setAnimationSprint(String animationSprint){
this.animationSprint = animationSprint;
}
public void setAnimationSprintWindDown(String animationSprintWindDown){
this.animationSprintWindDown = animationSprintWindDown;
}
public void setServerSprintTree(ServerSprintTree sprintTree){
this.sprintTree = sprintTree;
}
public void setServerJumpTree(ServerJumpTree jumpTree){
this.jumpTree = jumpTree;
}
public void setServerFallTree(ServerFallTree fallTree){
this.fallTree = fallTree;
}
public MovementRelativeFacing getFacing(){
return facing;
}
public String determineCorrectAnimation(){
String rVal = "";
if(sprintTree != null){
switch(sprintTree.state){
case SPRINTING:
switch(state){
case IDLE:
break;
case STARTUP:
rVal = animationSprintStart;
break;
case MOVE:
rVal = animationSprint;
break;
case SLOWDOWN:
rVal = animationSprintWindDown;
break;
}
break;
case NOT_SPRINTING:
switch(state){
case IDLE:
break;
case STARTUP:
switch(facing){
case FORWARD:
rVal = animationStartUp;
break;
case BACKWARD:
rVal = animationStartUp;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
case MOVE:
switch(facing){
case FORWARD:
rVal = animationMain;
break;
case BACKWARD:
rVal = animationMain;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
case SLOWDOWN:
switch(facing){
case FORWARD:
rVal = animationSlowDown;
break;
case BACKWARD:
rVal = animationSlowDown;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
}
break;
}
} else {
switch(state){
case IDLE:
break;
case STARTUP:
switch(facing){
case FORWARD:
rVal = animationStartUp;
break;
case BACKWARD:
rVal = animationStartUp;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
case MOVE:
switch(facing){
case FORWARD:
rVal = animationMain;
break;
case BACKWARD:
rVal = animationMain;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
case SLOWDOWN:
switch(facing){
case FORWARD:
rVal = animationSlowDown;
break;
case BACKWARD:
rVal = animationSlowDown;
break;
case LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case FORWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case FORWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
case BACKWARD_LEFT:
rVal = Animation.ANIMATION_WALK_LEFT;
break;
case BACKWARD_RIGHT:
rVal = Animation.ANIMATION_WALK_RIGHT;
break;
}
break;
}
}
return rVal;
}
}