@@ -13,7 +13,7 @@ add_compile_options(-std=c++2a -Wall -Wextra -Wpedantic -Wno-unused-parameter) | |||
if(CMAKE_BUILD_TYPE STREQUAL Sanitize OR CMAKE_BUILD_TYPE STREQUAL "") | |||
message(STATUS "Build mode: Sanitize") | |||
add_compile_options(-g -fsanitize=address) | |||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") | |||
add_link_options(-fsanitize=address) | |||
elseif(CMAKE_BUILD_TYPE STREQUAL Debug) | |||
message(STATUS "Build mode: Debug") | |||
add_compile_options(-g) | |||
@@ -27,7 +27,9 @@ endif() | |||
add_subdirectory(third_party) | |||
set(libraries sfml-graphics sfml-system sfml-window sfml-audio ImGui-SFML::ImGui-SFML imgui dl) | |||
set(libraries | |||
sfml-graphics sfml-system sfml-window sfml-audio ImGui-SFML::ImGui-SFML | |||
imgui SDL2 SDL2_image dl z) | |||
# We want to be able to use C++20 designated initializers, | |||
# but Clang doesn't support them yet. | |||
@@ -44,7 +46,7 @@ add_subdirectory(core.mod) | |||
add_executable(swan | |||
src/main.cc) | |||
target_link_libraries(swan libswan ${libraries} imgui) | |||
target_link_libraries(swan libswan ${libraries}) | |||
set(assets | |||
assets/icon.png |
@@ -7,38 +7,49 @@ | |||
extern "C" void mod_init(Swan::Mod &mod) { | |||
mod.init("core"); | |||
mod.registerTile("air", new Swan::Tile{ | |||
.image = mod.loadImage("assets/tiles/air.png"), | |||
mod.registerImage("air", "tiles/air.png"); | |||
mod.registerImage("stone", "tiles/stone.png"); | |||
mod.registerImage("dirt", "tiles/dirt.png"); | |||
mod.registerImage("grass", "tiles/grass.png"); | |||
mod.registerImage("player-running", "entities/player-running.png"); | |||
mod.registerImage("player-still", "entities/player-still.png"); | |||
mod.registerTile({ | |||
.name = "air", | |||
.image = "core::air", | |||
.is_solid = false, | |||
}); | |||
mod.registerTile("stone", new Swan::Tile{ | |||
.image = mod.loadImage("assets/tiles/stone.png"), | |||
mod.registerTile({ | |||
.name = "stone", | |||
.image = "core::stone", | |||
.dropped_item = "core::stone", | |||
}); | |||
mod.registerTile("dirt", new Swan::Tile{ | |||
.image = mod.loadImage("assets/tiles/dirt.png"), | |||
mod.registerTile({ | |||
.name = "dirt", | |||
.image = "core::dirt", | |||
.dropped_item = "core::dirt", | |||
}); | |||
mod.registerTile("grass", new Swan::Tile{ | |||
.image = mod.loadImage("assets/tiles/grass.png"), | |||
mod.registerTile({ | |||
.name = "grass", | |||
.image = "core::grass", | |||
.dropped_item = "core::dirt", | |||
}); | |||
mod.registerItem("stone", new Swan::Item{ | |||
.image = mod.loadImage("assets/tiles/stone.png"), | |||
mod.registerItem({ | |||
.name = "stone", | |||
.image = "core::stone", | |||
}); | |||
mod.registerItem("dirt", new Swan::Item{ | |||
.image = mod.loadImage("assets/tiles/dirt.png"), | |||
mod.registerItem({ | |||
.name = "dirt", | |||
.image = "core::dirt", | |||
}); | |||
mod.registerItem("grass", new Swan::Item{ | |||
.image = mod.loadImage("assets/tiles/grass.png"), | |||
mod.registerItem({ | |||
.name = "grass", | |||
.image = "core::grass", | |||
}); | |||
mod.registerWorldGen("default", new WGDefault::Factory()); | |||
mod.registerEntity("player", new EntPlayer::Factory()); | |||
mod.registerEntity("item-stack", new EntItemStack::Factory()); | |||
mod.registerAsset("player-running", new Swan::Asset("assets/entities/player-running.png")); | |||
mod.registerAsset("player-still", new Swan::Asset("assets/entities/player-still.png")); | |||
} |
@@ -1,10 +1,10 @@ | |||
add_library(libswan SHARED | |||
src/Animation.cc | |||
src/Asset.cc | |||
src/Body.cc | |||
src/Chunk.cc | |||
src/Game.cc | |||
src/Item.cc | |||
src/Resource.cc | |||
src/Mod.cc | |||
src/SRF.cc | |||
src/Tile.cc |
@@ -1,10 +1,9 @@ | |||
#pragma once | |||
#include <SFML/Graphics/Sprite.hpp> | |||
#include "common.h" | |||
#include "Asset.h" | |||
#include "Resource.h" | |||
#include "Timer.h" | |||
#include "Resource.h" | |||
namespace Swan { | |||
@@ -14,27 +13,19 @@ public: | |||
HFLIP = 1, | |||
}; | |||
Animation() = default; | |||
Animation(int w, int h, float interval, const Asset &asset, int flags = 0) { | |||
init(w, h, interval, asset, flags); | |||
} | |||
void init(int w, int h, float interval, const Asset &asset, int flags = 0); | |||
Animation(ImageResource &resource, float interval): | |||
resource_(resource), interval_(interval), timer_(interval) {} | |||
void tick(float dt); | |||
void draw(Win &win); | |||
void reset(); | |||
int width_, height_; | |||
void reset(); | |||
private: | |||
ImageResource &resource_; | |||
float interval_; | |||
const Asset *asset_; | |||
int fcount_; | |||
float timer_; | |||
int frame_ = 0; | |||
bool dirty_ = true; | |||
Timer timer_; | |||
sf::Sprite sprite_; | |||
}; | |||
} |
@@ -1,23 +0,0 @@ | |||
#pragma once | |||
#include <SFML/Graphics/Image.hpp> | |||
#include <SFML/Graphics/Texture.hpp> | |||
namespace Swan { | |||
class Asset { | |||
public: | |||
Asset(const std::string &path): path_(path) {} | |||
bool load(const std::string &pfx); | |||
std::string name_; | |||
std::string path_; | |||
sf::Image image_; | |||
sf::Texture tex_; | |||
static Asset INVALID_ASSET; | |||
static void initGlobal(); | |||
}; | |||
} |
@@ -42,7 +42,7 @@ public: | |||
bool isActive() { return deactivate_timer_ > 0; } | |||
private: | |||
static constexpr float DEACTIVATE_TIME = 20; | |||
static constexpr float DEACTIVATE_INTERVAL = 20; | |||
static sf::Uint8 *renderbuf; | |||
bool isCompressed() { return compressed_size_ != -1; } | |||
@@ -51,7 +51,7 @@ private: | |||
ssize_t compressed_size_ = -1; // -1 if not compressed, a positive number if compressed | |||
bool need_render_ = false; | |||
float deactivate_timer_ = DEACTIVATE_TIME; | |||
float deactivate_timer_ = DEACTIVATE_INTERVAL; | |||
struct Visuals { | |||
sf::Texture tex_; |
@@ -6,19 +6,17 @@ | |||
#include "common.h" | |||
#include "World.h" | |||
#include "Mod.h" | |||
namespace Swan { | |||
class Game { | |||
public: | |||
Game(Win &win): | |||
win_(win), | |||
mouse_pos_(0, 0), | |||
keys_pressed_(sf::Keyboard::Key::KeyCount, false), | |||
mouse_pressed_(sf::Mouse::Button::ButtonCount, false), | |||
win_(win) {} | |||
mouse_pressed_(sf::Mouse::Button::ButtonCount, false) {} | |||
void loadMod(const std::string &path); | |||
void createWorld(const std::string &worldgen); | |||
void onKeyPressed(sf::Keyboard::Key key) { keys_pressed_[(int)key] = true; } | |||
@@ -36,16 +34,16 @@ public: | |||
void update(float dt); | |||
void tick(float dt); | |||
static void initGlobal(); | |||
std::unique_ptr<World> world_ = NULL; | |||
std::unique_ptr<ImageResource> invalid_image_ = NULL; | |||
std::unique_ptr<Tile> invalid_tile_ = NULL; | |||
std::unique_ptr<Item> invalid_item_ = NULL; | |||
Win &win_; | |||
private: | |||
Vec2i mouse_pos_; | |||
std::vector<bool> keys_pressed_; | |||
std::vector<bool> mouse_pressed_; | |||
std::vector<Mod> registered_mods_; | |||
Win &win_; | |||
}; | |||
} |
@@ -1,19 +1,25 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <string> | |||
#include <SFML/Graphics/Image.hpp> | |||
#include "Resource.h" | |||
namespace Swan { | |||
struct Item { | |||
std::unique_ptr<sf::Image> image; | |||
class Item { | |||
public: | |||
struct Builder { | |||
std::string name; | |||
std::string image; | |||
}; | |||
Item(const ImageResource &image, const std::string &mod, const Builder &builder): | |||
name_(mod+"::"+builder.name), image_(image) {} | |||
std::string name = ""; | |||
const std::string name_; | |||
const ImageResource &image_; | |||
static Item INVALID_ITEM; | |||
static Item *createInvalid(); | |||
static void initGlobal(); | |||
static std::unique_ptr<Item> createInvalid(Context &ctx); | |||
}; | |||
} |
@@ -4,36 +4,36 @@ | |||
#include <string> | |||
#include <vector> | |||
#include <memory> | |||
#include <SFML/Graphics/Image.hpp> | |||
#include <SDL2/SDL.h> | |||
#include "Tile.h" | |||
#include "Item.h" | |||
#include "WorldGen.h" | |||
#include "Entity.h" | |||
#include "Asset.h" | |||
#include "Resource.h" | |||
namespace Swan { | |||
class Mod { | |||
public: | |||
using ModID = uint32_t; | |||
void init(const std::string &name); | |||
void registerTile(const std::string &name, Tile *tile); | |||
void registerItem(const std::string &name, Item *item); | |||
void registerWorldGen(const std::string &name, WorldGen::Factory *gen); | |||
void registerEntity(const std::string &name, Entity::Factory *ent); | |||
void registerAsset(const std::string &name, Asset *asset); | |||
std::unique_ptr<sf::Image> loadImage(const std::string &path); | |||
void registerImage(const std::string &name, const std::string &path, int frame_height = -1); | |||
void registerTile(const Tile::Builder &tile); | |||
void registerItem(const Item::Builder &item); | |||
void registerWorldGen(const std::string &name, std::unique_ptr<WorldGen::Factory> gen); | |||
void registerEntity(const std::string &name, std::unique_ptr<Entity::Factory> ent); | |||
std::string name_; | |||
std::string path_; | |||
std::vector<std::shared_ptr<Tile>> tiles_; | |||
std::vector<std::shared_ptr<Item>> items_; | |||
std::vector<std::shared_ptr<WorldGen::Factory>> worldgens_; | |||
std::vector<std::shared_ptr<Entity::Factory>> entities_; | |||
std::vector<std::shared_ptr<Asset>> assets_; | |||
std::unordered_map<std::string, std::unique_ptr<ImageResource>> images_; | |||
std::unordered_map<std::string, Tile::Builder> tiles_; | |||
std::unordered_map<std::string, Item::Builder> items_; | |||
std::unordered_map<std::string, std::unique_ptr<WorldGen::Factory>> worldgens_; | |||
std::unordered_map<std::string, std::unique_ptr<Entity::Factory>> entities_; | |||
SDL_Renderer *renderer_; | |||
bool inited_ = false; | |||
}; | |||
@@ -0,0 +1,38 @@ | |||
#pragma once | |||
#include <SDL2/SDL.h> | |||
#include <stdint.h> | |||
#include <string> | |||
#include <memory> | |||
#include "common.h" | |||
namespace Swan { | |||
class ImageResource { | |||
public: | |||
ImageResource( | |||
SDL_Renderer *renderer, const std::string &name, | |||
const std::string &path, int frame_height = -1); | |||
ImageResource( | |||
SDL_Renderer *renderer, const std::string &name, | |||
int w, int h, uint8_t r, uint8_t g, uint8_t b); | |||
void tick(float dt); | |||
std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> surface_{nullptr, &SDL_FreeSurface}; | |||
std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)> texture_{nullptr, &SDL_DestroyTexture}; | |||
int frame_height_; | |||
int num_frames_; | |||
std::string name_; | |||
int frame_ = 0; | |||
static std::unique_ptr<ImageResource> createInvalid(Context &ctx); | |||
private: | |||
float switch_interval_ = 1; | |||
float switch_timer_ = switch_interval_; | |||
}; | |||
} |
@@ -2,25 +2,36 @@ | |||
#include <stdint.h> | |||
#include <string> | |||
#include <optional> | |||
#include <memory> | |||
#include <SFML/Graphics/Image.hpp> | |||
#include "Item.h" | |||
#include "Resource.h" | |||
namespace Swan { | |||
struct Tile { | |||
class Tile { | |||
public: | |||
using ID = uint16_t; | |||
std::unique_ptr<sf::Image> image; | |||
bool is_solid = true; | |||
std::string dropped_item = ""; | |||
struct Builder { | |||
std::string name; | |||
std::string image; | |||
bool is_solid = true; | |||
std::optional<std::string> dropped_item = std::nullopt; | |||
}; | |||
Tile(const ImageResource &image, const std::string &mod, const Builder &builder): | |||
name_(mod+"::"+builder.name), image_(image), | |||
is_solid_(builder.is_solid), dropped_item_(builder.dropped_item) {} | |||
std::string name = ""; | |||
const std::string name_; | |||
const ImageResource &image_; | |||
const bool is_solid_; | |||
const std::optional<std::string> dropped_item_; | |||
static Tile INVALID_TILE; | |||
static std::unique_ptr<Tile> createInvalid(Context &ctx); | |||
static ID INVALID_ID; | |||
static Tile *createInvalid(); | |||
static void initGlobal(); | |||
}; | |||
} |
@@ -2,33 +2,33 @@ | |||
#include "common.h" | |||
#include <SDL2/SDL.h> | |||
namespace Swan { | |||
class Win { | |||
public: | |||
Win(sf::RenderWindow *win): window_(win) {} | |||
Win(SDL_Renderer *renderer): renderer_(renderer) {} | |||
void setPos(const Vec2 &pos) { | |||
transform_ = sf::Transform() | |||
.scale(scale_, scale_) | |||
.translate((pos - cam_) * TILE_SIZE); | |||
//transform_ = sf::Transform() | |||
// .scale(scale_, scale_) | |||
// .translate((pos - cam_) * TILE_SIZE); | |||
} | |||
void draw(const sf::Drawable &drawable) { | |||
window_->draw(drawable, transform_); | |||
//window_->draw(drawable, transform_); | |||
} | |||
Vec2 getSize() { | |||
sf::Vector2u v = window_->getSize(); | |||
return Vec2(v.x, v.y) / (TILE_SIZE * scale_); | |||
//sf::Vector2u v = window_->getSize(); | |||
//return Vec2(v.x, v.y) / (TILE_SIZE * scale_); | |||
return Vec2(10, 10); | |||
} | |||
float scale_ = 2; | |||
Vec2 cam_; | |||
private: | |||
sf::RenderWindow *window_; | |||
sf::Transform transform_; | |||
SDL_Renderer *renderer_; | |||
}; | |||
} |
@@ -6,12 +6,12 @@ | |||
#include <random> | |||
#include "common.h" | |||
#include "Asset.h" | |||
#include "Item.h" | |||
#include "Tile.h" | |||
#include "WorldPlane.h" | |||
#include "WorldGen.h" | |||
#include "Entity.h" | |||
#include "Resource.h" | |||
namespace Swan { | |||
@@ -30,13 +30,13 @@ public: | |||
void registerItem(std::shared_ptr<Item> i); | |||
void registerWorldGen(std::shared_ptr<WorldGen::Factory> gen); | |||
void registerEntity(std::shared_ptr<Entity::Factory> ent); | |||
void registerAsset(std::shared_ptr<Asset> asset); | |||
void registerImage(std::shared_ptr<ImageResource> i); | |||
Tile &getTileByID(Tile::ID id); | |||
Tile::ID getTileID(const std::string &name); | |||
Tile &getTile(const std::string &name); | |||
Item &getItem(const std::string &name); | |||
Asset &getAsset(const std::string &name); | |||
ImageResource &getImage(const std::string &name); | |||
void draw(Win &win); | |||
void update(float dt); | |||
@@ -47,7 +47,7 @@ public: | |||
std::map<std::string, std::shared_ptr<Item>> items_; | |||
std::map<std::string, std::shared_ptr<WorldGen::Factory>> worldgens_; | |||
std::map<std::string, std::shared_ptr<Entity::Factory>> ents_; | |||
std::map<std::string, std::shared_ptr<Asset>> assets_; | |||
std::map<std::string, std::shared_ptr<ImageResource>> images_; | |||
Entity *player_; | |||
Game *game_; | |||
@@ -11,6 +11,9 @@ static constexpr int TILE_SIZE = 32; | |||
static constexpr int TICK_RATE = 20; | |||
static constexpr int CHUNK_HEIGHT = 64; | |||
static constexpr int CHUNK_WIDTH = 64; | |||
static constexpr int PLACEHOLDER_RED = 245; | |||
static constexpr int PLACEHOLDER_GREEN = 66; | |||
static constexpr int PLACEHOLDER_BLUE = 242; | |||
using TilePos = Vec2i; | |||
using ChunkPos = Vec2i; |
@@ -1,13 +1,13 @@ | |||
#pragma once | |||
#include <swan/Animation.h> | |||
#include <swan/Asset.h> | |||
#include <swan/Body.h> | |||
#include <swan/BoundingBox.h> | |||
#include <swan/Chunk.h> | |||
#include <swan/Entity.h> | |||
#include <swan/Game.h> | |||
#include <swan/Mod.h> | |||
#include <swan/Resource.h> | |||
#include <swan/SRF.h> | |||
#include <swan/Tile.h> | |||
#include <swan/Timer.h> |
@@ -4,41 +4,26 @@ | |||
namespace Swan { | |||
void Animation::init(int w, int h, float interval, const Asset &asset, int flags) { | |||
width_ = w; | |||
height_ = h; | |||
interval_ = interval; | |||
asset_ = &asset; | |||
fcount_ = (int)asset_->image_.getSize().y / height_; | |||
sprite_.setTexture(asset_->tex_); | |||
sprite_.setTextureRect(sf::IntRect(0, 0, width_, height_)); | |||
if (flags & (int)Flags::HFLIP) { | |||
sprite_.setOrigin(Vec2(width_, 0)); | |||
sprite_.setScale(Vec2(-1, 1)); | |||
} | |||
} | |||
void Animation::tick(float dt) { | |||
timer_.tick(dt); | |||
if (timer_.periodic(interval_)) { | |||
dirty_ = true; | |||
timer_ -= dt; | |||
if (timer_ <= 0) { | |||
timer_ += interval_; | |||
frame_ += 1; | |||
if (frame_ >= fcount_) | |||
if (frame_ >= resource_.num_frames_) | |||
frame_ = 0; | |||
sprite_.setTextureRect(sf::IntRect(0, height_ * frame_, width_, height_)); | |||
dirty_ = true; | |||
} | |||
} | |||
void Animation::draw(Win &win) { | |||
win.draw(sprite_); | |||
} | |||
void Animation::reset() { | |||
timer_.reset(); | |||
frame_ = 0; | |||
dirty_ = true; | |||
timer_ = interval_; | |||
frame_ = 0; | |||
dirty_ = true; | |||
} | |||
} |
@@ -1,26 +0,0 @@ | |||
#include "Asset.h" | |||
#include "common.h" | |||
namespace Swan { | |||
Asset Asset::INVALID_ASSET(""); | |||
bool Asset::load(const std::string &pfx) { | |||
if (!image_.loadFromFile(pfx + "/" + path_)) | |||
return false; | |||
auto size = image_.getSize(); | |||
tex_.create(size.x, size.y); | |||
tex_.update(image_); | |||
return true; | |||
} | |||
void Asset::initGlobal() { | |||
INVALID_ASSET.name_ = "@internal::invalid"; | |||
INVALID_ASSET.image_.create(TILE_SIZE, TILE_SIZE, sf::Color(245, 66, 242)); | |||
INVALID_ASSET.tex_.create(TILE_SIZE, TILE_SIZE); | |||
INVALID_ASSET.tex_.update(INVALID_ASSET.image_); | |||
} | |||
} |
@@ -26,7 +26,7 @@ void Body::collideX(WorldPlane &plane) { | |||
for (int y = (int)floor(bounds.top() + epsilon); y <= (int)floor(bounds.bottom() - epsilon); ++y) { | |||
int lx = (int)floor(bounds.left() + epsilon); | |||
Tile &left = plane.getTile({ lx, y }); | |||
if (left.is_solid) { | |||
if (left.is_solid_) { | |||
bounds.pos.x = (float)lx + 1.0; | |||
collided = true; | |||
break; | |||
@@ -34,7 +34,7 @@ void Body::collideX(WorldPlane &plane) { | |||
int rx = (int)floor(bounds.right() - epsilon); | |||
Tile &right = plane.getTile({ rx, y }); | |||
if (right.is_solid) { | |||
if (right.is_solid_) { | |||
bounds.pos.x = (float)rx - bounds.size.x; | |||
collided = true; | |||
break; | |||
@@ -58,7 +58,7 @@ void Body::collideY(WorldPlane &plane) { | |||
for (int x = (int)floor(bounds.left() + epsilon); x <= (int)floor(bounds.right() - epsilon); ++x) { | |||
int ty = (int)floor(bounds.top() + epsilon); | |||
Tile &top = plane.getTile({ x, ty }); | |||
if (top.is_solid) { | |||
if (top.is_solid_) { | |||
bounds.pos.y = (float)ty + 1.0; | |||
collided = true; | |||
break; | |||
@@ -66,7 +66,7 @@ void Body::collideY(WorldPlane &plane) { | |||
int by = (int)floor(bounds.bottom() - epsilon); | |||
Tile &right = plane.getTile({ x, by }); | |||
if (right.is_solid) { | |||
if (right.is_solid_) { | |||
bounds.pos.y = (float)by - bounds.size.y; | |||
collided = true; | |||
on_ground_ = true; |
@@ -27,7 +27,7 @@ void Chunk::setTileID(RelPos pos, Tile::ID id) { | |||
void Chunk::drawBlock(RelPos pos, const Tile &t) { | |||
keepActive(); | |||
visuals_->tex_.update(*t.image, pos.x * TILE_SIZE, pos.y * TILE_SIZE); | |||
//visuals_->tex_.update(*t.image_, pos.x * TILE_SIZE, pos.y * TILE_SIZE); | |||
visuals_->dirty_ = true; | |||
} | |||
@@ -97,7 +97,7 @@ void Chunk::decompress() { | |||
void Chunk::render(const Context &ctx) { | |||
Tile::ID prevID = Tile::INVALID_ID; | |||
Tile *tile = &Tile::INVALID_TILE; | |||
Tile *tile = ctx.game.invalid_tile_.get(); | |||
for (int x = 0; x < CHUNK_WIDTH; ++x) { | |||
for (int y = 0; y < CHUNK_HEIGHT; ++y) { | |||
@@ -107,7 +107,8 @@ void Chunk::render(const Context &ctx) { | |||
tile = &ctx.world.getTileByID(id); | |||
} | |||
const sf::Uint8 *imgptr = tile->image->getPixelsPtr(); | |||
const sf::Uint8 *imgptr = NULL; | |||
//const sf::Uint8 *imgptr = tile->image->getPixelsPtr(); | |||
for (int imgy = 0; imgy < TILE_SIZE; ++imgy) { | |||
int pixx = x * TILE_SIZE; | |||
int pixy = y * TILE_SIZE + imgy; | |||
@@ -153,7 +154,7 @@ void Chunk::tick(float dt) { | |||
bool Chunk::keepActive() { | |||
bool wasActive = isActive(); | |||
deactivate_timer_ = DEACTIVATE_TIME; | |||
deactivate_timer_ = DEACTIVATE_INTERVAL; | |||
if (wasActive) | |||
return false; |
@@ -4,48 +4,15 @@ | |||
#include <math.h> | |||
#include <SFML/Window/Mouse.hpp> | |||
#include <time.h> | |||
#include <memory> | |||
#include "Tile.h" | |||
#include "Asset.h" | |||
#include "Win.h" | |||
namespace Swan { | |||
void Game::loadMod(const std::string &path) { | |||
std::string dlpath = path + "/mod.so"; | |||
void *dl = dlopen(dlpath.c_str(), RTLD_LAZY); | |||
if (dl == NULL) { | |||
fprintf(stderr, "%s\n", dlerror()); | |||
return; | |||
} | |||
void (*mod_init)(Mod &) = (void (*)(Mod &))dlsym(dl, "mod_init"); | |||
if (mod_init == NULL) { | |||
fprintf(stderr, "%s\n", dlerror()); | |||
} | |||
registered_mods_.push_back(Mod()); | |||
Mod &mod = registered_mods_.back(); | |||
mod.path_ = path; | |||
mod_init(mod); | |||
} | |||
void Game::createWorld(const std::string &worldgen) { | |||
world_.reset(new World(this, time(NULL))); | |||
for (auto &mod: registered_mods_) { | |||
world_->registerTile(std::shared_ptr<Tile>(Tile::createInvalid())); | |||
for (auto &tile: mod.tiles_) | |||
world_->registerTile(tile); | |||
for (auto &item: mod.items_) | |||
world_->registerItem(item); | |||
for (auto &worldgen: mod.worldgens_) | |||
world_->registerWorldGen(worldgen); | |||
for (auto &entity: mod.entities_) | |||
world_->registerEntity(entity); | |||
for (auto &asset: mod.assets_) | |||
world_->registerAsset(asset); | |||
} | |||
world_->setWorldGen(worldgen); | |||
world_->setCurrentPlane(world_->addPlane()); | |||
@@ -71,10 +38,4 @@ void Game::tick(float dt) { | |||
world_->tick(dt); | |||
} | |||
void Game::initGlobal() { | |||
Tile::initGlobal(); | |||
Item::initGlobal(); | |||
Asset::initGlobal(); | |||
} | |||
} |
@@ -1,24 +1,16 @@ | |||
#include "Item.h" | |||
#include "Resource.h" | |||
#include "Game.h" | |||
#include "common.h" | |||
namespace Swan { | |||
Item Item::INVALID_ITEM; | |||
static void initInvalid(Item *i) { | |||
i->name = "@internal::invalid"; | |||
i->image.reset(new sf::Image()); | |||
i->image->create(TILE_SIZE, TILE_SIZE, sf::Color(254, 66, 242)); | |||
} | |||
Item *Item::createInvalid() { | |||
Item *i = new Item(); | |||
initInvalid(i); | |||
return i; | |||
} | |||
void Item::initGlobal() { | |||
initInvalid(&INVALID_ITEM); | |||
std::unique_ptr<Item> Item::createInvalid(Context &ctx) { | |||
return std::make_unique<Item>(*ctx.game.invalid_image_, "@internal", Builder{ | |||
.name = "invalid", | |||
.image = "invalid", | |||
}); | |||
} | |||
} |
@@ -11,47 +11,32 @@ void Mod::init(const std::string &name) { | |||
fprintf(stderr, "Mod initing: %s\n", name_.c_str()); | |||
} | |||
void Mod::registerTile(const std::string &name, Tile *tile) { | |||
tile->name = name_ + "::" + name; | |||
fprintf(stderr, "Adding tile: %s\n", tile->name.c_str()); | |||
tiles_.push_back(std::shared_ptr<Tile>(tile)); | |||
void Mod::registerImage(const std::string &name, const std::string &path, int frame_height) { | |||
images_[name] = std::make_unique<ImageResource>( | |||
renderer_, name_ = "::" + name, path, frame_height); | |||
fprintf(stderr, "Adding image: %s\n", images_[name]->name_.c_str()); | |||
} | |||
void Mod::registerItem(const std::string &name, Item *item) { | |||
item->name = name_ + "::" + name; | |||
fprintf(stderr, "Adding item: %s\n", item->name.c_str()); | |||
items_.push_back(std::shared_ptr<Item>(item)); | |||
void Mod::registerTile(const Tile::Builder &tile) { | |||
tiles_[tile.name] = tile; | |||
fprintf(stderr, "Adding tile: %s::%s\n", name_.c_str(), tile.name.c_str()); | |||
} | |||
void Mod::registerWorldGen(const std::string &name, WorldGen::Factory *gen) { | |||
void Mod::registerItem(const Item::Builder &item) { | |||
items_[item.name] = item; | |||
fprintf(stderr, "Adding item: %s::%s\n", name_.c_str(), item.name.c_str()); | |||
} | |||
void Mod::registerWorldGen(const std::string &name, std::unique_ptr<WorldGen::Factory> gen) { | |||
gen->name_ = name_ + "::" + name; | |||
fprintf(stderr, "Adding world gen: %s\n", gen->name_.c_str()); | |||
worldgens_.push_back(std::shared_ptr<WorldGen::Factory>(gen)); | |||
worldgens_[name] = std::move(gen); | |||
} | |||
void Mod::registerEntity(const std::string &name, Entity::Factory *ent) { | |||
void Mod::registerEntity(const std::string &name, std::unique_ptr<Entity::Factory> ent) { | |||
ent->name_ = name_ + "::" + name; | |||
fprintf(stderr, "Adding entity: %s\n",ent->name_.c_str()); | |||
entities_.push_back(std::shared_ptr<Entity::Factory>(ent)); | |||
} | |||
void Mod::registerAsset(const std::string &name, Asset *asset) { | |||
asset->name_ = name_ + "::" + name; | |||
if (!asset->load(path_)) { | |||
fprintf(stderr, "Asset %s: Failed to load image '%s'", name.c_str(), (path_ + "/" + asset->path_).c_str()); | |||
abort(); | |||
} | |||
assets_.push_back(std::shared_ptr<Asset>(asset)); | |||
} | |||
std::unique_ptr<sf::Image> Mod::loadImage(const std::string &path) { | |||
std::unique_ptr<sf::Image> img(new sf::Image()); | |||
if (!img->loadFromFile(path_ + "/" + path)) | |||
img->create(TILE_SIZE, TILE_SIZE, sf::Color(245, 66, 242)); | |||
return img; | |||
entities_[name] = std::move(ent); | |||
} | |||
} |
@@ -0,0 +1,70 @@ | |||
#include "Resource.h" | |||
#include <stdio.h> | |||
#include <SDL2/SDL_image.h> | |||
#include "common.h" | |||
#include "Game.h" | |||
#include "Win.h" | |||
namespace Swan { | |||
ImageResource::ImageResource( | |||
SDL_Renderer *renderer, const std::string &name, | |||
const std::string &path, int frame_height) { | |||
surface_.reset(IMG_Load(path.c_str())); | |||
if (surface_ == nullptr) { | |||
fprintf(stderr, "Loading %s failed: %s\n", path.c_str(), SDL_GetError()); | |||
surface_.reset(SDL_CreateRGBSurface( | |||
0, TILE_SIZE, TILE_SIZE, 32, 0, 0, 0, 0)); | |||
SDL_FillRect(surface_.get(), NULL, SDL_MapRGB(surface_->format, | |||
PLACEHOLDER_RED, PLACEHOLDER_GREEN, PLACEHOLDER_BLUE)); | |||
} | |||
if (frame_height < 0) | |||
frame_height = surface_->h; | |||
texture_.reset(SDL_CreateTexture( | |||
renderer, surface_->format->format, SDL_TEXTUREACCESS_STATIC, | |||
surface_->w, frame_height)); | |||
frame_height_ = frame_height; | |||
num_frames_ = surface_->h / frame_height_; | |||
name_ = name; | |||
} | |||
ImageResource::ImageResource( | |||
SDL_Renderer *renderer, const std::string &name, | |||
int w, int h, uint8_t r, uint8_t g, uint8_t b) { | |||
surface_.reset(SDL_CreateRGBSurface( | |||
0, TILE_SIZE, TILE_SIZE, 32, 0, 0, 0, 0)); | |||
SDL_FillRect(surface_.get(), NULL, SDL_MapRGB(surface_->format, r, g, b)); | |||
texture_.reset(SDL_CreateTexture( | |||
renderer, surface_->format->format, SDL_TEXTUREACCESS_STATIC, w, h)); | |||
frame_height_ = h; | |||
num_frames_ = 1; | |||
name_ = name; | |||
} | |||
void ImageResource::tick(float dt) { | |||
switch_timer_ -= dt; | |||
if (switch_timer_ <= 0) { | |||
switch_timer_ += switch_interval_; | |||
frame_ += 1; | |||
if (frame_ >= num_frames_) | |||
frame_ = 0; | |||
} | |||
} | |||
std::unique_ptr<ImageResource> ImageResource::createInvalid(Context &ctx) { | |||
return std::make_unique<ImageResource>( | |||
ctx.game.win_.renderer_, "@internal::invalid", TILE_SIZE, TILE_SIZE, | |||
PLACEHOLDER_RED, PLACEHOLDER_GREEN, PLACEHOLDER_BLUE); | |||
} | |||
} |
@@ -1,26 +1,17 @@ | |||
#include "Tile.h" | |||
#include "common.h" | |||
#include <Game.h> | |||
namespace Swan { | |||
Tile Tile::INVALID_TILE; | |||
Tile::ID Tile::INVALID_ID = 0; | |||
static void initInvalid(Tile *t) { | |||
t->name = "@internal::invalid"; | |||
t->image.reset(new sf::Image()); | |||
t->image->create(TILE_SIZE, TILE_SIZE, sf::Color(245, 66, 242)); | |||
t->is_solid = false; | |||
} | |||
Tile *Tile::createInvalid() { | |||
Tile *t = new Tile(); | |||
initInvalid(t); | |||
return t; | |||
} | |||
void Tile::initGlobal() { | |||
initInvalid(&INVALID_TILE); | |||
std::unique_ptr<Tile> Tile::createInvalid(Context &ctx) { | |||
return std::make_unique<Tile>(*ctx.game.invalid_image_, "@internal", Builder{ | |||
.name = "invalid", | |||
.image = "invalid", | |||
}); | |||
} | |||
} |
@@ -49,12 +49,12 @@ void World::spawnPlayer() { | |||
void World::registerTile(std::shared_ptr<Tile> t) { | |||
Tile::ID id = tiles_.size(); | |||
tiles_map_[t->name] = id; | |||
tiles_map_[t->name_] = id; | |||
tiles_.push_back(std::move(t)); | |||
} | |||
void World::registerItem(std::shared_ptr<Item> i) { | |||
items_[i->name] = std::move(i); | |||
items_[i->name_] = std::move(i); | |||
} | |||
void World::registerWorldGen(std::shared_ptr<WorldGen::Factory> gen) { | |||
@@ -65,15 +65,15 @@ void World::registerEntity(std::shared_ptr<Entity::Factory> ent) { | |||
ents_[ent->name_] = std::move(ent); | |||
} | |||
void World::registerAsset(std::shared_ptr<Asset> asset) { | |||
assets_[asset->name_] = std::move(asset); | |||
void World::registerImage(std::shared_ptr<ImageResource> i) { | |||
images_[i->name_] = std::move(i); | |||
} | |||
Asset &World::getAsset(const std::string &name) { | |||
auto iter = assets_.find(name); | |||
if (iter == assets_.end()) { | |||
ImageResource &World::getImage(const std::string &name) { | |||
auto iter = images_.find(name); | |||
if (iter == images_.end()) { | |||
fprintf(stderr, "Tried to get non-existant asset ''%s'!\n", name.c_str()); | |||
return Asset::INVALID_ASSET; | |||
abort(); | |||
} | |||
return *iter->second; | |||
@@ -83,7 +83,7 @@ Item &World::getItem(const std::string &name) { | |||
auto iter = items_.find(name); | |||
if (iter == items_.end()) { | |||
fprintf(stderr, "Tried to get non-existant item ''%s'!\n", name.c_str()); | |||
return Item::INVALID_ITEM; | |||
return *game_->invalid_item_; | |||
} | |||
return *iter->second; |
@@ -108,10 +108,10 @@ void WorldPlane::breakBlock(TilePos pos) { | |||
Tile &t = getTile(pos); | |||
setTile(pos, "core::air"); | |||
if (t.dropped_item != "") { | |||
if (t.dropped_item_ != std::nullopt) { | |||
spawnEntity("core::item-stack", SRFArray{ | |||
new SRFFloatArray{ (float)pos.x, (float)pos.y }, | |||
new SRFString{ t.dropped_item }, | |||
new SRFString{ *t.dropped_item_ }, | |||
}); | |||
} | |||
} |
@@ -1,7 +1,13 @@ | |||
#include <vector> | |||
#include <time.h> | |||
#include <unistd.h> | |||
#include <stdio.h> | |||
#include <vector> | |||
#include <memory> | |||
#include <chrono> | |||
#include <ratio> | |||
#include <SDL2/SDL.h> | |||
#include <SDL2/SDL_image.h> | |||
#include <swan/common.h> | |||
#include <swan/World.h> | |||
@@ -9,94 +15,58 @@ | |||
#include <swan/Timer.h> | |||
#include <swan/Win.h> | |||
#include <SFML/System/Clock.hpp> | |||
#include <SFML/Audio.hpp> | |||
using namespace Swan; | |||
#include <imgui/imgui.h> | |||
#include <imgui-SFML.h> | |||
#define errassert(expr, str, errfn) do { \ | |||
if (!(expr)) { \ | |||
fprintf(stderr, "%s: %s\n", str, errfn()); \ | |||
return EXIT_FAILURE; \ | |||
} \ | |||
} while (0) | |||
using namespace Swan; | |||
#define sdlassert(expr, str) errassert(expr, str, SDL_GetError); | |||
#define imgassert(expr, str) errassert(expr, str, IMG_GetError); | |||
template<typename T> | |||
using DeleteFunc = void (*)(T *); | |||
int main() { | |||
sf::Image icon; | |||
if (!icon.loadFromFile("assets/icon.png")) { | |||
fprintf(stderr, "Failed to load image 'icon.png'\n"); | |||
abort(); | |||
} | |||
sdlassert(SDL_Init(SDL_INIT_VIDEO) >= 0, "Could not initialize SDL"); | |||
std::unique_ptr<void, DeleteFunc<void>> sdl(nullptr, [](void *){ SDL_Quit(); }); | |||
// Cretate window | |||
sf::RenderWindow window(sf::VideoMode(800, 600), "Project: SWAN"); | |||
window.setVerticalSyncEnabled(true); | |||
window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr()); | |||
Win win(&window); | |||
// Initialize ImGui | |||
ImGui::SFML::Init(window); | |||
// Create music | |||
sf::SoundBuffer musicbuf; | |||
sf::Sound music; | |||
if (musicbuf.loadFromFile("assets/music/happy-1.wav")) { | |||
music.setBuffer(musicbuf); | |||
music.setLoop(true); | |||
music.play(); | |||
} else { | |||
fprintf(stderr, "Failed to load music! Am very sad.\n"); | |||
} | |||
int imgflags = IMG_INIT_PNG; | |||
imgassert(IMG_Init(imgflags) == imgflags, "Could not initialize SDL_Image"); | |||
std::unique_ptr<void, DeleteFunc<void>> sdl_image(nullptr, [](void *){ IMG_Quit(); }); | |||
std::unique_ptr<SDL_Window, DeleteFunc<SDL_Window>> window( | |||
SDL_CreateWindow( | |||
"Project: SWAN", | |||
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, | |||
640, 480, | |||
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE), | |||
SDL_DestroyWindow); | |||
std::unique_ptr<SDL_Renderer, DeleteFunc<SDL_Renderer>> renderer( | |||
SDL_CreateRenderer( | |||
window.get(), -1, SDL_RENDERER_ACCELERATED), | |||
SDL_DestroyRenderer); | |||
sdlassert(renderer, "Could not create renderer\n"); | |||
Game::initGlobal(); | |||
Win win(renderer.get()); | |||
Game game(win); | |||
game.loadMod("core.mod"); | |||
game.createWorld("core::default"); | |||
auto prevTime = std::chrono::steady_clock::now(); | |||
sf::Clock clock; | |||
float fpsAcc = 0; | |||
float tickAcc = 0; | |||
int fcount = 0; | |||
int slowFrames = 0; | |||
while (window.isOpen()) { | |||
sf::Event event; | |||
while (window.pollEvent(event)) { | |||
ImGui::SFML::ProcessEvent(event); | |||
switch (event.type) { | |||
case sf::Event::Closed: | |||
window.close(); | |||
break; | |||
case sf::Event::Resized: | |||
window.setView(sf::View(sf::FloatRect( | |||
0, 0, event.size.width, event.size.height))); | |||
break; | |||
case sf::Event::KeyPressed: | |||
game.onKeyPressed(event.key.code); | |||
break; | |||
case sf::Event::KeyReleased: | |||
game.onKeyReleased(event.key.code); | |||
break; | |||
case sf::Event::MouseMoved: | |||
game.onMouseMove(event.mouseMove.x, event.mouseMove.y); | |||
break; | |||
case sf::Event::MouseButtonPressed: | |||
game.onMousePressed(event.mouseButton.button); | |||
break; | |||
case sf::Event::MouseButtonReleased: | |||
game.onMouseReleased(event.mouseButton.button); | |||
break; | |||
default: break; | |||
} | |||
} | |||
float dt = clock.restart().asSeconds(); | |||
while (1) { | |||
auto now = std::chrono::steady_clock::now(); | |||
std::chrono::duration<float> dur(prevTime - now); | |||
prevTime = now; | |||
float dt = dur.count(); | |||
// Display FPS | |||
fpsAcc += dt; | |||
@@ -107,6 +77,8 @@ int main() { | |||
fcount = 0; | |||
} | |||
game.update(dt); | |||
if (dt > 0.1) { | |||
if (slowFrames == 0) | |||
fprintf(stderr, "Warning: delta time is too high! (%.3fs).\n", dt); | |||
@@ -118,9 +90,6 @@ int main() { | |||
slowFrames = 0; | |||
} | |||
game.update(dt); | |||
// Call tick TICK_RATE times per second | |||
tickAcc += dt; | |||
while (tickAcc >= 1.0 / TICK_RATE) { | |||
tickAcc -= 1.0 / TICK_RATE; | |||
@@ -128,17 +97,9 @@ int main() { | |||
} | |||
} | |||
ImGui::SFML::Update(window, sf::seconds(dt)); | |||
ImGui::Begin("Test Window"); | |||
ImGui::Button("No."); | |||
ImGui::End(); | |||
ImGui::ShowTestWindow(); | |||
window.clear(); | |||
game.draw(); | |||
ImGui::SFML::Render(window); | |||
window.display(); | |||
SDL_UpdateWindowSurface(window.get()); | |||
} | |||
return 0; | |||
return EXIT_SUCCESS; | |||
} |