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 "animator.h"
|
||||
|
||||
using namespace TL;
|
||||
|
||||
void Hurtable::update()
|
||||
{
|
||||
if (collider && on_hurt && m_cooldown_timer <= 0)
|
||||
if (collider && on_hurt && stun_timer <= 0)
|
||||
{
|
||||
if (collider->check(hurt_by))
|
||||
{
|
||||
m_cooldown_timer = cooldown;
|
||||
stun_timer = 0.5f;
|
||||
flicker_timer = 0.5f;
|
||||
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
|
||||
{
|
||||
private:
|
||||
float m_cooldown_timer = 0;
|
||||
|
||||
public:
|
||||
float stun_timer = 0;
|
||||
float flicker_timer = 0;
|
||||
Collider* collider = nullptr;
|
||||
uint32_t hurt_by = 0;
|
||||
float cooldown = 1.0f;
|
||||
std::function<void(Hurtable* self)> on_hurt;
|
||||
|
||||
void update() override;
|
||||
|
|
|
@ -13,6 +13,9 @@ bool Mover::move_x(int amount)
|
|||
{
|
||||
if (collider->check(Mask::solid, Point(sign, 0)))
|
||||
{
|
||||
if (on_hit_x)
|
||||
on_hit_x(this);
|
||||
else
|
||||
stop_x();
|
||||
return true;
|
||||
}
|
||||
|
@ -37,6 +40,9 @@ bool Mover::move_y(int amount)
|
|||
{
|
||||
if (collider->check(Mask::solid, Point(0, sign)))
|
||||
{
|
||||
if (on_hit_y)
|
||||
on_hit_y(this);
|
||||
else
|
||||
stop_y();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "../world.h"
|
||||
#include "collider.h"
|
||||
#include <blah.h>
|
||||
#include <functional>
|
||||
|
||||
using namespace Blah;
|
||||
|
||||
|
@ -16,6 +17,8 @@ namespace TL
|
|||
Collider* collider = nullptr;
|
||||
Vec2 speed;
|
||||
float gravity = 0;
|
||||
std::function<void(Mover*)> on_hit_x;
|
||||
std::function<void(Mover*)> on_hit_y;
|
||||
|
||||
bool move_x(int amount);
|
||||
bool move_y(int amount);
|
||||
|
|
|
@ -65,8 +65,16 @@ void Player::update()
|
|||
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
|
||||
if (m_state == st_normal)
|
||||
else if (m_state == st_normal)
|
||||
{
|
||||
// Current Animation
|
||||
if (m_on_ground)
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace TL
|
|||
static constexpr int st_normal = 0;
|
||||
static constexpr int st_attack = 1;
|
||||
static constexpr int st_hurt = 2;
|
||||
static constexpr int st_start = 3;
|
||||
|
||||
int health = 3;
|
||||
|
||||
|
@ -24,12 +25,13 @@ namespace TL
|
|||
void update() override;
|
||||
|
||||
private:
|
||||
int m_state = st_normal;
|
||||
int m_state = st_start;
|
||||
int m_facing = 1;
|
||||
float m_jump_timer = 0;
|
||||
float m_attack_timer = 0;
|
||||
float m_hurt_timer = 0;
|
||||
float m_invincible_timer = 0;
|
||||
float m_start_timer = 1;
|
||||
Collider* m_attack_collider = nullptr;
|
||||
bool m_on_ground;
|
||||
};
|
||||
|
|
145
src/factory.cpp
|
@ -66,3 +66,148 @@ Entity* Factory::pop(World* world, Point position)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,5 +11,8 @@ namespace TL
|
|||
Entity* player(World* world, Point position);
|
||||
Entity* bramble(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
|
||||
batch.default_sampler = TextureSampler(TextureFilter::Nearest);
|
||||
|
||||
m_draw_colliders = false;
|
||||
|
||||
// camera setup
|
||||
camera = Vec2::zero;
|
||||
// load first room
|
||||
load_room(Point(0, 0));
|
||||
camera = Vec2(room.x * width, room.y * height);
|
||||
}
|
||||
|
||||
void Game::load_room(Point cell)
|
||||
|
@ -45,6 +44,7 @@ void Game::load_room(Point cell)
|
|||
auto castle = Content::find_tileset("castle");
|
||||
auto grass = Content::find_tileset("grass");
|
||||
auto plants = Content::find_tileset("plants");
|
||||
auto backs = Content::find_tileset("back");
|
||||
|
||||
// make the floor
|
||||
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 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];
|
||||
uint32_t rgb =
|
||||
((uint32_t)col.r << 16) |
|
||||
|
@ -86,15 +86,30 @@ void Game::load_room(Point cell)
|
|||
tilemap->set_cell(x, y, &plants->random_tile());
|
||||
break;
|
||||
|
||||
// back tiles
|
||||
case 0x45283c:
|
||||
tilemap->set_cell(x, y, &backs->random_tile());
|
||||
break;
|
||||
|
||||
// player (only if it doesn't already exist)
|
||||
case 0x6abe30:
|
||||
if (!world.first<Player>())
|
||||
Factory::player(&world, world_position + Point(tile_width / 2, tile_height));
|
||||
Factory::player(&world, world_position);
|
||||
break;
|
||||
|
||||
// brambles
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +179,14 @@ void Game::update()
|
|||
{
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|