Attack Function Revisited - UQdeco2800/2022-studio-2 GitHub Wiki
Summary
This wiki page will explain the implementation of the Attack()
function which allows the player to deal damage to enemies (revisited for Sprint 3 on 04/10/22, see original wiki: https://github.com/UQdeco2800/2022-studio-2/wiki/%5BDeprecated%5D-Attack-function)
Key Binds
Press SPACE
to attack
- With no weapon in hand, this will not play an animation.
5
attack damage is dealt to nearby enemies. - With a weapon in hand, this will play the attack animation for whichever weapon has been equipped from the inventory and show the weapon in the player's hand, as well as deal damage to the closest enemy based on the attack statistics of the weapons defined in
source/core/assets/configs/Weapons.json
How does attacking deal damage?
PlayerTouchAttackComponent
Located in: source/core/src/main/com/deco2800/game/components/player/PlayerTouchAttackComponent.java
We created a new class PlayerTouchAttackComponent
which extends TouchAttackComponent
to create a player-specifc attack function.
First, the attack listeners are created which actively check for any attacks and collisions and the combat statistics of each weapons are fetched from CombatStatsComponent
.
entity.getEvents().addListener("attack", this::attack);
entity.getEvents().addListener("collisionStart", this::playerCollidesEnemyStart);
combatStats = entity.getComponent(CombatStatsComponent.class);
entity.getEvents().addListener("collisionEnd", this::playerCollidesEnemyEnd);
playerCollidesEnemyStart function
Upon recording a collision from the listener shown above, the code below will check that when the Entity the Player is colliding with is an ENEMY
.
If the collision is with an ENEMY
, then the variable enemyCollide
will be set to TRUE.
private void onCollisionStart(Fixture me, Fixture other) {
if (((BodyUserData) other.getBody().getUserData()).entity.checkEntityType(EntityTypes.ENEMY)) {
target = ((BodyUserData) other.getBody().getUserData()).entity;
enemyCollide = true;
}
}
Fixture me
: The fixture of the object which is colliding (player)
Fixture other
: The fixture of the object which is being collided with (enemy)
Then, when the collision is over, the playerCollidesEnemyEnd
function is called which sets the enemyCollide
variable to FALSE
private void playerCollidesEnemyEnd(Fixture me, Fixture other) {
enemyCollide = false;
}
Given the condition for collision has been met, the attack()
function will be triggered.
Attack() function
The attack function is triggered upon press of SPACE
, but will only be applied if bool canAttack
is TRUE. This Boolean is a part of the weapon cooldown system and is explained here: Attack Cooldown Function
Given this condition has been satisfied, the currently equipped weapon and equipped auras will be checked. This will determine which attack animations will be played. For more information about the logic behind Aura implementation, refer to: Aura Pickup and Application.
To learn more about how the animation system works, see: Combat Animation System
Below, we will discuss_** how damage is applied upon performing an attack.**_
When enemyCollide
is set to TRUE, then applyDamageToTarget
is called.
if (enemyCollide) {
applyDamageToTarget(target);
applyDamageToTarget() function
This function gets the CombatStatsComponent
to determine the appropriate damage to deal to the enemy and calls the CombatStatsComponent's hit
function.
private void applyDamage(Entity target) {
CombatStatsComponent targetStats = target.getComponent(CombatStatsComponent.class);
targetStats.hit(combatStats);
}
hit() function
Located in: source/core/src/main/com/deco2800/game/components/player/CombatStatsComponent.java
In the hit
function, if there is a weapon currently equipped by the player, the attackDmg
variable is set to be the damage of the weapon that is currently equipped, by using the getEquipable()
function of InventoryComponent and checking index 0, which is the slot dedicated to the weapons.
public void hit(CombatStatsComponent attacker) {
if (attacker.getEntity().checkEntityType(EntityTypes.PLAYER) &&
(playerWeapon = attacker.getEntity().getComponent(InventoryComponent.class).getEquipable(0)) != null) {
attackDmg = (int) playerWeapon.getComponent(MeleeStatsComponent.class).getDamage();
int newHealth = getHealth() - (int)((1 - damageReduction) * attackDmg);
setHealth(newHealth);
}
else { //if it's not a player, or if it is a player without a weapon
int newHealth = getHealth() - (int)((1 - damageReduction) * attacker.getBaseAttack());
setHealth(newHealth);
}
}
However, if the player does not currently have a weapon equipped, the base attack of the player is pulled from the player config file (source/core/assets/configs/player.json) and this is used to make the attack. Or, if the attacker is an enemy (presumably attacking the player), the base attack of the enemy is used.
A death condition has been implemented where if the enemy's health reaches 0, that the entity is disposed of and the animation is stopped. Additionally, that materials are dropped for the player to pick up.
if (isDead() && entity.checkEntityType(EntityTypes.ENEMY)) {
Gdx.app.postRunnable(() -> {
dropMaterial();
dropWeapon();
entity.dispose();
});
Modifications had to be made to the dispose() method in the Entity class to not dispose the AnimationRenderComponent (TextureAtlas) shared by all enemies when a enemy entity is disposed.
public void dispose() {
for (Component component : createdComponents) {
if (!(component instanceof AnimationRenderComponent)) {
component.dispose();
} //this prevents the other entities using the same animation from having their atlases disposed (black box)
}
ServiceLocator.getEntityService().unregister(this);
}
Additionally, the animations of the enemy are stopped to avoid visual glitches.
if (entity.getComponent(AnimationRenderComponent.class) != null) {
Gdx.app.postRunnable(() -> entity.getComponent(AnimationRenderComponent.class).stopAnimation()); //this is the magic line)
}
To learn more about the implementation of ranged weapons, see: Ranged Weapon Implementation
Author
- Lachlan Benson
- GitHub: @LachlanBenson
- Discord: Lachlan.Benson#4926
- Slack: Lachlan Benson