/*
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 {
render::animated_texture_t *icon;
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 icon_builder,
icon_crystals,
icon_fire,
icon_grenade,
icon_grenades,
icon_health,
icon_move,
icon_repair,
icon_replicator,
icon_scientist,
icon_shells,
icon_soldier,
icon_stop,
icon_teleporter;
audio::sound_t crystal_tick;
} 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) {};
};
};