diff --git a/assets/Data/entity/creatures/human.json b/assets/Data/entity/creatures/human.json index fe2c956a..71426230 100644 --- a/assets/Data/entity/creatures/human.json +++ b/assets/Data/entity/creatures/human.json @@ -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", diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 0c6ed1c5..07776c62 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1430,6 +1430,7 @@ Bush hitbox Harvesting interaction happens on server Fix PoseActor position/rotation caching bug Bush drops sticks +Unarmed combat diff --git a/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java b/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java index 72d4d269..fffbfe96 100644 --- a/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java +++ b/src/main/java/electrosphere/entity/state/attack/ClientAttackTree.java @@ -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 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 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 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 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)){ diff --git a/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java b/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java index 1f50669c..a1eb10c5 100644 --- a/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java +++ b/src/main/java/electrosphere/entity/state/attack/ServerAttackTree.java @@ -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 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 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 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 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 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 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)){ diff --git a/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java b/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java index 77e04c82..85f0b480 100644 --- a/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java +++ b/src/main/java/electrosphere/entity/state/equip/ServerToolbarState.java @@ -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(); } } diff --git a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java index 0eb730ba..30148c9f 100644 --- a/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java +++ b/src/main/java/electrosphere/entity/state/hitbox/HitboxCollectionState.java @@ -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 getHitboxes(String bone){ + public List getHitboxes(String bone){ return this.boneHitboxMap.get(bone); } diff --git a/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java b/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java index 98fa4086..56bb1263 100644 --- a/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java +++ b/src/main/java/electrosphere/entity/state/life/ServerLifeTree.java @@ -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); } } diff --git a/src/main/java/electrosphere/game/data/collidable/HitboxData.java b/src/main/java/electrosphere/game/data/collidable/HitboxData.java index 775ab876..e115d666 100644 --- a/src/main/java/electrosphere/game/data/collidable/HitboxData.java +++ b/src/main/java/electrosphere/game/data/collidable/HitboxData.java @@ -62,6 +62,11 @@ public class HitboxData { //The offset from the bone List 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 offset){ this.offset = offset; } + + /** + * Gets the damage of this hitbox + * @return The damage + */ + public int getDamage(){ + return this.damage; + } } diff --git a/src/main/java/electrosphere/game/data/creature/type/attack/AttackMove.java b/src/main/java/electrosphere/game/data/creature/type/attack/AttackMove.java index b7dc5cc7..1217fd77 100644 --- a/src/main/java/electrosphere/game/data/creature/type/attack/AttackMove.java +++ b/src/main/java/electrosphere/game/data/creature/type/attack/AttackMove.java @@ -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 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 getActiveBones() { + return activeBones; + } + + } diff --git a/src/main/java/electrosphere/server/collision/ServerHitboxResolutionCallback.java b/src/main/java/electrosphere/server/collision/ServerHitboxResolutionCallback.java index dd3b8254..0e442005 100644 --- a/src/main/java/electrosphere/server/collision/ServerHitboxResolutionCallback.java +++ b/src/main/java/electrosphere/server/collision/ServerHitboxResolutionCallback.java @@ -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); } //