package electrosphere.entity.state.movement; import java.util.concurrent.CopyOnWriteArrayList; import org.joml.Quaterniond; import org.joml.Quaternionf; import org.joml.Vector3d; import org.joml.Vector3f; import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; import electrosphere.engine.Main; import electrosphere.entity.Entity; import electrosphere.entity.EntityUtils; import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.state.collidable.Impulse; import electrosphere.entity.types.camera.CameraEntityUtils; import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.renderer.actor.Actor; import electrosphere.renderer.anim.Animation; import electrosphere.server.datacell.utils.DataCellSearchUtils; @Deprecated public class AirplaneMovementTree implements BehaviorTree { public static enum AirplaneMovementTreeState { ACCELERATING, DECELERATING, } float minVelocity = 0; float maxRotationSpeed = 1.0f; //The yaw value last simulation frame float previousYaw = 270; //how much we're rolling currently float rollVal = 0; // the factor to increment rollVal by while swinging the camera around float rollFactor = 0.05f; 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; AirplaneMovementTreeState state; Entity parent; Collidable collidable; CopyOnWriteArrayList networkMessageQueue = new CopyOnWriteArrayList(); //when the latest network message for this tree was received //used to filter out packets before the most recent one long lastUpdateTime = 0; float pitchCalculationTolerance = 0.99f; /** * Constructs an airplane movement tree * @param e The entity this tree will be attached to * @param collidable The collidable of the entity that parents this tree */ public AirplaneMovementTree(Entity e, Collidable collidable){ state = AirplaneMovementTreeState.ACCELERATING; parent = e; this.collidable = collidable; } /** * Simulates a step of the behavior tree */ public void simulate(float deltaTime){ // //Get important initial values // float velocity = CreatureUtils.getVelocity(parent); float acceleration = CreatureUtils.getAcceleration(parent); float maxNaturalVelocity = CreatureUtils.getMaxNaturalVelocity(parent); Actor entityActor = EntityUtils.getActor(parent); Vector3d position = EntityUtils.getPosition(parent); Vector3d facingVector = CreatureUtils.getFacingVector(parent); Quaterniond movementQuaternion = new Quaterniond().rotationTo(new Vector3d(0,0,1), new Vector3d(facingVector.x,0,facingVector.z)).normalize(); Quaterniond rotation = EntityUtils.getRotation(parent); // //handle network messages // for(EntityMessage message : networkMessageQueue){ networkMessageQueue.remove(message); long updateTime = message.gettime(); switch(message.getMessageSubtype()){ //received a message to update the tree case MOVEUPDATE: { if(updateTime > lastUpdateTime){ lastUpdateTime = updateTime; //update the behavior tree state switch(message.gettreeState()){ case 0: state = AirplaneMovementTreeState.ACCELERATING; break; case 1: state = AirplaneMovementTreeState.DECELERATING; break; } //if we're the client snap to the reported position as appropriate if(!Globals.RUN_SERVER){ if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_HARD_UPDATE_THRESHOLD){ EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ()); } else if(position.distance(message.getpositionX(),message.getpositionY(),message.getpositionZ()) > STATE_DIFFERENCE_SOFT_UPDATE_THRESHOLD){ EntityUtils.getPosition(parent).add(new Vector3d(message.getpositionX(),message.getpositionY(),message.getpositionZ()).mul(SOFT_UPDATE_MULTIPLIER)); } } //we want to always update the server facing vector with where the client says they're facing CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ())); } } break; case ATTACHENTITYTOENTITY: case ATTACKUPDATE: case CREATE: case DESTROY: case KILL: case SETPROPERTY: case SPAWNCREATURE: case SPAWNITEM: //do nothing break; } } // // Actual simulation // switch(state){ case ACCELERATING: { //velocity calculation velocity = velocity + acceleration; if(velocity > maxNaturalVelocity){ velocity = maxNaturalVelocity; } CreatureUtils.setVelocity(parent, velocity); //update rotation updateRotation(rotation,facingVector); //add movement impulse addMovementForce(velocity,rotation,collidable); //if server, update all clients to simulation changes serverUpdateTree(position,rotation,velocity); } break; case DECELERATING: { //velocity calculation velocity = velocity - acceleration; if(velocity < minVelocity){ velocity = minVelocity; } CreatureUtils.setVelocity(parent, velocity); //update rotation updateRotation(rotation,facingVector); //add movement impulse addMovementForce(velocity,rotation,collidable); //if server, update all clients to simulation changes serverUpdateTree(position,rotation,velocity); } break; } } /** * Updates the rotation of the airplane * @param rotation Rotation quaternion * @param rotationVector Rotation vector */ void updateRotation(Quaterniond rotation, Vector3d rotationVector){ if(Globals.RUN_CLIENT && this.parent == Globals.playerEntity){ Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera); float pitch = CameraEntityUtils.getCameraPitch(Globals.playerCamera) / 180 * (float)Math.PI; float yaw = -(CameraEntityUtils.getCameraYaw(Globals.playerCamera) + 180) / 180 * (float)Math.PI; float deltaYaw = yaw - previousYaw; if(deltaYaw > 0){ rollVal += -rollFactor; } if(deltaYaw < 0){ rollVal += rollFactor; } Quaterniond yawQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,1,0), yaw); Quaterniond pitchQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(1,0,0), pitch); Quaterniond rollQuat = new Quaterniond().fromAxisAngleRad(new Vector3d(0,0,1), rollVal); rotation.slerp(yawQuat.mul(pitchQuat).mul(rollQuat),0.1); //rotate thrust vector rotationVector.set(rotation.transform(new Vector3d(0,0,1))); // rotationVector.set(new Vector3f((float)rotationVector.x,(float)rotationVector.y,(float)rotationVector.z).mul(1.0f - this.maxRotationSpeed).add(new Vector3f(cameraEyeVector).mul(-this.maxRotationSpeed))); rollVal = rollVal * 0.9f; previousYaw = yaw; } } /** * Adds the force to actually move the airplane entity * @param velocity The current velocity * @param facingVector The current facing vector * @param collidable The collidable of the entity */ void addMovementForce(float velocity, Quaterniond rotation, Collidable collidable){ Vector3d impulseDir = rotation.transform(new Vector3d(0,0,1)); collidable.addImpulse(new Impulse(new Vector3d(impulseDir), new Vector3d(0,0,0), new Vector3d(0,0,0), velocity * Globals.timekeeper.getSimFrameTime(), "movement")); } /** * If the instance is a server, update all clients within the chunk as appropriate * @param position The position of the airplane * @param facingVector The facing vector of the airplane * @param velocity The velocity of the airplane */ void serverUpdateTree(Vector3d position, Quaterniond rotation, float velocity){ if(Globals.RUN_SERVER){ int stateNumber = 0; switch(this.state){ case ACCELERATING: stateNumber = 0; break; case DECELERATING: stateNumber = 1; break; } DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage( EntityMessage.constructmoveUpdateMessage( parent.getId(), Globals.timekeeper.getNumberOfSimFramesElapsed(), position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, velocity, 0, stateNumber ) ); } } /** * Register a network message relavent to this tree * @param networkMessage The network message to register */ public void addNetworkMessage(EntityMessage networkMessage) { networkMessageQueue.add(networkMessage); } /** * Determines the animation to play at a given point in the simulation loop * @return The animation to play as a string */ public String determineCorrectAnimation(){ String rVal = ""; switch(state){ case ACCELERATING: rVal = Animation.ANIMATION_IDLE_1; break; case DECELERATING: rVal = Animation.ANIMATION_IDLE_1; break; } return rVal; } /** * Gets the current state of the behavior tree * @return The current state of the behavior tree */ public AirplaneMovementTreeState getState(){ return state; } /** * Sets the minimum velocity of this airplane tree * @param minVelocity The minimum velocity */ public void setMinimumVelocity(float minVelocity){ this.minVelocity = minVelocity; } /** * Sets the max rotation speed of this airplane movement tree * @param maxRotationSpeed The max rotation speed */ public void setMaxRotationSpeed(float maxRotationSpeed){ this.maxRotationSpeed = maxRotationSpeed; } }