From 3c15d808b5bca6348cebf219f5f17e560c828c76 Mon Sep 17 00:00:00 2001 From: Sara Date: Thu, 18 Jan 2024 18:30:41 +0100 Subject: [PATCH] feat: implemented slide attack with QCF+H --- game/src/Player.c | 55 ++++++++++++++++++++++++++++++++++++++--- game/src/Player.h | 17 +++++++++++++ game/src/PlayerStates.c | 50 +++++++++++++++++++++++++++++++++---- game/src/PlayerStates.h | 9 +++++++ 4 files changed, 123 insertions(+), 8 deletions(-) diff --git a/game/src/Player.c b/game/src/Player.c index a351f44..d70dcbf 100644 --- a/game/src/Player.c +++ b/game/src/Player.c @@ -6,8 +6,10 @@ #include "PlayerStates.h" #include "Layers.h" +#include "program.h" const Vector PLAYER_SPEED = { 1.0f, 0.50f }; +static const float PLAYER_INPUT_RATE = 1.f/15.f; START_REFLECT(Player) REFLECT_TYPECLASS(Player, Drop) @@ -86,8 +88,12 @@ Player* MakePlayer() { .jab_b = NULL, .kick_a = NULL, .air_heavy = NULL, + .slide = NULL, .animationStateMachine = NULL, + + .pushInputTimer = 0.f, + .inputLog = list_from_type(PlayerInputFrame), }; self->rigidbody = rigidbody_make(Player_as_PhysicsEntity(self)); @@ -113,6 +119,7 @@ Player* MakePlayer() { self->jab_b = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Jab_B.png", IVectorFrom(512)), 10.f, LoopMode_Stop); self->kick_a = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Kick_A.png", IVectorFrom(512)), 12.f, LoopMode_Stop); self->air_heavy = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Air_Heavy.png", IVectorFrom(512)), 10.f, LoopMode_Stop); + self->slide = animation_sprite_new(self->sprite, spritesheet_load("assets/Player_Slide.png", IVectorFrom(512)), 1.f, LoopMode_Loop); self->animationStateMachine = state_machine_init(self, PlayerIdle()); @@ -150,6 +157,24 @@ void PlayerStart(Player* self) { animation_sprite_play_from(self->currentAnimation, 0.0f); } +static +void PlayerPushInput(Player* self) { + PlayerInputFrame state = { + .time = game_time(), + .direction = self->moveInput, + .light = self->attackInput == 1, + .heavy = self->attackInput == 2, + .jump = self->jumpInput, + .facing = self->facing, + }; + list_add(&self->inputLog, &state); + if(self->inputLog.len >= 5) + list_erase(&self->inputLog, 0); + 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) { state_machine_update(self->animationStateMachine, deltaTime); self->height += self->verticalVelocity * deltaTime; @@ -165,24 +190,33 @@ void PlayerDraw(Player* self) { void PlayerHorizontalInput(Player* self, InputEvent value) { self->moveInput.x = value.as_float; + PlayerPushInput(self); } void PlayerVerticalInput(Player* self, InputEvent value) { self->moveInput.y = -value.as_float; + PlayerPushInput(self); } void PlayerJumpInput(Player* self, InputEvent value) { - self->jumpInput = value.as_bool; + if(value.as_bool) { + self->jumpInput = value.as_bool; + PlayerPushInput(self); + } } void PlayerLightAttackInput(Player* self, InputEvent value) { - if(value.as_bool) + if(value.as_bool) { self->attackInput = 1; + PlayerPushInput(self); + } } void PlayerHeavyAttackInput(Player* self, InputEvent value) { - if(value.as_bool) + if(value.as_bool) { self->attackInput = 2; + PlayerPushInput(self); + } } void PlayerOnCollision(Player* self, Collision collision) {} @@ -195,3 +229,18 @@ Transform* PlayerGetTransform(Player* self) { RigidBody* PlayerGetRigidBody(Player* self) { return self->rigidbody; } + +PlayerInputFrame* PlayerInputHistory(Player* self) { + return (PlayerInputFrame*)self->inputLog.data; +} + +int PlayerInputIsQuarterCircleForward(Player* self) { + PlayerInputFrame* history = PlayerInputHistory(self); + const size_t last = self->inputLog.len-1; + const float forward = history[last-3].facing; + if(game_time() - history[last-2].time > 0.225f) + return 0; + 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/Player.h b/game/src/Player.h index 914ab88..5d8b249 100644 --- a/game/src/Player.h +++ b/game/src/Player.h @@ -14,6 +14,15 @@ extern const Vector PLAYER_SPEED; +typedef struct PlayerInputFrame { + float time; + Vector direction; + int light; + int heavy; + int jump; + float facing; +} PlayerInputFrame; + typedef struct Player { Transform transform; float height; @@ -42,10 +51,15 @@ typedef struct Player { AnimationSprite* jab_b; AnimationSprite* kick_a; AnimationSprite* air_heavy; + AnimationSprite* slide; StateMachine* animationStateMachine; AnimationSprite* currentAnimation; + + float pushInputTimer; + List inputLog; + PlayerInputFrame nextInputFrame; } Player; Player* MakePlayer(); @@ -68,6 +82,9 @@ void PlayerOnOverlap(Player* self, Collider* other); Transform* PlayerGetTransform(Player* self); RigidBody* PlayerGetRigidBody(Player* self); +PlayerInputFrame* PlayerInputHistory(Player* self); + +int PlayerInputIsQuarterCircleForward(Player* self); static long PlayerGetDepth(Player* self) { return (int)(-10-self->transform.position.y * 1000); } diff --git a/game/src/PlayerStates.c b/game/src/PlayerStates.c index b615bcd..c0054e8 100644 --- a/game/src/PlayerStates.c +++ b/game/src/PlayerStates.c @@ -18,6 +18,22 @@ void PlayerAnimationExit(Player* self) { self->attackInput = 0; } +static +const State* PlayerTryStartNewChain(Player* self) { + if(self->attackInput == 1) { + return PlayerJabA(); + } + if(self->attackInput == 2) { + if(self->height > 0.f) + return PlayerAirHeavy(); + else if(PlayerInputIsQuarterCircleForward(self)) + return PlayerSlide(); + else + return PlayerKickA(); + } + return NULL; +} + void PlayerIdleEnter(Player* self) { self->currentAnimation = self->idle; rigidbody_set_velocity(self->rigidbody, ZeroVector); @@ -46,10 +62,9 @@ const State* PlayerWalk_Update(Player* self, float deltaTime) { if(veqf(self->moveInput, ZeroVector)) return PlayerIdle(); - if(self->attackInput == 1) - return PlayerJabA(); - if(self->attackInput == 2) - return PlayerKickA(); + const State* attackState = PlayerTryStartNewChain(self); + if(attackState) + return attackState; if(self->jumpInput) return PlayerJump(); InternalSpriteFlipWithMovement(self); @@ -148,7 +163,7 @@ const State* PlayerKickA_Update(Player* self, float deltaTime) { }; const float ntime = animation_sprite_get_time_normalized(self->currentAnimation); const size_t frame = sprite_get_tile(self->sprite); - if(frame >= 2 && frame <= 3) { + if(frame >= 3 && frame <= 4) { PlayerHurtbox(self, damage, MakeVector(0.16f, 0.06f), MakeVector(0.33f, -0.4f)); } if(!veqf(self->moveInput, ZeroVector) && ntime > 1.05f) @@ -158,6 +173,31 @@ const State* PlayerKickA_Update(Player* self, float deltaTime) { return PlayerKickA(); } +void PlayerSlide_Enter(Player* self) { + PlayerAttackEnter(self); + self->currentAnimation = self->slide; + // adjust for the downward motion of the quartercircle + Transform* trans = rigidbody_get_transform(self->rigidbody); + trans->position.y -= 0.03f; + animation_sprite_play_from(self->currentAnimation, 0.f); +} + +const State* PlayerSlide_Update(Player* self, float deltaTime) { + static const DamageEventData damage = { + .damageAmount = 3, + .knockdown = 1, + .stun = 1.f, + .knockback = 0.3f, + }; + const float time = animation_sprite_get_time(self->currentAnimation); + rigidbody_set_velocity(self->rigidbody, MakeVector(self->facing * 3.f, 0.f)); + if(time > 0.34f) + return PlayerIdle(); + if(time > 0.1f) + PlayerHurtbox(self, damage, MakeVector(0.16, 0.06f), MakeVector(0.33f, -0.03f)); + return PlayerSlide(); +} + void PlayerJump_Enter(Player* self) { if(self->jumpInput) { self->currentAnimation = self->jump; diff --git a/game/src/PlayerStates.h b/game/src/PlayerStates.h index 30a7a5c..f83c022 100644 --- a/game/src/PlayerStates.h +++ b/game/src/PlayerStates.h @@ -54,6 +54,15 @@ DefineState(PlayerKickA, Player, PlayerAnimationExit ) +extern void PlayerSlide_Enter(Player* self); +extern const State* PlayerSlide_Update(Player* self, float deltaTime); + +DefineState(PlayerSlide, Player, + PlayerSlide_Enter, + PlayerSlide_Update, + PlayerAnimationExit +) + extern void PlayerJump_Enter(Player *self); extern const State* PlayerJump_Update(Player* self, float deltaTime);