@@ -16,6 +16,7 @@ set(assets | |||
assets/tile/dirt.png | |||
assets/tile/leaves.png | |||
assets/tile/tree-trunk.png | |||
assets/tile/torch.png | |||
assets/misc/background-cave.png) | |||
foreach(a ${assets}) | |||
configure_file("${a}" "${a}" COPYONLY) |
@@ -31,6 +31,13 @@ void PlayerEntity::update(const Swan::Context &ctx, float dt) { | |||
if (ctx.game.isMousePressed(SDL_BUTTON_LEFT)) | |||
ctx.plane.breakTile(mouse_tile_); | |||
// Place block | |||
if (ctx.game.isMousePressed(SDL_BUTTON_RIGHT)) { | |||
if (ctx.plane.getTileID(mouse_tile_) == ctx.world.getTileID("@::air")) { | |||
ctx.plane.setTile(mouse_tile_, "core::torch"); | |||
} | |||
} | |||
// Move left | |||
if (ctx.game.isKeyPressed(SDL_SCANCODE_A) || ctx.game.isKeyPressed(SDL_SCANCODE_LEFT)) { | |||
physics_.force += Swan::Vec2(-MOVE_FORCE, 0); |
@@ -15,6 +15,7 @@ public: | |||
registerImage("tile/grass"); | |||
registerImage("tile/tree-trunk"); | |||
registerImage("tile/leaves"); | |||
registerImage("tile/torch"); | |||
registerImage("entity/player-running"); | |||
registerImage("entity/player-still"); | |||
registerImage("misc/background-cave"); | |||
@@ -43,6 +44,12 @@ public: | |||
.name = "leaves", | |||
.image = "core/tile/leaves", | |||
}); | |||
registerTile({ | |||
.name = "torch", | |||
.image = "core/tile/torch", | |||
.is_solid = false, | |||
.light_level = 20, | |||
}); | |||
registerItem({ | |||
.name = "stone", |
@@ -30,7 +30,7 @@ public: | |||
Chunk(ChunkPos pos): pos_(pos) { | |||
data_.reset(new uint8_t[DATA_SIZE]); | |||
memset(getLightData(), 255, CHUNK_WIDTH * CHUNK_HEIGHT); | |||
memset(getLightData(), 0, CHUNK_WIDTH * CHUNK_HEIGHT); | |||
} | |||
Tile::ID *getTileData() { | |||
@@ -65,10 +65,11 @@ public: | |||
void setLightData(RelPos pos, uint8_t level) { | |||
getLightData()[pos.y * CHUNK_WIDTH + pos.x] = level; | |||
need_render_ = true; | |||
need_light_render_ = true; | |||
} | |||
void render(const Context &ctx, SDL_Renderer *rnd); | |||
void renderLight(const Context &ctx, SDL_Renderer *rnd); | |||
void compress(); | |||
void decompress(); | |||
@@ -93,6 +94,7 @@ private: | |||
ssize_t compressed_size_ = -1; // -1 if not compressed, a positive number if compressed | |||
bool need_render_ = false; | |||
bool need_light_render_ = false; | |||
float deactivate_timer_ = DEACTIVATE_INTERVAL; | |||
bool is_modified_ = false; | |||
@@ -19,16 +19,19 @@ public: | |||
std::string name; | |||
std::string image; | |||
bool is_solid = true; | |||
uint8_t light_level = 0; | |||
std::optional<std::string> dropped_item = std::nullopt; | |||
}; | |||
Tile(const ResourceManager &resources, const Builder &builder): | |||
name_(builder.name), image_(resources.getImage(builder.image)), | |||
is_solid_(builder.is_solid), dropped_item_(builder.dropped_item) {} | |||
is_solid_(builder.is_solid), light_level_(builder.light_level), | |||
dropped_item_(builder.dropped_item) {} | |||
const std::string name_; | |||
const ImageResource &image_; | |||
const bool is_solid_; | |||
const uint8_t light_level_; | |||
const std::optional<std::string> dropped_item_; | |||
static std::unique_ptr<Tile> createInvalid(const ResourceManager &ctx); |
@@ -79,6 +79,9 @@ public: | |||
std::unique_ptr<WorldGen> gen_; | |||
private: | |||
void addLight(TilePos pos, uint8_t level); | |||
void removeLight(TilePos pos, uint8_t level); | |||
std::map<std::pair<int, int>, Chunk> chunks_; | |||
std::vector<Chunk *> active_chunks_; | |||
std::vector<std::pair<ChunkPos, Chunk *>> tick_chunks_; |
@@ -72,6 +72,40 @@ void Chunk::decompress() { | |||
compressed_size_ = -1; | |||
} | |||
void Chunk::renderLight(const Context &ctx, SDL_Renderer *rnd) { | |||
std::optional<RenderTarget> target; | |||
// The texture might not be created yet | |||
if (!light_texture_) { | |||
light_texture_.reset(SDL_CreateTexture( | |||
ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, | |||
CHUNK_WIDTH, CHUNK_HEIGHT)); | |||
SDL_SetTextureBlendMode(light_texture_.get(), SDL_BLENDMODE_BLEND); | |||
target.emplace(rnd, texture_.get()); | |||
} else { | |||
target.emplace(rnd, texture_.get()); | |||
} | |||
// Fill light texture | |||
target.emplace(rnd, light_texture_.get()); | |||
RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE); | |||
RenderDrawColor color(rnd, 0, 0, 0, 0); | |||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | |||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | |||
int level = getLightLevel({ x, y }); | |||
if (level >= 8) | |||
color.change(0, 0, 0, 0); | |||
else | |||
color.change(0, 0, 0, 255 - level * 32); | |||
SDL_Rect rect{ x, y, 1, 1 }; | |||
SDL_RenderFillRect(rnd, &rect); | |||
} | |||
} | |||
need_light_render_ = false; | |||
} | |||
void Chunk::render(const Context &ctx, SDL_Renderer *rnd) { | |||
std::optional<RenderTarget> target; | |||
@@ -91,14 +125,6 @@ void Chunk::render(const Context &ctx, SDL_Renderer *rnd) { | |||
target.emplace(rnd, texture_.get()); | |||
} | |||
// Same with light texture | |||
if (!light_texture_) { | |||
light_texture_.reset(SDL_CreateTexture( | |||
ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, | |||
CHUNK_WIDTH, CHUNK_HEIGHT)); | |||
SDL_SetTextureBlendMode(light_texture_.get(), SDL_BLENDMODE_BLEND); | |||
} | |||
// We're caching tiles so we don't have to world.getTileByID() every time | |||
Tile::ID prevID = Tile::INVALID_ID; | |||
Tile *tile = ctx.game.invalid_tile_.get(); | |||
@@ -117,19 +143,9 @@ void Chunk::render(const Context &ctx, SDL_Renderer *rnd) { | |||
} | |||
} | |||
// Fill light texture | |||
target.emplace(rnd, light_texture_.get()); | |||
RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE); | |||
RenderDrawColor color(rnd, 0, 0, 0, 0); | |||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | |||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | |||
color.change(0, 0, 0, 255 - getLightLevel({ x, y })); | |||
SDL_Rect rect{ x, y, 1, 1 }; | |||
SDL_RenderFillRect(rnd, &rect); | |||
} | |||
} | |||
need_render_ = false; | |||
renderLight(ctx, rnd); | |||
} | |||
void Chunk::renderList(SDL_Renderer *rnd) { | |||
@@ -159,12 +175,15 @@ void Chunk::draw(const Context &ctx, Win &win) { | |||
if (need_render_) | |||
return; | |||
// We're responsible for the light level rendering though | |||
if (need_light_render_) | |||
renderLight(ctx, win.renderer_); | |||
if (draw_list_.size() > 0) { | |||
renderList(win.renderer_); | |||
draw_list_.clear(); | |||
} | |||
auto chunkpos = pos_ * Vec2i(CHUNK_WIDTH, CHUNK_HEIGHT); | |||
SDL_Rect rect{ 0, 0, CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE }; | |||
win.showTexture(chunkpos, texture_.get(), &rect); |
@@ -103,8 +103,18 @@ void WorldPlane::setTileID(TilePos pos, Tile::ID id) { | |||
Tile::ID old = chunk.getTileID(rp); | |||
if (id != old) { | |||
chunk.setTileID(rp, id, world_->getTileByID(id).image_.texture_.get()); | |||
Tile &newTile = world_->getTileByID(id); | |||
Tile &oldTile = world_->getTileByID(old); | |||
chunk.setTileID(rp, id, newTile.image_.texture_.get()); | |||
chunk.markModified(); | |||
if (oldTile.light_level_ > 0) { | |||
removeLight(pos, oldTile.light_level_); | |||
} | |||
if (newTile.light_level_ > 0) { | |||
addLight(pos, newTile.light_level_); | |||
} | |||
} | |||
} | |||
@@ -255,4 +265,64 @@ void WorldPlane::debugBox(TilePos pos) { | |||
debug_boxes_.push_back(pos); | |||
} | |||
void WorldPlane::addLight(TilePos pos, uint8_t level) { | |||
int sqrLevel = level * level; | |||
for (int y = -level; y <= level; ++y) { | |||
for (int x = -level; x <= level; ++x) { | |||
int sqrDist = x * x + y * y; | |||
if (sqrDist > sqrLevel) { | |||
continue; | |||
} | |||
int lightDiff = level - (int)sqrt(sqrDist); | |||
if (lightDiff <= 0) { | |||
continue; | |||
} | |||
TilePos tp = pos + Vec2i(x, y); | |||
ChunkPos cp = chunkPos(tp); | |||
Chunk::RelPos rp = relPos(tp); | |||
Chunk &ch = getChunk(cp); | |||
int light = (int)ch.getLightLevel(rp) + lightDiff; | |||
if (light > 255) { | |||
light = 255; | |||
} | |||
ch.setLightData(rp, light); | |||
} | |||
} | |||
} | |||
void WorldPlane::removeLight(TilePos pos, uint8_t level) { | |||
int sqrLevel = level * level; | |||
for (int y = -level; y <= level; ++y) { | |||
for (int x = -level; x <= level; ++x) { | |||
int sqrDist = x * x + y * y; | |||
if (sqrDist > sqrLevel) { | |||
continue; | |||
} | |||
int lightDiff = level - (int)sqrt(sqrDist); | |||
if (lightDiff <= 0) { | |||
continue; | |||
} | |||
TilePos tp = pos + Vec2i(x, y); | |||
ChunkPos cp = chunkPos(tp); | |||
Chunk::RelPos rp = relPos(tp); | |||
Chunk &ch = getChunk(cp); | |||
int light = (int)ch.getLightLevel(rp) - lightDiff; | |||
if (light < 0) { | |||
light = 0; | |||
} | |||
ch.setLightData(rp, light); | |||
} | |||
} | |||
} | |||
} |