/* This file is part of Minitrem. Minitrem is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Minitrem is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Minitrem. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "math.hpp" #ifdef __linux #define CONFIG_SHOW_RSS #endif #define COUNT(A) (sizeof(A) / sizeof((A)[0])) extern bool debug_draw_cmodels; extern bool debug_draw_paths; extern bool debug_draw_tile_coords; extern bool debug_AI; // time in nanoseconds // nclock() never returns 0, so it can be used as a null value typedef uint64_t ntime_t; ntime_t nclock(void); #define SEC(x) ((ntime_t)x * 1000000000) #define MSEC(x) ((ntime_t)x * 1000000) class freq_counter_t { size_t ticks; ntime_t t0 = 0; float last_reading; public: void tick(void); float read(ntime_t ival); }; extern freq_counter_t fc_render; extern freq_counter_t fc_game; extern freq_counter_t fc_traces, fc_trace_tiles, fc_trace_ents; extern freq_counter_t fc_get_entities, fc_get_entities_ents; #ifdef CONFIG_SHOW_RSS size_t sys_get_rss(void); #endif namespace procgen { class prng_t { uint32_t state = 0; public: void seed(uint32_t seed); uint32_t next(void); float next_float(void); float next_float(float n, float p); void unit_vec(float out[2]); v2f_t unit_vec2(void); }; 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; enum { LAYER_FLAT, LAYER_NORMAL, LAYER_TOP, LAYER_COUNT }; // Apparently you can't increment an enum type... typedef int layer_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} }; 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; typedef enum { SECTOR_EMPTY, SECTOR_PARTIAL, SECTOR_FULL } sector_level_t; class sector_t { public: sector_index_t index; rectf_t bounds; std::unordered_set ents; sector_level_t level; tile_t tiles[SECTOR_SIZE * SECTOR_SIZE]; }; typedef int cflags_t; typedef struct { cflags_t cflags; rectf_t bounds; bool ignore = false; } cmodel_t; typedef struct { bool hit; v2f_t end; float frac; entity_t *ent = nullptr; tile_t *tile = nullptr; } trace_t; // for testing trace_t ray_v_rect(v2f_t start, v2f_t end, rectf_t rect); void register_tile(uint8_t type, cflags_t cflags); class world_t { procgen::prng_t prng; std::map sectors; void generate_tile(tile_t *tile, tile_index_t index); void generate(sector_t *sector, sector_index_t index, sector_level_t level); protected: friend render::state_t; public: procgen::perlin_noise_t perlin; void seed_procgen(uint32_t seed); void (*generator)(world_t *world, sector_index_t index, sector_t *sector, bool gen_tiles, bool gen_decos, void *data) = 0; void *generator_data = 0; sector_t *get_sector(sector_index_t index, sector_level_t level); tile_t *get_tile(tile_index_t index, sector_level_t level); 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, sector_level_t level); 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 point_v_tiles(v2f_t at, cflags_t cflags); trace_t ray_v_ents(v2f_t start, v2f_t end, cflags_t cflags, const entity_t *ignore); trace_t ray_v_tiles(v2f_t start, v2f_t end, cflags_t cflags); trace_t ray_v_all(v2f_t start, v2f_t end, cflags_t cflags, const entity_t *ignore); trace_t ray_v_all_p3d(v2f_t start, v2f_t end, cflags_t cflags, cflags_t end_cflags, const entity_t *ignore); struct { size_t sectors = 0, tiles = 0; size_t entities = 0; } stats; }; class entity_t { std::vector parents; void link_to_sector(sector_t *sector); protected: friend world_t; size_t cookie = 0; public: world_t *world = 0; int type; cmodel_t cmodel; rectf_t render_bounds; render::layer_t render_layer = render::LAYER_NORMAL; entity_t(int type_); virtual ~entity_t(void) = 0; void link(world_t *world); void unlink(); virtual void render_to(render::state_t *render) = 0; virtual void render_late_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 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_LEFT_TOP, ALIGN_RIGHT_TOP, ALIGN_LEFT_BOTTOM, ALIGN_CENTER_BOTTOM, ALIGN_RIGHT_BOTTOM } text_align_t; void register_tile(uint8_t type, const char *top, const char *side, float height, layer_t layer); bool rendering_order(const world::entity_t *x, const world::entity_t *y); bool visibility_order(const world::entity_t *x, const world::entity_t *y); rectf_t window_bounds(sf::RenderWindow *window); class state_t { sf::RenderWindow *window; void render_water(world::world_t *world, rectf_t rect, double time); void render_tile(world::world_t *world, v2f_t tx, world::tile_t *tile); void render_layer(world::world_t *world, rect_t §ors, std::list &ents, layer_t layer); void drender_text(rectf_t rect, std::string str); void drender_entity(world::entity_t *ent); public: ntime_t time; double dt; struct { size_t sectors, tiles, entities; } stats; state_t(sf::RenderWindow *window_); void begin_frame(ntime_t time_, double dt); void end_frame(void); void render(world::world_t *world, double time); void render(double phase, animated_texture_t *anim, rectf_t bounds, sf::Color color = sf::Color::White, bool mirror = false); void render(double phase, oriented_sprite_t *sprite, rectf_t bounds, float angle); void render_text(v2f_t x, float height, std::string str, text_align_t align, sf::Color color); void render_rect(rectf_t rect, sf::Color color); void render_hlrect(rectf_t rect, sf::Color color); void render_line(v2f_t x0, v2f_t x1, sf::Color color); void render_circle(v2f_t x, float r, sf::Color color); void render_ring_sect(v2f_t x, float r0, float r1, float t0, float t1, sf::Color color); void debug_path(v2f_t x, std::list *path); }; } namespace audio { void update(v3f_t view, bool paused); class sound_t { std::vector sounds; public: float volume = 1.0f; void load(const char *path); void play(void); void play_3d(v2f_t x); }; class ambient_t { public: sf::Music sound; bool playing = false; float volume = 1.0f; v2f_t origin = v2f_t(0.0f, 0.0f); float weight = 0.0f; void load(const char *path); }; } namespace game { class state_t; class pseudostate_t { state_t *pimpl; public: pseudostate_t(sf::RenderWindow *window_); ~pseudostate_t(void); void start(void); void stop(void); void tick(ntime_t time, double dt); world::world_t *get_world(void); double get_render_time(void); void render_interface_to(render::state_t *render); }; bool load_assets(void); } extern render::state_t *debug_render; // 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; } // Linear map between intervals. template T remap(T am, T ap, T bm, T bp, T x) { return (x - am) * (bp - bm) / (ap - am) + bm; } template T clamp(T x, T m, T p) { if (x < m) return m; if (x > p) return p; return 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); } template static inline void expfade(T *a, T b, T l, T dt) { *a = b + (*a - b) * exp(-l * dt); } template static inline void expfade(vec_t *a, vec_t b, float l, float dt) { *a = b + (*a - b) * exp(-l * dt); }