/* 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 "../common.hpp" namespace game { class state_t; class entity_t; class unit_t; class effect_t; } namespace interface { class state_t; typedef struct { std::string label; int action; float r0, r1; float t0, t1; } pie_item_t; class pie_menu_t { v2f_t x, x_world; float radius; pie_item_t *selected = nullptr; protected: friend state_t; std::vector items; public: bool is_open = false; void open(v2f_t wmouse, v2f_t mouse); void close(game::state_t *game); void update(v2f_t mouse); void render_to(render::state_t *render); }; class state_t { protected: friend game::pseudostate_t; sf::RenderWindow *window; game::state_t *game; float em; struct { v2f_t zero_point_s = v2f_t(0, 0); v2f_t center = v2f_t(0, 0); int zoom = 17; float zoom_s = 3.0f; bool panning = false; sf::Vector2f pan_ref; bool following = true; } camera; struct { bool selecting = false; int type; rectf_t rect; } select; pie_menu_t pie_menu; typedef struct { double time; std::string text; } log_entry_t; std::list log; void start_following(void); void stop_following(void); public: rectf_t world_view; v3f_t camera_3d; // for audio void tick(double dt); void render_to(render::state_t *render); void print(std::string str); }; } namespace game { enum { SELECT_NEW, SELECT_OR, SELECT_XOR }; class state_t { void group_say(std::string text); bool select_unit(unit_t *unit, int type); public: world::world_t world; interface::state_t interface; procgen::prng_t prng; std::unordered_set awake_entities; std::unordered_set selected_units; // Some deletes have to be deferred to the next frame. std::unordered_set deletion_list; ntime_t time = 1; // game time double now, dt; // FIXME: refactor the code, use ntime_t everywhere // frame timing data (game runs at a different frequency than the renderer) bool paused; ntime_t t0; size_t frames = 0, frames_since_t0, frames_behind = 0; void start(void); void stop(void); void tick(ntime_t time); void compute_ambience(void); void pause(void); void resume(void); bool first_teleporter_placed = false; size_t crystals = 150; size_t num_scientists = 0; size_t num_builders = 0; size_t num_teleporters = 0; bool game_over_displayed = false; void check_game_over(void); void command_first_teleporter(v2f_t where); void wake_area(v2f_t x); void explosion(v2f_t x); void hivemind_alert(v2f_t x, float r, bool do_move, v2f_t move_to); // These are called by the interface. void select(rectf_t rect, int type); bool populate_pie_menu(std::vector &items); void command(v2f_t x, int number); }; enum { ET_NONE, ET_UNIT, ET_EFFECT, ET_DECO, ET_ROCKET }; enum { TILE_WATER = 0, // special value TILE_DIRT, TILE_DIRT_RED, TILE_STONE, TILE_STONE_RED, TILE_GRAVEL }; enum { CF_SURFACE = 1, CF_SURFACE2 = 2, // entities on the surface (FIXME: the name) CF_BACKGROUND = 4, CF_SOLID = 8, CF_BODY = 16, CF_BODY_SMALL = 32, CF_WATER = 64, CF_DECOS = 128 }; enum { AMBIENT_NEXUS, AMBIENT_CHASM, AMBIENT_WIND, AMBIENT_WATER, AMBIENT_COUNT }; extern size_t selection_cookie; void worldgen(world::world_t *world, world::sector_index_t index, world::sector_t *sector, bool gen_tiles, bool gen_decos, void *data); namespace assets { typedef struct { render::oriented_sprite_4M_t head_idle, body_idle; render::oriented_sprite_4M_t body_aiming, body_firing; render::oriented_sprite_4M2_t legs_idle, legs_walking; render::animated_texture_t dead, gibbing; render::animated_texture_t avatar; audio::sound_t fire, step_stone, step_dirt, pain, death; render::animated_texture_t grenade; audio::sound_t grenade_bounce; } soldier_assets_t; typedef struct { render::oriented_sprite_4M_t body_idle, body_walking; render::oriented_sprite_4M_t head_idle; render::animated_texture_t avatar; audio::sound_t laugh, gather; } scientist_assets_t; typedef struct { render::oriented_sprite_4M_t head_idle, body_idle; render::animated_texture_t dead, gibbing, repairing; render::animated_texture_t avatar; } builder_assets_t; typedef struct { render::oriented_sprite_4M_t idle, walking; render::animated_texture_t dead; audio::sound_t sounds, bite; } spider_assets_t; typedef struct { render::animated_texture_t idle, dead; audio::sound_t spawn, pain, death; } nest_assets_t; typedef struct { render::animated_texture_t idle, unfinished, avatar; audio::sound_t sound, damage; } teleporter_assets_t; typedef struct { render::animated_texture_t idle, working, unfinished, avatar; } replicator_assets_t; typedef struct { render::animated_texture_t blood, flash, explosion, ricochet, water_splash; audio::sound_t gibbing, corpse_hit; audio::sound_t explosion_sound, ricochet_sound, water_splash_sound; } fx_assets_t; typedef struct { render::animated_texture_t stone, stone_cracked; render::animated_texture_t eyething, eyething_dead; render::animated_texture_t spike, spike_broken, spike_small; render::animated_texture_t wart; render::animated_texture_t crystal, crystal_broken; } deco_assets_t; typedef struct { render::animated_texture_t crystals; audio::sound_t crystal_tick; render::animated_texture_t icon_health, icon_grenades, icon_shells; } ui_assets_t; extern soldier_assets_t soldier; extern scientist_assets_t scientist; extern builder_assets_t builder; extern spider_assets_t spider; extern nest_assets_t nest; extern teleporter_assets_t teleporter; extern replicator_assets_t replicator; extern fx_assets_t fx; extern deco_assets_t deco; extern audio::ambient_t ambients[AMBIENT_COUNT]; extern render::animated_texture_t unit_selected; extern render::animated_texture_t unit_selected_halo; extern render::animated_texture_t move_marker, aim_marker; extern ui_assets_t ui; void load(void); } class entity_t : public world::entity_t { public: game::state_t *game = 0; v2f_t x; rectf_t size, render_size; entity_t(game::state_t *game, int type_); virtual ~entity_t(void) = 0; void place(world::world_t *world); void place(world::world_t *world, v2f_t x_); bool ignore_waking = true; // Most entities won't need this mechanism. bool awake = false; double wake_time = -INFINITY; void wake(void); void sleep(void); virtual void damage(int points, unit_t *attacker) = 0; virtual void on_think(void) = 0; virtual void on_spawn(void) = 0; virtual void on_wake(void) = 0; }; class fx_move_marker_t; class fx_aim_marker_t; unit_t *find_target(world::world_t *world, v2f_t x, float r, bool friendly); typedef struct { size_t shells, max_shells = 0; size_t grenades, max_grenades = 0; } storage_t; class unit_t : public entity_t { protected: double next_targetting = -INFINITY; ntime_t next_attack = 0; public: size_t selected = 0; typedef enum { UNIT_SOLDIER, UNIT_SCIENTIST, UNIT_BUILDER, UNIT_SPIDER, UNIT_NEST, UNIT_TELEPORTER, UNIT_REPLICATOR } type_t; game::state_t *game; type_t type; std::string name; unit_t(game::state_t *game_, type_t type_); bool friendly = false; bool controllable = false; struct { world::cflags_t cflags; bool moving = false; bool blocked; v2f_t dst; float angle = 0.0f; std::list path; v2f_t last_step; ntime_t random_walk_time = 0; } move; bool keep_moving(double speed); bool start_moving(v2f_t dst); void stop_moving(void); void random_walk(void); bool dead = false; double death_time = -INFINITY; int health = 1, max_health = 1; void damage(int points, unit_t *attacker); void try_attack(unit_t *target); void die(unit_t *killer); // FIXME: move buildings to another class bool constructed = true; virtual void on_damage(unit_t *attacker) = 0; virtual void on_death(void) = 0; bool have_target = false; storage_t storage; std::string say_text; double say_time = -INFINITY; void say(std::string str, bool on_interface=true); void render_to(render::state_t *render); }; class unit_soldier_t : public unit_t { public: double last_target_time = -INFINITY; v2f_t last_target_x; ntime_t last_attack = 0; std::unique_ptr move_marker; std::unique_ptr aim_marker; sf::Color selection_color; bool manual_firing = false; v2f_t manual_firing_target; void check_area(void); void shoot(v2f_t from, v2f_t at, int damage); void fire_shotgun(v2f_t aim); void target_and_attack(void); unit_soldier_t(game::state_t *game_); ~unit_soldier_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render); bool gibbed = false; void on_think(void); void on_spawn(void) {}; void on_wake(void) {}; void on_damage(unit_t *attacker); void on_death(void); void command_throw_grenade(v2f_t at); void command_restock(bool grenades); }; class unit_scientist_t : public unit_t { ntime_t last_laugh = 0; public: std::unique_ptr move_marker; std::unique_ptr gather_marker; sf::Color selection_color; bool gathering = false, gathered; v2f_t gathering_at; void gather_crystals(void); unit_scientist_t(game::state_t *game_); ~unit_scientist_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render); void on_think(void); void on_spawn(void); void on_wake(void) {}; void on_damage(unit_t *attacker) {}; void on_death(void); }; class unit_builder_t : public unit_t { bool gibbed = false; bool repairing = false, building; v2f_t repairing_at; void repair(void); ntime_t next_repair = 0, last_repair = 0; std::unique_ptr repair_marker; public: std::unique_ptr move_marker; sf::Color selection_color; unit_builder_t(game::state_t *game_); ~unit_builder_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render); void on_think(void); void on_spawn(void) {}; void on_wake(void) {}; void on_damage(unit_t *attacker); void on_death(void); void command_repair(v2f_t where); void command_build(v2f_t where, type_t what); void command_stop(void); }; class unit_spider_t : public unit_t { public: unit_spider_t(game::state_t *game_); ~unit_spider_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void target_and_attack(void); void on_think(void); void on_spawn(void) {}; void on_wake(void); void on_damage(unit_t *attacker); void on_death(void); }; class unit_nest_t : public unit_t { double next_spawning = -INFINITY; public: unit_nest_t(game::state_t *game_); ~unit_nest_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void on_think(void); void on_spawn(void); void on_wake(void) {}; void on_damage(unit_t *attacker); void on_death(void); }; class unit_teleporter_t : public unit_t { public: unit_teleporter_t(game::state_t *game_); ~unit_teleporter_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void on_think(void) {}; void on_spawn(void) {}; void on_wake(void) {}; void on_damage(unit_t *attacker); void on_death(void); void activate(unit_t::type_t type); }; class unit_replicator_t : public unit_t { ntime_t last_activation = 0; public: unit_replicator_t(game::state_t *game_); ~unit_replicator_t(void) {}; void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void on_think(void) {}; void on_spawn(void) {}; void on_wake(void) {}; void on_damage(unit_t *attacker); void on_death(void); void activate(bool grenades); }; class effect_t : public game::entity_t { public: double ttl = +INFINITY; effect_t(game::state_t *game_); virtual ~effect_t() {}; void on_think(void); void on_spawn(void) {}; void on_wake(void) {}; void damage(int points, unit_t *attacker) {}; void render_late_to(render::state_t *render) {}; }; class fx_tracer_t : public effect_t { v2f_t x0, x1; public: fx_tracer_t(game::state_t *game_, v2f_t x0_, v2f_t x1_); ~fx_tracer_t(void) {}; void render_to(render::state_t *render); }; class fx_flash_t : public effect_t { v2f_t x; float radius; sf::Color color; public: fx_flash_t(game::state_t *game_, v2f_t x_, float radius_, sf::Color color_); ~fx_flash_t(void) {}; void render_to(render::state_t *render); }; class fx_blood_t : public effect_t { bool alien; v2f_t x; public: fx_blood_t(game::state_t *game_, v2f_t x_, bool alien_); ~fx_blood_t(void) {}; void render_to(render::state_t *render); }; class fx_move_marker_t : public effect_t { v2f_t x; public: fx_move_marker_t(game::state_t *game_, v2f_t x_); ~fx_move_marker_t(void) {}; void render_to(render::state_t *render); }; class fx_aim_marker_t : public effect_t { v2f_t x; public: fx_aim_marker_t(game::state_t *game_, v2f_t x_); ~fx_aim_marker_t(void) {}; void render_to(render::state_t *render); }; class fx_explosion_t : public effect_t { v2f_t x; public: fx_explosion_t(game::state_t *game_, v2f_t x_); ~fx_explosion_t(void) {}; void render_to(render::state_t *render); }; class fx_bullet_miss_t : public effect_t { v2f_t x; bool water; public: fx_bullet_miss_t(game::state_t *game_, v2f_t x_, bool water_); ~fx_bullet_miss_t(void) {}; void render_to(render::state_t *render); }; class fx_debug_hivemind_t : public effect_t { v2f_t x, move_to; float r; bool do_move; public: fx_debug_hivemind_t(game::state_t *game_, v2f_t x_, float r_, bool do_move_, v2f_t move_to_); ~fx_debug_hivemind_t(void) {}; void render_to(render::state_t *render); }; typedef enum { DECO_STONE, DECO_STONE_CRACKED, DECO_STONE_SMALL, DECO_EYETHING, DECO_EYETHING_DEAD, DECO_SPIKE, DECO_SPIKE_BROKEN, DECO_SPIKE_SMALL, DECO_WART, DECO_CRYSTAL, DECO_CRYSTAL_BROKEN } deco_type_t; class deco_t : public game::entity_t { public: deco_type_t type; double phase_shift; deco_t(game::state_t *game, deco_type_t type_); void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void on_think(void) {}; void on_spawn(void) {}; void on_wake(void) {}; void damage(int points, unit_t *attacker); }; class grenade_t : public game::entity_t { unit_t *shooter; v2f_t v; float z, v_z; ntime_t explosion_time; void compute_bounds(void); public: grenade_t(game::state_t *game, v2f_t x_, v2f_t target, unit_t *shooter_); void render_to(render::state_t *render); void render_late_to(render::state_t *render) {}; void on_think(void); void on_spawn(void); void on_wake(void) {}; void damage(int points, unit_t *attacker) {}; }; };