From 73104a02c4197e1826cf185bcc38a8371751ebaf Mon Sep 17 00:00:00 2001 From: Sara Date: Sat, 20 Jan 2024 12:34:13 +0100 Subject: [PATCH] feat: documentation pass --- game/src/Enemy.c | 24 +++++++++++++++++------- game/src/EnemyStates.c | 7 +++++-- game/src/Player.c | 26 +++++++++++++++++++++++++- game/src/PlayerStates.c | 17 ++++++----------- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/game/src/Enemy.c b/game/src/Enemy.c index 0711beb..a741c3e 100644 --- a/game/src/Enemy.c +++ b/game/src/Enemy.c @@ -60,19 +60,20 @@ Enemy* MakeEnemy() { }; self->rigidbody = rigidbody_make(Enemy_as_PhysicsEntity(self)); + // collider used for physics and movement self->collider = collider_new(Enemy_as_PhysicsEntity(self), shape_new_square(MakeVector(0.2f, 0.05f)), 0, PHYSICS_LAYER_DEFAULT, PHYSICS_LAYER_DEFAULT); + // hitbox used to detect damage self->hitbox = collider_new(Enemy_as_PhysicsEntity(self), shape_new((Vector[]){ MakeVector(-0.1f, -0.9f), MakeVector( 0.1f, -0.9f), MakeVector( 0.1f, 0.0f), MakeVector(-0.1f, 0.0f), - }, 4), 1, PHYSICS_LAYER_COMBAT, 0x0); - PhysicsEntity pe = Enemy_as_PhysicsEntity(self); - LOG_INFO("enemy instantiated mirroring as: %s", pe.mirror->get_typestring(pe.data)); + }, 4), 1, PHYSICS_LAYER_COMBAT, 0x0); // does not query sprite_set_origin(self->sprite, MakeVector(0.45f, 0.925f)); + // load and configure animations self->idleAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Idle.png", IVectorFrom(512)), 1.5f, LoopMode_Loop); self->walkAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Walk.png", IVectorFrom(512)), 1.5f, LoopMode_Loop); self->hurtAnim = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Hurt.png", IVectorFrom(512)), 5.f, LoopMode_Stop); @@ -83,7 +84,9 @@ Enemy* MakeEnemy() { Enemy* SpawnEnemy(Vector location, const State* entryState) { Enemy* self = MakeEnemy(); self->behaviour = state_machine_init(self, entryState); + // spawn at location rigidbody_get_transform(self->rigidbody)->position = location; + // register into game world game_world_add_entity(Enemy_as_BehaviourEntity(self)); physics_world_add_entity(Enemy_as_PhysicsEntity(self)); return self; @@ -102,6 +105,7 @@ void EnemyDraw(Enemy* self) { void EnemyDestroy(Enemy* self) { state_machine_destroy(self->behaviour); + // unregister *before* deallocating physics components physics_world_remove_entity(Enemy_as_PhysicsEntity(self)); collider_destroy(self->collider); rigidbody_destroy(self->rigidbody); @@ -122,20 +126,26 @@ RigidBody* EnemyGetRigidBody(Enemy* self) { } int EnemyDamage(Enemy* self, DamageEventData* data) { + // being stunned gives iframes if(self->stun_time > game_time()) return 0; + // subtract damage self->health -= data->damageAmount; - LOG_INFO("Damage received"); + // die if health drops below zero if(self->health <= 0) { + // TODO: swap to death state instead game_world_destroy_entity(Enemy_as_BehaviourEntity(self)); return 1; } else { + // store the most recent damage event self->last_damage = *data; - const float stun_time = game_time() + data->stun; - if(stun_time > self->stun_time) - self->stun_time = stun_time; + // get stunned + self->stun_time = game_time() + data->stun; + // calculate which direction the damage is coming from float direction = ((data->origin.x - self->transform.position.x) > 0) * 2 - 1; + // face the direction damage is coming from self->facing = direction; + // add knockback according to damage data in the calculated direction rigidbody_add_impulse(self->rigidbody, MakeVector(-direction * data->knockback, 0), 0); return 0; } diff --git a/game/src/EnemyStates.c b/game/src/EnemyStates.c index 2db53b5..ceafefa 100644 --- a/game/src/EnemyStates.c +++ b/game/src/EnemyStates.c @@ -11,8 +11,10 @@ void EnemyIdle_Enter(Enemy* self) { } const State* EnemyIdle_Update(Enemy* self, float deltaTime) { + // apply drag Vector velocity = vmovetowardsf(rigidbody_get_velocity(self->rigidbody), ZeroVector, 0.1f); rigidbody_set_velocity(self->rigidbody, velocity); + // state transitions if(self->stun_time >= game_time()) return EnemyHurt(); return EnemyIdle(); @@ -34,10 +36,11 @@ void EnemyHurt_Enter(Enemy* self) { const State* EnemyHurt_Update(Enemy* self, float deltaTime) { const float time = animation_sprite_get_time(self->currentAnimation); + // apply drag Vector velocity = vmovetowardsf(rigidbody_get_velocity(self->rigidbody), ZeroVector, 0.1f); rigidbody_set_velocity(self->rigidbody, velocity); + // state transitions if(self->stun_time < game_time()) return EnemyIdle(); - else - return EnemyHurt(); + return EnemyHurt(); } diff --git a/game/src/Player.c b/game/src/Player.c index d70dcbf..0244784 100644 --- a/game/src/Player.c +++ b/game/src/Player.c @@ -97,21 +97,24 @@ Player* MakePlayer() { }; self->rigidbody = rigidbody_make(Player_as_PhysicsEntity(self)); + // physics collider used for movement self->physicsCollider = collider_new(Player_as_PhysicsEntity(self), shape_new((Vector[]){ MakeVector(-0.2f, -0.065f), MakeVector( 0.2f, -0.065f), MakeVector( 0.2f, 0.065f), MakeVector(-0.2f, 0.065f) }, 4), 0, PHYSICS_LAYER_CHARACTERS, PHYSICS_LAYER_DEFAULT); + // hitbox is used for combat only self->hitbox = collider_new(Player_as_PhysicsEntity(self), shape_new((Vector[]){ MakeVector(-0.1f, -0.9f), MakeVector( 0.1f, -0.9f), MakeVector( 0.1f, 0.00f), MakeVector(-0.1f, 0.00f) - }, 4), 1, PHYSICS_LAYER_COMBAT, 0x0); + }, 4), 1, PHYSICS_LAYER_COMBAT, 0x0); // empty mask and overlap means this does not detect collisions itself sprite_set_origin(self->sprite, MakeVector(0.45f, 0.925f)); + // load and configure animations self->idle = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Idle.png", IVectorFrom(512)), 1.5f, LoopMode_Loop); self->walk = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Walk.png", IVectorFrom(512)), 5.f, LoopMode_Loop); self->jump = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Jumping.png", IVectorFrom(512)), 1.f, LoopMode_Stop); @@ -138,16 +141,22 @@ Player* SpawnPlayer(Vector location) { } void DestroyPlayer(Player* self) { + // deregister and free physics components physics_world_remove_entity(Player_as_PhysicsEntity(self)); collider_destroy(self->physicsCollider); rigidbody_destroy(self->rigidbody); playerinput_drop(self->playerInput); + // erase animations animation_sprite_destroy(self->idle); animation_sprite_destroy(self->walk); + animation_sprite_destroy(self->jump); animation_sprite_destroy(self->jab_a); animation_sprite_destroy(self->jab_b); + animation_sprite_destroy(self->kick_a); + animation_sprite_destroy(self->air_heavy); + animation_sprite_destroy(self->slide); sprite_destroy(self->sprite); state_machine_destroy(self->animationStateMachine); @@ -159,6 +168,7 @@ void PlayerStart(Player* self) { static void PlayerPushInput(Player* self) { + // the current input state PlayerInputFrame state = { .time = game_time(), .direction = self->moveInput, @@ -167,22 +177,28 @@ void PlayerPushInput(Player* self) { .jump = self->jumpInput, .facing = self->facing, }; + // push onto the end of the log list_add(&self->inputLog, &state); + // erase oldest input from the log if(self->inputLog.len >= 5) list_erase(&self->inputLog, 0); + // log current input state LOG_INFO("%f %f, L:%x, H:%x, J:%x", state.direction.x, state.direction.y, state.light, state.heavy, state.jump); } void PlayerUpdate(Player* self, float deltaTime) { + // update state machine state_machine_update(self->animationStateMachine, deltaTime); + // update gravity self->height += self->verticalVelocity * deltaTime; if(self->height <= 0.f) self->height = 0.f; } void PlayerDraw(Player* self) { + // create a new transform that adjusts the "ground" transform to reflect distance from the ground as well Transform trans = self->transform; trans.position.y -= self->height; animation_sprite_draw(self->currentAnimation, &trans); @@ -235,11 +251,19 @@ PlayerInputFrame* PlayerInputHistory(Player* self) { } int PlayerInputIsQuarterCircleForward(Player* self) { + // at least four inputs have to be logged for a valid quartercircle + // (three directional, one button, in that order) + if(self->inputLog.len < 4) + return 0; PlayerInputFrame* history = PlayerInputHistory(self); + // the most recent input (assumed to be a button input) const size_t last = self->inputLog.len-1; + // the forward direction at the assumed start of the action const float forward = history[last-3].facing; if(game_time() - history[last-2].time > 0.225f) return 0; + // check if the three inputs before the most recent input are a quartercircle towards the facing direction at last-3 (three before current) + // current (history[last]) is assumed to be a button input return veqf(history[last-3].direction, MakeVector(0.f, 1.f)) && veqf(history[last-2].direction, MakeVector(forward, 1.f)) && veqf(history[last-1].direction, MakeVector(forward, 0.f)); diff --git a/game/src/PlayerStates.c b/game/src/PlayerStates.c index 7acb1d5..59d99df 100644 --- a/game/src/PlayerStates.c +++ b/game/src/PlayerStates.c @@ -4,6 +4,8 @@ #include "Damagable.h" #include "Player.h" +// flip the facing direction of the player based on input +// does not do movement static inline void InternalSpriteFlipWithMovement(Player* self) { if(self->moveInput.x > 0.f) @@ -19,7 +21,7 @@ void PlayerAnimationExit(Player* self) { } static -const State* PlayerTryStartNewChain(Player* self) { +const State* PlayerTryStartNewChain(Player* self, const State* fallback) { if(self->attackInput == 1) { return PlayerJabA(); } @@ -31,7 +33,7 @@ const State* PlayerTryStartNewChain(Player* self) { else return PlayerKickA(); } - return NULL; + return fallback; } void PlayerIdleEnter(Player* self) { @@ -43,13 +45,9 @@ void PlayerIdleEnter(Player* self) { const State* PlayerIdleUpdate(Player* self, float deltaTime) { if(!veqf(self->moveInput, ZeroVector)) return PlayerWalk(); - if(self->attackInput == 1) - return PlayerJabA(); - if(self->attackInput == 2) - return PlayerKickA(); if(self->jumpInput) return PlayerJump(); - return PlayerIdle(); + return PlayerTryStartNewChain(self, PlayerIdle()); } void PlayerWalk_Enter(Player* self) { @@ -62,13 +60,10 @@ const State* PlayerWalk_Update(Player* self, float deltaTime) { if(veqf(self->moveInput, ZeroVector)) return PlayerIdle(); - const State* attackState = PlayerTryStartNewChain(self); - if(attackState) - return attackState; if(self->jumpInput) return PlayerJump(); InternalSpriteFlipWithMovement(self); - return PlayerWalk(); + return PlayerTryStartNewChain(self, PlayerWalk()); } void PlayerAttackEnter(Player* self) {