more rooms and enemies!
Before Width: | Height: | Size: 187 B After Width: | Height: | Size: 215 B |
Before Width: | Height: | Size: 130 B After Width: | Height: | Size: 280 B |
BIN
content/map/2x0.png
Normal file
After Width: | Height: | Size: 261 B |
BIN
content/map/3x0.png
Normal file
After Width: | Height: | Size: 261 B |
BIN
content/map/3x1.png
Normal file
After Width: | Height: | Size: 338 B |
BIN
content/map/4x1.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
content/map/5x1.png
Normal file
After Width: | Height: | Size: 325 B |
BIN
content/sprites/bullet.ase
Normal file
BIN
content/sprites/mosquito.ase
Normal file
BIN
content/sprites/spitter.ase
Normal file
BIN
content/tilesets/back.ase
Normal file
@ -1,17 +1,29 @@
|
|||||||
#include "hurtable.h"
|
#include "hurtable.h"
|
||||||
|
#include "animator.h"
|
||||||
|
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
void Hurtable::update()
|
void Hurtable::update()
|
||||||
{
|
{
|
||||||
if (collider && on_hurt && m_cooldown_timer <= 0)
|
if (collider && on_hurt && stun_timer <= 0)
|
||||||
{
|
{
|
||||||
if (collider->check(hurt_by))
|
if (collider->check(hurt_by))
|
||||||
{
|
{
|
||||||
m_cooldown_timer = cooldown;
|
stun_timer = 0.5f;
|
||||||
|
flicker_timer = 0.5f;
|
||||||
on_hurt(this);
|
on_hurt(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cooldown_timer -= Time::delta;
|
stun_timer -= Time::delta;
|
||||||
|
|
||||||
|
if (flicker_timer > 0)
|
||||||
|
{
|
||||||
|
flicker_timer -= Time::delta;
|
||||||
|
|
||||||
|
if (Time::on_interval(0.05f))
|
||||||
|
entity()->visible = !entity()->visible;
|
||||||
|
if (flicker_timer <= 0)
|
||||||
|
entity()->visible = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,13 +10,11 @@ namespace TL
|
|||||||
{
|
{
|
||||||
class Hurtable : public Component
|
class Hurtable : public Component
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
float m_cooldown_timer = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
float stun_timer = 0;
|
||||||
|
float flicker_timer = 0;
|
||||||
Collider* collider = nullptr;
|
Collider* collider = nullptr;
|
||||||
uint32_t hurt_by = 0;
|
uint32_t hurt_by = 0;
|
||||||
float cooldown = 1.0f;
|
|
||||||
std::function<void(Hurtable* self)> on_hurt;
|
std::function<void(Hurtable* self)> on_hurt;
|
||||||
|
|
||||||
void update() override;
|
void update() override;
|
||||||
|
@ -13,7 +13,10 @@ bool Mover::move_x(int amount)
|
|||||||
{
|
{
|
||||||
if (collider->check(Mask::solid, Point(sign, 0)))
|
if (collider->check(Mask::solid, Point(sign, 0)))
|
||||||
{
|
{
|
||||||
stop_x();
|
if (on_hit_x)
|
||||||
|
on_hit_x(this);
|
||||||
|
else
|
||||||
|
stop_x();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +40,10 @@ bool Mover::move_y(int amount)
|
|||||||
{
|
{
|
||||||
if (collider->check(Mask::solid, Point(0, sign)))
|
if (collider->check(Mask::solid, Point(0, sign)))
|
||||||
{
|
{
|
||||||
stop_y();
|
if (on_hit_y)
|
||||||
|
on_hit_y(this);
|
||||||
|
else
|
||||||
|
stop_y();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "../world.h"
|
#include "../world.h"
|
||||||
#include "collider.h"
|
#include "collider.h"
|
||||||
#include <blah.h>
|
#include <blah.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ namespace TL
|
|||||||
Collider* collider = nullptr;
|
Collider* collider = nullptr;
|
||||||
Vec2 speed;
|
Vec2 speed;
|
||||||
float gravity = 0;
|
float gravity = 0;
|
||||||
|
std::function<void(Mover*)> on_hit_x;
|
||||||
|
std::function<void(Mover*)> on_hit_y;
|
||||||
|
|
||||||
bool move_x(int amount);
|
bool move_x(int amount);
|
||||||
bool move_y(int amount);
|
bool move_y(int amount);
|
||||||
|
@ -65,8 +65,16 @@ void Player::update()
|
|||||||
anim->scale.x = Calc::abs(anim->scale.x) * m_facing;
|
anim->scale.x = Calc::abs(anim->scale.x) * m_facing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// START
|
||||||
|
if (m_state == st_start)
|
||||||
|
{
|
||||||
|
anim->play("sword");
|
||||||
|
m_start_timer -= Time::delta;
|
||||||
|
if (m_start_timer <= 0)
|
||||||
|
m_state = st_normal;
|
||||||
|
}
|
||||||
// NORMAL STATE
|
// NORMAL STATE
|
||||||
if (m_state == st_normal)
|
else if (m_state == st_normal)
|
||||||
{
|
{
|
||||||
// Current Animation
|
// Current Animation
|
||||||
if (m_on_ground)
|
if (m_on_ground)
|
||||||
|
@ -13,6 +13,7 @@ namespace TL
|
|||||||
static constexpr int st_normal = 0;
|
static constexpr int st_normal = 0;
|
||||||
static constexpr int st_attack = 1;
|
static constexpr int st_attack = 1;
|
||||||
static constexpr int st_hurt = 2;
|
static constexpr int st_hurt = 2;
|
||||||
|
static constexpr int st_start = 3;
|
||||||
|
|
||||||
int health = 3;
|
int health = 3;
|
||||||
|
|
||||||
@ -24,12 +25,13 @@ namespace TL
|
|||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_state = st_normal;
|
int m_state = st_start;
|
||||||
int m_facing = 1;
|
int m_facing = 1;
|
||||||
float m_jump_timer = 0;
|
float m_jump_timer = 0;
|
||||||
float m_attack_timer = 0;
|
float m_attack_timer = 0;
|
||||||
float m_hurt_timer = 0;
|
float m_hurt_timer = 0;
|
||||||
float m_invincible_timer = 0;
|
float m_invincible_timer = 0;
|
||||||
|
float m_start_timer = 1;
|
||||||
Collider* m_attack_collider = nullptr;
|
Collider* m_attack_collider = nullptr;
|
||||||
bool m_on_ground;
|
bool m_on_ground;
|
||||||
};
|
};
|
||||||
|
151
src/factory.cpp
@ -59,9 +59,154 @@ Entity* Factory::pop(World* world, Point position)
|
|||||||
anim->depth = -20;
|
anim->depth = -20;
|
||||||
|
|
||||||
auto timer = en->add(Timer(anim->animation()->duration(), [](Timer* self)
|
auto timer = en->add(Timer(anim->animation()->duration(), [](Timer* self)
|
||||||
{
|
{
|
||||||
self->entity()->destroy();
|
self->entity()->destroy();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* Factory::spitter(World* world, Point position)
|
||||||
|
{
|
||||||
|
auto en = world->add_entity(position);
|
||||||
|
|
||||||
|
auto anim = en->add(Animator("spitter"));
|
||||||
|
anim->play("idle");
|
||||||
|
anim->depth = -5;
|
||||||
|
|
||||||
|
auto hitbox = en->add(Collider::make_rect(RectI(-6, -12, 12, 12)));
|
||||||
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
|
auto hurtable = en->add(Hurtable());
|
||||||
|
hurtable->hurt_by = Mask::player_attack;
|
||||||
|
hurtable->collider = hitbox;
|
||||||
|
hurtable->on_hurt = [](Hurtable* self) mutable
|
||||||
|
{
|
||||||
|
Time::pause_for(0.1f);
|
||||||
|
pop(self->world(), self->entity()->position + Point(0, -4));
|
||||||
|
self->entity()->destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto timer = en->add(Timer(1.0f, [](Timer* self)
|
||||||
|
{
|
||||||
|
bullet(self->world(), self->entity()->position + Point(-8, -8), -1);
|
||||||
|
self->get<Animator>()->play("shoot");
|
||||||
|
self->entity()->add(Timer(0.4f, [](Timer* self) { self->get<Animator>()->play("idle"); }));
|
||||||
|
self->start(3.0f);
|
||||||
|
}));
|
||||||
|
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* Factory::bullet(World* world, Point position, int direction)
|
||||||
|
{
|
||||||
|
auto en = world->add_entity(position);
|
||||||
|
|
||||||
|
auto anim = en->add(Animator("bullet"));
|
||||||
|
anim->play("idle");
|
||||||
|
anim->depth = -5;
|
||||||
|
|
||||||
|
auto hitbox = en->add(Collider::make_rect(RectI(-4, -4, 8, 8)));
|
||||||
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
|
auto mover = en->add(Mover());
|
||||||
|
mover->collider = hitbox;
|
||||||
|
mover->speed = Vec2(direction * 40, 0);
|
||||||
|
mover->gravity = 130;
|
||||||
|
mover->on_hit_x = [](Mover* self) { self->entity()->destroy(); };
|
||||||
|
mover->on_hit_y = [](Mover* self) { self->speed.y = -60; };
|
||||||
|
|
||||||
|
auto hurtable = en->add(Hurtable());
|
||||||
|
hurtable->hurt_by = Mask::player_attack;
|
||||||
|
hurtable->collider = hitbox;
|
||||||
|
hurtable->on_hurt = [](Hurtable* self) mutable
|
||||||
|
{
|
||||||
|
Time::pause_for(0.1f);
|
||||||
|
pop(self->world(), self->entity()->position + Point(0, -4));
|
||||||
|
self->entity()->destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
en->add(Timer(2.5f, [](Timer* self)
|
||||||
|
{
|
||||||
|
self->get<Hurtable>()->flicker_timer = 100;
|
||||||
|
}));
|
||||||
|
|
||||||
|
en->add(Timer(3.0f, [](Timer* self)
|
||||||
|
{
|
||||||
|
self->entity()->destroy();
|
||||||
|
}));
|
||||||
|
|
||||||
|
return en;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class MosquitoBehavior : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int health = 2;
|
||||||
|
float timer = 0;
|
||||||
|
|
||||||
|
void update() override
|
||||||
|
{
|
||||||
|
auto mover = get<Mover>();
|
||||||
|
auto player = world()->first<Player>();
|
||||||
|
if (player)
|
||||||
|
{
|
||||||
|
auto diff = player->entity()->position.x - entity()->position.x;
|
||||||
|
auto dist = Calc::abs(diff);
|
||||||
|
|
||||||
|
if (dist < 100)
|
||||||
|
mover->speed.x += Calc::sign(diff) * 100 * Time::delta;
|
||||||
|
else
|
||||||
|
mover->speed.x = Calc::approach(mover->speed.x, 0, 100 * Time::delta);
|
||||||
|
|
||||||
|
if (Calc::abs(mover->speed.x) > 50)
|
||||||
|
mover->speed.x = Calc::approach(mover->speed.x, Calc::sign(mover->speed.x) * 50, 800 * Time::delta);
|
||||||
|
|
||||||
|
mover->speed.y = Calc::sin(timer) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer += Time::delta * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hurt()
|
||||||
|
{
|
||||||
|
health--;
|
||||||
|
if (health <= 0)
|
||||||
|
{
|
||||||
|
Factory::pop(world(), entity()->position);
|
||||||
|
entity()->destroy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto mover = get<Mover>();
|
||||||
|
auto player = world()->first<Player>();
|
||||||
|
auto sign = Calc::sign(player->entity()->position.x - entity()->position.x);
|
||||||
|
mover->speed.x = -sign * 140;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* Factory::mosquito(World* world, Point position)
|
||||||
|
{
|
||||||
|
auto en = world->add_entity(position);
|
||||||
|
auto mosquito = en->add(MosquitoBehavior());
|
||||||
|
|
||||||
|
auto anim = en->add(Animator("mosquito"));
|
||||||
|
anim->play("fly");
|
||||||
|
anim->depth = -5;
|
||||||
|
|
||||||
|
auto hitbox = en->add(Collider::make_rect(RectI(-4, -4, 8, 8)));
|
||||||
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
|
auto mover = en->add(Mover());
|
||||||
|
|
||||||
|
auto hurtable = en->add(Hurtable());
|
||||||
|
hurtable->hurt_by = Mask::player_attack;
|
||||||
|
hurtable->collider = hitbox;
|
||||||
|
hurtable->on_hurt = [](Hurtable* self) { self->get<MosquitoBehavior>()->hurt(); };
|
||||||
|
|
||||||
return en;
|
return en;
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,8 @@ namespace TL
|
|||||||
Entity* player(World* world, Point position);
|
Entity* player(World* world, Point position);
|
||||||
Entity* bramble(World* world, Point position);
|
Entity* bramble(World* world, Point position);
|
||||||
Entity* pop(World* world, Point position);
|
Entity* pop(World* world, Point position);
|
||||||
|
Entity* spitter(World* world, Point position);
|
||||||
|
Entity* bullet(World* world, Point position, int direction);
|
||||||
|
Entity* mosquito(World* world, Point position);
|
||||||
}
|
}
|
||||||
}
|
}
|
36
src/game.cpp
@ -24,12 +24,11 @@ void Game::startup()
|
|||||||
|
|
||||||
// set batcher to use Nearest Filter
|
// set batcher to use Nearest Filter
|
||||||
batch.default_sampler = TextureSampler(TextureFilter::Nearest);
|
batch.default_sampler = TextureSampler(TextureFilter::Nearest);
|
||||||
|
|
||||||
m_draw_colliders = false;
|
m_draw_colliders = false;
|
||||||
|
|
||||||
// camera setup
|
// load first room
|
||||||
camera = Vec2::zero;
|
|
||||||
load_room(Point(0, 0));
|
load_room(Point(0, 0));
|
||||||
|
camera = Vec2(room.x * width, room.y * height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::load_room(Point cell)
|
void Game::load_room(Point cell)
|
||||||
@ -45,6 +44,7 @@ void Game::load_room(Point cell)
|
|||||||
auto castle = Content::find_tileset("castle");
|
auto castle = Content::find_tileset("castle");
|
||||||
auto grass = Content::find_tileset("grass");
|
auto grass = Content::find_tileset("grass");
|
||||||
auto plants = Content::find_tileset("plants");
|
auto plants = Content::find_tileset("plants");
|
||||||
|
auto backs = Content::find_tileset("back");
|
||||||
|
|
||||||
// make the floor
|
// make the floor
|
||||||
auto floor = world.add_entity(offset);
|
auto floor = world.add_entity(offset);
|
||||||
@ -56,7 +56,7 @@ void Game::load_room(Point cell)
|
|||||||
for (int x = 0; x < columns; x ++)
|
for (int x = 0; x < columns; x ++)
|
||||||
for (int y = 0; y < rows; y++)
|
for (int y = 0; y < rows; y++)
|
||||||
{
|
{
|
||||||
Point world_position = offset + Point(x * tile_width, y * tile_height);
|
Point world_position = offset + Point(x * tile_width, y * tile_height) + Point(tile_width / 2, tile_height);
|
||||||
Color col = grid->pixels[x + y * columns];
|
Color col = grid->pixels[x + y * columns];
|
||||||
uint32_t rgb =
|
uint32_t rgb =
|
||||||
((uint32_t)col.r << 16) |
|
((uint32_t)col.r << 16) |
|
||||||
@ -86,15 +86,30 @@ void Game::load_room(Point cell)
|
|||||||
tilemap->set_cell(x, y, &plants->random_tile());
|
tilemap->set_cell(x, y, &plants->random_tile());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// back tiles
|
||||||
|
case 0x45283c:
|
||||||
|
tilemap->set_cell(x, y, &backs->random_tile());
|
||||||
|
break;
|
||||||
|
|
||||||
// player (only if it doesn't already exist)
|
// player (only if it doesn't already exist)
|
||||||
case 0x6abe30:
|
case 0x6abe30:
|
||||||
if (!world.first<Player>())
|
if (!world.first<Player>())
|
||||||
Factory::player(&world, world_position + Point(tile_width / 2, tile_height));
|
Factory::player(&world, world_position);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// brambles
|
// brambles
|
||||||
case 0xd77bba:
|
case 0xd77bba:
|
||||||
Factory::bramble(&world, world_position + Point(tile_width / 2, tile_height));
|
Factory::bramble(&world, world_position);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// spitter plant
|
||||||
|
case 0xac3232:
|
||||||
|
Factory::spitter(&world, world_position);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// mosquito
|
||||||
|
case 0xfbf236:
|
||||||
|
Factory::mosquito(&world, world_position + Point(0, -8));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +179,14 @@ void Game::update()
|
|||||||
{
|
{
|
||||||
player->entity()->position = Point(
|
player->entity()->position = Point(
|
||||||
Calc::clamp_int(pos.x, bounds.x, bounds.x + bounds.w),
|
Calc::clamp_int(pos.x, bounds.x, bounds.x + bounds.w),
|
||||||
Calc::clamp_int(pos.y, bounds.y, bounds.y + bounds.h));
|
Calc::clamp_int(pos.y, bounds.y, bounds.y + bounds.h + 100));
|
||||||
|
|
||||||
|
// reload if they fell out the bottom
|
||||||
|
if (player->entity()->position.y > bounds.y + bounds.h + 64)
|
||||||
|
{
|
||||||
|
world.clear();
|
||||||
|
load_room(room);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|