#include #include #include #include #include #include #include #include #include #include "math.hpp" extern bool debug_draw_cmodels; extern bool debug_draw_paths; extern bool debug_draw_tile_coords; 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 typedef int64_t coord_t; typedef vec_t sector_index_t; typedef vec_t tile_index_t; static tile_index_t neighbor_offsets[8] = { {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, {-1, 0}, {-1, -1}, {0, -1}, {+1, -1} }; enum { TILE_NONE, TILE_DIRT, TILE_WALL }; class tile_t { public: unsigned type : 8; unsigned neighbors : 8; unsigned variant : 8; }; 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]; }; typedef int cflags_t; typedef struct { cflags_t cflags; rectf_t bounds; } cmodel_t; typedef struct { bool hit; v2f_t end; float frac; } trace_t; 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, bool partial); protected: friend render::state_t; public: world_t(void); sector_t *get_sector(sector_index_t index, bool partial = false); tile_t *get_tile(tile_index_t index, bool partial = false); bool find_path(v2f_t src, v2f_t dst, cmodel_t *cmodel, entity_t *ignore, std::list *path); // FIXME: iterators instead of returning std::lists std::list get_sectors(rectf_t rect); std::list get_entities(rectf_t rect, cflags_t cflags); std::list get_render_entities(rectf_t rect); bool test_rect(const cmodel_t *cmodel, const entity_t *ignore); trace_t trace(v2f_t start, v2f_t end, cflags_t cflags); 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: int type; cmodel_t cmodel; rectf_t render_bounds; entity_t(int type_); void link(world_t *world); void unlink(); virtual void render_to(render::state_t *render) = 0; }; typedef struct { bool accessible; float dist; } path_node_t; class path_finder_t { path_node_t *node_at(tile_index_t index); bool is_accessible(tile_index_t index); bool diagonal_test(tile_index_t index, size_t i); public: v2f_t src, dst, tile_center; rectf_t bounds; cflags_t cflags; path_node_t *nodes; size_t width, height; tile_index_t base; float shortest_dist; std::deque path, shortest_path; ~path_finder_t(); void setup_nodes(v2f_t src_, v2f_t dst_, cflags_t cflags_); void eliminate_nodes(rectf_t rect); void find_r(tile_index_t index, float dist, float limit); bool find(void); void export_path(std::list *list); }; } namespace game { bool load_assets(void); class unit_t; class state_t { double now, dt; std::unordered_set units; std::unordered_set selected_units; public: world::world_t world; void start(void); void stop(void); void tick(double now_, double dt_); // These are called by the interface. void select(rectf_t rect); void command(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 panning = false; sf::Vector2f pan_ref; } camera; struct { bool selecting = false; rectf_t rect; } select; public: state_t(sf::RenderWindow *window_, game::state_t *game); void tick(double dt); void render_to(render::state_t *render); }; } 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); }; typedef enum { ALIGN_CENTER_BOTTOM } text_align_t; class state_t { sf::RenderWindow *window; void drender_text(rectf_t rect, std::string str); void drender_entity(world::entity_t *ent); public: double now, dt; state_t(sf::RenderWindow *window_); void begin_frame(double time_, double dt_); 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); void render_text(v2f_t x, float height, const wchar_t *wstr, text_align_t align, sf::Color color); void render_hlrect(rectf_t rect, sf::Color color); void render_arrow(v2f_t x0, v2f_t x1, sf::Color color); void debug_path(std::list *path); }; } extern render::state_t *debug_render; namespace assets { void load(void); }; namespace text { extern const wchar_t *unit_no_path; extern const wchar_t *unit_blocked; void load_strings(std::string lang); } // 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); } static inline float expfade(float a, float b, float l, float dt) { return b + (a - b) * exp(-l * dt); }