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!"); } } } }