Compare commits

...

33 Commits
v1.0.0 ... main

Author SHA1 Message Date
Noel Berry
f62a60cb3e
Merge pull request #6 from feresr/main
clang not valid constexp -> update blah lib to latest master
2022-05-07 18:14:52 -07:00
Fernando Raviola
f4c1bba61d add build/ to .gitignore 2022-05-07 02:44:14 -03:00
Fernando Raviola
71121df896 Update blah lib 2022-05-07 02:20:45 -03:00
Noel Berry
98c59cfdfd
Updated Readme to reflect Blah changes 2022-02-12 13:04:15 -08:00
Noel Berry
6405f5c897 updated to latest version of blah 2022-02-12 13:03:02 -08:00
Noel Berry
37a5afc52c updated to latest blah version 2021-04-06 17:46:30 -07:00
Noel Berry
4740c54465 updated to latest 'blah' version 2021-03-20 20:44:04 -07:00
Noel Berry
26bc63236d updated to latest version of blah 2021-02-21 18:46:55 -08:00
Noel Berry
3d94a9dad0
fixed trailing bracket in readme 2021-01-11 11:16:01 -08:00
Noel Berry
48c6326cc7
updated stream archive links 2021-01-06 11:32:05 -08:00
Noel Berry
ce72329084
note about submodule 2021-01-06 00:37:54 -08:00
Noel Berry
8936a5bbb7
cleaning up CMakeLists.txt 2021-01-05 20:48:25 -08:00
Noel Berry
14123e5b5e
Merge pull request #4 from kevinbchen/main
Emscripten build
2021-01-05 20:46:17 -08:00
Noel Berry
cc5f137fa6
Merge branch 'main' into main 2021-01-05 20:46:09 -08:00
Noel Berry
14c1d625a1 few additional notes 2021-01-04 18:57:46 -08:00
Noel Berry
7173d65263 added some documentation & explanations 2021-01-04 18:55:26 -08:00
Noel Berry
68bb409f98 removed shared_ptr arrays as clang isn't a fan 2021-01-04 18:38:12 -08:00
kevinbchen
7ffa649160 Emscripten build 2021-01-04 01:22:39 -08:00
Noel Berry
f8254013c3
Merge pull request #3 from akien-mga/cmake-3.12
CMake: Bump required version to 3.12 (needed by blah)
2021-01-04 00:46:49 -08:00
Rémi Verschelde
04f2b68139
CMake: Bump required version to 3.12 (needed by blah) 2021-01-04 09:37:14 +01:00
Noel Berry
c1859f6946
notes about documenation 2021-01-03 23:22:02 -08:00
Noel Berry
54fd84f450
Added note about Aseprite use 2021-01-03 23:20:36 -08:00
Noel Berry
39c0c516c8
clarified SDL2 notes 2021-01-03 23:19:25 -08:00
Noel Berry
1ec75743c5
Merge pull request #2 from impiaaa/main
Fixes for building on Linux
2021-01-03 23:15:45 -08:00
Noel Berry
3f3ffbf52c
no 'the' in the title 2021-01-03 23:14:11 -08:00
Noel Berry
e2268a3cdf
added game title 2021-01-03 23:13:31 -08:00
Spencer Alves
5623d0948a Update submodule 2021-01-03 23:11:20 -08:00
Spencer Alves
ee21d2dfe4 Include stdlib.h in content.cpp for strtol 2021-01-03 23:11:20 -08:00
Noel Berry
e0fef8cc62
Update README.md 2021-01-03 22:25:38 -08:00
Noel Berry
e1bb593219 Update README.md 2021-01-03 22:14:59 -08:00
Noel Berry
1874ff2a9b Update README.md 2021-01-03 22:14:42 -08:00
Noel Berry
1519bf2f87 boxart and screenshot 2021-01-03 22:14:11 -08:00
Noel Berry
98ac005985
Update README.md 2021-01-03 22:05:47 -08:00
33 changed files with 202 additions and 181 deletions

3
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vs .vs
.vscode .vscode
out out
CMakeSettings.json CMakeSettings.json
build/*

View File

@ -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()

View File

@ -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)
![Screenshot](https://github.com/noelfb/tiny_link/raw/main/screenshot.png "Screenshot")
### box art
![box art](https://github.com/noelfb/tiny_link/raw/main/boxart.jpg "Box art by Grayson")
*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

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

3
content/map/readme.md Normal file
View 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!

View 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.

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@ -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;

View File

@ -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];
} }

View File

@ -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();
} }

View File

@ -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;

View File

@ -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++)

View File

@ -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);

View File

@ -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 { };
} }

View File

@ -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;

View File

@ -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();

View File

@ -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:

View File

@ -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()

View File

@ -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;

View File

@ -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();
} }

View File

@ -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;

View File

@ -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();
} }

View File

@ -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;

View File

@ -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()

View File

@ -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;

View File

@ -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);

View File

@ -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));
}

View File

@ -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;

View File

@ -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);
} }

View File

@ -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;

View File

@ -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;