assets/tiles/stone.png | assets/tiles/stone.png | ||||
assets/tiles/dirt.png | assets/tiles/dirt.png | ||||
assets/tiles/leaves.png | assets/tiles/leaves.png | ||||
assets/tiles/tree-trunk.png) | |||||
assets/tiles/tree-trunk.png | |||||
assets/misc/background-cave.png) | |||||
foreach(a ${assets}) | foreach(a ${assets}) | ||||
configure_file("${a}" "${a}" COPYONLY) | configure_file("${a}" "${a}" COPYONLY) | ||||
endforeach(a) | endforeach(a) |
#include "WGDefault.h" | #include "WGDefault.h" | ||||
#include <algorithm> | |||||
static int grassLevel(const siv::PerlinNoise &perlin, int x) { | static int grassLevel(const siv::PerlinNoise &perlin, int x) { | ||||
return (int)(perlin.noise(x / 50.0, 0) * 13); | return (int)(perlin.noise(x / 50.0, 0) * 13); | ||||
} | } | ||||
return (int)(perlin.noise(x / 50.0, 10) * 10) + 10; | return (int)(perlin.noise(x / 50.0, 10) * 10) + 10; | ||||
} | } | ||||
void WGDefault::drawBackground(const Swan::Context &ctx, Swan::Win &win, Swan::Vec2 pos) { | |||||
int texmin = 10; | |||||
int texmax = 20; | |||||
if (pos.y > texmin) { | |||||
SDL_Texture *tex = bgCave_.texture_.get(); | |||||
Uint8 alpha = std::clamp( | |||||
(pos.y - texmin) / (texmax - texmin), 0.0f, 1.0f) * 255; | |||||
Swan::TexAlphaMod amod(tex, alpha); | |||||
Swan::Draw::parallaxBackground( | |||||
win, tex, std::nullopt, std::nullopt, | |||||
pos.x * Swan::TILE_SIZE, pos.y * Swan::TILE_SIZE, 0.7); | |||||
} | |||||
} | |||||
SDL_Color WGDefault::backgroundColor(Swan::Vec2 pos) { | |||||
float y = pos.y; | |||||
float deep = 20; | |||||
float deeper = deep + 100; | |||||
if (y < deep) { | |||||
return Swan::Draw::linearColor( | |||||
{ 128, 220, 250, 255 }, | |||||
{ 107, 87, 5, 255 }, | |||||
y / deep); | |||||
} else if (y < deeper) { | |||||
return Swan::Draw::linearColor( | |||||
{ 107, 87, 5, 255 }, | |||||
{ 15, 3, 3, 255 }, | |||||
(y - deep) / (deeper - deep)); | |||||
} else { | |||||
return { 15, 3, 3, 255 }; | |||||
} | |||||
} | |||||
Swan::Tile::ID WGDefault::genTile(Swan::TilePos pos) { | Swan::Tile::ID WGDefault::genTile(Swan::TilePos pos) { | ||||
int grass_level = grassLevel(perlin_, pos.x); | int grass_level = grassLevel(perlin_, pos.x); | ||||
int stone_level = stoneLevel(perlin_, pos.x); | int stone_level = stoneLevel(perlin_, pos.x); |
}; | }; | ||||
WGDefault(Swan::World &world): | WGDefault(Swan::World &world): | ||||
tGrass_(world.getTileID("core::grass")), tDirt_(world.getTileID("core::dirt")), | |||||
tStone_(world.getTileID("core::stone")), tAir_(world.getTileID("core::air")), | |||||
tTreeTrunk_(world.getTileID("core::tree-trunk")), tLeaves_(world.getTileID("core::leaves")) {} | |||||
tGrass_(world.getTileID("core::grass")), | |||||
tDirt_(world.getTileID("core::dirt")), | |||||
tStone_(world.getTileID("core::stone")), | |||||
tAir_(world.getTileID("core::air")), | |||||
tTreeTrunk_(world.getTileID("core::tree-trunk")), | |||||
tLeaves_(world.getTileID("core::leaves")), | |||||
bgCave_(world.resources_.getImage("core::background-cave")) {} | |||||
void drawBackground(const Swan::Context &ctx, Swan::Win &win, Swan::Vec2 pos) override; | |||||
SDL_Color backgroundColor(Swan::Vec2 pos) override; | |||||
void genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) override; | void genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) override; | ||||
Swan::BodyTrait::HasBody *spawnPlayer(Swan::WorldPlane &plane) override; | Swan::BodyTrait::HasBody *spawnPlayer(Swan::WorldPlane &plane) override; | ||||
private: | private: | ||||
Swan::Tile::ID genTile(Swan::TilePos pos); | Swan::Tile::ID genTile(Swan::TilePos pos); | ||||
Swan::Tile::ID tGrass_, tDirt_, tStone_, tAir_, tTreeTrunk_, tLeaves_; | Swan::Tile::ID tGrass_, tDirt_, tStone_, tAir_, tTreeTrunk_, tLeaves_; | ||||
Swan::ImageResource &bgCave_; | |||||
siv::PerlinNoise perlin_ = siv::PerlinNoise(100); | siv::PerlinNoise perlin_ = siv::PerlinNoise(100); | ||||
}; | }; |
mod.registerImage({ "leaves", "tiles/leaves.png" }); | mod.registerImage({ "leaves", "tiles/leaves.png" }); | ||||
mod.registerImage({ "player-running", "entities/player-running.png", 64 }); | mod.registerImage({ "player-running", "entities/player-running.png", 64 }); | ||||
mod.registerImage({ "player-still", "entities/player-still.png", 64 }); | mod.registerImage({ "player-still", "entities/player-still.png", 64 }); | ||||
mod.registerImage({ "background-cave", "misc/background-cave.png" }); | |||||
mod.registerImage({ "background-cave-2", "misc/background-cave-2.png" }); | |||||
mod.registerTile({ | mod.registerTile({ | ||||
.name = "air", | .name = "air", |
src/Animation.cc | src/Animation.cc | ||||
src/Chunk.cc | src/Chunk.cc | ||||
src/Clock.cc | src/Clock.cc | ||||
src/drawutil.cc | |||||
src/Game.cc | src/Game.cc | ||||
src/gfxutil.cc | src/gfxutil.cc | ||||
src/Item.cc | src/Item.cc |
#include <bitset> | #include <bitset> | ||||
#include <map> | #include <map> | ||||
#include <string> | #include <string> | ||||
#include <SDL.h> | |||||
#include "common.h" | #include "common.h" | ||||
#include "Resource.h" | #include "Resource.h" | ||||
TilePos getMouseTile(); | TilePos getMouseTile(); | ||||
SDL_Color backgroundColor(); | |||||
void draw(); | void draw(); | ||||
void update(float dt); | void update(float dt); | ||||
void tick(float dt); | void tick(float dt); |
#include <vector> | #include <vector> | ||||
#include <string> | #include <string> | ||||
#include <random> | #include <random> | ||||
#include <SDL.h> | |||||
#include "common.h" | #include "common.h" | ||||
#include "Item.h" | #include "Item.h" | ||||
Tile &getTile(const std::string &name); | Tile &getTile(const std::string &name); | ||||
Item &getItem(const std::string &name); | Item &getItem(const std::string &name); | ||||
SDL_Color backgroundColor(); | |||||
void draw(Win &win); | void draw(Win &win); | ||||
void update(float dt); | void update(float dt); | ||||
void tick(float dt); | void tick(float dt); |
#pragma once | #pragma once | ||||
#include <memory> | #include <memory> | ||||
#include <SDL.h> | |||||
#include "common.h" | |||||
#include "Chunk.h" | #include "Chunk.h" | ||||
#include "Entity.h" | #include "Entity.h" | ||||
#include "traits/BodyTrait.h" | #include "traits/BodyTrait.h" | ||||
#include "Vector2.h" | |||||
namespace Swan { | namespace Swan { | ||||
class World; | class World; | ||||
class WorldPlane; | class WorldPlane; | ||||
class ImageResource; | |||||
class WorldGen { | class WorldGen { | ||||
public: | public: | ||||
virtual ~WorldGen() = default; | virtual ~WorldGen() = default; | ||||
virtual void drawBackground(const Context &ctx, Win &win, Vec2 pos) = 0; | |||||
virtual SDL_Color backgroundColor(Vec2 pos) = 0; | |||||
virtual void genChunk(WorldPlane &plane, Chunk &chunk) = 0; | virtual void genChunk(WorldPlane &plane, Chunk &chunk) = 0; | ||||
virtual BodyTrait::HasBody *spawnPlayer(WorldPlane &plane) = 0; | virtual BodyTrait::HasBody *spawnPlayer(WorldPlane &plane) = 0; | ||||
}; | }; | ||||
class WorldGenStructure { | |||||
public: | |||||
virtual ~WorldGenStructure() = 0; | |||||
virtual bool isBase(TilePos pos); | |||||
}; | |||||
} | } |
BodyTrait::HasBody *spawnPlayer(); | BodyTrait::HasBody *spawnPlayer(); | ||||
void breakBlock(TilePos pos); | void breakBlock(TilePos pos); | ||||
SDL_Color backgroundColor(); | |||||
void draw(Win &win); | void draw(Win &win); | ||||
void update(float dt); | void update(float dt); | ||||
void tick(float dt); | void tick(float dt); |
#pragma once | |||||
#include <SDL.h> | |||||
#include <optional> | |||||
#include "Win.h" | |||||
namespace Swan { | |||||
namespace Draw { | |||||
SDL_Color linearColor(SDL_Color from, SDL_Color to, float frac); | |||||
void parallaxBackground( | |||||
Win &win, SDL_Texture *tex, | |||||
std::optional<SDL_Rect> srcrect, std::optional<SDL_Rect> destrect, | |||||
float x, float y, float factor); | |||||
} | |||||
} |
#include "util.h" | #include "util.h" | ||||
#include "log.h" | |||||
namespace Swan { | namespace Swan { | ||||
inline std::ostream &operator<<(std::ostream &os, const SDL_Rect &rect) { | inline std::ostream &operator<<(std::ostream &os, const SDL_Rect &rect) { | ||||
return os; | return os; | ||||
} | } | ||||
class RenderBlendMode: NonCopyable { | |||||
public: | |||||
RenderBlendMode(SDL_Renderer *rnd, SDL_BlendMode mode): rnd_(rnd) { | |||||
SDL_GetRenderDrawBlendMode(rnd_, &mode_); | |||||
SDL_SetRenderDrawBlendMode(rnd_, mode); | |||||
} | |||||
~RenderBlendMode() { | |||||
SDL_SetRenderDrawBlendMode(rnd_, mode_); | |||||
} | |||||
private: | |||||
SDL_Renderer *rnd_; | |||||
SDL_BlendMode mode_; | |||||
}; | |||||
class RenderDrawColor: NonCopyable { | |||||
public: | |||||
RenderDrawColor(SDL_Renderer *rnd, Uint8 r, Uint8 g, Uint8 b, Uint8 a = 255): rnd_(rnd) { | |||||
SDL_GetRenderDrawColor(rnd_, &r_, &g_, &b_, &a_); | |||||
SDL_SetRenderDrawColor(rnd_, r, g, b, a); | |||||
} | |||||
~RenderDrawColor() { | |||||
SDL_SetRenderDrawColor(rnd_, r_, g_, b_, a_); | |||||
} | |||||
private: | |||||
SDL_Renderer *rnd_; | |||||
Uint8 r_, g_, b_, a_; | |||||
}; | |||||
class RenderClipRect: NonCopyable { | |||||
public: | |||||
RenderClipRect(SDL_Renderer *rnd, SDL_Rect *rect): rnd_(rnd) { | |||||
enabled_ = SDL_RenderIsClipEnabled(rnd_); | |||||
SDL_RenderGetClipRect(rnd_, &rect_); | |||||
SDL_RenderSetClipRect(rnd_, rect); | |||||
} | |||||
~RenderClipRect() { | |||||
if (enabled_) | |||||
SDL_RenderSetClipRect(rnd_, &rect_); | |||||
else | |||||
SDL_RenderSetClipRect(rnd_, nullptr); | |||||
} | |||||
private: | |||||
SDL_Renderer *rnd_; | |||||
bool enabled_; | |||||
SDL_Rect rect_; | |||||
}; | |||||
class RenderTarget: NonCopyable { | class RenderTarget: NonCopyable { | ||||
public: | public: | ||||
RenderTarget(SDL_Renderer *rnd, SDL_Texture *tex): rnd_(rnd) { | RenderTarget(SDL_Renderer *rnd, SDL_Texture *tex): rnd_(rnd) { | ||||
class TexColorMod: NonCopyable { | class TexColorMod: NonCopyable { | ||||
public: | public: | ||||
TexColorMod(SDL_Texture *tex, uint8_t r, uint8_t g, uint8_t b): tex_(tex) { | |||||
TexColorMod(SDL_Texture *tex, Uint8 r, Uint8 g, Uint8 b): tex_(tex) { | |||||
SDL_GetTextureColorMod(tex_, &r_, &g_, &b_); | SDL_GetTextureColorMod(tex_, &r_, &g_, &b_); | ||||
SDL_SetTextureColorMod(tex_, r, g, b); | SDL_SetTextureColorMod(tex_, r, g, b); | ||||
} | } | ||||
private: | private: | ||||
SDL_Texture *tex_; | SDL_Texture *tex_; | ||||
uint8_t r_, g_, b_; | |||||
Uint8 r_, g_, b_; | |||||
}; | }; | ||||
class TexAlphaMod: NonCopyable { | class TexAlphaMod: NonCopyable { | ||||
public: | public: | ||||
TexAlphaMod(SDL_Texture *tex, uint8_t alpha): tex_(tex) { | |||||
TexAlphaMod(SDL_Texture *tex, Uint8 alpha): tex_(tex) { | |||||
SDL_GetTextureAlphaMod(tex_, &alpha_); | SDL_GetTextureAlphaMod(tex_, &alpha_); | ||||
SDL_SetTextureAlphaMod(tex_, alpha); | SDL_SetTextureAlphaMod(tex_, alpha); | ||||
} | } | ||||
private: | private: | ||||
SDL_Texture *tex_; | SDL_Texture *tex_; | ||||
uint8_t alpha_; | |||||
Uint8 alpha_; | |||||
}; | }; | ||||
} | } |
#include <swan/WorldPlane.h> | #include <swan/WorldPlane.h> | ||||
#include <swan/common.h> | #include <swan/common.h> | ||||
#include <swan/log.h> | #include <swan/log.h> | ||||
#include <swan/drawutil.h> | |||||
#include <swan/gfxutil.h> | #include <swan/gfxutil.h> | ||||
#include <swan/util.h> | #include <swan/util.h> |
texture_.reset(SDL_CreateTexture( | texture_.reset(SDL_CreateTexture( | ||||
ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, | ctx.game.win_.renderer_, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, | ||||
CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE)); | CHUNK_WIDTH * TILE_SIZE, CHUNK_HEIGHT * TILE_SIZE)); | ||||
SDL_SetTextureBlendMode(texture_.get(), SDL_BLENDMODE_BLEND); | |||||
} | } | ||||
// We wanna render directly to the texture | // We wanna render directly to the texture | ||||
// We still wanna render directly to the target texture | // We still wanna render directly to the target texture | ||||
RenderTarget target(rnd, texture_.get()); | RenderTarget target(rnd, texture_.get()); | ||||
// We must make sure the blend mode is NONE, because we want transparent | |||||
// pixels to actually overwrite non-transparent pixels | |||||
RenderBlendMode mode(rnd, SDL_BLENDMODE_NONE); | |||||
// When we FillRect, we must fill transparency. | |||||
RenderDrawColor color(rnd, 0, 0, 0, 0); | |||||
for (auto &[pos, tex]: draw_list_) { | for (auto &[pos, tex]: draw_list_) { | ||||
SDL_Rect dest{pos.x * TILE_SIZE, pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE}; | SDL_Rect dest{pos.x * TILE_SIZE, pos.y * TILE_SIZE, TILE_SIZE, TILE_SIZE}; | ||||
SDL_RenderFillRect(rnd, &dest); | |||||
SDL_RenderCopy(rnd, tex, nullptr, &dest); | SDL_RenderCopy(rnd, tex, nullptr, &dest); | ||||
} | } | ||||
} | } |
(int)floor(win_.cam_.y + mousePos.y / (Swan::TILE_SIZE * win_.zoom_))); | (int)floor(win_.cam_.y + mousePos.y / (Swan::TILE_SIZE * win_.zoom_))); | ||||
} | } | ||||
SDL_Color Game::backgroundColor() { | |||||
return world_->backgroundColor(); | |||||
} | |||||
void Game::draw() { | void Game::draw() { | ||||
world_->draw(win_); | world_->draw(win_); | ||||
} | } |
#include "World.h" | #include "World.h" | ||||
#include <algorithm> | |||||
#include "log.h" | #include "log.h" | ||||
#include "Game.h" | #include "Game.h" | ||||
#include "Win.h" | #include "Win.h" | ||||
return getTileByID(id); | return getTileByID(id); | ||||
} | } | ||||
SDL_Color World::backgroundColor() { | |||||
return planes_[current_plane_].backgroundColor(); | |||||
} | |||||
void World::draw(Win &win) { | void World::draw(Win &win) { | ||||
auto bounds = player_->getBody().getBounds(); | auto bounds = player_->getBody().getBounds(); | ||||
win.cam_ = bounds.pos - (win.getSize() / 2) + (bounds.size / 2); | win.cam_ = bounds.pos - (win.getSize() / 2) + (bounds.size / 2); |
} | } | ||||
} | } | ||||
SDL_Color WorldPlane::backgroundColor() { | |||||
return gen_->backgroundColor(world_->player_->getBody().getBounds().pos); | |||||
} | |||||
void WorldPlane::draw(Win &win) { | void WorldPlane::draw(Win &win) { | ||||
auto pbounds = world_->player_->getBody().getBounds(); | auto pbounds = world_->player_->getBody().getBounds(); | ||||
gen_->drawBackground(getContext(), win, pbounds.pos); | |||||
ChunkPos pcpos = ChunkPos( | ChunkPos pcpos = ChunkPos( | ||||
(int)floor(pbounds.pos.x / CHUNK_WIDTH), | (int)floor(pbounds.pos.x / CHUNK_WIDTH), | ||||
(int)floor(pbounds.pos.y / CHUNK_HEIGHT)); | (int)floor(pbounds.pos.y / CHUNK_HEIGHT)); |
#include "drawutil.h" | |||||
#include <algorithm> | |||||
#include <cmath> | |||||
#include "gfxutil.h" | |||||
namespace Swan { | |||||
namespace Draw { | |||||
static Uint8 linearLine(float from, float to, float frac) { | |||||
return (Uint8)std::clamp(to * frac + from * (1 - frac), 0.0f, 255.0f); | |||||
} | |||||
SDL_Color linearColor(SDL_Color from, SDL_Color to, float frac) { | |||||
return { | |||||
.r = linearLine(from.r, to.r, frac), | |||||
.g = linearLine(from.g, to.g, frac), | |||||
.b = linearLine(from.b, to.b, frac), | |||||
.a = linearLine(from.a, to.a, frac), | |||||
}; | |||||
} | |||||
void parallaxBackground( | |||||
Win &win, SDL_Texture *tex, | |||||
std::optional<SDL_Rect> srcrect, std::optional<SDL_Rect> destrect, | |||||
float x, float y, float factor) { | |||||
SDL_Renderer *rnd = win.renderer_; | |||||
// We only need to set a clip rect if we have a destrect | |||||
std::optional<RenderClipRect> clip; | |||||
if (!srcrect) { | |||||
Uint32 fmt; | |||||
int access, w, h; | |||||
SDL_QueryTexture(tex, &fmt, &access, &w, &h); | |||||
srcrect = SDL_Rect{ 0, 0, w, h }; | |||||
} | |||||
if (destrect) { | |||||
clip.emplace(rnd, &*destrect); | |||||
} else { | |||||
int w, h; | |||||
SDL_RenderGetLogicalSize(rnd, &w, &h); | |||||
destrect = SDL_Rect{ 0, 0, w, h }; | |||||
} | |||||
x = (x * win.zoom_) * -factor; | |||||
y = (y * win.zoom_) * -factor; | |||||
SDL_Rect rect{ | |||||
0, 0, | |||||
(int)(srcrect->w * win.zoom_), | |||||
(int)(srcrect->h * win.zoom_), | |||||
}; | |||||
rect.x = (int)std::floor((int)x % rect.w); | |||||
if (rect.x > 0) rect.x -= rect.w; | |||||
rect.y = (int)std::floor((int)y % rect.h); | |||||
if (rect.y > 0) rect.y -= rect.h; | |||||
int numx = destrect->w / rect.w + 2; | |||||
int numy = destrect->h / rect.h + 2; | |||||
for (int x = 0; x < numx; ++x) { | |||||
for (int y = 0; y < numy; ++y) { | |||||
SDL_Rect r{ rect.x + x * rect.w, rect.y + y * rect.h, rect.w, rect.h }; | |||||
SDL_RenderCopy(rnd, tex, &*srcrect, &r); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
CPtr<SDL_Renderer, SDL_DestroyRenderer> renderer( | CPtr<SDL_Renderer, SDL_DestroyRenderer> renderer( | ||||
SDL_CreateRenderer(window.get(), -1, renderflags)); | SDL_CreateRenderer(window.get(), -1, renderflags)); | ||||
sdlassert(renderer, "Could not create renderer"); | sdlassert(renderer, "Could not create renderer"); | ||||
SDL_SetRenderDrawBlendMode(renderer.get(), SDL_BLENDMODE_BLEND); | |||||
SDL_SetRenderDrawColor(renderer.get(), 0, 0, 0, 255); | |||||
Win win(window.get(), renderer.get(), gui_scale); | Win win(window.get(), renderer.get(), gui_scale); | ||||
pcounter.countGameTick(tick_clock.duration()); | pcounter.countGameTick(tick_clock.duration()); | ||||
} | } | ||||
SDL_RenderClear(renderer.get()); | |||||
{ | |||||
auto [r, g, b, a] = game.backgroundColor(); | |||||
RenderDrawColor c(renderer.get(), r, g, b, a); | |||||
SDL_RenderClear(renderer.get()); | |||||
} | |||||
// ImGUI | // ImGUI | ||||
imgui_io.DeltaTime = dt; | imgui_io.DeltaTime = dt; |