468 lines
20 KiB
Java
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;
|
|
}
|
|
|
|
}
|