unarmed combat
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit

This commit is contained in:
austin 2025-04-03 17:50:15 -04:00
parent fed342aa62
commit 3379f5ca26
10 changed files with 162 additions and 28 deletions

View File

@ -3,15 +3,27 @@
{
"id" : "human",
"hitboxes" : [
{
"type": "hit",
"bone": "Hand.L",
"radius": 0.04,
"damage": 5
},
{
"type": "hit",
"bone": "Hand.R",
"radius": 0.04,
"damage": 5
},
{
"type": "hurt",
"bone": "Bicep.L",
"radius": 0.04
"radius": 0.03
},
{
"type": "hurt",
"bone": "Bicep.R",
"radius": 0.04
"radius": 0.03
},
{
"type": "hurt",
@ -405,7 +417,10 @@
"nameThirdPerson" : "AttackUnarmed1",
"priorityCategory" : "MOVEMENT_MODIFIER"
}
}
},
"activeBones" : [
"Hand.R", "Hand.L"
]
},
{
"attackMoveId" : "Unarmed2",
@ -422,7 +437,10 @@
"nameThirdPerson" : "AttackUnarmed2",
"priorityCategory" : "MOVEMENT_MODIFIER"
}
}
},
"activeBones" : [
"Hand.R", "Hand.L"
]
},
{
"attackMoveId" : "Sword1HSlash1",

View File

@ -1430,6 +1430,7 @@ Bush hitbox
Harvesting interaction happens on server
Fix PoseActor position/rotation caching bug
Bush drops sticks
Unarmed combat

View File

@ -13,6 +13,8 @@ import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.equip.ClientToolbarState;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType;
import electrosphere.entity.state.movement.fall.ClientFallTree;
import electrosphere.entity.state.movement.jump.ClientJumpTree;
import electrosphere.entity.state.rotator.RotatorTree;
@ -106,8 +108,8 @@ public class ClientAttackTree implements BehaviorTree {
StateTransitionUtil stateTransitionUtil;
private ClientAttackTree(Entity e, Object ... params){
setState(AttackTreeState.IDLE);
setDriftState(AttackTreeDriftState.NO_DRIFT);
this.setState(AttackTreeState.IDLE);
this.setDriftState(AttackTreeDriftState.NO_DRIFT);
parent = e;
this.stateTransitionUtil = StateTransitionUtil.create(parent, false, new StateTransitionUtilItem[]{
StateTransitionUtilItem.create(
@ -267,11 +269,11 @@ public class ClientAttackTree implements BehaviorTree {
currentWeapon = null;
attackingPoint = null;
//figure out attack type we should be doing
String attackType = getAttackType();
String attackType = this.getAttackType();
//if we can attack, setup doing so
if(canAttack(attackType)){
setAttackMoveTypeActive(attackType);
currentMoveset = getMoveset(attackType);
if(this.canAttack(attackType)){
this.setAttackMoveTypeActive(attackType);
currentMoveset = this.getMoveset(attackType);
if(currentMoveset != null){
Globals.clientConnection.queueOutgoingMessage(EntityMessage.constructstartAttackMessage());
}
@ -283,11 +285,11 @@ public class ClientAttackTree implements BehaviorTree {
}
public void interrupt(){
setState(AttackTreeState.IDLE);
this.setState(AttackTreeState.IDLE);
}
public void slowdown(){
setState(AttackTreeState.COOLDOWN);
this.setState(AttackTreeState.COOLDOWN);
}
@Override
@ -298,7 +300,7 @@ public class ClientAttackTree implements BehaviorTree {
//
//synchronize move from server
if(this.currentMoveset == null){
this.currentMoveset = getMoveset(getAttackType());
this.currentMoveset = this.getMoveset(this.getAttackType());
}
if(
this.currentMoveset != null &&
@ -320,14 +322,14 @@ public class ClientAttackTree implements BehaviorTree {
//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()){
setDriftState(AttackTreeDriftState.NO_DRIFT);
this.setDriftState(AttackTreeDriftState.NO_DRIFT);
}
}
break;
case NO_DRIFT:
if(currentMove != null){
if(frameCurrent > currentMove.getDriftFrameStart() && frameCurrent < currentMove.getDriftFrameEnd()){
setDriftState(AttackTreeDriftState.DRIFT);
this.setDriftState(AttackTreeDriftState.DRIFT);
}
}
break;
@ -356,10 +358,21 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(true);
}
}
}
}
} break;
case BLOCK_RECOIL: {
this.stateTransitionUtil.simulate(AttackTreeState.BLOCK_RECOIL);
//activate hitboxes
//deactivate hitboxes
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
if(attachedEntities != null){
for(Entity currentAttached : attachedEntities){
@ -369,6 +382,17 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}
} break;
case COOLDOWN: {
this.stateTransitionUtil.simulate(AttackTreeState.COOLDOWN);
@ -382,6 +406,17 @@ public class ClientAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}
if(currentMove != null && frameCurrent > currentMove.getWindupFrames() + currentMove.getAttackFrames() + currentMove.getCooldownFrames()){
frameCurrent = 0;
if(parent.containsKey(EntityDataStrings.CLIENT_ROTATOR_TREE)){

View File

@ -19,6 +19,8 @@ import electrosphere.entity.state.collidable.Impulse;
import electrosphere.entity.state.equip.ServerEquipState;
import electrosphere.entity.state.equip.ServerToolbarState;
import electrosphere.entity.state.hitbox.HitboxCollectionState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxType;
import electrosphere.entity.state.movement.fall.ServerFallTree;
import electrosphere.entity.state.movement.jump.ServerJumpTree;
import electrosphere.entity.state.rotator.ServerRotatorTree;
@ -277,7 +279,7 @@ public class ServerAttackTree implements BehaviorTree {
* Interrupts the tree
*/
public void interrupt(){
setState(AttackTreeState.IDLE);
this.setState(AttackTreeState.IDLE);
//activate hitboxes
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
for(Entity currentAttached : attachedEntities){
@ -293,7 +295,7 @@ public class ServerAttackTree implements BehaviorTree {
*/
public void recoilFromBlock(){
if(currentMove != null){
setState(AttackTreeState.BLOCK_RECOIL);
this.setState(AttackTreeState.BLOCK_RECOIL);
//deactivate hitboxes
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
for(Entity currentAttached : attachedEntities){
@ -318,7 +320,7 @@ public class ServerAttackTree implements BehaviorTree {
EntityUtils.getPosition(parent).set(message.getpositionX(),message.getpositionY(),message.getpositionZ());
break;
case STARTATTACK: {
start();
this.start();
} break;
default:
//silently ignore
@ -333,14 +335,14 @@ public class ServerAttackTree implements BehaviorTree {
//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()){
setDriftState(AttackTreeDriftState.NO_DRIFT);
this.setDriftState(AttackTreeDriftState.NO_DRIFT);
}
}
break;
case NO_DRIFT:
if(currentMove != null){
if(frameCurrent > currentMove.getDriftFrameStart() && frameCurrent < currentMove.getDriftFrameEnd()){
setDriftState(AttackTreeDriftState.DRIFT);
this.setDriftState(AttackTreeDriftState.DRIFT);
}
}
break;
@ -361,7 +363,7 @@ public class ServerAttackTree implements BehaviorTree {
case HOLD: {
this.stateTransitionUtil.simulate(AttackTreeState.HOLD);
if(!stillHold){
setState(AttackTreeState.ATTACK);
this.setState(AttackTreeState.ATTACK);
this.stateTransitionUtil.interrupt(AttackTreeState.HOLD);
}
} break;
@ -377,6 +379,17 @@ public class ServerAttackTree implements BehaviorTree {
}
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(true);
}
}
}
}
if(firesProjectile && projectileToFire != null){
//spawn projectile
//TODO: solve spawnPosition, initialVector
@ -414,7 +427,7 @@ public class ServerAttackTree implements BehaviorTree {
} break;
case BLOCK_RECOIL: {
this.stateTransitionUtil.simulate(AttackTreeState.BLOCK_RECOIL);
//activate hitboxes
//deactivate hitboxes
List<Entity> attachedEntities = AttachUtils.getChildrenList(parent);
for(Entity currentAttached : attachedEntities){
if(HitboxCollectionState.hasHitboxState(currentAttached)){
@ -422,6 +435,17 @@ public class ServerAttackTree implements BehaviorTree {
currentState.setActive(false);
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}
} break;
case COOLDOWN: {
this.stateTransitionUtil.simulate(AttackTreeState.COOLDOWN);
@ -433,8 +457,19 @@ public class ServerAttackTree implements BehaviorTree {
currentState.setActive(false);
}
}
if(this.currentMove.getActiveBones() != null && HitboxCollectionState.hasHitboxState(this.parent)){
HitboxCollectionState hitboxCollectionState = HitboxCollectionState.getHitboxState(this.parent);
for(String boneName : this.currentMove.getActiveBones()){
List<HitboxState> hitboxes = hitboxCollectionState.getHitboxes(boneName);
for(HitboxState hitbox : hitboxes){
if(hitbox.getType() == HitboxType.HIT){
hitbox.setActive(false);
}
}
}
}
if(frameCurrent > currentMove.getWindupFrames() + currentMove.getAttackFrames() + currentMove.getCooldownFrames()){
setState(AttackTreeState.IDLE);
this.setState(AttackTreeState.IDLE);
this.stateTransitionUtil.interrupt(AttackTreeState.COOLDOWN);
frameCurrent = 0;
if(parent.containsKey(EntityDataStrings.SERVER_ROTATOR_TREE)){

View File

@ -265,10 +265,10 @@ public class ServerToolbarState implements BehaviorTree {
*/
public void unequip(Entity item){
if(item == this.realWorldItem){
removeVisuals();
this.removeVisuals();
}
if(ItemUtils.getRealWorldEntity(item) != null && ItemUtils.getRealWorldEntity(item) == this.realWorldItem){
removeVisuals();
this.removeVisuals();
}
}

View File

@ -615,7 +615,7 @@ public class HitboxCollectionState {
* @param bone The bone
* @return The list of hitboxes if at least one is present, null otherwise
*/
private List<HitboxState> getHitboxes(String bone){
public List<HitboxState> getHitboxes(String bone){
return this.boneHitboxMap.get(bone);
}

View File

@ -23,6 +23,7 @@ import org.joml.Vector3d;
import electrosphere.engine.Globals;
import electrosphere.entity.state.hitbox.HitboxCollectionState.HitboxState;
import electrosphere.entity.state.life.ClientLifeTree.LifeStateEnum;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.collidable.HitboxData;
import electrosphere.game.data.common.life.HealthSystem;
@ -76,6 +77,8 @@ public class ServerLifeTree implements BehaviorTree {
if(iFrameCurrent == 0){
isInvincible = false;
}
} else if(iFrameMaxCount == 0){
isInvincible = false;
}
} break;
case DYING: {
@ -200,7 +203,12 @@ public class ServerLifeTree implements BehaviorTree {
//do damage calculation
if(this.isAlive()){
int damage = ItemUtils.getWeaponDataRaw(event.source).getDamage();
int damage = 0;
if(ItemUtils.isItem(event.source)){
damage = ItemUtils.getWeaponDataRaw(event.source).getDamage();
} else if(CreatureUtils.isCreature(event.source)){
damage = event.sourceHitboxData.getHitboxData().getDamage();
}
this.damage(damage);
}
}

View File

@ -62,6 +62,11 @@ public class HitboxData {
//The offset from the bone
List<Double> offset;
/**
* The damage this hitbox can do (ie if it is tied directly to a creature)
*/
int damage;
/**
* Gets the type of hitbox
* @return the type of hitbox
@ -213,6 +218,14 @@ public class HitboxData {
public void setOffset(List<Double> offset){
this.offset = offset;
}
/**
* Gets the damage of this hitbox
* @return The damage
*/
public int getDamage(){
return this.damage;
}
}

View File

@ -1,5 +1,7 @@
package electrosphere.game.data.creature.type.attack;
import java.util.List;
import electrosphere.game.data.common.treedata.TreeDataState;
/**
@ -46,6 +48,11 @@ public class AttackMove {
int driftFrameStart; //when do we start drifting
int driftFrameEnd; //when do we stop drifting
/**
* The list of bones to activate as hitboxes when this attack move is active
*/
List<String> activeBones;
/**
* Hitstun
*/
@ -203,6 +210,16 @@ public class AttackMove {
public Integer getHitstun(){
return hitstun;
}
/**
* Gets the list of bones to activate as hitboxes while the move is active
* @return The list of bones
*/
public List<String> getActiveBones() {
return activeBones;
}
}

View File

@ -13,6 +13,7 @@ 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;
@ -157,6 +158,9 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba
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){
@ -164,6 +168,9 @@ public class ServerHitboxResolutionCallback implements CollisionResolutionCallba
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);
}
//