Renderer/src/main/java/electrosphere/server/collision/ServerHitboxResolutionCallback.java
austin 3379f5ca26
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
unarmed combat
2025-04-03 17:50:15 -04:00

249 lines
12 KiB
Java

package electrosphere.server.collision;
import org.joml.Vector3d;
import org.ode4j.ode.DContactGeom;
import org.ode4j.ode.DGeom;
import electrosphere.collision.CollisionEngine.CollisionResolutionCallback;
import electrosphere.collision.collidable.Collidable;
import electrosphere.entity.Entity;
import electrosphere.entity.state.attach.AttachUtils;
import electrosphere.entity.state.attack.ServerAttackTree;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType;
import electrosphere.entity.state.life.ServerLifeTree;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.logger.LoggerInterface;
/**
* Callback for managing collisions on the server
*/
public class ServerHitboxResolutionCallback implements CollisionResolutionCallback {
@Override
public void resolve(DContactGeom contactGeom, DGeom impactorGeom, DGeom receiverGeom, Collidable impactor, Collidable receiver, Vector3d normal, Vector3d localPosition, Vector3d worldPos, float magnitude) {
Entity impactorEntity = impactor.getParent();
Entity receiverEntity = receiver.getParent();
HitboxCollectionState impactorState = HitboxCollectionState.getHitboxState(impactorEntity);
HitboxCollectionState receiverState = HitboxCollectionState.getHitboxState(receiverEntity);
HitboxState impactorShapeStatus = impactorState.getShapeStatus(impactorGeom);
HitboxState receiverShapeStatus = receiverState.getShapeStatus(receiverGeom);
//basic error checking
if(impactorEntity == null){
throw new IllegalStateException("Impactor's entity is null");
}
if(receiverEntity == null){
throw new IllegalStateException("Receiver's entity is null");
}
if(!HitboxCollectionState.hasHitboxState(impactorEntity)){
throw new IllegalStateException("Impactor state is null");
}
if(!HitboxCollectionState.hasHitboxState(receiverEntity)){
throw new IllegalStateException("Receiver state is null");
}
if(impactorGeom == null){
throw new IllegalStateException("Impactor geom is null");
}
if(receiverGeom == null){
throw new IllegalStateException("Receiver geom is null");
}
if(!impactorState.getGeometries().contains(impactorGeom)){
String message = "Impactor geom has wrong parent assigned!\n" +
"Problem geom: " + impactorGeom + "\n" +
"All geometries tracked: " + impactorState.getGeometries() + "n\""
;
throw new IllegalStateException(message);
}
if(impactorShapeStatus == null){
String message = "Impactor shape status is null\n" +
"Problem geom: " + impactorGeom + "\n" +
"All geometries tracked: " + impactorState.getGeometries() + "n\""
;
throw new IllegalStateException(message);
}
if(receiverShapeStatus == null){
String message = "Receiver shape status is null\n" +
"Problem geom: " + receiverGeom + "\n" +
"All geometries tracked: " + receiverState.getGeometries() + "n\""
;
throw new IllegalStateException(message);
}
boolean impactorShapeStatusIsNull = impactorShapeStatus == null;
boolean receiverShapeStatusIsNull = receiverShapeStatus == null;
boolean impactorIsHit = impactorShapeStatus != null && impactorShapeStatus.getType() == HitboxType.HIT;
boolean receiverIsHurt = receiverShapeStatus != null && receiverShapeStatus.getType() == HitboxType.HURT;
boolean receiverIsBlock = receiverShapeStatus != null && (receiverShapeStatus.getType() == HitboxType.BLOCK || (receiverShapeStatus.getType() == HitboxType.HIT && receiverShapeStatus.isBlockOverride()));
boolean parentsAreDifferent = AttachUtils.getParent(impactorEntity) != receiverEntity;
boolean impactorCollisionBlocked = false;
//check if the impactor thinks it can collide with the receiver
//
//sword has attack tree
if(impactorEntity != null && ServerAttackTree.getServerAttackTree(impactorEntity) != null){
ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(impactorEntity);
//
//sword-on-sword check
//if we collide with the creature directly
if(!impactorAttackTree.canCollideEntity(receiverEntity)){
impactorCollisionBlocked = true;
}
//
//sword-on-creature check
//if we collide with an item attached to the creature
if(AttachUtils.hasParent(receiverEntity) && !impactorAttackTree.canCollideEntity(AttachUtils.getParent(receiverEntity))){
impactorCollisionBlocked = true;
}
//
//creature has attack tree
} else if(impactorEntity != null && AttachUtils.hasParent(impactorEntity) && AttachUtils.getParent(impactorEntity) != null && ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorEntity)) != null){
ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorEntity));
//
//creature-on-sword check
//if we collide with the creature directly
if(!impactorAttackTree.canCollideEntity(receiverEntity)){
impactorCollisionBlocked = true;
}
//
//creature-on-creature check
//if we collide with an item attached to the creature
if(AttachUtils.hasParent(receiverEntity) && !impactorAttackTree.canCollideEntity(AttachUtils.getParent(receiverEntity))){
impactorCollisionBlocked = true;
}
}
//
//check if is damage event
boolean isDamageEvent =
!impactorShapeStatusIsNull &&
!receiverShapeStatusIsNull &&
impactorIsHit &&
receiverIsHurt &&
parentsAreDifferent &&
!impactorCollisionBlocked
;
//
//check if is block event
boolean isBlockEvent =
!impactorShapeStatusIsNull &&
!receiverShapeStatusIsNull &&
impactorIsHit &&
receiverIsBlock &&
parentsAreDifferent &&
!impactorCollisionBlocked
;
if(!impactorShapeStatusIsNull){
impactorShapeStatus.setHadCollision(true);
}
if(!receiverShapeStatusIsNull){
receiverShapeStatus.setHadCollision(true);
}
if(isDamageEvent){
//if the entity is attached to is an item, we need to compare with the parent of the item
//to make sure you don't stab yourself for instance
boolean isItem = ItemUtils.isItem(impactorEntity);
Entity hitboxAttachParent = AttachUtils.getParent(impactorEntity);
//if the impactor is the creature itself, need separate handling
boolean isCreature = CreatureUtils.isCreature(impactorEntity);
//
//handle receiver
if(isItem){
if(hitboxAttachParent != receiverEntity){
ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(receiverEntity);
serverLifeTree.addCollisionEvent(impactorEntity, impactorShapeStatus, receiverShapeStatus, worldPos, isDamageEvent, isBlockEvent);
}
} else if(isCreature){
ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(receiverEntity);
serverLifeTree.addCollisionEvent(impactorEntity, impactorShapeStatus, receiverShapeStatus, worldPos, isDamageEvent, isBlockEvent);
}
//
//handle attacker
this.handleAttackerCollision(impactorEntity,receiverEntity, isBlockEvent);
}
if(isBlockEvent){
//
//handle receiver
boolean receiverIsItem = ItemUtils.isItem(receiverEntity);
boolean receiverHasParent = AttachUtils.hasParent(receiverEntity);
if(receiverIsItem && receiverHasParent){
//item is equipped to something
ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(AttachUtils.getParent(receiverEntity));
if(serverLifeTree != null){
serverLifeTree.addCollisionEvent(impactorEntity, impactorShapeStatus, receiverShapeStatus, worldPos, isDamageEvent, isBlockEvent);
}
} else {
//attacking an item that is not equipped to anything
ServerLifeTree serverLifeTree = ServerLifeTree.getServerLifeTree(receiverEntity);
if(serverLifeTree != null){
serverLifeTree.addCollisionEvent(impactorEntity, impactorShapeStatus, receiverShapeStatus, worldPos, isDamageEvent, isBlockEvent);
}
}
//
//handle attacker
this.handleAttackerCollision(impactorEntity,receiverEntity, isBlockEvent);
}
}
/**
* Handles collision tracking from the impactor's side
* @param impactorEntity The impactor hitbox's parent entity
* @param receiverParent The receiver hitbox's parent entity
* @param isBlock true if this is a block, false otherwise
*/
private void handleAttackerCollision(Entity impactorEntity, Entity receiverEntity, boolean isBlock){
boolean receiverIsItem = ItemUtils.isItem(receiverEntity);
boolean receiverHasParent = AttachUtils.hasParent(receiverEntity);
//
//The sword has the attack tree
if(impactorEntity != null && ServerAttackTree.getServerAttackTree(impactorEntity) != null){
ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(impactorEntity);
impactorAttackTree.collideEntity(receiverEntity);
if(isBlock){
impactorAttackTree.recoilFromBlock();
}
//if the receiver is an item that is equipped, collide with parent too
if(receiverIsItem && receiverHasParent){
impactorAttackTree.collideEntity(AttachUtils.getParent(receiverEntity));
} else if(receiverHasParent){
LoggerInterface.loggerEngine.WARNING("Potentially unhandled case with server collision!");
}
//
//The parent of the sword has the attack tree
} else if(impactorEntity != null && AttachUtils.hasParent(impactorEntity) && AttachUtils.getParent(impactorEntity) != null && ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorEntity)) != null){
ServerAttackTree impactorAttackTree = ServerAttackTree.getServerAttackTree(AttachUtils.getParent(impactorEntity));
impactorAttackTree.collideEntity(receiverEntity);
if(isBlock){
impactorAttackTree.recoilFromBlock();
}
//if the receiver is an item that is equipped, collide with parent too
if(receiverIsItem && receiverHasParent){
impactorAttackTree.collideEntity(AttachUtils.getParent(receiverEntity));
} else if(receiverHasParent){
LoggerInterface.loggerEngine.WARNING("Potentially unhandled case with server collision!");
}
}
}
}