Renderer/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java
austin ba5537bcf7
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
ground move refactor
2024-03-21 18:47:07 -04:00

468 lines
20 KiB
Java

package electrosphere.entity.state.attack;
import electrosphere.collision.collidable.Collidable;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.state.BehaviorTree;
import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.state.equip.EquipState;
import electrosphere.entity.state.movement.groundmove.GroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
import electrosphere.entity.state.rotator.RotatorTree;
import electrosphere.entity.state.rotator.ServerRotatorTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.hitbox.HitboxUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.entity.types.projectile.ProjectileUtils;
import electrosphere.game.data.creature.type.attack.AttackMove;
import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.renderer.actor.Actor;
import electrosphere.renderer.anim.Animation;
import electrosphere.server.datacell.Realm;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.poseactor.PoseActor;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Quaternionfc;
import org.joml.Vector3d;
import org.joml.Vector3f;
public class ServerAttackTree implements BehaviorTree {
public static enum AttackTreeState {
WINDUP,
HOLD,
ATTACK,
COOLDOWN,
IDLE,
}
//the state of drifting forward during the attack
public static enum AttackTreeDriftState {
DRIFT,
NO_DRIFT,
}
AttackTreeState state;
AttackTreeDriftState driftState;
Entity parent;
CopyOnWriteArrayList<EntityMessage> networkMessageQueue = new CopyOnWriteArrayList<EntityMessage>();
long lastUpdateTime = 0;
float frameCurrent;
String animationName = "SwingWeapon";
int maxFrame = 60;
List<AttackMove> currentMoveset = null;
AttackMove currentMove = null;
Entity currentWeapon = null;
boolean currentMoveHasWindup;
boolean currentMoveCanHold;
boolean stillHold = true;
boolean firesProjectile = false;
String projectileToFire = null;
String attackingPoint = null;
public ServerAttackTree(Entity e){
state = AttackTreeState.IDLE;
driftState = AttackTreeDriftState.NO_DRIFT;
parent = e;
}
public AttackTreeState getState(){
return state;
}
public void start(){
currentMoveCanHold = false;
currentMoveHasWindup = false;
stillHold = true;
firesProjectile = false;
projectileToFire = null;
currentWeapon = null;
attackingPoint = null;
//figure out attack type we should be doing
String attackType = getAttackType();
//if we can attack, setup doing so
if(canAttack(attackType)){
parent.putData(EntityDataStrings.ATTACK_MOVE_TYPE_ACTIVE, attackType);
currentMoveset = (List<AttackMove>)parent.getData(attackType);
if(currentMoveset != null){
if(currentMove == null){
currentMove = currentMoveset.get(0);
} else {
currentMove = getNextMove(currentMoveset,currentMove.getNextMoveId());
}
if(currentMove != null){
firesProjectile = currentMove.getFiresProjectile();
if(firesProjectile){
projectileToFire = ItemUtils.getWeaponDataRaw(currentWeapon).getProjectileModel();
}
animationName = currentMove.getAttackAnimationName();
//intuit windup from presence of windup anim
currentMoveHasWindup = currentMove.getWindupAnimationName() != null;
if(currentMoveHasWindup){
animationName = currentMove.getWindupAnimationName();
}
//intuit can hold from presence of windup anim
currentMoveCanHold = currentMove.getHoldAnimationName() != null;
//stop movement tree
if(parent.containsKey(EntityDataStrings.SERVER_MOVEMENT_BT)){
BehaviorTree movementTree = CreatureUtils.serverGetEntityMovementTree(parent);
if(movementTree instanceof ServerGroundMovementTree){
((ServerGroundMovementTree)movementTree).interrupt();
}
}
Vector3d movementVector = CreatureUtils.getFacingVector(parent);
EntityUtils.getRotation(parent).rotationTo(new Vector3d(0,0,1), new Vector3d(movementVector.x,movementVector.y,movementVector.z));
//set initial stuff
state = AttackTreeState.WINDUP;
frameCurrent = 0;
} else {
state = AttackTreeState.IDLE;
}
}
}
}
public void release(){
stillHold = false;
}
public void interrupt(){
state = AttackTreeState.IDLE;
}
public void slowdown(){
state = AttackTreeState.COOLDOWN;
}
@Override
public void simulate(float deltaTime){
frameCurrent = frameCurrent + (float)Globals.timekeeper.getSimFrameTime();
float velocity = CreatureUtils.getVelocity(parent);
PoseActor entityPoseActor = EntityUtils.getPoseActor(parent);
Vector3d position = EntityUtils.getPosition(parent);
Vector3d movementVector = CreatureUtils.getFacingVector(parent);
//parse attached network messages
for(EntityMessage message : networkMessageQueue){
networkMessageQueue.remove(message);
// System.out.println("MOVE to " + message.getX() + " " + message.getY() + " " + message.getZ());
long updateTime = message.gettime();
switch(message.getMessageSubtype()){
case ATTACKUPDATE:
if(updateTime > lastUpdateTime){
lastUpdateTime = updateTime;
switch(message.gettreeState()){
case 0:
state = AttackTreeState.WINDUP;
frameCurrent = 0;
// System.out.println("Set state STARTUP");
break;
case 1:
frameCurrent = currentMove.getWindupFrames()+1;
state = AttackTreeState.ATTACK;
// System.out.println("Set state MOVE");
break;
case 2:
frameCurrent = currentMove.getWindupFrames()+currentMove.getAttackFrames()+1;
state = AttackTreeState.COOLDOWN;
// System.out.println("Set state SLOWDOWN");
break;
case 3:
frameCurrent = 60;
state = AttackTreeState.IDLE;
// System.out.println("Set state IDLE");
break;
}
}
EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
CreatureUtils.setFacingVector(parent, new Vector3d(message.getrotationX(),message.getrotationY(),message.getrotationZ()));
break;
case ATTACHENTITYTOENTITY:
case CREATE:
case DESTROY:
case MOVE:
case MOVEUPDATE:
case SETBEHAVIORTREE:
case SETFACING:
case SETPOSITION:
case SETPROPERTY:
case KILL:
case SPAWNCREATURE:
//silently ignore
break;
}
}
//handle the drifting if we're supposed to currently
switch(driftState){
case DRIFT:
if(currentMove != null){
//calculate the vector of movement
CollisionObjUtils.getCollidable(parent).addImpulse(new Impulse(new Vector3d(movementVector), new Vector3d(0,0,0), new Vector3d(0,0,0), currentMove.getDriftGoal() * Globals.timekeeper.getSimFrameTime(), "movement"));
if(frameCurrent > currentMove.getDriftFrameEnd()){
driftState = AttackTreeDriftState.NO_DRIFT;
}
}
break;
case NO_DRIFT:
if(currentMove != null){
if(frameCurrent > currentMove.getDriftFrameStart() && frameCurrent < currentMove.getDriftFrameEnd()){
driftState = AttackTreeDriftState.DRIFT;
}
}
break;
}
// if(state != AttackTreeState.IDLE){
// System.out.println(frameCurrent);
// }
//state machine
switch(state){
case WINDUP:
if(parent.containsKey(EntityDataStrings.SERVER_ROTATOR_TREE)){
ServerRotatorTree.getServerRotatorTree(parent).setActive(true);
}
if(entityPoseActor != null){
if(!entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationName)){
entityPoseActor.playAnimation(animationName,1);
entityPoseActor.incrementAnimationTime(0.0001);
}
}
if(frameCurrent > currentMove.getWindupFrames()){
if(currentMoveCanHold && stillHold){
state = AttackTreeState.HOLD;
} else {
state = AttackTreeState.ATTACK;
}
}
Globals.server.broadcastMessage(
EntityMessage.constructattackUpdateMessage(
parent.getId(),
System.currentTimeMillis(),
(float)position.x,
(float)position.y,
(float)position.z,
movementVector.x,
movementVector.y,
movementVector.z,
velocity,
0
)
);
break;
case HOLD:
if(entityPoseActor != null){
if(!entityPoseActor.isPlayingAnimation() || !entityPoseActor.isPlayingAnimation(animationName)){
entityPoseActor.playAnimation(animationName,1);
entityPoseActor.incrementAnimationTime(0.0001);
}
}
if(!stillHold){
state = AttackTreeState.ATTACK;
}
break;
case ATTACK:
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
for(Entity currentAttached : attachedEntities){
if(currentAttached.containsKey(EntityDataStrings.HITBOX_ASSOCIATED_LIST)){
List<Entity> hitboxes = HitboxUtils.getHitboxAssociatedList(currentAttached);
for(Entity hitbox : hitboxes){
HitboxUtils.getHitboxData(hitbox).setActive(true);
}
}
}
}
if(firesProjectile && projectileToFire != null){
//spawn projectile
//TODO: solve spawnPosition, initialVector
Vector3d spawnPosition = new Vector3d(0,0,0);
Quaterniond arrowRotation = new Quaterniond();
String targetBone = null;
EquipState equipState = EquipState.getEquipState(parent);
EquipPoint weaponPoint = null;
if((weaponPoint = equipState.getEquipPoint(attackingPoint)) != null){
targetBone = weaponPoint.getBone();
}
if(targetBone != null){
Actor parentActor = EntityUtils.getActor(parent);
//transform bone space
spawnPosition = new Vector3d(parentActor.getBonePosition(targetBone));
spawnPosition = spawnPosition.mul(((Vector3f)EntityUtils.getScale(parent)));
Quaterniond rotation = EntityUtils.getRotation(parent);
spawnPosition = spawnPosition.rotate(new Quaterniond(rotation.x,rotation.y,rotation.z,rotation.w));
//transform worldspace
spawnPosition.add(new Vector3d(EntityUtils.getPosition(parent)));
//set
// EntityUtils.getPosition(currentEntity).set(position);
//set rotation
// Quaternionf rotation = parentActor.getBoneRotation(targetBone);
// EntityUtils.getRotation(currentEntity).set(rotation).normalize();
// Vector3d facingAngle = CreatureUtils.getFacingVector(parent);
arrowRotation = parentActor.getBoneRotation(targetBone);
// EntityUtils.getRotation(currentEntity).rotationTo(new Vector3f(0,0,1), new Vector3f((float)facingAngle.x,(float)facingAngle.y,(float)facingAngle.z)).mul(parentActor.getBoneRotation(targetBone)).normalize();
}
Vector3f initialVector = new Vector3f((float)movementVector.x,(float)movementVector.y,(float)movementVector.z).normalize();
Realm parentRealm = Globals.realmManager.getEntityRealm(parent);
ProjectileUtils.serverSpawnBasicProjectile(parentRealm, projectileToFire, spawnPosition, arrowRotation, 750, initialVector, 0.03f);
projectileToFire = null;
}
if(frameCurrent > currentMove.getWindupFrames() + currentMove.getAttackFrames()){
state = AttackTreeState.COOLDOWN;
}
Globals.server.broadcastMessage(
EntityMessage.constructattackUpdateMessage(
parent.getId(),
System.currentTimeMillis(),
(float)position.x,
(float)position.y,
(float)position.z,
movementVector.x,
movementVector.y,
movementVector.z,
velocity,
1
)
);
break;
case COOLDOWN:
if(parent.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST)){
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
for(Entity currentAttached : attachedEntities){
if(currentAttached.containsKey(EntityDataStrings.HITBOX_ASSOCIATED_LIST)){
List<Entity> hitboxes = HitboxUtils.getHitboxAssociatedList(currentAttached);
for(Entity hitbox : hitboxes){
HitboxUtils.getHitboxData(hitbox).setActive(false);
}
}
}
}
if(frameCurrent > currentMove.getWindupFrames() + currentMove.getAttackFrames() + currentMove.getCooldownFrames()){
state = AttackTreeState.IDLE;
frameCurrent = 0;
if(parent.containsKey(EntityDataStrings.SERVER_ROTATOR_TREE)){
ServerRotatorTree.getServerRotatorTree(parent).setActive(false);
}
}
Globals.server.broadcastMessage(
EntityMessage.constructattackUpdateMessage(
parent.getId(),
System.currentTimeMillis(),
(float)position.x,
(float)position.y,
(float)position.z,
movementVector.x,
movementVector.y,
movementVector.z,
velocity,
2
)
);
break;
case IDLE:
currentMove = null;
currentMoveset = null;
break;
}
}
public void addNetworkMessage(EntityMessage networkMessage) {
networkMessageQueue.add(networkMessage);
}
String getAttackType(){
String rVal = null;
if(EquipState.hasEquipState(parent)){
EquipState equipState = EquipState.getEquipState(parent);
for(String point : equipState.equippedPoints()){
Entity item = equipState.getEquippedItemAtPoint(point);
if(ItemUtils.isWeapon(item)){
attackingPoint = point;
currentWeapon = item;
switch(ItemUtils.getWeaponClass(item)){
case "sword1h":
rVal = EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND;
break;
case "bow2h":
rVal = EntityDataStrings.ATTACK_MOVE_TYPE_BOW_TWO_HAND;
break;
}
}
}
}
return rVal;
}
boolean canAttack(String attackType){
boolean rVal = true;
if(attackType == null){
return false;
} else if(state != AttackTreeState.IDLE){
//checks if we have a next move and if we're in the specified range of frames when we're allowed to chain into it
if(
currentMove.getNextMoveId() != null &&
!currentMove.getNextMoveId().equals("") &&
frameCurrent >= currentMove.getMoveChainWindowStart() && frameCurrent <= currentMove.getMoveChainWindowEnd()
){
rVal = true;
}
} else {
if(EquipState.hasEquipState(parent)){
EquipState equipState = EquipState.getEquipState(parent);
// if(equipState.hasEquipPrimary()){
// switch(attackType){
// case EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND:
// break;
// default:
// rVal = false;
// break;
// }
// } else {
// switch(attackType){
// case EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND:
// rVal = false;
// break;
// default:
// rVal = false;
// break;
// }
// }
}
}
return rVal;
}
AttackMove getNextMove(List<AttackMove> moveset, String nextMoveId){
AttackMove rVal = null;
for(AttackMove move : moveset){
if(move.getAttackMoveId().equals(nextMoveId)){
rVal = move;
break;
}
}
return rVal;
}
}