LightGame 完成 Windows

This commit is contained in:
SepComet
2026-06-14 09:42:27 +08:00
parent 254a8ce5c8
commit 786170f40a
50 changed files with 85787 additions and 1392 deletions
+1
View File
@@ -7,6 +7,7 @@ set(LIGHTGAME_SOURCES
src/engine/LevelRenderer.cpp
src/engine/GameStateManager.cpp
src/engine/LightGameApp.cpp
src/engine/RoomLayout.cpp
src/systems/LightEffectSystem.cpp
src/editor/LevelEditor.cpp
src/main.cpp
File diff suppressed because it is too large Load Diff
+267 -45
View File
@@ -2,6 +2,7 @@
#include "DrawContext.h"
#include "BitmapFont.h"
#include "LevelLoader.h"
#include "TileIds.h"
#include "imgui.h"
#include "tile_atlas.h"
#include <algorithm>
@@ -14,6 +15,11 @@ namespace LightGame
{
namespace
{
static const int32_t kRoomTilesW = 32;
static const int32_t kRoomTilesH = 18;
static const int32_t kWorldRoomsW = 3;
static const int32_t kWorldRoomsH = 3;
static RenderData::Sprite MakeTileSprite(const RenderData::Image* atlas, int32_t tile_id,
int32_t tile_size, int32_t atlas_columns)
{
@@ -22,13 +28,13 @@ namespace LightGame
return RenderData::Sprite(atlas, col * tile_size, row * tile_size, tile_size, tile_size);
}
static const RenderData::Sprite spr_platform = MakeTileSprite(&tile_atlas_image, 3, 32, 4);
static const RenderData::Sprite spr_spike = MakeTileSprite(&tile_atlas_image, 4, 32, 4);
static const RenderData::Sprite spr_light_platform = MakeTileSprite(&tile_atlas_image, 6, 32, 4);
static const RenderData::Sprite spr_shadow_platform = MakeTileSprite(&tile_atlas_image, 8, 32, 4);
static const RenderData::Sprite spr_door = MakeTileSprite(&tile_atlas_image, 9, 32, 4);
static const RenderData::Sprite spr_coin = MakeTileSprite(&tile_atlas_image, 11, 32, 4);
static const RenderData::Sprite spr_checkpoint = MakeTileSprite(&tile_atlas_image, 13, 32, 4);
static const RenderData::Sprite spr_platform = MakeTileSprite(&tile_atlas_image, TileId::Platform, 32, tile_atlas_columns);
static const RenderData::Sprite spr_spike = MakeTileSprite(&tile_atlas_image, TileId::Spike, 32, tile_atlas_columns);
static const RenderData::Sprite spr_light_platform = MakeTileSprite(&tile_atlas_image, TileId::LightPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_shadow_platform = MakeTileSprite(&tile_atlas_image, TileId::ShadowPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_door = MakeTileSprite(&tile_atlas_image, TileId::DoorClosed, 32, tile_atlas_columns);
static const RenderData::Sprite spr_coin = MakeTileSprite(&tile_atlas_image, TileId::Coin, 32, tile_atlas_columns);
static const RenderData::Sprite spr_checkpoint = MakeTileSprite(&tile_atlas_image, TileId::Checkpoint, 32, tile_atlas_columns);
static const char* ObjTypeToString(GameObjectType type)
{
@@ -63,12 +69,15 @@ namespace LightGame
active_(false),
should_quit_(false),
current_tool_(Tool::TileBrush),
selected_tile_id_(1),
selected_layer_(TileLayer::Foreground),
selected_tile_id_(TileId::GroundTopGreen),
selected_object_type_(GameObjectType::Player),
selected_object_id_(0),
preview_light_level_(2048),
show_grid_(true),
show_colliders_(false),
selected_object_id_(0),
preview_light_level_(2048),
show_grid_(true),
show_room_guides_(true),
show_colliders_(false),
show_background_layer_(true),
map_width_(0),
map_height_(0),
tile_size_(32),
@@ -77,7 +86,7 @@ namespace LightGame
last_tile_y_(-1),
pending_load_level_(-1)
{
camera_.configure(screen_width_, screen_height_);
camera_.configure(screen_width_, screen_height_ - LevelRenderer::kTopBlackBorder);
std::memset(export_name_, 0, sizeof(export_name_));
export_open_ = false;
}
@@ -87,7 +96,7 @@ namespace LightGame
active_ = active;
if (active)
{
camera_.configure(screen_width_, screen_height_);
camera_.configure(screen_width_, screen_height_ - LevelRenderer::kTopBlackBorder);
}
}
@@ -98,6 +107,15 @@ namespace LightGame
map_width_ = data.map_width;
map_height_ = data.map_height;
tile_size_ = data.tile_size;
if (!level_.get_background_tiles())
{
const int32_t total = map_width_ * map_height_;
std::vector<uint16_t> empty_tiles(static_cast<size_t>(total), 0);
RenderData::Tilemap background_tilemap = level_.get_tilemap();
background_tilemap.tiles = empty_tiles.data();
level_.set_background_tilemap(background_tilemap);
level_.init_background_buffer(empty_tiles.data(), total);
}
camera_.set_level_bounds(LevelBounds(0, 0, map_width_ * tile_size_, map_height_ * tile_size_));
camera_.snap_to(level_.get_spawn_point());
selected_object_id_ = 0;
@@ -124,6 +142,11 @@ namespace LightGame
level_.set_tilemap(tilemap);
level_.init_tile_buffer(empty_tiles.data(), total);
RenderData::Tilemap background_tilemap = tilemap;
background_tilemap.tiles = empty_tiles.data();
level_.set_background_tilemap(background_tilemap);
level_.init_background_buffer(empty_tiles.data(), total);
level_.set_bounds(LevelBounds(0, 0, width * tile_size, height * tile_size));
level_.set_spawn_point(Math::Vector2Int(tile_size * 2, tile_size * 2));
@@ -161,6 +184,29 @@ namespace LightGame
// 中键拖拽可后续扩展
(void)pointer;
}
// UI 面板会遮挡视图边缘,允许相机超出地图边界以便编辑被遮挡处的瓦片。
const int32_t kUiLeftMargin = 160; // Tools 工具栏宽 140,多留余量
const int32_t kUiRightMargin = 240; // Properties 面板宽 220
const int32_t kUiTopMargin = 32; // 主菜单栏高度
const int32_t kUiBottomMargin = 32; // 状态栏高度
const int32_t world_w = map_width_ * tile_size_;
const int32_t world_h = map_height_ * tile_size_;
int32_t min_x = -kUiLeftMargin;
int32_t min_y = -kUiTopMargin;
int32_t max_x = world_w - camera_.get_screen_width() + kUiRightMargin;
int32_t max_y = world_h - camera_.get_screen_height() + kUiBottomMargin;
if (max_x < min_x) max_x = min_x;
if (max_y < min_y) max_y = min_y;
int32_t clamped_x = camera_.get_x();
int32_t clamped_y = camera_.get_y();
if (clamped_x < min_x) clamped_x = min_x;
if (clamped_y < min_y) clamped_y = min_y;
if (clamped_x > max_x) clamped_x = max_x;
if (clamped_y > max_y) clamped_y = max_y;
camera_.set_position(clamped_x, clamped_y);
}
void LevelEditor::handle_mouse(Platform::IPointerInput* pointer)
@@ -205,12 +251,17 @@ namespace LightGame
void LevelEditor::paint_tile(int32_t tile_x, int32_t tile_y, uint16_t tile_id)
{
uint16_t* tiles = level_.get_mutable_tiles();
uint16_t* tiles = (selected_layer_ == TileLayer::Foreground)
? level_.get_mutable_tiles()
: level_.get_mutable_background_tiles();
if (!tiles) return;
const int32_t idx = tile_y * map_width_ + tile_x;
tiles[idx] = tile_id;
uint16_t* orig = const_cast<uint16_t*>(level_.get_original_tiles());
if (orig) orig[idx] = tile_id;
if (selected_layer_ == TileLayer::Foreground)
{
uint16_t* orig = const_cast<uint16_t*>(level_.get_original_tiles());
if (orig) orig[idx] = tile_id;
}
}
void LevelEditor::place_object(int32_t tile_x, int32_t tile_y)
@@ -224,10 +275,27 @@ namespace LightGame
switch (selected_object_type_)
{
case GameObjectType::Player:
{
std::vector<uint32_t> old_player_ids;
const auto& objects = level_.get_all_objects();
for (size_t i = 0; i < objects.size(); ++i)
{
if (objects[i].type == GameObjectType::Player)
{
old_player_ids.push_back(objects[i].id);
}
}
for (size_t i = 0; i < old_player_ids.size(); ++i)
{
level_.remove_object(old_player_ids[i]);
}
selected_object_id_ = 0;
obj.collider = RenderData::BoundingBox2D(Math::Vector2Int(8, 0), Math::Vector2Int(24, 32));
obj.solid = false;
level_.set_spawn_point(obj.position);
break;
}
case GameObjectType::LightPlatform:
obj.collider = RenderData::BoundingBox2D(Math::Vector2Int(0, 0), Math::Vector2Int(ts, ts));
obj.solid = false;
@@ -292,9 +360,9 @@ namespace LightGame
light_system_.update(level_.get_all_objects(), preview_light_level_);
light_system_.update_tilemap(level_, preview_light_level_);
renderer_.draw(ctx, level_, camera_, light_system_, preview_light_level_);
renderer_.draw(ctx, level_, camera_, light_system_, preview_light_level_, show_background_layer_);
if (show_grid_) draw_grid(ctx);
if (show_grid_ || show_room_guides_) draw_grid(ctx);
draw_spawn_point(ctx);
if (selected_object_id_ != 0) draw_selection_highlight(ctx);
if (show_colliders_) renderer_.draw_debug(ctx, level_, camera_, font);
@@ -317,20 +385,46 @@ namespace LightGame
const int32_t cam_y = camera_.get_y();
const int32_t start_x = (cam_x / ts) * ts;
const int32_t start_y = (cam_y / ts) * ts;
const int32_t end_x = cam_x + screen_width_ + ts;
const int32_t end_y = cam_y + screen_height_ + ts;
const int32_t end_x = cam_x + camera_.get_screen_width() + ts;
const int32_t end_y = cam_y + camera_.get_screen_height() + ts;
const RenderData::Color grid_color(80, 80, 80, 128);
for (int32_t x = start_x; x < end_x; x += ts)
if (show_grid_)
{
const int32_t sx = x - cam_x;
ctx.draw_line(Math::Vector2Int(sx, LightGame::LevelRenderer::kTopBlackBorder), Math::Vector2Int(sx, screen_height_), grid_color);
const RenderData::Color grid_color(80, 80, 80, 128);
for (int32_t x = start_x; x < end_x; x += ts)
{
const int32_t sx = x - cam_x;
ctx.draw_line(Math::Vector2Int(sx, LightGame::LevelRenderer::kTopBlackBorder), Math::Vector2Int(sx, screen_height_), grid_color);
}
for (int32_t y = start_y; y < end_y; y += ts)
{
const int32_t sy = y - cam_y + LightGame::LevelRenderer::kTopBlackBorder;
ctx.draw_line(Math::Vector2Int(0, sy), Math::Vector2Int(screen_width_, sy), grid_color);
}
}
for (int32_t y = start_y; y < end_y; y += ts)
if (show_room_guides_)
{
const int32_t sy = y - cam_y + LightGame::LevelRenderer::kTopBlackBorder;
ctx.draw_line(Math::Vector2Int(0, sy), Math::Vector2Int(screen_width_, sy), grid_color);
const RenderData::Color room_color(255, 196, 64, 255);
const int32_t room_w = kRoomTilesW * ts;
const int32_t room_h = kRoomTilesH * ts;
for (int32_t x = room_w; x < map_width_ * ts; x += room_w)
{
const int32_t sx = x - cam_x;
ctx.draw_line(Math::Vector2Int(sx, LightGame::LevelRenderer::kTopBlackBorder),
Math::Vector2Int(sx, screen_height_), room_color);
ctx.draw_line(Math::Vector2Int(sx + 1, LightGame::LevelRenderer::kTopBlackBorder),
Math::Vector2Int(sx + 1, screen_height_), room_color);
}
for (int32_t y = room_h; y < map_height_ * ts; y += room_h)
{
const int32_t sy = y - cam_y + LightGame::LevelRenderer::kTopBlackBorder;
ctx.draw_line(Math::Vector2Int(0, sy), Math::Vector2Int(screen_width_, sy), room_color);
ctx.draw_line(Math::Vector2Int(0, sy + 1), Math::Vector2Int(screen_width_, sy + 1), room_color);
}
}
}
@@ -339,11 +433,19 @@ namespace LightGame
const Math::Vector2Int sp = level_.get_spawn_point();
const int32_t sx = sp.x - camera_.get_x();
const int32_t sy = sp.y - camera_.get_y() + LightGame::LevelRenderer::kTopBlackBorder;
if (sx < -16 || sx > screen_width_ + 16 || sy < -16 || sy > screen_height_ + 16) return;
const int32_t ts = tile_size_;
if (sx + ts < 0 || sx > screen_width_ || sy + ts < 0 || sy > screen_height_) return;
const RenderData::Color color(0, 255, 0, 255);
ctx.draw_line(Math::Vector2Int(sx - 8, sy - 8), Math::Vector2Int(sx + 8, sy + 8), color);
ctx.draw_line(Math::Vector2Int(sx + 8, sy - 8), Math::Vector2Int(sx - 8, sy + 8), color);
ctx.draw_line(Math::Vector2Int(sx, sy), Math::Vector2Int(sx + ts, sy), color);
ctx.draw_line(Math::Vector2Int(sx + ts, sy), Math::Vector2Int(sx + ts, sy + ts), color);
ctx.draw_line(Math::Vector2Int(sx + ts, sy + ts), Math::Vector2Int(sx, sy + ts), color);
ctx.draw_line(Math::Vector2Int(sx, sy + ts), Math::Vector2Int(sx, sy), color);
const int32_t cx = sx + ts / 2;
const int32_t cy = sy + ts / 2;
ctx.draw_line(Math::Vector2Int(cx - 8, cy - 8), Math::Vector2Int(cx + 8, cy + 8), color);
ctx.draw_line(Math::Vector2Int(cx + 8, cy - 8), Math::Vector2Int(cx - 8, cy + 8), color);
}
void LevelEditor::draw_selection_highlight(Core::DrawContext& ctx)
@@ -372,15 +474,17 @@ namespace LightGame
{
if (ImGui::BeginMenu("File"))
{
if (ImGui::MenuItem("New Level", "Ctrl+N"))
if (ImGui::MenuItem("New 3x3 World", "Ctrl+N"))
{
new_level(32, 18, 32);
new_level(kRoomTilesW * kWorldRoomsW, kRoomTilesH * kWorldRoomsH, 32);
}
if (ImGui::MenuItem("New Single Room"))
{
new_level(kRoomTilesW, kRoomTilesH, 32);
}
if (ImGui::BeginMenu("Load Level"))
{
if (ImGui::MenuItem("Level 1")) pending_load_level_ = 0;
if (ImGui::MenuItem("Level 2")) pending_load_level_ = 1;
if (ImGui::MenuItem("Level 3")) pending_load_level_ = 2;
if (ImGui::MenuItem("Level Total 3x3")) pending_load_level_ = 0;
ImGui::EndMenu();
}
if (ImGui::MenuItem("Export to Header..."))
@@ -396,7 +500,9 @@ namespace LightGame
if (ImGui::BeginMenu("View"))
{
ImGui::MenuItem("Show Grid", nullptr, &show_grid_);
ImGui::MenuItem("Show Room Guides", nullptr, &show_room_guides_);
ImGui::MenuItem("Show Colliders", nullptr, &show_colliders_);
ImGui::MenuItem("Show Background", nullptr, &show_background_layer_);
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
@@ -423,17 +529,89 @@ namespace LightGame
ImGui::Separator();
if (current_tool_ == Tool::TileBrush || current_tool_ == Tool::Eraser)
{
ImGui::Text("Layer");
if (ImGui::RadioButton("Foreground", selected_layer_ == TileLayer::Foreground))
{
selected_layer_ = TileLayer::Foreground;
const uint16_t selected_tile = static_cast<uint16_t>(selected_tile_id_);
if (current_tool_ == Tool::TileBrush &&
selected_tile != TileId::Empty &&
!is_solid_tile(selected_tile) &&
!is_deadly_tile(selected_tile))
{
selected_tile_id_ = TileId::GroundTopGreen;
}
}
if (ImGui::RadioButton("Background", selected_layer_ == TileLayer::Background))
{
selected_layer_ = TileLayer::Background;
if (current_tool_ == Tool::TileBrush && is_solid_tile(static_cast<uint16_t>(selected_tile_id_)))
{
selected_tile_id_ = TileId::Background1;
}
}
ImGui::Separator();
}
if (current_tool_ == Tool::TileBrush)
{
ImGui::Text("Tile Type");
const char* tile_names[] = {
"0 Empty", "1 Ground Top", "2 Ground Fill", "3 Platform", "4 Spike"
struct TileEntry { uint16_t id; const char* name; };
static const TileEntry foreground_tile_entries[] = {
{ TileId::Empty, "0 Empty" },
{ TileId::GroundTopGreen, "5 Top Green" },
{ TileId::GroundFillGreen, "6 Fill Green" },
{ TileId::GroundTopGray, "7 Top Gray" },
{ TileId::GroundFillGray, "8 Fill Gray" },
{ TileId::GroundTopPurple, "9 Top Purple" },
{ TileId::GroundFillPurple, "10 Fill Purple" },
{ TileId::Platform, "11 Platform" },
{ TileId::Spike, "12 Spike Floor" },
{ TileId::SpikeCeiling, "22 Spike Ceiling" }
};
for (int i = 0; i < 5; ++i)
static const TileEntry background_tile_entries[] = {
{ TileId::Empty, "0 Empty" },
{ TileId::Background1, "1 Background 1" },
{ TileId::Background2, "2 Background 2" },
{ TileId::Background3, "3 Background 3" },
{ TileId::Background4, "4 Background 4" },
{ TileId::DecorVine, "23 Decor Vine" },
{ TileId::GroundTopGreen, "5 Ground Top Green" },
{ TileId::GroundFillGreen, "6 Ground Fill Green" },
{ TileId::GroundTopGray, "7 Ground Top Gray" },
{ TileId::GroundFillGray, "8 Ground Fill Gray" },
{ TileId::GroundTopPurple, "9 Ground Top Purple" },
{ TileId::GroundFillPurple, "10 Ground Fill Purple" },
{ TileId::Platform, "11 Platform" },
{ TileId::Spike, "12 Spike" },
{ TileId::SpikeCeiling, "22 Spike Ceiling" },
{ TileId::LightPlatformOff, "13 Light Off Visual" },
{ TileId::LightPlatformOn, "14 Light On Visual" },
{ TileId::ShadowPlatformOff, "15 Shadow Off Visual" },
{ TileId::ShadowPlatformOn, "16 Shadow On Visual" },
{ TileId::DoorClosed, "17 Door Closed Visual" },
{ TileId::DoorOpen, "18 Door Open Visual" },
{ TileId::Coin, "19 Coin Visual" },
{ TileId::Flag, "20 Flag Visual" },
{ TileId::Checkpoint, "21 Checkpoint Visual" }
};
const TileEntry* entries = (selected_layer_ == TileLayer::Foreground)
? foreground_tile_entries
: background_tile_entries;
const size_t entry_count = (selected_layer_ == TileLayer::Foreground)
? sizeof(foreground_tile_entries) / sizeof(foreground_tile_entries[0])
: sizeof(background_tile_entries) / sizeof(background_tile_entries[0]);
ImGui::BeginChild("TilePalette", ImVec2(0, 250), true);
for (size_t i = 0; i < entry_count; ++i)
{
if (ImGui::RadioButton(tile_names[i], selected_tile_id_ == i))
selected_tile_id_ = i;
if (ImGui::RadioButton(entries[i].name, selected_tile_id_ == entries[i].id))
selected_tile_id_ = entries[i].id;
}
ImGui::EndChild();
}
else if (current_tool_ == Tool::ObjectPlace)
{
@@ -452,6 +630,10 @@ namespace LightGame
if (ImGui::RadioButton(entries[i].name, selected_object_type_ == entries[i].type))
selected_object_type_ = entries[i].type;
}
if (selected_object_type_ == GameObjectType::Player)
{
ImGui::TextWrapped("Player marks the spawn tile. Only one Player spawn is kept.");
}
}
else if (current_tool_ == Tool::Select)
{
@@ -464,7 +646,7 @@ namespace LightGame
}
else if (current_tool_ == Tool::Eraser)
{
ImGui::Text("Click to erase tiles");
ImGui::Text("Erase %s layer", selected_layer_name());
}
ImGui::Separator();
@@ -535,9 +717,18 @@ namespace LightGame
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoScrollbar);
ImGui::Text("Map: %dx%d | Tile: %d | Objects: %zu | Light: %d",
ImGui::Text("Map: %dx%d | Tile: %d | Layer: %s | Objects: %zu | Light: %d",
map_width_, map_height_, tile_size_,
selected_layer_name(),
level_.object_count(), preview_light_level_);
ImGui::SameLine();
ImGui::Text("| Bg: %s/%s",
level_.get_background_tiles() ? "data" : "none",
show_background_layer_ ? "visible" : "hidden");
ImGui::SameLine();
ImGui::Text("| Room: %d,%d",
camera_.get_x() / (kRoomTilesW * tile_size_),
camera_.get_y() / (kRoomTilesH * tile_size_));
ImGui::End();
}
@@ -641,6 +832,23 @@ namespace LightGame
}
oss << " };\n\n";
const uint16_t* background_tiles = level_.get_background_tiles();
if (background_tiles)
{
oss << " static const uint16_t background_tiles[] = {\n";
for (int32_t y = 0; y < map_height_; ++y)
{
oss << " ";
for (int32_t x = 0; x < map_width_; ++x)
{
oss << background_tiles[y * map_width_ + x];
if (x < map_width_ - 1 || y < map_height_ - 1) oss << ",";
}
oss << "\n";
}
oss << " };\n\n";
}
const auto& objects = level_.get_all_objects();
int32_t spawn_count = 0;
for (size_t i = 0; i < objects.size(); ++i)
@@ -687,11 +895,25 @@ namespace LightGame
oss << " " << b.min_x << ",\n";
oss << " " << b.min_y << ",\n";
oss << " " << b.max_x << ",\n";
oss << " " << b.max_y << "\n";
oss << " " << b.max_y << ",\n";
if (background_tiles)
{
oss << " background_tiles\n";
}
else
{
oss << " nullptr\n";
}
oss << " };\n";
oss << " }\n";
oss << "}\n";
return oss.str();
}
const char* LevelEditor::selected_layer_name() const
{
return selected_layer_ == TileLayer::Foreground ? "Foreground" : "Background";
}
}
@@ -20,6 +20,7 @@ namespace LightGame
{
public:
enum class Tool { TileBrush, ObjectPlace, Select, Eraser };
enum class TileLayer { Foreground, Background };
LevelEditor(int32_t screen_w, int32_t screen_h);
@@ -57,6 +58,7 @@ namespace LightGame
void draw_ui_properties();
void draw_ui_status_bar();
void draw_ui_export_dialog();
const char* selected_layer_name() const;
int32_t world_to_tile_x(int32_t world_x) const;
int32_t world_to_tile_y(int32_t world_y) const;
@@ -76,13 +78,16 @@ namespace LightGame
bool should_quit_;
Tool current_tool_;
TileLayer selected_layer_;
int32_t selected_tile_id_;
GameObjectType selected_object_type_;
uint32_t selected_object_id_;
uint16_t preview_light_level_;
bool show_grid_;
bool show_room_guides_;
bool show_colliders_;
bool show_background_layer_;
int32_t map_width_;
int32_t map_height_;
+16 -1
View File
@@ -4,7 +4,8 @@
namespace LightGame
{
Level::Level()
: tilemap_(),
: background_tilemap_(),
tilemap_(),
bounds_(),
spawn_point_(),
next_id_(1)
@@ -151,4 +152,18 @@ namespace LightGame
}
tilemap_.tiles = tile_buffer_.data();
}
void Level::init_background_buffer(const uint16_t* source, int32_t count)
{
if (count <= 0 || source == nullptr)
{
return;
}
background_tiles_.resize(count);
for (int32_t i = 0; i < count; ++i)
{
background_tiles_[i] = source[i];
}
background_tilemap_.tiles = background_tiles_.data();
}
}
+8
View File
@@ -36,7 +36,9 @@ namespace LightGame
std::vector<Checkpoint> checkpoints_;
std::vector<uint16_t> original_tiles_;
std::vector<uint16_t> tile_buffer_;
std::vector<uint16_t> background_tiles_;
std::vector<TileLightRule> tile_light_rules_;
RenderData::Tilemap background_tilemap_;
RenderData::Tilemap tilemap_;
LevelBounds bounds_;
Math::Vector2Int spawn_point_;
@@ -63,6 +65,12 @@ namespace LightGame
uint16_t* get_mutable_tiles() { return tile_buffer_.empty() ? nullptr : tile_buffer_.data(); }
const uint16_t* get_original_tiles() const { return original_tiles_.empty() ? nullptr : original_tiles_.data(); }
void set_background_tilemap(const RenderData::Tilemap& tilemap) { background_tilemap_ = tilemap; }
const RenderData::Tilemap& get_background_tilemap() const { return background_tilemap_; }
void init_background_buffer(const uint16_t* source, int32_t count);
uint16_t* get_mutable_background_tiles() { return background_tiles_.empty() ? nullptr : background_tiles_.data(); }
const uint16_t* get_background_tiles() const { return background_tiles_.empty() ? nullptr : background_tiles_.data(); }
void add_tile_light_rule(const TileLightRule& rule) { tile_light_rules_.push_back(rule); }
const std::vector<TileLightRule>& get_tile_light_rules() const { return tile_light_rules_; }
@@ -48,5 +48,11 @@ namespace LightGame
int32_t bounds_min_y;
int32_t bounds_max_x;
int32_t bounds_max_y;
// Optional visual-only layer. Uses the same map size, tile size, atlas,
// and atlas_columns as tiles. It is rendered behind tiles and ignored
// by physics, triggers, and light tile rules. Omitted legacy LevelData
// aggregate initializers value-initialize this to nullptr.
const uint16_t* background_tiles;
};
}
+17 -8
View File
@@ -1,5 +1,6 @@
#include "LevelLoader.h"
#include "Sprite.h"
#include "TileIds.h"
#include "tile_atlas.h"
namespace LightGame
@@ -14,14 +15,14 @@ namespace LightGame
return RenderData::Sprite(atlas, col * tile_size, row * tile_size, tile_size, tile_size);
}
static const RenderData::Sprite spr_platform = MakeTileSprite(&tile_atlas_image, 3, 32, 4);
static const RenderData::Sprite spr_spike = MakeTileSprite(&tile_atlas_image, 4, 32, 4);
static const RenderData::Sprite spr_light_platform_on = MakeTileSprite(&tile_atlas_image, 6, 32, 4);
static const RenderData::Sprite spr_shadow_platform_on = MakeTileSprite(&tile_atlas_image, 8, 32, 4);
static const RenderData::Sprite spr_door_closed = MakeTileSprite(&tile_atlas_image, 9, 32, 4);
static const RenderData::Sprite spr_coin = MakeTileSprite(&tile_atlas_image, 11, 32, 4);
static const RenderData::Sprite spr_flag = MakeTileSprite(&tile_atlas_image, 12, 32, 4);
static const RenderData::Sprite spr_checkpoint = MakeTileSprite(&tile_atlas_image, 13, 32, 4);
static const RenderData::Sprite spr_platform = MakeTileSprite(&tile_atlas_image, TileId::Platform, 32, tile_atlas_columns);
static const RenderData::Sprite spr_spike = MakeTileSprite(&tile_atlas_image, TileId::Spike, 32, tile_atlas_columns);
static const RenderData::Sprite spr_light_platform_on = MakeTileSprite(&tile_atlas_image, TileId::LightPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_shadow_platform_on = MakeTileSprite(&tile_atlas_image, TileId::ShadowPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_door_closed = MakeTileSprite(&tile_atlas_image, TileId::DoorClosed, 32, tile_atlas_columns);
static const RenderData::Sprite spr_coin = MakeTileSprite(&tile_atlas_image, TileId::Coin, 32, tile_atlas_columns);
static const RenderData::Sprite spr_flag = MakeTileSprite(&tile_atlas_image, TileId::Flag, 32, tile_atlas_columns);
static const RenderData::Sprite spr_checkpoint = MakeTileSprite(&tile_atlas_image, TileId::Checkpoint, 32, tile_atlas_columns);
}
void LevelLoader::load(Level& level, const LevelData& data)
@@ -37,6 +38,14 @@ namespace LightGame
level.set_tilemap(tilemap);
level.init_tile_buffer(data.tiles, data.map_width * data.map_height);
if (data.background_tiles != nullptr)
{
RenderData::Tilemap background_tilemap = tilemap;
background_tilemap.tiles = data.background_tiles;
level.set_background_tilemap(background_tilemap);
level.init_background_buffer(data.background_tiles, data.map_width * data.map_height);
}
level.set_bounds(LevelBounds(
data.bounds_min_x,
data.bounds_min_y,
@@ -4,15 +4,35 @@
namespace LightGame
{
void LevelRenderer::draw(Core::DrawContext& ctx, const Level& level, const Camera2D& camera,
const LightEffectSystem& light_system, uint16_t light_level)
namespace
{
static const int32_t kBackgroundShadeNumerator = 80;
static const int32_t kBackgroundShadeShift = 7;
}
void LevelRenderer::draw(Core::DrawContext& ctx, const Level& level, const Camera2D& camera,
const LightEffectSystem& light_system, uint16_t light_level,
bool draw_background)
{
if (draw_background)
{
const RenderData::Tilemap& background_tilemap = level.get_background_tilemap();
if (background_tilemap.tiles != nullptr && background_tilemap.atlas != nullptr)
{
ctx.draw_tilemap_shaded(background_tilemap,
0, kTopBlackBorder,
camera.get_screen_width(), camera.get_screen_height(),
camera.get_x(), camera.get_y(),
kBackgroundShadeNumerator, kBackgroundShadeShift);
}
}
const RenderData::Tilemap& tilemap = level.get_tilemap();
if (tilemap.tiles != nullptr && tilemap.atlas != nullptr)
{
ctx.draw_tilemap(tilemap,
0, kTopBlackBorder,
camera.get_screen_width(), camera.get_screen_height() - kTopBlackBorder,
camera.get_screen_width(), camera.get_screen_height(),
camera.get_x(), camera.get_y());
}
@@ -35,7 +55,8 @@ namespace LightGame
const int32_t screen_y = obj.position.y - camera.get_y() + kTopBlackBorder;
if (screen_x + sprite->width < 0 || screen_x > camera.get_screen_width() ||
screen_y + sprite->height < 0 || screen_y > camera.get_screen_height())
screen_y + sprite->height < kTopBlackBorder ||
screen_y > kTopBlackBorder + camera.get_screen_height())
{
return;
}
@@ -22,7 +22,8 @@ namespace LightGame
static const int32_t kTopBlackBorder = 24;
void draw(Core::DrawContext& ctx, const Level& level, const Camera2D& camera,
const LightEffectSystem& light_system, uint16_t light_level);
const LightEffectSystem& light_system, uint16_t light_level,
bool draw_background = true);
void draw_debug(Core::DrawContext& ctx, const Level& level, const Camera2D& camera,
const RenderData::BitmapFont& font);
+127 -96
View File
@@ -6,39 +6,15 @@
#include "PointerInput.h"
#include "LevelData.h"
#include "LevelLoader.h"
#include "Level1Data.h"
#include "Level2Data.h"
#include "Level3Data.h"
#include "Level4Data.h"
#include "Level5Data.h"
#include "Level6Data.h"
#include "Level7Data.h"
#include "Level8Data.h"
#include "Level9Data.h"
#include "Level10Data.h"
#include "LevelTotalData.h"
#include "LevelTotalRooms.h"
#include "sprite_atlas.h"
#include "TileIds.h"
#include "Tilemap.h"
#include <cassert>
namespace LightGame
{
namespace
{
const LevelData* kLevels[] = {
&Level1::data,
&Level2::data,
&Level3::data,
&Level4::data,
&Level5::data,
&Level6::data,
&Level7::data,
&Level8::data,
&Level9::data,
&Level10::data
};
const int32_t kLevelCount = 10;
}
LightGameApp::LightGameApp(
int32_t screen_width,
int32_t screen_height,
@@ -59,15 +35,17 @@ namespace LightGame
light_system_(),
state_manager_(),
renderer_(),
current_level_index_(0),
player_id_(0),
room_grid_(&LevelTotal::room_grid),
current_room_index_(-1),
level_total_loaded_(false),
manual_light_level_(2048),
light_step_(256),
has_manual_override_(false),
debug_mode_(false),
death_processed_(false)
{
camera_.configure(screen_width, screen_height);
camera_.configure(screen_width, screen_height - LevelRenderer::kTopBlackBorder);
camera_.set_dead_zone(60, 40);
static const RenderData::Sprite player_idle[] = {
@@ -162,63 +140,6 @@ namespace LightGame
}
}
void LightGameApp::load_level(int32_t level_index)
{
if (level_index < 0 || level_index >= kLevelCount)
{
return;
}
current_level_index_ = level_index;
level_ = Level();
LevelLoader::load(level_, *kLevels[level_index]);
// Validate spawn point: must be on solid ground and not inside solid tiles.
{
GameObject temp_player;
temp_player.position = level_.get_spawn_point();
temp_player.collider = RenderData::BoundingBox2D(
Math::Vector2Int(8, 0), Math::Vector2Int(24, 32));
assert(physics_.is_grounded(temp_player, level_) &&
"Player spawn point is not on solid ground");
const RenderData::Tilemap& tilemap = level_.get_tilemap();
if (tilemap.tile_w > 0)
{
const RenderData::BoundingBox2D world_box = temp_player.get_world_collider();
const int32_t ts = tilemap.tile_w;
const int32_t tile_left = world_box.min.x / ts;
const int32_t tile_right = (world_box.max.x - 1) / ts;
const int32_t tile_top = world_box.min.y / ts;
const int32_t tile_bottom = (world_box.max.y - 1) / ts;
for (int32_t ty = tile_top; ty <= tile_bottom; ++ty)
{
for (int32_t tx = tile_left; tx <= tile_right; ++tx)
{
if (tx < 0 || tx >= tilemap.width || ty < 0 || ty >= tilemap.height)
continue;
const uint16_t tile_id = tilemap.get_tile(tx, ty);
assert((tile_id == 0 || tile_id == RenderData::Tilemap::EmptyTile) &&
"Player spawn point overlaps solid tiles");
}
}
}
}
const auto players = level_.get_objects_by_type(GameObjectType::Player);
if (!players.empty())
{
player_id_ = players[0]->id;
}
camera_.set_level_bounds(level_.get_bounds());
camera_.snap_to(level_.get_spawn_point());
state_manager_.get_hud().current_level = level_index + 1;
}
bool LightGameApp::is_action_pressed()
{
return button_input_ && button_input_->was_pressed();
@@ -229,7 +150,7 @@ namespace LightGame
(void)dt_ms;
if (is_action_pressed())
{
load_level(0);
load_room(LevelTotal::kStartRoomIndex);
state_manager_.set_state(GameState::Playing);
}
}
@@ -244,6 +165,9 @@ namespace LightGame
GameObject* player = level_.get_object(player_id_);
if (player)
{
check_room_transition();
update_room_respawn_anchor();
player_controller_.update(*player, level_, physics_, keyboard_input_, pointer_input_, screen_width_, screen_height_, dt_ms);
camera_.follow(player->position);
@@ -275,7 +199,8 @@ namespace LightGame
{
state_manager_.get_hud().lives = 3;
state_manager_.get_hud().collectibles = 0;
load_level(0);
level_total_loaded_ = false;
load_room(LevelTotal::kStartRoomIndex);
state_manager_.set_state(GameState::Playing);
}
}
@@ -285,17 +210,123 @@ namespace LightGame
(void)dt_ms;
if (is_action_pressed())
{
const int32_t next = current_level_index_ + 1;
if (next < kLevelCount)
state_manager_.set_state(GameState::Title);
}
}
void LightGameApp::load_room(int32_t room_index)
{
if (!room_grid_)
{
return;
}
const RoomDef* room = room_by_index(*room_grid_, room_index);
if (!room)
{
return;
}
if (!level_total_loaded_)
{
level_ = Level();
LevelLoader::load(level_, LevelTotal::data);
level_total_loaded_ = true;
const auto players = level_.get_objects_by_type(GameObjectType::Player);
if (!players.empty())
{
load_level(next);
state_manager_.set_state(GameState::Playing);
}
else
{
state_manager_.set_state(GameState::Title);
player_id_ = players[0]->id;
}
}
GameObject* player = level_.get_object(player_id_);
if (player)
{
player->position = room->default_spawn;
player->velocity = Math::Vector2Int(0, 0);
player->active = true;
}
current_room_index_ = room_index;
level_.set_spawn_point(room->default_spawn);
// Deactivate checkpoints outside the active room so respawn falls back to room spawn.
auto& checkpoints = level_.get_checkpoints();
for (size_t i = 0; i < checkpoints.size(); ++i)
{
Math::Vector2Int p = checkpoints[i].position;
if (room_index_of(*room_grid_, p) != room_index)
{
checkpoints[i].activated = false;
}
}
LevelBounds rb(room->bounds.min_x, room->bounds.min_y,
room->bounds.max_x, room->bounds.max_y);
camera_.set_level_bounds(rb);
camera_.snap_to(room->default_spawn);
state_manager_.get_hud().current_level = room_index + 1;
}
void LightGameApp::check_room_transition()
{
if (!room_grid_)
{
return;
}
const GameObject* player = level_.get_object(player_id_);
if (!player)
{
return;
}
const int32_t new_idx = room_index_of(*room_grid_, player->position);
if (new_idx == current_room_index_ || new_idx < 0)
{
return;
}
const RoomDef* room = room_by_index(*room_grid_, new_idx);
if (!room)
{
return;
}
current_room_index_ = new_idx;
LevelBounds rb(room->bounds.min_x, room->bounds.min_y,
room->bounds.max_x, room->bounds.max_y);
camera_.set_level_bounds(rb);
camera_.snap_to(player->position);
state_manager_.get_hud().current_level = new_idx + 1;
}
void LightGameApp::update_room_respawn_anchor()
{
if (!room_grid_ || current_room_index_ < 0)
{
return;
}
const RoomDef* room = room_by_index(*room_grid_, current_room_index_);
if (!room)
{
return;
}
// Drop activations belonging to other rooms; keep only current-room ones.
auto& checkpoints = level_.get_checkpoints();
for (size_t i = 0; i < checkpoints.size(); ++i)
{
if (!checkpoints[i].activated)
{
continue;
}
if (room_index_of(*room_grid_, checkpoints[i].position) != current_room_index_)
{
checkpoints[i].activated = false;
}
}
level_.set_spawn_point(room->default_spawn);
}
void LightGameApp::check_triggers()
+8 -2
View File
@@ -8,6 +8,7 @@
#include "LightEffectSystem.h"
#include "GameStateManager.h"
#include "LevelRenderer.h"
#include "RoomLayout.h"
#include "IPhotoSensor.h"
namespace Core
@@ -49,9 +50,12 @@ namespace LightGame
GameStateManager state_manager_;
LevelRenderer renderer_;
int32_t current_level_index_;
uint32_t player_id_;
const RoomGrid* room_grid_;
int32_t current_room_index_;
bool level_total_loaded_;
uint16_t manual_light_level_;
uint16_t light_step_;
@@ -75,12 +79,14 @@ namespace LightGame
void set_debug_mode(bool enabled) { debug_mode_ = enabled; }
private:
void load_level(int32_t level_index);
void load_room(int32_t room_index);
void update_playing(uint32_t dt_ms);
void update_title(uint32_t dt_ms);
void update_game_over(uint32_t dt_ms);
void update_level_complete(uint32_t dt_ms);
void check_triggers();
void check_room_transition();
void update_room_respawn_anchor();
bool is_action_pressed();
};
}
+2 -10
View File
@@ -1,4 +1,5 @@
#include "Physics2D.h"
#include "TileIds.h"
#include <algorithm>
#include <cstdlib>
@@ -40,16 +41,7 @@ namespace LightGame
}
const uint16_t tile_id = tilemap.get_tile(tile_x, tile_y);
if (tile_id == 0 || tile_id == RenderData::Tilemap::EmptyTile)
{
return false;
}
// spike(4) 是杀伤物,不参与碰撞 — 玩家须能踩进去触发 check_death
if (tile_id == 4)
{
return false;
}
return true;
return is_solid_tile(tile_id);
}
void Physics2D::collide_tilemap(GameObject& obj, const RenderData::Tilemap& tilemap,
@@ -1,6 +1,7 @@
#include "PlayerController.h"
#include "IKeyboardState.h"
#include "PointerInput.h"
#include "TileIds.h"
#include <algorithm>
namespace LightGame
@@ -257,7 +258,7 @@ void PlayerController::check_death(GameObject& obj, Level& level)
continue;
}
const uint16_t tile_id = tilemap.get_tile(tx, ty);
if (tile_id == 4)
if (is_deadly_tile(tile_id))
{
const int32_t spike_top = (ty + 1) * ts - kSpikeHitHeight;
const int32_t spike_bottom = (ty + 1) * ts;
@@ -0,0 +1,40 @@
#include "RoomLayout.h"
namespace LightGame
{
int32_t room_index_of(const RoomGrid& grid, const Math::Vector2Int& world_pos)
{
if (grid.room_pixel_w <= 0 || grid.room_pixel_h <= 0)
{
return -1;
}
int32_t col = world_pos.x / grid.room_pixel_w;
int32_t row = world_pos.y / grid.room_pixel_h;
if (col < 0) col = 0;
if (row < 0) row = 0;
if (col >= grid.cols) col = grid.cols - 1;
if (row >= grid.rows) row = grid.rows - 1;
return row * grid.cols + col;
}
const RoomDef* room_at(const RoomGrid& grid, int32_t col, int32_t row)
{
if (col < 0 || col >= grid.cols || row < 0 || row >= grid.rows)
{
return nullptr;
}
return &grid.rooms[row * grid.cols + col];
}
const RoomDef* room_by_index(const RoomGrid& grid, int32_t index)
{
if (index < 0 || index >= grid.cols * grid.rows)
{
return nullptr;
}
return &grid.rooms[index];
}
}
@@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
#include "Vector2.h"
namespace LightGame
{
struct RoomBounds
{
int32_t min_x;
int32_t min_y;
int32_t max_x;
int32_t max_y;
};
struct RoomDef
{
RoomBounds bounds;
Math::Vector2Int default_spawn;
};
struct RoomGrid
{
const RoomDef* rooms;
int32_t cols;
int32_t rows;
int32_t room_pixel_w;
int32_t room_pixel_h;
};
int32_t room_index_of(const RoomGrid& grid, const Math::Vector2Int& world_pos);
const RoomDef* room_at(const RoomGrid& grid, int32_t col, int32_t row);
const RoomDef* room_by_index(const RoomGrid& grid, int32_t index);
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <cstdint>
#include "Tilemap.h"
namespace LightGame
{
namespace TileId
{
static const uint16_t Empty = 0;
static const uint16_t Hidden = RenderData::Tilemap::EmptyTile;
static const uint16_t Background1 = 1;
static const uint16_t Background2 = 2;
static const uint16_t Background3 = 3;
static const uint16_t Background4 = 4;
static const uint16_t GroundTopGreen = 5;
static const uint16_t GroundFillGreen = 6;
static const uint16_t GroundTopGray = 7;
static const uint16_t GroundFillGray = 8;
static const uint16_t GroundTopPurple = 9;
static const uint16_t GroundFillPurple = 10;
static const uint16_t Platform = 11;
static const uint16_t Spike = 12;
static const uint16_t LightPlatformOff = 13;
static const uint16_t LightPlatformOn = 14;
static const uint16_t ShadowPlatformOff = 15;
static const uint16_t ShadowPlatformOn = 16;
static const uint16_t DoorClosed = 17;
static const uint16_t DoorOpen = 18;
static const uint16_t Coin = 19;
static const uint16_t Flag = 20;
static const uint16_t Checkpoint = 21;
static const uint16_t SpikeCeiling = 22;
static const uint16_t DecorVine = 23;
}
inline bool is_deadly_tile(uint16_t tile_id)
{
return tile_id == TileId::Spike || tile_id == TileId::SpikeCeiling;
}
inline bool is_solid_tile(uint16_t tile_id)
{
switch (tile_id)
{
case TileId::GroundTopGreen:
case TileId::GroundFillGreen:
case TileId::GroundTopGray:
case TileId::GroundFillGray:
case TileId::GroundTopPurple:
case TileId::GroundFillPurple:
case TileId::Platform:
return true;
default:
return false;
}
}
}
-104
View File
@@ -1,104 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level10
{
// 终极之塔:综合最终关。三段、两个 Checkpoint。
// 段 1:地面尖刺 + 光暗台错位 + 暗门(须切到 0-1023)。
// 段 2:阶梯爬升,错位光暗台。
// 段 3:顶板走廊,中间光门(1500-2500)+ 高速节奏跳到出口。
static const int32_t kMapWidth = 50;
static const int32_t kMapHeight = 12;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
// 顶天墙 1:列 18 row 0..8 (仅 row 9 通道给暗门)
// 顶天墙 2:列 34 row 0..2 (仅 row 3 通道给中间光门,row 4 是门下挡板)
// 阶梯块:(32,4)(33,4) 单格 ground_fill 让玩家从段 2 跳到顶板高度
// 顶板:row 4=ground_top, row 5=ground_fill (列 35..49),玩家走在 row 3
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,1,2,1,1,4,4,4,4,4,4,4,4,4,1,1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 9, 0, 0, TriggerAction::None, 0 },
// === 段 1(4-16)地面尖刺 + 光暗错位 ===
{ GameObjectType::ShadowPlatform, 5, 8, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 7, 7, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 8, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 11, 7, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 13, 6, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 7, 3072, 4095, TriggerAction::None, 0 },
// 暗门入口(须切到 0-1023)
{ GameObjectType::Door, 18, 9, 0, 1023, TriggerAction::None, 0 },
// Checkpoint 1
{ GameObjectType::Trigger, 19, 9, 0, 0, TriggerAction::Checkpoint, 0 },
// === 段 2(20-31)阶梯爬升,错位切换 ===
{ GameObjectType::LightPlatform, 22, 7, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 24, 6, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 26, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 28, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 30, 5, 3072, 4095, TriggerAction::None, 0 },
// 段 2 顶端阶梯:(32,4)(33,4) 是 tilemap 中的 ground_fill,
// 玩家立足 row 3 在阶梯顶部停留并切换光强。
// 中间光门:玩家走在 row 3,门挡在 (34,3)
{ GameObjectType::Door, 34, 3, 1500, 2500, TriggerAction::None, 0 },
// Checkpoint 2(顶板入口)
{ GameObjectType::Trigger, 36, 3, 0, 0, TriggerAction::Checkpoint, 0 },
// === 段 3(35-49)顶板走廊:浮空台节奏跳 ===
{ GameObjectType::ShadowPlatform, 39, 2, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 42, 1, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 45, 2, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 7, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 13, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 26, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 42, 0, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 45, 1, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 49, 3, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
25,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 9 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,53 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level1
{
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,4,4,4,4,1,1,4,4,4,4,4,4,1,1,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 5, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 10, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 18, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 23, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
5,
nullptr,
0,
Math::Vector2Int(64, 160),
0,
0,
800,
256
};
}
}
@@ -1,67 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level2
{
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 5=light_platform_off 6=light_platform_on
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 4, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 6, 3, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 2, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 10, 3, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 12, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 14, 3, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 16, 2, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 18, 3, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 20, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 22, 5, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 6, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 10, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 16, 1, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 24, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
15,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 5 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,70 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level3
{
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill
// Objects: LightPlatform, ShadowPlatform, Door via spawns
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 4, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 6, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 10, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 12, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Door, 15, 4, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 17, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 19, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 21, 4, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 22, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 6, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 10, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 19, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 24, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
15,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 5 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,71 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level4
{
// 影中漫步:前半段须降到暗(0-1023)踩 ShadowPlatform,
// 中央安全岛切换光强,后半段须升到亮(3072-4095)踩 LightPlatform。
// 地面间隙下方是 spike,掉下即死。
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,4,4,4,4,4,4,4,1,1,1,4,4,4,4,4,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
// 前半段:暗时实
{ GameObjectType::ShadowPlatform, 4, 4, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 6, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 8, 4, 0, 1023, TriggerAction::None, 0 },
// 后半段:亮时实
{ GameObjectType::LightPlatform, 13, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 3, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 17, 2, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 19, 3, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 21, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 6, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 11, 5, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 17, 1, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 24, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
13,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 5 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,73 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level5
{
// 三色之门:必须依次通过 3 道光阈值不同的门。
// 实心墙到顶,门是唯一通路。
// 门 1:暗门(0-1023)
// 门 2:中间光门(1500-2500)
// 门 3:亮门(3300-4095)
// 每两段间留有空间和提示性平台供玩家手动调光。
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 3=platform 4=spike
// 墙体 y=0..4 顶天,y=5 留空作为门的通道,y=6 是 ground_top 玩家立足
static const uint16_t tiles[] = {
0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,0,0,2,0,0,0,0,0,0,2,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,2,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
// 三道门(站立层)
{ GameObjectType::Door, 4, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Door, 12, 5, 1500, 2500, TriggerAction::None, 0 },
{ GameObjectType::Door, 19, 5, 3300, 4095, TriggerAction::None, 0 },
// 中段提示:每段中央放对应阈值的浮空平台
{ GameObjectType::ShadowPlatform, 7, 3, 0, 1500, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 3, 1500, 3000, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 22, 3, 3000, 4095, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 7, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 15, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 22, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 24, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
11,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 5 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,79 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level6
{
// 刺影回廊:长尖刺走廊,依靠光感平台跳跃前进。
// 光台与暗台错位排列,强迫玩家在跳跃中切换光强。
// 此关引入 Checkpoint,约 1/2 处一个救援点。
static const int32_t kMapWidth = 35;
static const int32_t kMapHeight = 8;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,1,1,1,1,4,4,4,4,4,4,4,4,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 5, 0, 0, TriggerAction::None, 0 },
// 第一段尖刺走廊:暗台与光台错位(玩家须在跳跃间切换)
{ GameObjectType::ShadowPlatform, 5, 4, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 7, 3, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 4, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 11, 3, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 13, 4, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 3, 3072, 4095, TriggerAction::None, 0 },
// 中段安全岛 + Checkpoint
{ GameObjectType::Trigger, 18, 5, 0, 0, TriggerAction::Checkpoint, 0 },
// 第二段更密:跳跃间隔更短
{ GameObjectType::LightPlatform, 21, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 23, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 25, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 27, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 29, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 31, 4, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 7, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 13, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 25, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 31, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 34, 5, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
19,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 5 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,80 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level7
{
// 暗井攀登:垂直关卡,从底部 Z 字形爬升到顶部。
// 光台与暗台交替排布,玩家须不断切换光强。
// 中段 Checkpoint 作为安全点,顶部出口落在左侧。
static const int32_t kMapWidth = 25;
static const int32_t kMapHeight = 12;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
// 顶台在 y=1(ground_top) / y=2(ground_fill),玩家从下方跳到顶台上方 y=0 站立。
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 9, 0, 0, TriggerAction::None, 0 },
// Z 字形阶梯:右上方向爬升
{ GameObjectType::LightPlatform, 5, 9, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 8, 8, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 11, 7, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 14, 6, 0, 1023, TriggerAction::None, 0 },
// 中段 Checkpoint:站在亮光实台上
{ GameObjectType::LightPlatform, 17, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 17, 5, 0, 0, TriggerAction::Checkpoint, 0 },
// 上半段回头向左爬
{ GameObjectType::LightPlatform, 14, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 11, 3, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 3, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 8, 7, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 17, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 11, 2, 0, 0, TriggerAction::None, 0 },
// 顶部出口:站在顶台 ground_top 上方
{ GameObjectType::Trigger, 7, 0, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
14,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 9 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,85 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level8
{
// 双相迷宫:节奏跳 + 门锁 + 长尖刺综合关。
// 三段式:spike 节奏 → 双门夹道(须切换光强通过)→ 长走廊高速切换。
// Checkpoint 设在第二段后。
static const int32_t kMapWidth = 40;
static const int32_t kMapHeight = 10;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
// 列 14 与列 24 是顶天实墙(仅留 y=7 一格作为门通道)
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,4,4,4,4,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,4,4,4,4,4,4,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 7, 0, 0, TriggerAction::None, 0 },
// 第一段:spike 4-7 节奏跳
{ GameObjectType::LightPlatform, 5, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 7, 5, 0, 1023, TriggerAction::None, 0 },
// 第一道暗门(站立层 14,7):暗光通过
{ GameObjectType::Door, 14, 7, 0, 1023, TriggerAction::None, 0 },
// 中段安全区 15-23
{ GameObjectType::Collectible, 18, 6, 0, 0, TriggerAction::None, 0 },
// 第二道亮门(24,7):亮光通过
{ GameObjectType::Door, 24, 7, 3072, 4095, TriggerAction::None, 0 },
// Checkpoint:刚过亮门
{ GameObjectType::Trigger, 25, 7, 0, 0, TriggerAction::Checkpoint, 0 },
// 第三段:spike 27-37 高速切换
{ GameObjectType::LightPlatform, 28, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 30, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 32, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 34, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 36, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 5, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 30, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 36, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 39, 7, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
16,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 7 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -1,93 +0,0 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace Level9
{
// 光暗交界:综合挑战。三段,每段一个 Checkpoint。
// 段 1:尖刺 + 阶梯,引入"中间光"门(1500-2500)。
// 段 2:长尖刺走廊,光台暗台双层错位。
// 段 3:双门夹道(暗 → 亮),失误重置回上一 CP。
static const int32_t kMapWidth = 45;
static const int32_t kMapHeight = 10;
static const int32_t kTileSize = 32;
// Tile IDs: 0=empty 1=ground_top 2=ground_fill 4=spike
// 列 16、列 30 是顶天实墙(仅 y=7 通道)
static const uint16_t tiles[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,4,4,4,4,4,4,4,4,1,1,1,1,2,1,4,4,4,4,4,4,4,4,4,4,4,1,2,1,4,4,4,4,1,1,4,4,4,4,4,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::Player, 2, 7, 0, 0, TriggerAction::None, 0 },
// === 段 1(4-15) ===
{ GameObjectType::LightPlatform, 5, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 7, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 11, 5, 0, 1023, TriggerAction::None, 0 },
// 中间光门(须把光强调到中间区间)
{ GameObjectType::Door, 16, 7, 1500, 2500, TriggerAction::None, 0 },
// 段 1 末尾 Checkpoint
{ GameObjectType::Trigger, 17, 7, 0, 0, TriggerAction::Checkpoint, 0 },
// === 段 2(18-29)双层错位 ===
{ GameObjectType::LightPlatform, 19, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 21, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 23, 4, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 25, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 27, 6, 3072, 4095, TriggerAction::None, 0 },
// 段 2 末尾 Checkpoint
{ GameObjectType::Trigger, 29, 7, 0, 0, TriggerAction::Checkpoint, 0 },
// === 段 3(30-43)双门夹道 ===
{ GameObjectType::Door, 30, 7, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 33, 6, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 35, 5, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 39, 6, 3072, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 41, 5, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 9, 4, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 23, 2, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 35, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 41, 3, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 44, 7, 0, 0, TriggerAction::LevelComplete, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
23,
nullptr,
0,
Math::Vector2Int(2 * kTileSize, 7 * kTileSize),
0,
0,
kMapWidth * kTileSize,
kMapHeight * kTileSize
};
}
}
@@ -0,0 +1,380 @@
#pragma once
#include "LevelData.h"
#include "tile_atlas.h"
namespace LightGame
{
namespace LevelTotal
{
static const int32_t kMapWidth = 96;
static const int32_t kMapHeight = 54;
static const int32_t kTileSize = 32;
static const uint16_t tiles[] = {
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,22,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,22,22,22,22,22,0,0,0,0,0,0,0,0,22,22,22,22,0,0,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,5,14,14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,17,17,0,0,0,0,0,0,0,0,6,6,5,6,6,5,6,6,5,5,5,5,5,6,6,6,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,14,14,14,14,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,5,6,6,6,6,6,6,5,5,5,5,5,6,6,6,6,6,6,6,8,8,6,6,6,6,6,6,6,6,6,6,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,5,6,6,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,6,6,6,6,5,5,5,5,5,5,5,0,0,0,0,0,0,0,0,0,0,0,0,
6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,0,0,0,0,0,5,5,5,5,5,
6,6,6,6,0,0,6,6,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,5,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,6,6,6,8,8,8,
6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,22,22,22,22,0,0,0,0,0,0,8,8,8,8,8,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,8,8,8,8,8,8,8,8,8,8,0,6,6,8,8,8,8,8,8,
6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,6,6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,6,6,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,14,8,8,8,8,8,8,8,8,22,0,0,0,0,8,8,8,8,8,
6,6,6,0,0,0,0,17,0,0,0,0,0,16,16,16,16,6,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,22,22,22,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,8,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,
8,8,8,0,0,17,17,17,0,0,0,0,16,16,16,16,16,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,8,8,8,8,
8,8,8,0,0,8,17,17,17,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,22,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,8,0,0,8,
8,8,8,7,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,7,0,0,0,0,0,0,0,0,0,0,0,0,14,17,0,0,0,0,0,0,0,0,0,0,16,16,0,0,0,0,8,8,
8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,17,17,17,17,0,0,0,0,0,0,0,22,22,0,0,0,16,8,8,
8,8,8,8,7,7,14,14,14,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,0,0,0,0,0,0,0,0,0,0,0,16,8,8,
8,8,8,8,8,8,14,14,14,14,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,8,8,8,
8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,8,
8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,16,16,8,
8,8,8,8,0,8,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,14,14,14,8,17,17,8,8,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,16,16,8,
8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,16,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,8,7,7,7,7,7,7,12,12,12,12,7,7,7,12,12,12,12,12,8,8,10,10,16,16,16,8,10,8,8,8,8,8,8,8,8,14,8,8,17,17,17,17,8,8,7,7,7,0,0,0,0,0,0,0,0,0,0,16,16,8,8,
8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,16,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,16,16,8,8,8,8,8,10,8,8,8,8,8,8,8,8,8,8,8,17,17,17,8,8,8,8,8,8,8,7,7,7,0,0,0,0,16,8,8,8,
8,8,8,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,8,8,8,8,8,8,8,10,8,8,10,8,8,8,8,8,8,8,8,8,8,8,8,8,17,17,8,8,8,8,8,8,8,8,8,8,7,0,0,0,0,8,8,8,
8,8,8,0,0,0,0,0,14,14,14,14,14,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,8,8,8,8,8,8,8,8,8,10,8,8,8,8,8,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,8,8,8,
8,8,8,7,7,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,8,8,8,8,8,8,8,8,10,10,10,10,10,8,8,8,8,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,8,8,8,
8,8,8,8,8,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,8,8,10,10,10,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,8,8,
8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,0,0,0,0,0,0,0,0,8,
8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,8,8,0,0,0,0,0,0,0,0,0,8,
8,8,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,10,10,10,0,0,10,10,10,10,10,10,10,10,8,8,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,8,8,10,0,0,0,0,0,0,0,0,0,8,
8,8,8,8,8,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,0,0,0,10,10,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,10,10,8,8,8,8,8,8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,8,
8,8,8,8,8,8,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,8,10,10,10,10,10,10,10,10,22,22,10,10,10,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,22,0,0,0,0,0,0,0,0,12,10,
10,10,10,8,8,8,8,8,8,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,0,0,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,0,0,0,0,0,0,0,0,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,0,0,0,0,10,10,10,10,10,10,10,10,10,10,10,10,22,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,22,22,0,0,0,0,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,10,10,10,0,0,0,0,10,10,10,10,10,10,10,10,10,10,10,22,0,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,10,16,16,16,16,16,16,10,10,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,10,10,0,0,0,0,10,10,10,10,0,0,0,0,0,0,10,10,0,0,0,0,10,10,10,10,0,0,0,0,0,10,10,10,10,10,10,10,22,22,0,0,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,10,16,17,10,10,17,16,10,10,10,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,0,0,0,0,14,14,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,10,16,16,16,16,16,16,10,10,10,10,10,10,10,10,10,10,10,10,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,14,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,10,
10,10,10,0,16,16,16,16,16,16,16,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,10,10,
10,10,0,0,14,17,10,10,17,14,14,14,10,10,10,10,10,10,10,10,10,10,10,9,9,9,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,10,10,10,10,
10,10,10,0,14,14,17,17,14,14,14,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,9,10,10,10,10,10,
10,10,10,10,14,14,14,14,14,14,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,0,0,0,0,0,0,0,0,0,0,0,12,12,12,12,12,9,9,9,9,9,9,9,9,9,0,0,9,9,9,9,9,10,10,10,14,14,14,14,14,14,14,14,14,14,14,14,0,12,0,0,0,9,9,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,0,0,0,0,0,0,12,12,12,9,9,9,9,9,10,10,10,10,10,10,10,10,10,9,10,10,10,10,10,10,10,10,10,10,12,12,12,12,12,12,12,12,12,12,12,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,
10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10
};
static const uint16_t background_tiles[] = {
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
};
static const ObjectSpawn spawns[] = {
{ GameObjectType::LightPlatform, 11, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 12, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 13, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 14, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 14, 10, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 10, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 15, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 16, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 16, 10, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 17, 10, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 17, 9, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Door, 40, 9, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 41, 9, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 42, 9, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 43, 9, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 92, 12, 0, 0, TriggerAction::LevelComplete, 0 },
{ GameObjectType::LightPlatform, 51, 29, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 68, 30, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 69, 30, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 70, 30, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 70, 31, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 58, 27, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 57, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 57, 32, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 56, 32, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 58, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 59, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 88, 24, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 89, 24, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 89, 23, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 90, 23, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 91, 23, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 91, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 25, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 26, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 27, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 92, 27, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 92, 28, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 28, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 29, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 30, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 92, 30, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 92, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 92, 32, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 93, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 94, 30, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 94, 29, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 94, 28, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 92, 29, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 91, 29, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 24, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 23, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 22, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 22, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 75, 21, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 75, 22, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 21, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 77, 21, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 77, 20, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 20, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 19, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 75, 19, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 75, 20, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 20, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 19, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Door, 72, 30, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 73, 30, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 73, 31, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 74, 31, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 75, 31, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 76, 31, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 76, 32, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 75, 32, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 76, 33, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 77, 33, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 77, 32, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 80, 25, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 79, 26, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 79, 25, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 80, 26, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 78, 25, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 77, 25, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 77, 24, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 69, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 70, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 71, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 78, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 79, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 80, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 72, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 73, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 75, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 76, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 77, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 72, 48, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 72, 47, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 73, 47, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 73, 48, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 47, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 46, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 73, 46, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 75, 46, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 75, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 57, 48, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 10, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 10, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 11, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 7, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 6, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 5, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 5, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 4, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 4, 51, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 4, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 7, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 7, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 6, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 6, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 5, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 5, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 4, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 4, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 4, 46, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 4, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 5, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 6, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 7, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 8, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 45, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 46, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 8, 47, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 8, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 9, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 10, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 46, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 8, 46, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 50, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 50, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 8, 49, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 49, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 34, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 34, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 10, 34, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 11, 34, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 12, 34, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 27, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 27, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 26, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 7, 26, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 7, 27, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 6, 27, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 6, 26, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 22, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 22, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 23, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 23, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 22, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 21, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 23, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 8, 23, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 13, 21, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 13, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 12, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 14, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 14, 21, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 21, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 16, 22, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 16, 21, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 32, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 31, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 3, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 2, 49, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 3, 50, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 3, 48, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 69, 27, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 89, 49, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 4, 34, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 15, 16, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 34, 10, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 37, 30, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Collectible, 42, 27, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 58, 26, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 51, 28, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 58, 25, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 87, 24, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 86, 24, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 45, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 43, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 74, 44, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 30, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 29, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 15, 28, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::ShadowPlatform, 14, 20, 0, 1023, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 37, 30, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Player, 37, 30, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 37, 30, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Collectible, 94, 23, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 90, 22, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Door, 81, 26, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 68, 43, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::Collectible, 71, 50, 0, 0, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 36, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 8, 35, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::LightPlatform, 9, 35, 3192, 4095, TriggerAction::None, 0 },
{ GameObjectType::Door, 4, 14, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 14, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 13, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 4, 13, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 12, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 12, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 13, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 13, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 7, 12, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 4, 11, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 5, 11, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Door, 6, 11, 1023, 3192, TriggerAction::None, 0 },
{ GameObjectType::Trigger, 71, 9, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 58, 50, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Trigger, 25, 48, 0, 0, TriggerAction::Checkpoint, 0 },
{ GameObjectType::Collectible, 52, 44, 0, 0, TriggerAction::None, 0 },
};
static const RenderData::Image kAtlas(tile_atlas_pixels, tile_atlas_width, tile_atlas_height);
static const LevelData data = {
tiles,
kMapWidth,
kMapHeight,
kTileSize,
&kAtlas,
tile_atlas_columns,
spawns,
228,
nullptr,
0,
Math::Vector2Int(1184, 960),
0,
0,
3072,
1728,
background_tiles
};
}
}
@@ -0,0 +1,34 @@
#pragma once
#include "RoomLayout.h"
namespace LightGame
{
namespace LevelTotal
{
// 3 columns x 3 rows; each room is 32x18 tiles = 1024x576 pixels.
// default_spawn 为该房间的 Checkpoint Trigger 像素位置(tile_x*32, tile_y*32)。
// 数据源:LevelTotalData.h spawns 中 TriggerAction::Checkpoint 条目。
static const RoomDef rooms_array[] = {
{ { 0, 0, 1024, 576 }, Math::Vector2Int( 480, 512) }, // tile(15,16)
{ { 1024, 0, 2048, 576 }, Math::Vector2Int(1088, 320) }, // tile(34,10)
{ { 2048, 0, 3072, 576 }, Math::Vector2Int(2272, 288) }, // tile(71, 9)
{ { 0, 576, 1024, 1152 }, Math::Vector2Int( 128, 1088) }, // tile( 4,34)
{ { 1024, 576, 2048, 1152 }, Math::Vector2Int(1184, 960) }, // tile(37,30)
{ { 2048, 576, 3072, 1152 }, Math::Vector2Int(2208, 864) }, // tile(69,27)
{ { 0, 1152, 1024, 1728 }, Math::Vector2Int( 800, 1536) }, // tile(25,48)
{ { 1024, 1152, 2048, 1728 }, Math::Vector2Int(1856, 1600) }, // tile(58,50)
{ { 2048, 1152, 3072, 1728 }, Math::Vector2Int(2848, 1568) } // tile(89,49)
};
static const RoomGrid room_grid = {
rooms_array,
3,
3,
1024,
576
};
static const int32_t kStartRoomIndex = 4; // center (1,1)
}
}
+2 -6
View File
@@ -14,9 +14,7 @@
#include "LightGameApp.h"
#include "LevelEditor.h"
#include "font_atlas.h"
#include "Level1Data.h"
#include "Level2Data.h"
#include "Level3Data.h"
#include "LevelTotalData.h"
#ifdef USE_FRAMEBUFFER
#include "FBDisplay.h"
@@ -163,9 +161,7 @@ int main(int argc, char* argv[])
if (editor.get_pending_load() >= 0)
{
int32_t idx = editor.get_pending_load();
if (idx == 0) editor.load_from_data(LightGame::Level1::data);
else if (idx == 1) editor.load_from_data(LightGame::Level2::data);
else if (idx == 2) editor.load_from_data(LightGame::Level3::data);
if (idx == 0) editor.load_from_data(LightGame::LevelTotal::data);
editor.clear_pending_load();
}
editor.update(timer.fixed_delta_ms(), &keyboardState, &pointerInput);
@@ -1,4 +1,5 @@
#include "LightEffectSystem.h"
#include "TileIds.h"
#include "tile_atlas.h"
namespace LightGame
@@ -13,12 +14,12 @@ namespace LightGame
return RenderData::Sprite(atlas, col * tile_size, row * tile_size, tile_size, tile_size);
}
static const RenderData::Sprite spr_light_platform_on = MakeTileSprite(&tile_atlas_image, 6, 32, 4);
static const RenderData::Sprite spr_light_platform_off = MakeTileSprite(&tile_atlas_image, 5, 32, 4);
static const RenderData::Sprite spr_shadow_platform_on = MakeTileSprite(&tile_atlas_image, 8, 32, 4);
static const RenderData::Sprite spr_shadow_platform_off = MakeTileSprite(&tile_atlas_image, 7, 32, 4);
static const RenderData::Sprite spr_door_closed = MakeTileSprite(&tile_atlas_image, 9, 32, 4);
static const RenderData::Sprite spr_door_open = MakeTileSprite(&tile_atlas_image, 10, 32, 4);
static const RenderData::Sprite spr_light_platform_on = MakeTileSprite(&tile_atlas_image, TileId::LightPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_light_platform_off = MakeTileSprite(&tile_atlas_image, TileId::LightPlatformOff, 32, tile_atlas_columns);
static const RenderData::Sprite spr_shadow_platform_on = MakeTileSprite(&tile_atlas_image, TileId::ShadowPlatformOn, 32, tile_atlas_columns);
static const RenderData::Sprite spr_shadow_platform_off = MakeTileSprite(&tile_atlas_image, TileId::ShadowPlatformOff, 32, tile_atlas_columns);
static const RenderData::Sprite spr_door_closed = MakeTileSprite(&tile_atlas_image, TileId::DoorClosed, 32, tile_atlas_columns);
static const RenderData::Sprite spr_door_open = MakeTileSprite(&tile_atlas_image, TileId::DoorOpen, 32, tile_atlas_columns);
}
void LightEffectSystem::update(std::vector<GameObject>& objects, uint16_t light_level)
+72 -3
View File
@@ -8,6 +8,34 @@
namespace Core
{
namespace
{
static uint16_t rgba5551_to_rgb565_shaded(uint16_t c, int32_t shade_numerator, int32_t shade_shift)
{
if (shade_numerator <= 0)
{
return 0;
}
const int32_t max_numerator = 1 << shade_shift;
if (shade_numerator >= max_numerator)
{
return RenderData::rgba5551_to_rgb565(c);
}
uint16_t r = static_cast<uint16_t>((c >> 11) & 0x1Fu);
uint16_t g = static_cast<uint16_t>((c >> 6) & 0x1Fu);
uint16_t b = static_cast<uint16_t>((c >> 1) & 0x1Fu);
r = static_cast<uint16_t>((static_cast<int32_t>(r) * shade_numerator) >> shade_shift);
g = static_cast<uint16_t>((static_cast<int32_t>(g) * shade_numerator) >> shade_shift);
b = static_cast<uint16_t>((static_cast<int32_t>(b) * shade_numerator) >> shade_shift);
const uint16_t g6 = static_cast<uint16_t>((g << 1) | (g >> 4));
return static_cast<uint16_t>((r << 11) | (g6 << 5) | b);
}
}
DrawContext::DrawContext(int32_t width, int32_t height)
{
frameBuffer = new Core::FrameBuffer(width, height);
@@ -168,11 +196,29 @@ namespace Core
int32_t screen_x, int32_t screen_y,
int32_t viewport_w, int32_t viewport_h,
int32_t camera_x, int32_t camera_y)
{
draw_tilemap_shaded(tilemap,
screen_x,
screen_y,
viewport_w,
viewport_h,
camera_x,
camera_y,
1,
0);
}
void DrawContext::draw_tilemap_shaded(const RenderData::Tilemap& tilemap,
int32_t screen_x, int32_t screen_y,
int32_t viewport_w, int32_t viewport_h,
int32_t camera_x, int32_t camera_y,
int32_t shade_numerator, int32_t shade_shift)
{
if (!tilemap.tiles || !tilemap.atlas || !tilemap.atlas->pixels) return;
if (tilemap.width <= 0 || tilemap.height <= 0) return;
if (tilemap.tile_w <= 0 || tilemap.tile_h <= 0 || tilemap.atlas_columns <= 0) return;
if (viewport_w <= 0 || viewport_h <= 0) return;
if (shade_shift < 0 || shade_shift > 15) return;
const int32_t viewport_left = screen_x;
const int32_t viewport_top = screen_y;
@@ -235,9 +281,32 @@ namespace Core
const int32_t clipped_w = clipped_right - clipped_left;
const int32_t clipped_h = clipped_bottom - clipped_top;
blit_sprite_pixels(clipped_left, clipped_top, *tilemap.atlas,
clipped_src_x, clipped_src_y, clipped_w, clipped_h,
1, false, false);
if (shade_numerator >= (1 << shade_shift))
{
blit_sprite_pixels(clipped_left, clipped_top, *tilemap.atlas,
clipped_src_x, clipped_src_y, clipped_w, clipped_h,
1, false, false);
}
else
{
const int32_t img_w = tilemap.atlas->width;
const uint16_t* src = static_cast<const uint16_t*>(tilemap.atlas->pixels);
Core::FramePixel* dst = static_cast<Core::FramePixel*>(frameBuffer->get_buffer());
const int32_t fb_w = frameBuffer->get_width();
for (int32_t y = 0; y < clipped_h; ++y)
{
const int32_t read_y = clipped_src_y + y;
Core::FramePixel* dst_row = dst + (clipped_top + y) * fb_w;
const uint16_t* src_row = src + read_y * img_w;
for (int32_t x = 0; x < clipped_w; ++x)
{
const uint16_t pixel = src_row[clipped_src_x + x];
if (!RenderData::rgba5551_is_opaque(pixel)) continue;
dst_row[clipped_left + x] = rgba5551_to_rgb565_shaded(pixel, shade_numerator, shade_shift);
}
}
}
}
}
}
+5
View File
@@ -74,6 +74,11 @@ namespace Core
int32_t screen_x, int32_t screen_y,
int32_t viewport_w, int32_t viewport_h,
int32_t camera_x, int32_t camera_y);
void draw_tilemap_shaded(const RenderData::Tilemap& tilemap,
int32_t screen_x, int32_t screen_y,
int32_t viewport_w, int32_t viewport_h,
int32_t camera_x, int32_t camera_y,
int32_t shade_numerator, int32_t shade_shift);
void fill_rect(int32_t x, int32_t y, int32_t w, int32_t h, const RenderData::Color& color);
void draw_text(const RenderData::BitmapFont& font, int32_t x, int32_t y,