diff --git a/content/map/0x0.png b/content/map/0x0.png index fa32c71..bfdb1ad 100644 Binary files a/content/map/0x0.png and b/content/map/0x0.png differ diff --git a/content/map/1x0.png b/content/map/1x0.png new file mode 100644 index 0000000..d71c89f Binary files /dev/null and b/content/map/1x0.png differ diff --git a/content/sprites/player.ase b/content/sprites/player.ase index 2ebbb89..15baa48 100644 Binary files a/content/sprites/player.ase and b/content/sprites/player.ase differ diff --git a/content/tilesets/castle.ase b/content/tilesets/castle.ase index 767066b..6fcbbb8 100644 Binary files a/content/tilesets/castle.ase and b/content/tilesets/castle.ase differ diff --git a/content/tilesets/grass.ase b/content/tilesets/grass.ase new file mode 100644 index 0000000..9c5c998 Binary files /dev/null and b/content/tilesets/grass.ase differ diff --git a/content/tilesets/plants.ase b/content/tilesets/plants.ase new file mode 100644 index 0000000..53ac464 Binary files /dev/null and b/content/tilesets/plants.ase differ diff --git a/src/assets/sprite.cpp b/src/assets/sprite.cpp index e69de29..6f76a13 100644 --- a/src/assets/sprite.cpp +++ b/src/assets/sprite.cpp @@ -0,0 +1,11 @@ +#include "sprite.h" + +using namespace TL; + +const Sprite::Animation* TL::Sprite::get_animation(const String& name) const +{ + for (auto& it : animations) + if (it.name == name) + return ⁢ + return nullptr; +} diff --git a/src/assets/sprite.h b/src/assets/sprite.h index e9c4e75..6ebd967 100644 --- a/src/assets/sprite.h +++ b/src/assets/sprite.h @@ -17,10 +17,20 @@ namespace TL { String name; Vector frames; + + float duration() const + { + float d = 0; + for (auto& it : frames) + d += it.duration; + return d; + } }; String name; Vec2 origin; Vector animations; + + const Animation* get_animation(const String& name) const; }; } \ No newline at end of file diff --git a/src/components/animator.cpp b/src/components/animator.cpp index 92f198a..ba2386b 100644 --- a/src/components/animator.cpp +++ b/src/components/animator.cpp @@ -9,7 +9,12 @@ Animator::Animator(const String& sprite) m_animation_index = 0; } -void Animator::play(const String& animation) +const Sprite* Animator::sprite() const +{ + return m_sprite; +} + +void Animator::play(const String& animation, bool restart) { BLAH_ASSERT(m_sprite, "No Sprite Assigned!"); @@ -17,8 +22,13 @@ void Animator::play(const String& animation) { if (m_sprite->animations[i].name == animation) { - m_animation_index = i; - m_frame_index = 0; + if (m_animation_index != i || restart) + { + m_animation_index = i; + m_frame_index = 0; + m_frame_counter = 0; + } + break; } } diff --git a/src/components/animator.h b/src/components/animator.h index c0515d3..007a34d 100644 --- a/src/components/animator.h +++ b/src/components/animator.h @@ -21,8 +21,8 @@ namespace TL Animator() = default; Animator(const String& sprite); - void play(const String& animation); - + const Sprite* sprite() const; + void play(const String& animation, bool restart = false); void update() override; void render(Batch& batch) override; diff --git a/src/components/player.cpp b/src/components/player.cpp index b460e12..2bfe552 100644 --- a/src/components/player.cpp +++ b/src/components/player.cpp @@ -27,12 +27,18 @@ Player::Player() .press_buffer(0.15f) .add_key(Key::X) .add_button(0, Button::A); + + input_attack = VirtualButton() + .press_buffer(0.15f) + .add_key(Key::C) + .add_button(0, Button::X); } void Player::update() { input_move.update(); input_jump.update(); + input_attack.update(); auto mover = get(); auto anim = get(); @@ -44,37 +50,99 @@ void Player::update() { // land squish if (!was_on_ground && m_on_ground) - anim->scale = Vec2(facing * 1.5f, 0.7f); + anim->scale = Vec2(m_facing * 1.5f, 0.7f); // lerp scale back to one - anim->scale = Calc::approach(anim->scale, Vec2(facing, 1.0f), Time::delta * 4); + anim->scale = Calc::approach(anim->scale, Vec2(m_facing, 1.0f), Time::delta * 4); + + // set m_facing + anim->scale.x = Calc::abs(anim->scale.x) * m_facing; - // set facing - anim->scale.x = Calc::abs(anim->scale.x) * facing; } - // Horizontal Movement + // NORMAL STATE + if (m_state == st_normal) { - // Acceleration - mover->speed.x += input * (m_on_ground ? ground_accel : air_accel) * Time::delta; - - // Maxspeed - auto maxspd = (m_on_ground ? max_ground_speed : max_air_speed); - if (Calc::abs(mover->speed.x) > maxspd) + // Current Animation + if (m_on_ground) { - mover->speed.x = Calc::approach( - mover->speed.x, - Calc::sign(mover->speed.x) * maxspd, - 2000 * Time::delta); + if (input != 0) + anim->play("run"); + else + anim->play("idle"); + } + else + { + anim->play("jump"); } - // Friction - if (input == 0 && m_on_ground) - mover->speed.x = Calc::approach(mover->speed.x, 0, friction * Time::delta); + // Horizontal Movement + { + // Acceleration + mover->speed.x += input * (m_on_ground ? ground_accel : air_accel) * Time::delta; - // Facing Direction - if (input != 0 && m_on_ground) - facing = input; + // Maxspeed + auto maxspd = (m_on_ground ? max_ground_speed : max_air_speed); + if (Calc::abs(mover->speed.x) > maxspd) + { + mover->speed.x = Calc::approach( + mover->speed.x, + Calc::sign(mover->speed.x) * maxspd, + 2000 * Time::delta); + } + + // Friction + if (input == 0 && m_on_ground) + mover->speed.x = Calc::approach(mover->speed.x, 0, friction * Time::delta); + + // Facing Direction + if (input != 0 && m_on_ground) + m_facing = input; + } + + // Invoke Jumping + { + if (input_jump.pressed() && mover->on_ground()) + { + input_jump.clear_press_buffer(); + anim->scale = Vec2(m_facing * 0.65f, 1.4f); + mover->speed.x = input * max_air_speed; + m_jump_timer = jump_time; + } + } + + // Begin Attack + if (input_attack.pressed()) + { + input_attack.clear_press_buffer(); + + m_state = st_attack; + m_attack_timer = anim->sprite()->get_animation("attack")->duration(); + + if (m_on_ground) + mover->speed.x = 0; + } + } + // ATTACK STATE + else if (m_state == st_attack) + { + anim->play("attack"); + m_attack_timer -= Time::delta; + + if (m_attack_timer <= 0) + { + anim->play("idle"); + m_state = st_normal; + } + } + + // Variable Jumping + if (m_jump_timer > 0) + { + mover->speed.y = -100; + m_jump_timer -= Time::delta; + if (!input_jump.down()) + m_jump_timer = 0; } // Gravity @@ -86,22 +154,4 @@ void Player::update() mover->speed.y += grav * Time::delta; } - - // Jumping - { - if (input_jump.pressed() && mover->on_ground()) - { - anim->scale = Vec2(facing * 0.65f, 1.4f); - mover->speed.x = input * max_air_speed; - m_jump_timer = jump_time; - } - - if (m_jump_timer > 0) - { - mover->speed.y = -100; - m_jump_timer -= Time::delta; - if (!input_jump.down()) - m_jump_timer = 0; - } - } } diff --git a/src/components/player.h b/src/components/player.h index c66bbd6..bcc3cc7 100644 --- a/src/components/player.h +++ b/src/components/player.h @@ -9,15 +9,21 @@ namespace TL class Player : public Component { public: + static constexpr int st_normal = 0; + static constexpr int st_attack = 1; + VirtualStick input_move; VirtualButton input_jump; + VirtualButton input_attack; Player(); void update() override; private: - int facing = 1; + int m_state = st_normal; + int m_facing = 1; float m_jump_timer = 0; + float m_attack_timer = 0; bool m_on_ground; }; } \ No newline at end of file diff --git a/src/components/tilemap.cpp b/src/components/tilemap.cpp index 276ab62..289410c 100644 --- a/src/components/tilemap.cpp +++ b/src/components/tilemap.cpp @@ -52,10 +52,12 @@ void Tilemap::set_cells(int x, int y, int w, int h, const Subtexture* tex) void Tilemap::render(Batch& batch) { + batch.push_matrix(Mat3x2::create_translation(entity()->position)); for (int x = 0; x < m_columns; x ++) for (int y = 0; y < m_rows; y ++) if (m_grid[x + y * m_columns].texture) { batch.tex(m_grid[x + y * m_columns], Vec2(x * m_tile_width, y * m_tile_height)); } + batch.pop_matrix(); } diff --git a/src/game.cpp b/src/game.cpp index 83a676c..71d35cb 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -4,10 +4,16 @@ #include "assets/tileset.h" #include "components/collider.h" #include "components/tilemap.h" +#include "components/player.h" #include "factory.h" using namespace TL; +namespace +{ + constexpr float transition_duration = 0.75f; +} + void Game::startup() { // load our content @@ -21,6 +27,8 @@ void Game::startup() m_draw_colliders = false; + // camera setup + camera = Vec2::zero; load_room(Point(0, 0)); } @@ -30,14 +38,16 @@ void Game::load_room(Point cell) BLAH_ASSERT(grid, "Room doesn't exist!"); room = cell; - // destroy all the entities - world.clear(); + // get room offset + auto offset = Point(cell.x * width, cell.y * height); // get the castle tileset for now auto castle = Content::find_tileset("castle"); + auto grass = Content::find_tileset("grass"); + auto plants = Content::find_tileset("plants"); // make the floor - auto floor = world.add_entity(); + auto floor = world.add_entity(offset); auto tilemap = floor->add(Tilemap(8, 8, columns, rows)); auto solids = floor->add(Collider::make_grid(8, 40, 23)); solids->mask = Mask::solid; @@ -46,6 +56,7 @@ void Game::load_room(Point cell) for (int x = 0; x < columns; x ++) for (int y = 0; y < rows; y++) { + Point world_position = offset + Point(x * tile_width, y * tile_height); Color col = grid->pixels[x + y * columns]; uint32_t rgb = ((uint32_t)col.r << 16) | @@ -58,15 +69,27 @@ void Game::load_room(Point cell) case 0x000000: break; - // solids + // castle tiles case 0xffffff: tilemap->set_cell(x, y, &castle->random_tile()); solids->set_cell(x, y, true); break; - // player + // grass tiles + case 0x8f974a: + tilemap->set_cell(x, y, &grass->random_tile()); + solids->set_cell(x, y, true); + break; + + // plants tiles + case 0x4b692f: + tilemap->set_cell(x, y, &plants->random_tile()); + break; + + // player (only if it doesn't already exist) case 0x6abe30: - Factory::player(&world, Point(x * tile_width + tile_width / 2, (y + 1) * tile_height)); + if (!world.first()) + Factory::player(&world, world_position + Point(tile_width / 2, tile_height)); break; } } @@ -79,12 +102,87 @@ void Game::shutdown() void Game::update() { + // Toggle Collider Render if (Input::pressed(Key::F1)) m_draw_colliders = !m_draw_colliders; - if (Input::pressed(Key::F2)) - load_room(room); - world.update(); + // Reload Current Room + if (Input::pressed(Key::F2)) + { + m_transition = false; + world.clear(); + load_room(room); + } + + // Normal Update + if (!m_transition) + { + world.update(); + + auto player = world.first(); + if (player) + { + auto pos = player->entity()->position; + auto bounds = RectI(room.x * width, room.y * height, width, height); + if (!bounds.contains(pos)) + { + // target room + Point next_room = Point(pos.x / width, pos.y / height); + if (pos.x < 0) next_room.x--; + if (pos.y < 0) next_room.y--; + + // see if room exists + if (Content::find_room(next_room)) + { + // transiton to it! + m_transition = true; + m_next_ease = 0; + m_next_room = next_room; + m_last_room = room; + + // store entities from the previous room + m_last_entities.clear(); + Entity* e = world.first_entity(); + while (e) + { + m_last_entities.push_back(e); + e = e->next(); + } + + // load contents of the next room + load_room(next_room); + } + // doesn't exist, clamp player + else + { + player->entity()->position = Point( + Calc::clamp_int(pos.x, bounds.x, bounds.x + bounds.w), + Calc::clamp_int(pos.y, bounds.y, bounds.y + bounds.h)); + } + } + } + } + // Room Transition routine + else + { + auto last_cam = Vec2(m_last_room.x * width, m_last_room.y * height); + auto next_cam = Vec2(m_next_room.x * width, m_next_room.y * height); + + m_next_ease = Calc::approach(m_next_ease, 1.0f, Time::delta / transition_duration); + camera = last_cam + (next_cam - last_cam) * Ease::cube_in_out(m_next_ease); + + if (m_next_ease >= 1.0f) + { + // delete old objects (except player!) + for (auto& it : m_last_entities) + { + if (!it->get()) + world.destroy_entity(it); + } + + m_transition = false; + } + } } void Game::render() @@ -93,6 +191,7 @@ void Game::render() { buffer->clear(0x150e22); + batch.push_matrix(Mat3x2::create_translation(-camera)); world.render(batch); if (m_draw_colliders) @@ -105,6 +204,7 @@ void Game::render() } } + batch.pop_matrix(); batch.render(buffer); batch.clear(); } diff --git a/src/game.h b/src/game.h index af06765..6df38a2 100644 --- a/src/game.h +++ b/src/game.h @@ -20,6 +20,7 @@ namespace TL FrameBufferRef buffer; Batch batch; Point room; + Vec2 camera; void load_room(Point cell); void startup(); @@ -29,5 +30,10 @@ namespace TL private: bool m_draw_colliders; + bool m_transition = false; + float m_next_ease; + Point m_next_room; + Point m_last_room; + Vector m_last_entities; }; } \ No newline at end of file