mirror of
https://github.com/NoelFB/tiny_link.git
synced 2025-04-08 01:36:06 +08:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f62a60cb3e | ||
|
f4c1bba61d | ||
|
71121df896 | ||
|
98c59cfdfd | ||
|
6405f5c897 | ||
|
37a5afc52c | ||
|
4740c54465 | ||
|
26bc63236d | ||
|
3d94a9dad0 | ||
|
48c6326cc7 | ||
|
ce72329084 | ||
|
8936a5bbb7 | ||
|
14123e5b5e | ||
|
cc5f137fa6 | ||
|
14c1d625a1 | ||
|
7173d65263 | ||
|
68bb409f98 | ||
|
7ffa649160 | ||
|
f8254013c3 | ||
|
04f2b68139 | ||
|
c1859f6946 | ||
|
54fd84f450 | ||
|
39c0c516c8 | ||
|
1ec75743c5 | ||
|
3f3ffbf52c | ||
|
e2268a3cdf | ||
|
5623d0948a | ||
|
ee21d2dfe4 | ||
|
e0fef8cc62 | ||
|
e1bb593219 | ||
|
1874ff2a9b | ||
|
1519bf2f87 | ||
|
98ac005985 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.vs
|
.vs
|
||||||
.vscode
|
.vscode
|
||||||
out
|
out
|
||||||
CMakeSettings.json
|
CMakeSettings.json
|
||||||
|
build/*
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.6)
|
cmake_minimum_required(VERSION 3.12)
|
||||||
project(TinyLink)
|
project(TinyLink)
|
||||||
|
|
||||||
# C++ version
|
# C++ version
|
||||||
@ -11,45 +11,27 @@ add_subdirectory(libs/blah)
|
|||||||
# add our source
|
# add our source
|
||||||
add_executable(game
|
add_executable(game
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/world.h
|
|
||||||
src/world.cpp
|
src/world.cpp
|
||||||
src/game.h
|
|
||||||
src/game.cpp
|
src/game.cpp
|
||||||
src/content.h
|
|
||||||
src/content.cpp
|
src/content.cpp
|
||||||
src/masks.h
|
|
||||||
src/factory.h
|
|
||||||
src/factory.cpp
|
src/factory.cpp
|
||||||
src/assets/sprite.h
|
|
||||||
src/assets/sprite.cpp
|
src/assets/sprite.cpp
|
||||||
src/assets/tileset.h
|
|
||||||
src/assets/tileset.cpp
|
src/assets/tileset.cpp
|
||||||
src/components/animator.h
|
|
||||||
src/components/animator.cpp
|
src/components/animator.cpp
|
||||||
src/components/collider.h
|
|
||||||
src/components/collider.cpp
|
src/components/collider.cpp
|
||||||
src/components/player.h
|
|
||||||
src/components/player.cpp
|
src/components/player.cpp
|
||||||
src/components/mover.h
|
|
||||||
src/components/mover.cpp
|
src/components/mover.cpp
|
||||||
src/components/tilemap.h
|
|
||||||
src/components/tilemap.cpp
|
src/components/tilemap.cpp
|
||||||
src/components/hurtable.h
|
|
||||||
src/components/hurtable.cpp
|
src/components/hurtable.cpp
|
||||||
src/components/timer.h
|
|
||||||
src/components/timer.cpp
|
src/components/timer.cpp
|
||||||
src/components/enemy.h
|
src/components/ghost_frog.cpp
|
||||||
"src/components/ghost_frog.h" "src/components/ghost_frog.cpp" "src/components/orb.h" "src/components/orb.cpp")
|
src/components/orb.cpp
|
||||||
|
)
|
||||||
|
|
||||||
# Reference blah
|
# Reference blah
|
||||||
target_link_libraries(game blah)
|
target_link_libraries(game blah)
|
||||||
|
|
||||||
# copy SDL2 to the build directory
|
if( ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
|
||||||
set(SDL2_DLL "" CACHE FILEPATH "SDL2 DLL Path")
|
set_target_properties(game PROPERTIES LINK_FLAGS "-s USE_SDL=2 -s USE_WEBGL2=1 --preload-file ${CMAKE_SOURCE_DIR}/content@/content")
|
||||||
if (SDL2_ENABLED)
|
set(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||||
if (EXISTS ${SDL2_DLL})
|
|
||||||
add_custom_command(
|
|
||||||
TARGET game POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${SDL2_DLL} $<TARGET_FILE_DIR:game>)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
23
README.md
23
README.md
@ -1 +1,22 @@
|
|||||||
# tiny_link
|
## SWORD II: ADVENTURE OF FROG
|
||||||
|
A small game made entirely on live stream over about 15 hours.
|
||||||
|
I intend to add more documentation and clarify some of the code and assets over the next few days.
|
||||||
|
|
||||||
|
### building
|
||||||
|
- You need C++17 and CMake 3.14+
|
||||||
|
- Pull Submodule (`git submodule init`, `git submodule update`) (this repo references the [blah](https://github.com/NoelFB/blah) repo)
|
||||||
|
- All the art was made in [Aseprite](https://www.aseprite.org/). To modify or add new sprites, you'll need to use aseprite.
|
||||||
|
|
||||||
|
### links
|
||||||
|
- [windows build](https://github.com/NoelFB/tiny_link/releases/tag/v1.0.0)
|
||||||
|
- stream archive: [Day 1](https://www.youtube.com/watch?v=Yp4WNWnoDus), [Day 2](https://www.youtube.com/watch?v=jgkljqG2rKA), [twitch](https://twitch.tv/noelfb)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### box art
|
||||||
|

|
||||||
|
|
||||||
|
*Box Art by [Grayson](https://twitter.com/soft_rumpus/status/1345934041527144459/photo/1)*
|
||||||
|
|
||||||
|
*Mosquito enemy sprite by [Randy](https://twitter.com/RandyPGaul)*
|
||||||
|
|
||||||
|
BIN
boxart.jpg
Normal file
BIN
boxart.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 156 KiB |
3
content/map/readme.md
Normal file
3
content/map/readme.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Due to making this game under heavy time constraints, I opted not to make or use a level editor. Instead, each object is assigned a color and I used a generic paint tool to design the levels.
|
||||||
|
|
||||||
|
Under normal circumstances, I would argue against this method and instead use something like Ogmo Editor or Tiled. It's really hard to remember what object is what color!
|
5
content/sprites/readme.md
Normal file
5
content/sprites/readme.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
The sprites are stored in the [Aseprite](https://www.aseprite.org/) format, and you'll need Aseprite to edit them.
|
||||||
|
|
||||||
|
A few other notes:
|
||||||
|
- Animations are loaded automatically using Aseprite's `Tag` feature. Each tag is a unique animation
|
||||||
|
- Sprite Origin points use Aseprite's hidden `Slice` feature, which can be used with Shift+C. You can draw slices and give them a pivot point, which we're using to set the origin point of the sprite.
|
1
content/tilesets/readme.md
Normal file
1
content/tilesets/readme.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
The tilesets are stored in the [Aseprite](https://www.aseprite.org/) format, and you'll need Aseprite to edit them.
|
@ -1 +1 @@
|
|||||||
Subproject commit cc5e222be5545d3fd02e61cf0a0afb50407c2578
|
Subproject commit b2bcf66a37eb2925cf55fb2cf73e7b2f8b33d785
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 151 KiB |
@ -28,7 +28,7 @@ namespace TL
|
|||||||
};
|
};
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
Vec2 origin;
|
Vec2f origin;
|
||||||
Vector<Animation> animations;
|
Vector<Animation> animations;
|
||||||
|
|
||||||
const Animation* get_animation(const String& name) const;
|
const Animation* get_animation(const String& name) const;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
#include "tileset.h"
|
#include "tileset.h"
|
||||||
|
#include "../game.h"
|
||||||
|
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
const Subtexture& Tileset::random_tile() const
|
const Subtexture& Tileset::random_tile() const
|
||||||
{
|
{
|
||||||
int i = Calc::rand_int(columns * rows);
|
int i = Game::rand_int(0, columns * rows);
|
||||||
return tiles[i];
|
return tiles[i];
|
||||||
}
|
}
|
||||||
|
@ -72,11 +72,11 @@ void Animator::render(Batch& batch)
|
|||||||
if (in_valid_state())
|
if (in_valid_state())
|
||||||
{
|
{
|
||||||
batch.push_matrix(
|
batch.push_matrix(
|
||||||
Mat3x2::create_transform(entity()->position + offset, m_sprite->origin, scale, 0));
|
Mat3x2f::create_transform(entity()->position + offset, m_sprite->origin, scale, 0));
|
||||||
|
|
||||||
auto& anim = m_sprite->animations[m_animation_index];
|
auto& anim = m_sprite->animations[m_animation_index];
|
||||||
auto& frame = anim.frames[m_frame_index];
|
auto& frame = anim.frames[m_frame_index];
|
||||||
batch.tex(frame.image, Vec2::zero, Color::white);
|
batch.tex(frame.image, Vec2f::zero, Color::white);
|
||||||
|
|
||||||
batch.pop_matrix();
|
batch.pop_matrix();
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace TL
|
|||||||
float m_frame_counter = 0;
|
float m_frame_counter = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Vec2 scale = Vec2::one;
|
Vec2f scale = Vec2f::one;
|
||||||
Point offset = Point::zero;
|
Point offset = Point::zero;
|
||||||
|
|
||||||
Animator() = default;
|
Animator() = default;
|
||||||
|
@ -8,7 +8,7 @@ Collider::Collider()
|
|||||||
active = false;
|
active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Collider Collider::make_rect(const RectI& rect)
|
Collider Collider::make_rect(const Recti& rect)
|
||||||
{
|
{
|
||||||
Collider collider;
|
Collider collider;
|
||||||
collider.m_shape = Shape::Rect;
|
collider.m_shape = Shape::Rect;
|
||||||
@ -23,10 +23,7 @@ Collider Collider::make_grid(int tile_size, int columns, int rows)
|
|||||||
collider.m_grid.tile_size = tile_size;
|
collider.m_grid.tile_size = tile_size;
|
||||||
collider.m_grid.columns = columns;
|
collider.m_grid.columns = columns;
|
||||||
collider.m_grid.rows = rows;
|
collider.m_grid.rows = rows;
|
||||||
collider.m_grid.cells = std::shared_ptr<bool[]>(new bool[columns * rows]);
|
collider.m_grid.cells.expand(columns * rows);
|
||||||
|
|
||||||
memset(collider.m_grid.cells.get(), 0, sizeof(bool) * columns * rows);
|
|
||||||
|
|
||||||
return collider;
|
return collider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,13 +32,13 @@ Collider::Shape Collider::shape() const
|
|||||||
return m_shape;
|
return m_shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
RectI Collider::get_rect() const
|
Recti Collider::get_rect() const
|
||||||
{
|
{
|
||||||
BLAH_ASSERT(m_shape == Shape::Rect, "Collider is not a Rectangle");
|
BLAH_ASSERT(m_shape == Shape::Rect, "Collider is not a Rectangle");
|
||||||
return m_rect;
|
return m_rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Collider::set_rect(const RectI& value)
|
void Collider::set_rect(const Recti& value)
|
||||||
{
|
{
|
||||||
BLAH_ASSERT(m_shape == Shape::Rect, "Collider is not a Rectangle");
|
BLAH_ASSERT(m_shape == Shape::Rect, "Collider is not a Rectangle");
|
||||||
m_rect = value;
|
m_rect = value;
|
||||||
@ -145,7 +142,7 @@ void Collider::render(Batch& batch)
|
|||||||
{
|
{
|
||||||
static const Color color = Color::red;
|
static const Color color = Color::red;
|
||||||
|
|
||||||
batch.push_matrix(Mat3x2::create_translation(entity()->position));
|
batch.push_matrix(Mat3x2f::create_translation(entity()->position));
|
||||||
|
|
||||||
if (m_shape == Shape::Rect)
|
if (m_shape == Shape::Rect)
|
||||||
{
|
{
|
||||||
@ -172,8 +169,8 @@ void Collider::render(Batch& batch)
|
|||||||
|
|
||||||
bool TL::Collider::rect_to_rect(const Collider* a, const Collider* b, Point offset)
|
bool TL::Collider::rect_to_rect(const Collider* a, const Collider* b, Point offset)
|
||||||
{
|
{
|
||||||
RectI ar = a->m_rect + a->entity()->position + offset;
|
Recti ar = a->m_rect + a->entity()->position + offset;
|
||||||
RectI br = b->m_rect + b->entity()->position;
|
Recti br = b->m_rect + b->entity()->position;
|
||||||
|
|
||||||
return ar.overlaps(br);
|
return ar.overlaps(br);
|
||||||
}
|
}
|
||||||
@ -181,13 +178,13 @@ bool TL::Collider::rect_to_rect(const Collider* a, const Collider* b, Point offs
|
|||||||
bool TL::Collider::rect_to_grid(const Collider* a, const Collider* b, Point offset)
|
bool TL::Collider::rect_to_grid(const Collider* a, const Collider* b, Point offset)
|
||||||
{
|
{
|
||||||
// get a relative rectangle to the grid
|
// get a relative rectangle to the grid
|
||||||
RectI rect = a->m_rect + a->entity()->position + offset - b->entity()->position;
|
Recti rect = a->m_rect + a->entity()->position + offset - b->entity()->position;
|
||||||
|
|
||||||
// get the cells the rectangle overlaps
|
// get the cells the rectangle overlaps
|
||||||
int left = Calc::clamp_int(Calc::floor(rect.x / (float)b->m_grid.tile_size), 0, b->m_grid.columns);
|
int left = Calc::clamp((int)Calc::floor(rect.x / (float)b->m_grid.tile_size), 0, b->m_grid.columns);
|
||||||
int right = Calc::clamp_int(Calc::ceiling(rect.right() / (float)b->m_grid.tile_size), 0, b->m_grid.columns);
|
int right = Calc::clamp((int)Calc::ceiling(rect.right() / (float)b->m_grid.tile_size), 0, b->m_grid.columns);
|
||||||
int top = Calc::clamp_int(Calc::floor(rect.y / (float)b->m_grid.tile_size), 0, b->m_grid.rows);
|
int top = Calc::clamp((int)Calc::floor(rect.y / (float)b->m_grid.tile_size), 0, b->m_grid.rows);
|
||||||
int bottom = Calc::clamp_int(Calc::ceiling(rect.bottom() / (float)b->m_grid.tile_size), 0, b->m_grid.rows);
|
int bottom = Calc::clamp((int)Calc::ceiling(rect.bottom() / (float)b->m_grid.tile_size), 0, b->m_grid.rows);
|
||||||
|
|
||||||
// check each cell
|
// check each cell
|
||||||
for (int x = left; x < right; x++)
|
for (int x = left; x < right; x++)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <blah.h>
|
#include <blah.h>
|
||||||
#include <memory>
|
|
||||||
#include "../world.h"
|
#include "../world.h"
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
@ -21,12 +20,12 @@ namespace TL
|
|||||||
|
|
||||||
Collider();
|
Collider();
|
||||||
|
|
||||||
static Collider make_rect(const RectI& rect);
|
static Collider make_rect(const Recti& rect);
|
||||||
static Collider make_grid(int tile_size, int columns, int rows);
|
static Collider make_grid(int tile_size, int columns, int rows);
|
||||||
|
|
||||||
Shape shape() const;
|
Shape shape() const;
|
||||||
RectI get_rect() const;
|
Recti get_rect() const;
|
||||||
void set_rect(const RectI& value);
|
void set_rect(const Recti& value);
|
||||||
bool get_cell(int x, int y) const;
|
bool get_cell(int x, int y) const;
|
||||||
void set_cell(int x, int y, bool value);
|
void set_cell(int x, int y, bool value);
|
||||||
void set_cells(int x, int y, int w, int h, bool value);
|
void set_cells(int x, int y, int w, int h, bool value);
|
||||||
@ -44,11 +43,11 @@ namespace TL
|
|||||||
int columns;
|
int columns;
|
||||||
int rows;
|
int rows;
|
||||||
int tile_size;
|
int tile_size;
|
||||||
std::shared_ptr<bool[]> cells;
|
Vector<bool> cells;
|
||||||
};
|
};
|
||||||
|
|
||||||
Shape m_shape = Shape::None;
|
Shape m_shape = Shape::None;
|
||||||
RectI m_rect;
|
Recti m_rect;
|
||||||
Grid m_grid;
|
Grid m_grid;
|
||||||
|
|
||||||
static bool rect_to_rect(const Collider* a, const Collider* b, Point offset);
|
static bool rect_to_rect(const Collider* a, const Collider* b, Point offset);
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
{
|
{
|
||||||
class Enemy : public Component
|
// I just needed a way to track whether any enemies were still
|
||||||
{
|
// in the scene or not, so Locked Doors could open. The easiest
|
||||||
|
// way was to add an Enemy component, and check if there are any
|
||||||
};
|
// in the scene.
|
||||||
|
class Enemy : public Component { };
|
||||||
}
|
}
|
@ -44,7 +44,7 @@ void GhostFrog::update()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// flip sprite
|
// flip sprite
|
||||||
anim->scale = Vec2(m_facing, 1);
|
anim->scale = Vec2f(m_facing, 1);
|
||||||
|
|
||||||
// NORMAL STATE
|
// NORMAL STATE
|
||||||
if (m_state == st_readying_attack)
|
if (m_state == st_readying_attack)
|
||||||
@ -76,9 +76,9 @@ void GhostFrog::update()
|
|||||||
if (Time::on_time(m_timer, 0.8f))
|
if (Time::on_time(m_timer, 0.8f))
|
||||||
{
|
{
|
||||||
mover->speed.x = m_facing * 250;
|
mover->speed.x = m_facing * 250;
|
||||||
hitbox->set_rect(RectI(-4 + m_facing * 4, -12, 8, 12));
|
hitbox->set_rect(Recti(-4 + m_facing * 4, -12, 8, 12));
|
||||||
|
|
||||||
RectI rect(8, -8, 20, 8);
|
Recti rect(8, -8, 20, 8);
|
||||||
if (m_facing < 0)
|
if (m_facing < 0)
|
||||||
rect.x = -(rect.x + rect.w);
|
rect.x = -(rect.x + rect.w);
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ void GhostFrog::update()
|
|||||||
// end attack state
|
// end attack state
|
||||||
else if (m_timer >= anim->animation()->duration())
|
else if (m_timer >= anim->animation()->duration())
|
||||||
{
|
{
|
||||||
hitbox->set_rect(RectI(-4, -12, 8, 12));
|
hitbox->set_rect(Recti(-4, -12, 8, 12));
|
||||||
|
|
||||||
if (health > 0)
|
if (health > 0)
|
||||||
{
|
{
|
||||||
@ -107,7 +107,7 @@ void GhostFrog::update()
|
|||||||
{
|
{
|
||||||
phase = 1;
|
phase = 1;
|
||||||
health = max_health_2;
|
health = max_health_2;
|
||||||
m_side = Calc::rand_int(0, 2) == 0 ? -1 : 1;
|
m_side = Game::rand_int(0, 2) == 0 ? -1 : 1;
|
||||||
set_state(st_floating);
|
set_state(st_floating);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ void GhostFrog::update()
|
|||||||
// SHOOTING STATE
|
// SHOOTING STATE
|
||||||
else if (m_state == st_shoot)
|
else if (m_state == st_shoot)
|
||||||
{
|
{
|
||||||
mover->speed = Calc::approach(mover->speed, Vec2::zero, 300 * Time::delta);
|
mover->speed = Vec2f::approach(mover->speed, Vec2f::zero, 300 * Time::delta);
|
||||||
|
|
||||||
m_facing = Calc::sign(player_x - x);
|
m_facing = Calc::sign(player_x - x);
|
||||||
if (m_facing == 0)
|
if (m_facing == 0)
|
||||||
@ -182,7 +182,7 @@ void GhostFrog::update()
|
|||||||
{
|
{
|
||||||
if (m_reflect_count < 2)
|
if (m_reflect_count < 2)
|
||||||
{
|
{
|
||||||
if (Vec2(orb->entity()->position - orb->target()).length() < 16)
|
if (Vec2f(orb->entity()->position - orb->target()).length() < 16)
|
||||||
{
|
{
|
||||||
auto sign = Calc::sign(orb->entity()->position.x - x);
|
auto sign = Calc::sign(orb->entity()->position.x - x);
|
||||||
if (sign != 0)
|
if (sign != 0)
|
||||||
@ -197,7 +197,7 @@ void GhostFrog::update()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (Vec2(orb->entity()->position - orb->target()).length() < 8)
|
if (Vec2f(orb->entity()->position - orb->target()).length() < 8)
|
||||||
{
|
{
|
||||||
Factory::pop(world(), entity()->position + Point(0, -8));
|
Factory::pop(world(), entity()->position + Point(0, -8));
|
||||||
orb->entity()->destroy();
|
orb->entity()->destroy();
|
||||||
@ -215,7 +215,7 @@ void GhostFrog::update()
|
|||||||
|
|
||||||
if (Time::on_interval(0.25f))
|
if (Time::on_interval(0.25f))
|
||||||
{
|
{
|
||||||
auto offset = Point(Calc::rand_int(-16, 16), Calc::rand_int(-16, 16));
|
auto offset = Point(Game::rand_int(-16, 16), Game::rand_int(-16, 16));
|
||||||
Factory::pop(world(), entity()->position + Point(0, -8) + offset);
|
Factory::pop(world(), entity()->position + Point(0, -8) + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +233,7 @@ void GhostFrog::update()
|
|||||||
|
|
||||||
if (m_state == st_floating || m_state == st_shoot || m_state == st_reflect)
|
if (m_state == st_floating || m_state == st_shoot || m_state == st_reflect)
|
||||||
{
|
{
|
||||||
anim->offset.y = Calc::sin(Time::elapsed * 2) * 3;
|
anim->offset.y = Calc::sin(Time::seconds * 2) * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_last_pos = entity()->position;
|
m_last_pos = entity()->position;
|
||||||
|
@ -21,9 +21,18 @@ namespace TL
|
|||||||
static constexpr int st_reflect = 5;
|
static constexpr int st_reflect = 5;
|
||||||
static constexpr int st_dead_state = 6;
|
static constexpr int st_dead_state = 6;
|
||||||
|
|
||||||
|
// health during our first phase
|
||||||
static constexpr int max_health_1 = 10;
|
static constexpr int max_health_1 = 10;
|
||||||
|
|
||||||
|
// health during our second phase
|
||||||
static constexpr int max_health_2 = 3;
|
static constexpr int max_health_2 = 3;
|
||||||
|
|
||||||
|
// current health value (assigned to phase 1 health to start)
|
||||||
int health = max_health_1;
|
int health = max_health_1;
|
||||||
|
|
||||||
|
// phase 0 or 1
|
||||||
|
// 0 = running along the ground and slicing
|
||||||
|
// 1 = flying around in the air shooting orbs
|
||||||
int phase = 0;
|
int phase = 0;
|
||||||
|
|
||||||
GhostFrog();
|
GhostFrog();
|
||||||
|
@ -8,6 +8,9 @@ using namespace Blah;
|
|||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
{
|
{
|
||||||
|
// Automatically checks if the provided collider ever overlaps
|
||||||
|
// with something in the `hurt_by` mask. Makes it easy for enemies
|
||||||
|
// to check if they were hit by `Mask::player_attack`
|
||||||
class Hurtable : public Component
|
class Hurtable : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -28,6 +28,7 @@ bool Mover::move_x(int amount)
|
|||||||
{
|
{
|
||||||
entity()->position.x += amount;
|
entity()->position.x += amount;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mover::move_y(int amount)
|
bool Mover::move_y(int amount)
|
||||||
@ -64,6 +65,7 @@ bool Mover::move_y(int amount)
|
|||||||
{
|
{
|
||||||
entity()->position.y += amount;
|
entity()->position.y += amount;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mover::stop_x()
|
void Mover::stop_x()
|
||||||
|
@ -11,11 +11,11 @@ namespace TL
|
|||||||
class Mover : public Component
|
class Mover : public Component
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Vec2 m_remainder;
|
Vec2f m_remainder;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Collider* collider = nullptr;
|
Collider* collider = nullptr;
|
||||||
Vec2 speed;
|
Vec2f speed;
|
||||||
float gravity = 0;
|
float gravity = 0;
|
||||||
float friction = 0;
|
float friction = 0;
|
||||||
std::function<void(Mover*)> on_hit_x;
|
std::function<void(Mover*)> on_hit_x;
|
||||||
|
@ -24,43 +24,37 @@ namespace
|
|||||||
|
|
||||||
Player::Player()
|
Player::Player()
|
||||||
{
|
{
|
||||||
input_move = VirtualStick()
|
input_move = Input::register_binding(StickBinding());
|
||||||
.add_keys(Key::Left, Key::Right, Key::Up, Key::Down)
|
input_move->add_dpad(0);
|
||||||
.add_buttons(0, Button::Left, Button::Right, Button::Up, Button::Down)
|
input_move->add_left_stick(0, 0.2f);
|
||||||
.add_axes(0, Axis::LeftX, Axis::LeftY, 0.2f);
|
input_move->add(Key::Left, Key::Right, Key::Up, Key::Down);
|
||||||
|
|
||||||
input_jump = VirtualButton()
|
input_jump = Input::register_binding(ButtonBinding());
|
||||||
.press_buffer(0.15f)
|
input_jump->press_buffer = 0.15f;
|
||||||
.add_key(Key::X)
|
input_jump->add(Key::X, Button::A);
|
||||||
.add_button(0, Button::A);
|
|
||||||
|
|
||||||
input_attack = VirtualButton()
|
input_attack = Input::register_binding(ButtonBinding());
|
||||||
.press_buffer(0.15f)
|
input_attack->press_buffer = 0.15f;
|
||||||
.add_key(Key::C)
|
input_attack->add(Key::C, Button::X);
|
||||||
.add_button(0, Button::X);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::update()
|
void Player::update()
|
||||||
{
|
{
|
||||||
input_move.update();
|
|
||||||
input_jump.update();
|
|
||||||
input_attack.update();
|
|
||||||
|
|
||||||
auto mover = get<Mover>();
|
auto mover = get<Mover>();
|
||||||
auto anim = get<Animator>();
|
auto anim = get<Animator>();
|
||||||
auto hitbox = get<Collider>();
|
auto hitbox = get<Collider>();
|
||||||
auto was_on_ground = m_on_ground;
|
auto was_on_ground = m_on_ground;
|
||||||
m_on_ground = mover->on_ground();
|
m_on_ground = mover->on_ground();
|
||||||
int input = input_move.value_i().x;
|
int input = input_move->sign().x;
|
||||||
|
|
||||||
// Sprite Stuff
|
// Sprite Stuff
|
||||||
{
|
{
|
||||||
// land squish
|
// land squish
|
||||||
if (!was_on_ground && m_on_ground)
|
if (!was_on_ground && m_on_ground)
|
||||||
anim->scale = Vec2(m_facing * 1.5f, 0.7f);
|
anim->scale = Vec2f(m_facing * 1.5f, 0.7f);
|
||||||
|
|
||||||
// lerp scale back to one
|
// lerp scale back to one
|
||||||
anim->scale = Calc::approach(anim->scale, Vec2(m_facing, 1.0f), Time::delta * 4);
|
anim->scale = Vec2f::approach(anim->scale, Vec2f(m_facing, 1.0f), Time::delta * 4);
|
||||||
|
|
||||||
// set m_facing
|
// set m_facing
|
||||||
anim->scale.x = Calc::abs(anim->scale.x) * m_facing;
|
anim->scale.x = Calc::abs(anim->scale.x) * m_facing;
|
||||||
@ -119,25 +113,25 @@ void Player::update()
|
|||||||
|
|
||||||
// Invoke Jumping
|
// Invoke Jumping
|
||||||
{
|
{
|
||||||
if (input_jump.pressed() && mover->on_ground())
|
if (input_jump->pressed() && mover->on_ground())
|
||||||
{
|
{
|
||||||
input_jump.clear_press_buffer();
|
input_jump->consume_press();
|
||||||
anim->scale = Vec2(m_facing * 0.65f, 1.4f);
|
anim->scale = Vec2f(m_facing * 0.65f, 1.4f);
|
||||||
mover->speed.x = input * max_air_speed;
|
mover->speed.x = input * max_air_speed;
|
||||||
m_jump_timer = jump_time;
|
m_jump_timer = jump_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin Attack
|
// Begin Attack
|
||||||
if (input_attack.pressed())
|
if (input_attack->pressed())
|
||||||
{
|
{
|
||||||
input_attack.clear_press_buffer();
|
input_attack->consume_press();
|
||||||
|
|
||||||
m_state = st_attack;
|
m_state = st_attack;
|
||||||
m_attack_timer = 0;
|
m_attack_timer = 0;
|
||||||
|
|
||||||
if (!m_attack_collider)
|
if (!m_attack_collider)
|
||||||
m_attack_collider = entity()->add(Collider::make_rect(RectI()));
|
m_attack_collider = entity()->add(Collider::make_rect(Recti()));
|
||||||
m_attack_collider->mask = Mask::player_attack;
|
m_attack_collider->mask = Mask::player_attack;
|
||||||
|
|
||||||
if (m_on_ground)
|
if (m_on_ground)
|
||||||
@ -153,11 +147,11 @@ void Player::update()
|
|||||||
// setup hitbox
|
// setup hitbox
|
||||||
if (m_attack_timer < 0.2f)
|
if (m_attack_timer < 0.2f)
|
||||||
{
|
{
|
||||||
m_attack_collider->set_rect(RectI(-16, -12, 16, 8));
|
m_attack_collider->set_rect(Recti(-16, -12, 16, 8));
|
||||||
}
|
}
|
||||||
else if (m_attack_timer < 0.5f)
|
else if (m_attack_timer < 0.5f)
|
||||||
{
|
{
|
||||||
m_attack_collider->set_rect(RectI(8, -8, 16, 8));
|
m_attack_collider->set_rect(Recti(8, -8, 16, 8));
|
||||||
}
|
}
|
||||||
else if (m_attack_collider)
|
else if (m_attack_collider)
|
||||||
{
|
{
|
||||||
@ -195,7 +189,7 @@ void Player::update()
|
|||||||
{
|
{
|
||||||
mover->speed.y = -100;
|
mover->speed.y = -100;
|
||||||
m_jump_timer -= Time::delta;
|
m_jump_timer -= Time::delta;
|
||||||
if (!input_jump.down())
|
if (!input_jump->down())
|
||||||
m_jump_timer = 0;
|
m_jump_timer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,7 +208,7 @@ void Player::update()
|
|||||||
if (!m_on_ground)
|
if (!m_on_ground)
|
||||||
{
|
{
|
||||||
float grav = gravity;
|
float grav = gravity;
|
||||||
if (m_state == st_normal && Calc::abs(mover->speed.y) < 20 && input_jump.down())
|
if (m_state == st_normal && Calc::abs(mover->speed.y) < 20 && input_jump->down())
|
||||||
grav *= 0.4f;
|
grav *= 0.4f;
|
||||||
|
|
||||||
mover->speed.y += grav * Time::delta;
|
mover->speed.y += grav * Time::delta;
|
||||||
@ -235,7 +229,7 @@ void Player::update()
|
|||||||
m_attack_collider = nullptr;
|
m_attack_collider = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
mover->speed = Vec2(-m_facing * 100, -80);
|
mover->speed = Vec2f(-m_facing * 100, -80);
|
||||||
|
|
||||||
health--;
|
health--;
|
||||||
m_hurt_timer = hurt_duration;
|
m_hurt_timer = hurt_duration;
|
||||||
@ -244,6 +238,10 @@ void Player::update()
|
|||||||
|
|
||||||
// hack:
|
// hack:
|
||||||
// destroy orb
|
// destroy orb
|
||||||
|
// ideally we would have an "attack" component that the orb could
|
||||||
|
// subscribe to, and delete itself when it hits the player. since
|
||||||
|
// the orb currently has no way to know if it hit the player, we
|
||||||
|
// have to add this ugly hack!
|
||||||
if (hit->get<Orb>())
|
if (hit->get<Orb>())
|
||||||
hit->entity()->destroy();
|
hit->entity()->destroy();
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ namespace TL
|
|||||||
|
|
||||||
int health = max_health;
|
int health = max_health;
|
||||||
|
|
||||||
VirtualStick input_move;
|
StickBindingRef input_move;
|
||||||
VirtualButton input_jump;
|
ButtonBindingRef input_jump;
|
||||||
VirtualButton input_attack;
|
ButtonBindingRef input_attack;
|
||||||
|
|
||||||
Player();
|
Player();
|
||||||
void update() override;
|
void update() override;
|
||||||
|
@ -12,7 +12,7 @@ Tilemap::Tilemap(int tile_width, int tile_height, int columns, int rows)
|
|||||||
m_tile_height = tile_height;
|
m_tile_height = tile_height;
|
||||||
m_columns = columns;
|
m_columns = columns;
|
||||||
m_rows = rows;
|
m_rows = rows;
|
||||||
m_grid = std::shared_ptr<Subtexture[]>(new Subtexture[columns * rows]);
|
m_grid.expand(columns * rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Tilemap::tile_width() const
|
int Tilemap::tile_width() const
|
||||||
@ -52,12 +52,12 @@ void Tilemap::set_cells(int x, int y, int w, int h, const Subtexture* tex)
|
|||||||
|
|
||||||
void Tilemap::render(Batch& batch)
|
void Tilemap::render(Batch& batch)
|
||||||
{
|
{
|
||||||
batch.push_matrix(Mat3x2::create_translation(entity()->position));
|
batch.push_matrix(Mat3x2f::create_translation(entity()->position));
|
||||||
for (int x = 0; x < m_columns; x ++)
|
for (int x = 0; x < m_columns; x ++)
|
||||||
for (int y = 0; y < m_rows; y ++)
|
for (int y = 0; y < m_rows; y ++)
|
||||||
if (m_grid[x + y * m_columns].texture)
|
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.tex(m_grid[x + y * m_columns], Vec2f(x * m_tile_width, y * m_tile_height));
|
||||||
}
|
}
|
||||||
batch.pop_matrix();
|
batch.pop_matrix();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "../world.h"
|
#include "../world.h"
|
||||||
#include <blah.h>
|
#include <blah.h>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ namespace TL
|
|||||||
void render(Batch& batch) override;
|
void render(Batch& batch) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Subtexture[]> m_grid;
|
Vector<Subtexture> m_grid;
|
||||||
int m_tile_width = 0;
|
int m_tile_width = 0;
|
||||||
int m_tile_height = 0;
|
int m_tile_height = 0;
|
||||||
int m_columns = 0;
|
int m_columns = 0;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "assets/sprite.h"
|
#include "assets/sprite.h"
|
||||||
#include "assets/tileset.h"
|
#include "assets/tileset.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
@ -42,9 +43,12 @@ FilePath Content::path()
|
|||||||
} while (!Directory::exists(root) && up.length() < 30);
|
} while (!Directory::exists(root) && up.length() < 30);
|
||||||
|
|
||||||
if (!Directory::exists(root))
|
if (!Directory::exists(root))
|
||||||
BLAH_ERROR("Unable to find content directory!");
|
{
|
||||||
|
Log::error("Unable to find content directory!");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
Log::print("Content Path: %s", root.cstr());
|
Log::info("Content Path: %s", root.cstr());
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
@ -56,7 +60,7 @@ void Content::load()
|
|||||||
packer.padding = 0;
|
packer.padding = 0;
|
||||||
|
|
||||||
// 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::CharRange::ASCII);
|
||||||
font.line_gap = 4;
|
font.line_gap = 4;
|
||||||
|
|
||||||
// load sprites
|
// load sprites
|
||||||
@ -104,7 +108,7 @@ void Content::load()
|
|||||||
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++)
|
||||||
{
|
{
|
||||||
auto subrect = RectI(x * Game::tile_width, y * Game::tile_height, Game::tile_width, Game::tile_height);
|
auto subrect = Recti(x * Game::tile_width, y * Game::tile_height, Game::tile_width, Game::tile_height);
|
||||||
auto subimage = frame.image.get_sub_image(subrect);
|
auto subimage = frame.image.get_sub_image(subrect);
|
||||||
packer.add(pack_index, subimage);
|
packer.add(pack_index, subimage);
|
||||||
pack_index++;
|
pack_index++;
|
||||||
@ -117,8 +121,8 @@ void Content::load()
|
|||||||
packer.pack();
|
packer.pack();
|
||||||
sprite_atlas = Texture::create(packer.pages[0]);
|
sprite_atlas = Texture::create(packer.pages[0]);
|
||||||
|
|
||||||
subtextures.expand(packer.entries.size());
|
subtextures.expand(packer.entries().size());
|
||||||
for (auto& entry : packer.entries)
|
for (auto& entry : packer.entries())
|
||||||
subtextures[entry.id] = Subtexture(sprite_atlas, entry.packed, entry.frame);
|
subtextures[entry.id] = Subtexture(sprite_atlas, entry.packed, entry.frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,11 +131,11 @@ void Content::load()
|
|||||||
{
|
{
|
||||||
Sprite sprite;
|
Sprite sprite;
|
||||||
sprite.name = info.name;
|
sprite.name = info.name;
|
||||||
sprite.origin = Vec2::zero;
|
sprite.origin = Vec2f::zero;
|
||||||
|
|
||||||
if (info.aseprite.slices.size() > 0 && info.aseprite.slices[0].has_pivot)
|
if (info.aseprite.slices.size() > 0 && info.aseprite.slices[0].has_pivot)
|
||||||
{
|
{
|
||||||
sprite.origin = Vec2(
|
sprite.origin = Vec2f(
|
||||||
info.aseprite.slices[0].pivot.x,
|
info.aseprite.slices[0].pivot.x,
|
||||||
info.aseprite.slices[0].pivot.y);
|
info.aseprite.slices[0].pivot.y);
|
||||||
}
|
}
|
||||||
@ -198,7 +202,7 @@ void Content::load()
|
|||||||
|
|
||||||
void Content::unload()
|
void Content::unload()
|
||||||
{
|
{
|
||||||
font.dispose();
|
font = SpriteFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureRef Content::atlas()
|
TextureRef Content::atlas()
|
||||||
|
@ -20,7 +20,7 @@ Entity* Factory::player(World* world, Point position)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -10;
|
anim->depth = -10;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -12, 8, 12)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -12, 8, 12)));
|
||||||
hitbox->mask = Mask::player;
|
hitbox->mask = Mask::player;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
@ -38,7 +38,7 @@ Entity* Factory::bramble(World* world, Point position)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -8, 8, 8)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -8, 8, 8)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto hurtable = en->add(Hurtable());
|
auto hurtable = en->add(Hurtable());
|
||||||
@ -78,7 +78,7 @@ Entity* Factory::spitter(World* world, Point position)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-6, -12, 12, 12)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-6, -12, 12, 12)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto hurtable = en->add(Hurtable());
|
auto hurtable = en->add(Hurtable());
|
||||||
@ -110,12 +110,12 @@ Entity* Factory::bullet(World* world, Point position, int direction)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -4, 8, 8)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -4, 8, 8)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
mover->collider = hitbox;
|
mover->collider = hitbox;
|
||||||
mover->speed = Vec2(direction * 40, 0);
|
mover->speed = Vec2f(direction * 40, 0);
|
||||||
mover->gravity = 130;
|
mover->gravity = 130;
|
||||||
mover->on_hit_x = [](Mover* self) { self->entity()->destroy(); };
|
mover->on_hit_x = [](Mover* self) { self->entity()->destroy(); };
|
||||||
mover->on_hit_y = [](Mover* self) { self->speed.y = -60; };
|
mover->on_hit_y = [](Mover* self) { self->speed.y = -60; };
|
||||||
@ -203,7 +203,7 @@ Entity* Factory::mosquito(World* world, Point position)
|
|||||||
anim->play("fly");
|
anim->play("fly");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -4, 8, 8)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -4, 8, 8)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
@ -225,7 +225,7 @@ namespace
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -1;
|
anim->depth = -1;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-6, -16, 12, 16)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-6, -16, 12, 16)));
|
||||||
hitbox->mask = Mask::solid;
|
hitbox->mask = Mask::solid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ Entity* Factory::blob(World* world, Point position)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -8, 8, 8)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -8, 8, 8)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
@ -307,7 +307,7 @@ Entity* Factory::blob(World* world, Point position)
|
|||||||
auto dir = Calc::sign(player->entity()->position.x - self->entity()->position.x);
|
auto dir = Calc::sign(player->entity()->position.x - self->entity()->position.x);
|
||||||
if (dir == 0) dir = 1;
|
if (dir == 0) dir = 1;
|
||||||
|
|
||||||
self->get<Animator>()->scale = Vec2(dir, 1);
|
self->get<Animator>()->scale = Vec2f(dir, 1);
|
||||||
mover->speed.x = dir * 40;
|
mover->speed.x = dir * 40;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ Entity* Factory::ghost_frog(World* world, Point position)
|
|||||||
anim->play("sword");
|
anim->play("sword");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -12, 8, 12)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -12, 8, 12)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
@ -374,7 +374,7 @@ Entity* Factory::orb(World* world, Point position)
|
|||||||
anim->play("idle");
|
anim->play("idle");
|
||||||
anim->depth = -5;
|
anim->depth = -5;
|
||||||
|
|
||||||
auto hitbox = en->add(Collider::make_rect(RectI(-4, -4, 8, 8)));
|
auto hitbox = en->add(Collider::make_rect(Recti(-4, -4, 8, 8)));
|
||||||
hitbox->mask = Mask::enemy;
|
hitbox->mask = Mask::enemy;
|
||||||
|
|
||||||
auto mover = en->add(Mover());
|
auto mover = en->add(Mover());
|
||||||
@ -384,7 +384,7 @@ Entity* Factory::orb(World* world, Point position)
|
|||||||
|
|
||||||
auto hurtable = en->add(Hurtable());
|
auto hurtable = en->add(Hurtable());
|
||||||
hurtable->hurt_by = Mask::player_attack;
|
hurtable->hurt_by = Mask::player_attack;
|
||||||
hurtable->collider = en->add(Collider::make_rect(RectI(-8, -8, 16, 16)));
|
hurtable->collider = en->add(Collider::make_rect(Recti(-8, -8, 16, 16)));
|
||||||
hurtable->on_hurt = [](Hurtable* self) { self->get<Orb>()->on_hit(); };
|
hurtable->on_hurt = [](Hurtable* self) { self->get<Orb>()->on_hit(); };
|
||||||
|
|
||||||
return en;
|
return en;
|
||||||
|
@ -6,6 +6,8 @@ using namespace Blah;
|
|||||||
|
|
||||||
namespace TL
|
namespace TL
|
||||||
{
|
{
|
||||||
|
// Factory to create game objects
|
||||||
|
|
||||||
namespace Factory
|
namespace Factory
|
||||||
{
|
{
|
||||||
Entity* player(World* world, Point position);
|
Entity* player(World* world, Point position);
|
||||||
|
51
src/game.cpp
51
src/game.cpp
@ -8,6 +8,7 @@
|
|||||||
#include "components/mover.h"
|
#include "components/mover.h"
|
||||||
#include "assets/sprite.h"
|
#include "assets/sprite.h"
|
||||||
#include "factory.h"
|
#include "factory.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ void Game::startup()
|
|||||||
Content::load();
|
Content::load();
|
||||||
|
|
||||||
// framebuffer for the game
|
// framebuffer for the game
|
||||||
buffer = FrameBuffer::create(width, height);
|
buffer = Target::create(width, height);
|
||||||
|
|
||||||
// set batcher to use Nearest Filter
|
// set batcher to use Nearest Filter
|
||||||
batch.default_sampler = TextureSampler(TextureFilter::Nearest);
|
batch.default_sampler = TextureSampler(TextureFilter::Nearest);
|
||||||
@ -32,7 +33,7 @@ void Game::startup()
|
|||||||
|
|
||||||
// load first room
|
// load first room
|
||||||
load_room(Point(0, 0));
|
load_room(Point(0, 0));
|
||||||
camera = Vec2(room.x * width, room.y * height);
|
camera = Vec2f(room.x * width, room.y * height);
|
||||||
fullscreen = false;
|
fullscreen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ void Game::load_room(Point cell, bool is_reload)
|
|||||||
{
|
{
|
||||||
tilemap->set_cell(x, y, &jumpthrus->random_tile());
|
tilemap->set_cell(x, y, &jumpthrus->random_tile());
|
||||||
auto jumpthru_en = world.add_entity(offset + Point(x * tile_width, y * tile_height));
|
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)));
|
auto jumpthru_col = jumpthru_en->add(Collider::make_rect(Recti(0, 0, 8, 4)));
|
||||||
jumpthru_col->mask = Mask::jumpthru;
|
jumpthru_col->mask = Mask::jumpthru;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -176,6 +177,7 @@ void Game::update()
|
|||||||
m_transition = false;
|
m_transition = false;
|
||||||
world.clear();
|
world.clear();
|
||||||
load_room(Point(0, 0));
|
load_room(Point(0, 0));
|
||||||
|
camera = Vec2f(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle Fullscreen
|
// Toggle Fullscreen
|
||||||
@ -191,8 +193,8 @@ void Game::update()
|
|||||||
{
|
{
|
||||||
if (Time::on_interval(0.05f))
|
if (Time::on_interval(0.05f))
|
||||||
{
|
{
|
||||||
m_shake.x = Calc::rand_int(0, 2) == 0 ? -1 : 1;
|
m_shake.x = rand_int(0, 2) == 0 ? -1 : 1;
|
||||||
m_shake.y = Calc::rand_int(0, 2) == 0 ? -1 : 1;
|
m_shake.y = rand_int(0, 2) == 0 ? -1 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -206,7 +208,7 @@ void Game::update()
|
|||||||
if (player)
|
if (player)
|
||||||
{
|
{
|
||||||
auto pos = player->entity()->position;
|
auto pos = player->entity()->position;
|
||||||
auto bounds = RectI(room.x * width, room.y * height, width, height);
|
auto bounds = Recti(room.x * width, room.y * height, width, height);
|
||||||
if (!bounds.contains(pos))
|
if (!bounds.contains(pos))
|
||||||
{
|
{
|
||||||
// target room
|
// target room
|
||||||
@ -241,8 +243,8 @@ void Game::update()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
player->entity()->position = Point(
|
player->entity()->position = Point(
|
||||||
Calc::clamp_int(pos.x, bounds.x, bounds.x + bounds.w),
|
Calc::clamp(pos.x, bounds.x, bounds.x + bounds.w),
|
||||||
Calc::clamp_int(pos.y, bounds.y, bounds.y + bounds.h + 100));
|
Calc::clamp(pos.y, bounds.y, bounds.y + bounds.h + 100));
|
||||||
|
|
||||||
// reload if they fell out the bottom
|
// reload if they fell out the bottom
|
||||||
if (player->entity()->position.y > bounds.y + bounds.h + 64)
|
if (player->entity()->position.y > bounds.y + bounds.h + 64)
|
||||||
@ -275,8 +277,8 @@ void Game::update()
|
|||||||
m_next_ease = Calc::approach(m_next_ease, 1.0f, Time::delta / transition_duration);
|
m_next_ease = Calc::approach(m_next_ease, 1.0f, Time::delta / transition_duration);
|
||||||
|
|
||||||
// get last & next camera position
|
// get last & next camera position
|
||||||
auto last_cam = Vec2(m_last_room.x * width, m_last_room.y * height);
|
auto last_cam = Vec2f(m_last_room.x * width, m_last_room.y * height);
|
||||||
auto next_cam = Vec2(m_next_room.x * width, m_next_room.y * height);
|
auto next_cam = Vec2f(m_next_room.x * width, m_next_room.y * height);
|
||||||
|
|
||||||
// LERP camera position
|
// LERP camera position
|
||||||
camera = last_cam + (next_cam - last_cam) * Ease::cube_in_out(m_next_ease);
|
camera = last_cam + (next_cam - last_cam) * Ease::cube_in_out(m_next_ease);
|
||||||
@ -289,7 +291,7 @@ void Game::update()
|
|||||||
{
|
{
|
||||||
auto player = world.first<Player>();
|
auto player = world.first<Player>();
|
||||||
if (player)
|
if (player)
|
||||||
player->get<Mover>()->speed = Vec2(0, -150);
|
player->get<Mover>()->speed = Vec2f(0, -150);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete old objects (except player!)
|
// delete old objects (except player!)
|
||||||
@ -312,7 +314,7 @@ void Game::render()
|
|||||||
buffer->clear(0x150e22);
|
buffer->clear(0x150e22);
|
||||||
|
|
||||||
// push camera offset
|
// push camera offset
|
||||||
batch.push_matrix(Mat3x2::create_translation(-camera + m_shake));
|
batch.push_matrix(Mat3x2f::create_translation(-camera + m_shake));
|
||||||
|
|
||||||
// draw gameplay objects
|
// draw gameplay objects
|
||||||
world.render(batch);
|
world.render(batch);
|
||||||
@ -345,8 +347,8 @@ void Game::render()
|
|||||||
{
|
{
|
||||||
auto w = Content::font.width_of(ending);
|
auto w = Content::font.width_of(ending);
|
||||||
auto pos = Point(room.x * width + width / 2, room.y * height + 20);
|
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 + Point(0, 1), Vec2f(0.0, 0.0), 8, Color::black);
|
||||||
batch.str(Content::font, ending, pos, TextAlign::Top, 8, Color::white);
|
batch.str(Content::font, ending, pos, Vec2f(0.0, 0.0), 8, Color::white);
|
||||||
}
|
}
|
||||||
|
|
||||||
// end camera offset
|
// end camera offset
|
||||||
@ -381,17 +383,17 @@ void Game::render()
|
|||||||
// draw buffer to the screen
|
// draw buffer to the screen
|
||||||
{
|
{
|
||||||
float scale = Calc::min(
|
float scale = Calc::min(
|
||||||
App::backbuffer->width() / (float)buffer->width(),
|
App::backbuffer()->width() / (float)buffer->width(),
|
||||||
App::backbuffer->height() / (float)buffer->height());
|
App::backbuffer()->height() / (float)buffer->height());
|
||||||
|
|
||||||
Vec2 screen_center = Vec2(App::backbuffer->width(), App::backbuffer->height()) / 2;
|
Vec2f screen_center = Vec2f(App::backbuffer()->width(), App::backbuffer()->height()) / 2;
|
||||||
Vec2 buffer_center = Vec2(buffer->width(), buffer->height()) / 2;
|
Vec2f buffer_center = Vec2f(buffer->width(), buffer->height()) / 2;
|
||||||
|
|
||||||
App::backbuffer->clear(Color::black);
|
App::backbuffer()->clear(Color::black);
|
||||||
batch.push_matrix(Mat3x2::create_transform(screen_center, buffer_center, Vec2::one * scale, 0));
|
batch.push_matrix(Mat3x2f::create_transform(screen_center, buffer_center, Vec2f::one * scale, 0));
|
||||||
batch.tex(buffer->attachment(0), Vec2::zero, Color::white);
|
batch.tex(buffer->texture(0), Vec2f::zero, Color::white);
|
||||||
batch.pop_matrix();
|
batch.pop_matrix();
|
||||||
batch.render(App::backbuffer);
|
batch.render(App::backbuffer());
|
||||||
batch.clear();
|
batch.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,3 +402,8 @@ void Game::shake(float time)
|
|||||||
{
|
{
|
||||||
m_shake_timer = time;
|
m_shake_timer = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Game::rand_int(int min, int max)
|
||||||
|
{
|
||||||
|
return min + (rand() % (max - min));
|
||||||
|
}
|
||||||
|
@ -21,10 +21,10 @@ namespace TL
|
|||||||
static inline const char* ending = "YOU SAVED POND\nAND YOU ARE\nA REAL HERO";
|
static inline const char* ending = "YOU SAVED POND\nAND YOU ARE\nA REAL HERO";
|
||||||
|
|
||||||
World world;
|
World world;
|
||||||
FrameBufferRef buffer;
|
TargetRef buffer;
|
||||||
Batch batch;
|
Batch batch;
|
||||||
Point room;
|
Point room;
|
||||||
Vec2 camera;
|
Vec2f camera;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
|
|
||||||
void load_room(Point cell, bool is_reload = false);
|
void load_room(Point cell, bool is_reload = false);
|
||||||
@ -34,6 +34,8 @@ namespace TL
|
|||||||
void render();
|
void render();
|
||||||
void shake(float time);
|
void shake(float time);
|
||||||
|
|
||||||
|
static int rand_int(int min, int max);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_draw_colliders;
|
bool m_draw_colliders;
|
||||||
bool m_transition = false;
|
bool m_transition = false;
|
||||||
|
33
src/main.cpp
33
src/main.cpp
@ -4,42 +4,19 @@
|
|||||||
using namespace Blah;
|
using namespace Blah;
|
||||||
using namespace TL;
|
using namespace TL;
|
||||||
|
|
||||||
namespace
|
int main()
|
||||||
{
|
{
|
||||||
Game game;
|
Game game;
|
||||||
|
|
||||||
void startup()
|
|
||||||
{
|
|
||||||
game.startup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void shutdown()
|
|
||||||
{
|
|
||||||
game.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
void update()
|
|
||||||
{
|
|
||||||
game.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void render()
|
|
||||||
{
|
|
||||||
game.render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
Config config;
|
Config config;
|
||||||
config.name = "Sword II: Adventure of Frog";
|
config.name = "Sword II: Adventure of Frog";
|
||||||
config.width = 1280;
|
config.width = 1280;
|
||||||
config.height = 720;
|
config.height = 720;
|
||||||
|
|
||||||
config.on_startup = startup;
|
config.on_startup = [&]() { game.startup(); };
|
||||||
config.on_shutdown = shutdown;
|
config.on_shutdown = [&]() { game.shutdown(); };
|
||||||
config.on_update = update;
|
config.on_update = [&]() { game.update(); };
|
||||||
config.on_render = render;
|
config.on_render = [&]() { game.render(); };
|
||||||
|
|
||||||
App::run(&config);
|
App::run(&config);
|
||||||
}
|
}
|
@ -5,6 +5,8 @@ namespace TL
|
|||||||
{
|
{
|
||||||
struct Mask
|
struct Mask
|
||||||
{
|
{
|
||||||
|
// bitfield masks for collision types
|
||||||
|
|
||||||
static constexpr uint32_t solid = 1 << 0;
|
static constexpr uint32_t solid = 1 << 0;
|
||||||
static constexpr uint32_t jumpthru = 1 << 1;
|
static constexpr uint32_t jumpthru = 1 << 1;
|
||||||
static constexpr uint32_t player_attack = 1 << 2;
|
static constexpr uint32_t player_attack = 1 << 2;
|
||||||
|
@ -111,6 +111,11 @@ namespace TL
|
|||||||
public:
|
public:
|
||||||
static constexpr int max_component_types = 256;
|
static constexpr int max_component_types = 256;
|
||||||
|
|
||||||
|
// NOTE:
|
||||||
|
// I tossed this reference here at the very end of making the game,
|
||||||
|
// just so that the boss could tell the game to shake the screen.
|
||||||
|
// Ideally I think there should be a Camera component that handles
|
||||||
|
// that instead.
|
||||||
Game* game;
|
Game* game;
|
||||||
|
|
||||||
World() = default;
|
World() = default;
|
||||||
|
Loading…
Reference in New Issue
Block a user