1
0
mirror of https://github.com/borgesdan/xn65 synced 2024-12-29 21:54:47 +01:00
2024-08-01 16:10:05 -03:00

229 lines
8.4 KiB
C++

#include "player.hpp"
#include "level.hpp"
#include "enemy.hpp"
#include "extensions.hpp"
namespace PlatformerStarterKit {
Player::Player(xna::sptr<PlatformerStarterKit::Level> const& level, xna::Vector2 const& position)
: level(level)
{
LoadContent();
Reset(position);
}
void Player::LoadContent()
{
idleAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Idle"), 0.1f, true);
runAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Run"), 0.1f, true);
jumpAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Jump"), 0.1f, false);
celebrateAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Celebrate"), 0.1f, false);
dieAnimation = xna::snew<Animation>(level->Content()->Load<xna::PTexture2D>("Sprites/Player/Die"), 0.1f, false);
const auto width = static_cast<int>(idleAnimation->FrameWidth() * 0.4);
const auto left = (idleAnimation->FrameWidth() - width) / 2;
const auto height = static_cast<int>(idleAnimation->FrameWidth() * 0.8);
const auto top = idleAnimation->FrameHeight() - height;
localBounds = xna::Rectangle(left, top, width, height);
killedSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerKilled");
jumpSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerJump");
fallSound = level->Content()->Load<xna::PSoundEffect>("Sounds/PlayerFall");
}
void Player::Reset(xna::Vector2 const& position)
{
Position = position;
Velocity = xna::Vector2::Zero();
isAlive = true;
sprite.PlayAnimation(idleAnimation);
}
void Player::ApplyPhysics(xna::GameTime const& gameTime)
{
float elapsed = static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds());
auto previousPosition = Position;
Velocity.X += movement * MoveAcceleration * elapsed;
Velocity.Y = xna::MathHelper::Clamp(Velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);
Velocity.Y = DoJump(Velocity.Y, gameTime);
if (IsOnGround())
Velocity.X *= GroundDragFactor;
else
Velocity.X *= AirDragFactor;
Velocity.X = xna::MathHelper::Clamp(Velocity.X, -MaxMoveSpeed, MaxMoveSpeed);
Position = Position + (Velocity * elapsed);
Position = xna::Vector2(std::round(Position.X), std::round(Position.Y));
HandleCollisions();
if (Position.X == previousPosition.X)
Velocity.X = 0;
if (Position.Y == previousPosition.Y)
Velocity.Y = 0;
}
void Player::Update(xna::GameTime const& gameTime)
{
GetInput();
ApplyPhysics(gameTime);
if (IsAlive() && IsOnGround()) {
if (std::abs(Velocity.X) - 0.02f > 0) {
sprite.PlayAnimation(runAnimation);
}
else {
sprite.PlayAnimation(idleAnimation);
}
}
movement = 0.0f;
isJumping = false;
}
void Player::GetInput()
{
auto gamePadState = xna::GamePad::GetState(xna::PlayerIndex::One);
auto keyboardState = xna::Keyboard::GetState();
movement = gamePadState.ThumbSticks().Left().X * MoveStickScale;
if (std::abs(movement) < 0.5f)
movement = 0.0f;
if (gamePadState.IsButtonDown(xna::Buttons::DPadLeft) ||
keyboardState.IsKeyDown(xna::Keys::Left) ||
keyboardState.IsKeyDown(xna::Keys::A)) {
movement = -1.0f;
}
else if (gamePadState.IsButtonDown(xna::Buttons::DPadRight) ||
keyboardState.IsKeyDown(xna::Keys::Right) ||
keyboardState.IsKeyDown(xna::Keys::D)) {
movement = 1.0f;
}
isJumping =
gamePadState.IsButtonDown(JumpButton) ||
keyboardState.IsKeyDown(xna::Keys::Space) ||
keyboardState.IsKeyDown(xna::Keys::Up) ||
keyboardState.IsKeyDown(xna::Keys::W);
}
void Player::OnKilled(xna::sptr<Enemy>& killedBy)
{
isAlive = false;
if (!killedBy)
killedSound->Play();
else
fallSound->Play();
sprite.PlayAnimation(dieAnimation);
}
void Player::Draw(xna::GameTime const& gameTime, xna::SpriteBatch& spriteBatch)
{
if (Velocity.X > 0)
flip = xna::SpriteEffects::FlipHorizontally;
else if (Velocity.X < 0)
flip = xna::SpriteEffects::None;
sprite.Draw(gameTime, spriteBatch, Position, flip);
}
void Player::OnReachedExit()
{
sprite.PlayAnimation(celebrateAnimation);
}
float Player::DoJump(float velocityY, xna::GameTime const& gameTime)
{
if (isJumping) {
if ((!wasJumping && IsOnGround()) || jumpTime > 0.0f) {
if (jumpTime == 0.0f)
jumpSound->Play();
jumpTime += static_cast<float>(gameTime.ElapsedGameTime.TotalSeconds());
sprite.PlayAnimation(jumpAnimation);
}
if (0.0f < jumpTime && jumpTime <= MaxJumpTime) {
velocityY = JumpLaunchVelocity * (1.0f - static_cast<float>(std::pow(jumpTime / MaxJumpTime, JumpControlPower)));
}
else {
jumpTime = 0.0f;
}
}
else {
jumpTime = 0.0f;
}
wasJumping = isJumping;
return velocityY;
}
void Player::HandleCollisions()
{
auto bounds = BoundingRectangle();
auto leftTile = static_cast<int>(std::floor(static_cast<float>(bounds.Left()) / Tile::Width));
auto rightTile = static_cast<int>(std::ceil((static_cast<float>(bounds.Right()) / Tile::Width))) - 1;
auto topTile = static_cast<int>(std::floor(static_cast<float>(bounds.Top()) / Tile::Height));
auto bottomTile = static_cast<int>(std::ceil((static_cast<float>(bounds.Bottom()) / Tile::Height))) - 1;
isOnGround = false;
for (int y = topTile; y <= bottomTile; ++y) {
for (int x = leftTile; x <= rightTile; ++x) {
auto collision = level->GetCollision(x, y);
if (collision != TileCollision::Passable) {
auto tileBounds = level->GetBounds(x, y);
auto depth = RectangleExtensions::GetIntersectionDepth(bounds, tileBounds);
if (depth != xna::Vector2::Zero()) {
auto absDepthX = std::abs(depth.X);
auto absDepthY = std::abs(depth.Y);
if (absDepthY < absDepthX || collision == TileCollision::Platform) {
const auto tileBoundsTop = tileBounds.Top();
if (previousBottom <= tileBoundsTop)
isOnGround = true;
if (collision == TileCollision::Impassable || IsOnGround()) {
Position = xna::Vector2(Position.X, Position.Y + depth.Y);
bounds = BoundingRectangle();
}
}
else if (collision == TileCollision::Impassable)
{
Position = xna::Vector2(Position.X + depth.X, Position.Y);
bounds = BoundingRectangle();
}
}
}
}
}
previousBottom = bounds.Bottom();
}
xna::sptr<PlatformerStarterKit::Level> Player::Level() const
{
return level;
}
xna::Rectangle Player::BoundingRectangle() const {
auto left = std::round(Position.X - sprite.Origin().X) + localBounds.X;
auto top = std::round(Position.Y - sprite.Origin().Y) + localBounds.Y;
return xna::Rectangle(
static_cast<int>(left),
static_cast<int>(top),
localBounds.Width,
localBounds.Height);
}
}