everything except boss
BIN
content/map/11x0.png
Normal file
After Width: | Height: | Size: 207 B |
BIN
content/map/12x0.png
Normal file
After Width: | Height: | Size: 243 B |
BIN
content/map/13x0.png
Normal file
After Width: | Height: | Size: 194 B |
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 368 B |
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 252 B |
Before Width: | Height: | Size: 195 B After Width: | Height: | Size: 214 B |
BIN
content/tilesets/jumpthru.ase
Normal file
|
@ -17,7 +17,7 @@ namespace TL
|
||||||
Grid
|
Grid
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t mask;
|
uint32_t mask = 0;
|
||||||
|
|
||||||
Collider();
|
Collider();
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,16 @@ bool Mover::move_y(int amount)
|
||||||
|
|
||||||
while (amount != 0)
|
while (amount != 0)
|
||||||
{
|
{
|
||||||
if (collider->check(Mask::solid, Point(0, sign)))
|
// if hit solid
|
||||||
|
bool hit_something = collider->check(Mask::solid, Point(0, sign));
|
||||||
|
|
||||||
|
// no solid, but we're moving down, check for jumpthru
|
||||||
|
// but ONLY if we're not already overlapping a jumpthru
|
||||||
|
if (!hit_something && sign > 0)
|
||||||
|
hit_something = collider->check(Mask::jumpthru, Point(0, sign)) && !collider->check(Mask::jumpthru, Point(0, 0));
|
||||||
|
|
||||||
|
// stop movement
|
||||||
|
if (hit_something)
|
||||||
{
|
{
|
||||||
if (on_hit_y)
|
if (on_hit_y)
|
||||||
on_hit_y(this);
|
on_hit_y(this);
|
||||||
|
@ -82,7 +91,11 @@ bool Mover::on_ground(int dist) const
|
||||||
if (!collider)
|
if (!collider)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return collider->check(Mask::solid, Point(0, dist));
|
return
|
||||||
|
// solid
|
||||||
|
collider->check(Mask::solid, Point(0, dist)) ||
|
||||||
|
// jumpthru
|
||||||
|
(collider->check(Mask::jumpthru, Point(0, dist)) && !collider->check(Mask::jumpthru, Point(0, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mover::update()
|
void Mover::update()
|
||||||
|
|
|
@ -34,6 +34,6 @@ namespace TL
|
||||||
float m_invincible_timer = 0;
|
float m_invincible_timer = 0;
|
||||||
float m_start_timer = 1;
|
float m_start_timer = 1;
|
||||||
Collider* m_attack_collider = nullptr;
|
Collider* m_attack_collider = nullptr;
|
||||||
bool m_on_ground;
|
bool m_on_ground = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
|
@ -57,6 +57,7 @@ void Content::load()
|
||||||
|
|
||||||
// load the main font
|
// load the main font
|
||||||
font = SpriteFont(path() + "fonts/dogica.ttf", 8, SpriteFont::ASCII);
|
font = SpriteFont(path() + "fonts/dogica.ttf", 8, SpriteFont::ASCII);
|
||||||
|
font.line_gap = 4;
|
||||||
|
|
||||||
// load sprites
|
// load sprites
|
||||||
Vector<SpriteInfo> sprite_info;
|
Vector<SpriteInfo> sprite_info;
|
||||||
|
|
|
@ -213,26 +213,51 @@ Entity* Factory::mosquito(World* world, Point position)
|
||||||
return en;
|
return en;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity* Factory::door(World* world, Point position)
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void make_door_contents(Entity* en)
|
||||||
|
{
|
||||||
|
auto anim = en->add(Animator("door"));
|
||||||
|
anim->play("idle");
|
||||||
|
anim->depth = -1;
|
||||||
|
|
||||||
|
auto hitbox = en->add(Collider::make_rect(RectI(-6, -16, 12, 16)));
|
||||||
|
hitbox->mask = Mask::solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity* Factory::door(World* world, Point position, bool wait_for_player)
|
||||||
{
|
{
|
||||||
auto en = world->add_entity(position);
|
auto en = world->add_entity(position);
|
||||||
|
|
||||||
auto anim = en->add(Animator("door"));
|
if (!wait_for_player)
|
||||||
anim->play("idle");
|
make_door_contents(en);
|
||||||
anim->depth = -1;
|
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-6, -16, 12, 16)));
|
|
||||||
hitbox->mask = Mask::solid;
|
|
||||||
|
|
||||||
// check if all enemies are dead
|
// check if all enemies are dead
|
||||||
en->add(Timer(0.25f, [](Timer* self)
|
en->add(Timer(0.25f, [waiting = wait_for_player](Timer* self) mutable
|
||||||
{
|
{
|
||||||
self->start(0.25f);
|
self->start(0.25f);
|
||||||
|
|
||||||
|
if (waiting)
|
||||||
|
{
|
||||||
|
auto player = self->world()->first<Player>();
|
||||||
|
if (player->entity()->position.x > self->entity()->position.x + 12)
|
||||||
|
{
|
||||||
|
make_door_contents(self->entity());
|
||||||
|
Factory::pop(self->world(), self->entity()->position + Point(0, -8));
|
||||||
|
waiting = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!self->world()->first<Enemy>())
|
if (!self->world()->first<Enemy>())
|
||||||
{
|
{
|
||||||
Factory::pop(self->world(), self->entity()->position + Point(0, -8));
|
Factory::pop(self->world(), self->entity()->position + Point(0, -8));
|
||||||
self->entity()->destroy();
|
self->entity()->destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return en;
|
return en;
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace TL
|
||||||
Entity* spitter(World* world, Point position);
|
Entity* spitter(World* world, Point position);
|
||||||
Entity* bullet(World* world, Point position, int direction);
|
Entity* bullet(World* world, Point position, int direction);
|
||||||
Entity* mosquito(World* world, Point position);
|
Entity* mosquito(World* world, Point position);
|
||||||
Entity* door(World* world, Point position);
|
Entity* door(World* world, Point position, bool wait_for_player = false);
|
||||||
Entity* blob(World* world, Point position);
|
Entity* blob(World* world, Point position);
|
||||||
}
|
}
|
||||||
}
|
}
|
43
src/game.cpp
|
@ -29,7 +29,7 @@ void Game::startup()
|
||||||
m_draw_colliders = false;
|
m_draw_colliders = false;
|
||||||
|
|
||||||
// load first room
|
// load first room
|
||||||
load_room(Point(10, 0));
|
load_room(Point(11, 0));
|
||||||
camera = Vec2(room.x * width, room.y * height);
|
camera = Vec2(room.x * width, room.y * height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ void Game::load_room(Point cell, bool is_reload)
|
||||||
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");
|
auto backs = Content::find_tileset("back");
|
||||||
|
auto jumpthrus = Content::find_tileset("jumpthru");
|
||||||
|
|
||||||
// make the floor
|
// make the floor
|
||||||
auto floor = world.add_entity(offset);
|
auto floor = world.add_entity(offset);
|
||||||
|
@ -93,6 +94,16 @@ void Game::load_room(Point cell, bool is_reload)
|
||||||
tilemap->set_cell(x, y, &backs->random_tile());
|
tilemap->set_cell(x, y, &backs->random_tile());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// jumpthru tiles
|
||||||
|
case 0xdf7126:
|
||||||
|
{
|
||||||
|
tilemap->set_cell(x, y, &jumpthrus->random_tile());
|
||||||
|
auto jumpthru_en = world.add_entity(offset + Point(x * tile_width, y * tile_height));
|
||||||
|
auto jumpthru_col = jumpthru_en->add(Collider::make_rect(RectI(0, 0, 8, 4)));
|
||||||
|
jumpthru_col->mask = Mask::jumpthru;
|
||||||
|
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>())
|
||||||
|
@ -119,6 +130,11 @@ void Game::load_room(Point cell, bool is_reload)
|
||||||
Factory::door(&world, world_position);
|
Factory::door(&world, world_position);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// closing door
|
||||||
|
case 0x847e87:
|
||||||
|
Factory::door(&world, world_position, true);
|
||||||
|
break;
|
||||||
|
|
||||||
// blob
|
// blob
|
||||||
case 0x3f3f74:
|
case 0x3f3f74:
|
||||||
Factory::blob(&world, world_position);
|
Factory::blob(&world, world_position);
|
||||||
|
@ -164,7 +180,7 @@ void Game::update()
|
||||||
if (pos.y < 0) next_room.y--;
|
if (pos.y < 0) next_room.y--;
|
||||||
|
|
||||||
// see if room exists
|
// see if room exists
|
||||||
if (player->health > 0 && Content::find_room(next_room))
|
if (player->health > 0 && Content::find_room(next_room) && next_room.x >= room.x)
|
||||||
{
|
{
|
||||||
Time::pause_for(0.1f);
|
Time::pause_for(0.1f);
|
||||||
|
|
||||||
|
@ -238,7 +254,7 @@ void Game::update()
|
||||||
{
|
{
|
||||||
auto player = world.first<Player>();
|
auto player = world.first<Player>();
|
||||||
if (player)
|
if (player)
|
||||||
player->get<Mover>()->speed = Vec2(100, -200);
|
player->get<Mover>()->speed = Vec2(0, -150);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete old objects (except player!)
|
// delete old objects (except player!)
|
||||||
|
@ -277,6 +293,27 @@ void Game::render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hacky start / end screen text
|
||||||
|
if (room == Point(0, 0) || m_last_room == Point(0, 0))
|
||||||
|
{
|
||||||
|
auto w = Content::font.width_of(title);
|
||||||
|
auto pos = Point((width - w) / 2, 20);
|
||||||
|
batch.str(Content::font, title, pos + Point(0, 1), Color::black);
|
||||||
|
batch.str(Content::font, title, pos, Color::white);
|
||||||
|
|
||||||
|
w = Content::font.width_of(controls);
|
||||||
|
pos.x = (width - w) / 2;
|
||||||
|
pos.y += 20;
|
||||||
|
batch.str(Content::font, controls, pos, Color::white * 0.25f);
|
||||||
|
}
|
||||||
|
else if (room == Point(13, 0))
|
||||||
|
{
|
||||||
|
auto w = Content::font.width_of(ending);
|
||||||
|
auto pos = Point(room.x * width + width / 2, room.y * height + 20);
|
||||||
|
batch.str(Content::font, ending, pos + Point(0, 1), TextAlign::Top, 8, Color::black);
|
||||||
|
batch.str(Content::font, ending, pos, TextAlign::Top, 8, Color::white);
|
||||||
|
}
|
||||||
|
|
||||||
// end camera offset
|
// end camera offset
|
||||||
batch.pop_matrix();
|
batch.pop_matrix();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ namespace TL
|
||||||
static constexpr int columns = width / tile_width;
|
static constexpr int columns = width / tile_width;
|
||||||
static constexpr int rows = height / tile_height + 1;
|
static constexpr int rows = height / tile_height + 1;
|
||||||
|
|
||||||
|
static inline const char* title = "SWORD II: ADVENTURE OF FROG";
|
||||||
|
static inline const char* controls = "arrow keys + X / C\nstick + A / X";
|
||||||
|
static inline const char* ending = "YOU SAVED POND\nAND YOU ARE\nA REAL HERO";
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
FrameBufferRef buffer;
|
FrameBufferRef buffer;
|
||||||
Batch batch;
|
Batch batch;
|
||||||
|
|
|
@ -6,7 +6,8 @@ namespace TL
|
||||||
struct Mask
|
struct Mask
|
||||||
{
|
{
|
||||||
static constexpr uint32_t solid = 1 << 0;
|
static constexpr uint32_t solid = 1 << 0;
|
||||||
static constexpr uint32_t player_attack = 1 << 1;
|
static constexpr uint32_t jumpthru = 1 << 1;
|
||||||
static constexpr uint32_t enemy = 1 << 2;
|
static constexpr uint32_t player_attack = 1 << 2;
|
||||||
|
static constexpr uint32_t enemy = 1 << 3;
|
||||||
};
|
};
|
||||||
}
|
}
|