#include #include #include #include #include #include #include #include #include "math.hpp" namespace procgen { class prng_t { uint32_t state = 0; public: void seed(uint32_t seed); uint32_t next(void); float next_float(void); void unit_vec(float out[2]); }; class perlin_noise_t { size_t size; float (*table)[2] = nullptr; float table_dot(size_t nx, size_t ny, float dx, float dy); public: ~perlin_noise_t(); void generate(prng_t *prng, size_t size); float get(v2f_t x, float scale); }; } namespace render { class state_t; } namespace world { #define SECTOR_SIZE 8 class tile_t { public: char type; }; typedef vec_t sector_index_t; typedef vec_t tile_index_t; sector_index_t sector_index_at(v2f_t x); tile_index_t tile_index_at(v2f_t x); class entity_t; class sector_t { public: sector_index_t index; rectf_t bounds; std::unordered_set ents; bool empty = true; tile_t tiles[SECTOR_SIZE * SECTOR_SIZE]; }; class world_t { procgen::prng_t prng; procgen::perlin_noise_t perlin; std::map sectors; void generate_tile(tile_t *tile, tile_index_t index); void generate(sector_t *sector, sector_index_t index); protected: friend render::state_t; typedef struct { v2f_t x; std::string text; } debug_t; std::list debug; public: world_t(void); sector_t *get_sector(sector_index_t index); tile_t *get_tile(tile_index_t index); bool find_path(v2f_t src, v2f_t dst, rectf_t size, std::list *path); // FIXME: iterators instead of returning std::lists std::list get_sectors(rectf_t rect); std::list get_entities(rectf_t rect); std::list get_render_entities(rectf_t rect); void debug_point(sf::Vector2f point); }; class entity_t { world_t *parent_world; std::vector parents; void link_to_sector(sector_t *sector); protected: friend world_t; size_t cookie = 0; public: rectf_t bounds, render_bounds; void link(world_t *world); void unlink(); virtual void render_to(render::state_t *render) = 0; }; } namespace game { bool load_assets(void); class state_t { public: world::world_t world; void start(void); void tick(void); void debug_click(v2f_t x); }; } namespace interface { class state_t { sf::RenderWindow *window; game::state_t *game; struct { sf::Vector2f center; int target_zoom = 3; float zoom = 3.0f; bool dragging = false; sf::Vector2f drag_ref; } camera; public: state_t(sf::RenderWindow *window_, game::state_t *game); void tick(void); void render(void); }; } namespace render { class animated_texture_t { friend state_t; protected: sf::Texture *frames = NULL; size_t frame_count; public: ~animated_texture_t(void); bool load(std::string prefix, size_t frame_count_); }; class oriented_sprite_t { protected: friend state_t; animated_texture_t *textures; virtual size_t select_index(float angle, bool *mirror) = 0; public: ~oriented_sprite_t(void); }; class oriented_sprite_4M_t : public oriented_sprite_t { size_t select_index(float angle, bool *mirror); public: void load(std::string prefix, size_t xc, size_t yc, size_t nyc); }; class oriented_sprite_4M2_t : public oriented_sprite_t { size_t select_index(float angle, bool *mirror); public: void load(std::string prefix, size_t xc, size_t yc); }; class state_t { sf::RenderWindow *window; double now; public: state_t(sf::RenderWindow *window_); void begin_frame(double time_); void end_frame(void); void render(game::state_t *game); void render(animated_texture_t *anim, rectf_t bounds, bool mirror = false); void render(oriented_sprite_t *sprite, rectf_t bounds, float angle); }; } namespace assets { typedef struct { render::oriented_sprite_4M_t head_idle, body_idle; render::oriented_sprite_4M2_t legs_idle, legs_walking; } human_assets_t; extern human_assets_t human; void load(void); }; // Divide and round to minus infinity. template T divide_rmi(T x, T y, T *rem) { T rv; if (x >= 0) { *rem = x % y; return x / y; } rv = (x + 1) / y - 1; *rem = x - rv * y; return rv; } // Linear interpolation. template T lerp(T a, T b, T x) { return a * (1 - x) + b * x; } // Bilinear interpolation. template T bilerp(T a, T b, T c, T d, T x, T y) { T ab, cd; ab = lerp(a, b, x); cd = lerp(c, d, x); return lerp(ab, cd, y); }