mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-03 08:43:48 +00:00
438 lines
17 KiB
C++
438 lines
17 KiB
C++
/*
|
|
Copyright (C) 2003 - 2024
|
|
by David White <dave@whitevine.net>
|
|
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
|
|
|
This program 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.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "color_range.hpp"
|
|
#include "config.hpp"
|
|
#include "defeat_condition.hpp"
|
|
#include "game_config.hpp"
|
|
#include "game_events/fwd.hpp"
|
|
#include "map/location.hpp"
|
|
#include "recall_list_manager.hpp"
|
|
#include "side_controller.hpp"
|
|
#include "side_proxy_controller.hpp"
|
|
#include "team_shared_vision.hpp"
|
|
|
|
#include <set>
|
|
|
|
#include <boost/dynamic_bitset.hpp>
|
|
|
|
class game_data;
|
|
class gamemap;
|
|
struct color_t;
|
|
|
|
|
|
namespace wb {
|
|
class side_actions;
|
|
}
|
|
|
|
class shroud_map {
|
|
public:
|
|
shroud_map() : enabled_(false), data_() {}
|
|
|
|
void place(int x, int y);
|
|
bool clear(int x, int y);
|
|
void reset();
|
|
|
|
bool value(int x, int y) const;
|
|
bool shared_value(const std::vector<const shroud_map*>& maps, int x, int y) const;
|
|
|
|
bool copy_from(const std::vector<const shroud_map*>& maps);
|
|
|
|
std::string write() const;
|
|
void read(const std::string& shroud_data);
|
|
void merge(const std::string& shroud_data);
|
|
|
|
bool enabled() const { return enabled_; }
|
|
void set_enabled(bool enabled) { enabled_ = enabled; }
|
|
|
|
int width() const;
|
|
int height() const;
|
|
private:
|
|
bool enabled_;
|
|
std::vector<std::vector<bool>> data_;
|
|
};
|
|
|
|
/**
|
|
* This class stores all the data for a single 'side' (in game nomenclature).
|
|
* E.g., there is only one leader unit per team.
|
|
*/
|
|
class team
|
|
{
|
|
private:
|
|
struct team_info
|
|
{
|
|
team_info();
|
|
void read(const config &cfg);
|
|
void write(config& cfg) const;
|
|
int gold;
|
|
int start_gold;
|
|
int income;
|
|
int income_per_village;
|
|
int support_per_village;
|
|
mutable int minimum_recruit_price;
|
|
int recall_cost;
|
|
std::set<std::string> can_recruit;
|
|
std::string team_name;
|
|
t_string user_team_name;
|
|
t_string side_name;
|
|
std::string faction;
|
|
t_string faction_name;
|
|
std::string save_id;
|
|
// 'id' of the current player (not necessarily unique)
|
|
std::string current_player;
|
|
std::string countdown_time;
|
|
int action_bonus_count;
|
|
|
|
std::string flag;
|
|
std::string flag_icon;
|
|
|
|
std::string id;
|
|
|
|
bool scroll_to_leader;
|
|
|
|
t_string objectives; /** < Team's objectives for the current level. */
|
|
|
|
/** Set to true when the objectives for this time changes.
|
|
* Reset to false when the objectives for this team have been
|
|
* displayed to the user. */
|
|
mutable bool objectives_changed;
|
|
|
|
side_controller::type controller;
|
|
bool is_local;
|
|
defeat_condition::type defeat_cond;
|
|
|
|
side_proxy_controller::type proxy_controller; // when controller == HUMAN, the proxy controller determines what input method is actually used.
|
|
// proxy controller is an interface property, not gamestate. it is not synced, not known to server.
|
|
// also not saved in save game file
|
|
team_shared_vision::type share_vision;
|
|
bool disallow_observers;
|
|
bool allow_player;
|
|
bool chose_random;
|
|
bool no_leader;
|
|
bool hidden;
|
|
bool no_turn_confirmation; // Can suppress confirmations when ending a turn.
|
|
|
|
std::string color;
|
|
|
|
int side;
|
|
bool persistent;
|
|
bool lost;
|
|
|
|
int carryover_percentage;
|
|
bool carryover_add;
|
|
// TODO: maybe make this integer percentage? I like the float version more but this might cause OOS error because of floating point rounding differences on different hardware.
|
|
double carryover_bonus;
|
|
int carryover_gold;
|
|
config variables;
|
|
void handle_legacy_share_vision(const config& cfg);
|
|
};
|
|
|
|
static const int default_team_gold_;
|
|
|
|
public:
|
|
team();
|
|
virtual ~team();
|
|
|
|
/**
|
|
* Stores the attributes recognized by [side]. These should be stripped
|
|
* from a side's config before using it to create the side's leader.
|
|
*/
|
|
static const std::set<std::string> attributes;
|
|
|
|
void build(const config &cfg, const gamemap &map, int gold = default_team_gold_);
|
|
|
|
void write(config& cfg) const;
|
|
|
|
void fix_villages(const gamemap &map);
|
|
/**
|
|
* Acquires a village from owner_side.
|
|
* Pointer fire_event should be the game_data for the game if it is desired to fire an event -- a "capture" event with owner_side variable scoped in will be fired.
|
|
* For no event, pass it nullptr.
|
|
* Default is the resources::gamedata pointer.
|
|
*/
|
|
game_events::pump_result_t get_village(const map_location&, const int owner_side, game_data * fire_event);
|
|
void lose_village(const map_location&);
|
|
void clear_villages() { villages_.clear(); }
|
|
const std::set<map_location>& villages() const { return villages_; }
|
|
bool owns_village(const map_location& loc) const
|
|
{ return villages_.count(loc) > 0; }
|
|
|
|
int side() const { return info_.side; }
|
|
int gold() const { return gold_; }
|
|
int start_gold() const { return info_.start_gold; }
|
|
int base_income() const { return info_.income + game_config::base_income; }
|
|
int village_gold() const { return info_.income_per_village; }
|
|
int recall_cost() const { return info_.recall_cost; }
|
|
void set_village_gold(int income) { info_.income_per_village = income; }
|
|
void set_recall_cost(int cost) { info_.recall_cost = cost; }
|
|
int total_income() const { return base_income() + static_cast<int>(villages_.size()) * info_.income_per_village; }
|
|
/** @return The number of unit levels each village can support,
|
|
i.e. how much upkeep each village can bear. */
|
|
int village_support() const { return info_.support_per_village; }
|
|
/** @param support The number of unit levels each village can support */
|
|
void set_village_support(int support) { info_.support_per_village = support; }
|
|
/** Calculate total support capacity, based on support_per_village. */
|
|
int support() const { return static_cast<int>(villages_.size()) * village_support(); }
|
|
void new_turn() { gold_ += total_income(); }
|
|
void get_shared_maps();
|
|
void set_gold(int amount) { gold_ = amount; }
|
|
void set_start_gold(const int amount) { info_.start_gold = amount; }
|
|
void spend_gold(const int amount) { gold_ -= amount; }
|
|
void set_base_income(int amount) { info_.income = amount - game_config::base_income; }
|
|
int countdown_time() const { return countdown_time_; }
|
|
void set_countdown_time (const int amount) const
|
|
{ countdown_time_ = amount; }
|
|
int action_bonus_count() const { return action_bonus_count_; }
|
|
void set_action_bonus_count(const int count) { action_bonus_count_ = count; }
|
|
recall_list_manager& recall_list() {return recall_list_;}
|
|
const recall_list_manager & recall_list() const {return recall_list_;}
|
|
void set_current_player(const std::string& player)
|
|
{ info_.current_player = player; }
|
|
|
|
bool get_scroll_to_leader() const {return info_.scroll_to_leader;}
|
|
void set_scroll_to_leader(bool value) { info_.scroll_to_leader = value; }
|
|
|
|
const std::set<std::string>& recruits() const
|
|
{ return info_.can_recruit; }
|
|
void add_recruit(const std::string &);
|
|
void set_recruits(const std::set<std::string>& recruits);
|
|
int minimum_recruit_price() const;
|
|
const std::string& last_recruit() const { return last_recruit_; }
|
|
void last_recruit(const std::string & u_type) { last_recruit_ = u_type; }
|
|
|
|
const std::string& save_id() const { return info_.save_id; }
|
|
std::string save_id_or_number() const { return info_.save_id.empty() ? std::to_string(info_.side) : info_.save_id; }
|
|
void set_save_id(const std::string& save_id) { info_.save_id = save_id; }
|
|
const std::string& current_player() const { return info_.current_player; }
|
|
|
|
void set_objectives(const t_string& new_objectives, bool silently=false);
|
|
void set_objectives_changed(bool c = true) const { info_.objectives_changed = c; }
|
|
void reset_objectives_changed() const { info_.objectives_changed = false; }
|
|
|
|
const t_string& objectives() const { return info_.objectives; }
|
|
bool objectives_changed() const { return info_.objectives_changed; }
|
|
|
|
bool is_enemy(int n) const {
|
|
const std::size_t index = std::size_t(n-1);
|
|
if(index >= enemies_.size()) {
|
|
calculate_enemies(index);
|
|
}
|
|
if(index < enemies_.size()) {
|
|
return enemies_[index];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
side_controller::type controller() const { return info_.controller; }
|
|
const std::string& color() const { return info_.color; }
|
|
/** @note Call display::reinit_flag_for_side() after calling this */
|
|
void set_color(const std::string& color) { info_.color = color; }
|
|
bool is_empty() const { return info_.controller == side_controller::type::none; }
|
|
|
|
bool is_local() const { return !is_empty() && info_.is_local; }
|
|
bool is_network() const { return !is_empty() && !info_.is_local; }
|
|
|
|
bool is_human() const { return info_.controller == side_controller::type::human; }
|
|
bool is_ai() const { return info_.controller == side_controller::type::ai; }
|
|
|
|
bool is_local_human() const { return is_human() && is_local(); }
|
|
bool is_local_ai() const { return is_ai() && is_local(); }
|
|
bool is_network_human() const { return is_human() && is_network(); }
|
|
bool is_network_ai() const { return is_ai() && is_network(); }
|
|
|
|
void set_local(bool local) { info_.is_local = local; }
|
|
void make_human() { info_.controller = side_controller::type::human; }
|
|
void make_ai() { info_.controller = side_controller::type::ai; }
|
|
void change_controller(const std::string& new_controller) {
|
|
info_.controller = side_controller::get_enum(new_controller).value_or(side_controller::type::ai);
|
|
}
|
|
void change_controller(side_controller::type controller) { info_.controller = controller; }
|
|
void change_controller_by_wml(const std::string& new_controller);
|
|
|
|
side_proxy_controller::type proxy_controller() const { return info_.proxy_controller; }
|
|
bool is_proxy_human() const { return info_.proxy_controller == side_proxy_controller::type::human; }
|
|
bool is_droid() const { return info_.proxy_controller == side_proxy_controller::type::ai; }
|
|
bool is_idle() const { return info_.proxy_controller == side_proxy_controller::type::idle; }
|
|
|
|
void make_droid() { info_.proxy_controller = side_proxy_controller::type::ai; }
|
|
void make_idle() { info_.proxy_controller = side_proxy_controller::type::idle; }
|
|
void make_proxy_human() { info_.proxy_controller = side_proxy_controller::type::human; }
|
|
void clear_proxy() { make_proxy_human(); }
|
|
|
|
void change_proxy(side_proxy_controller::type proxy) { info_.proxy_controller = proxy; }
|
|
|
|
void toggle_droid() { info_.proxy_controller = (info_.proxy_controller == side_proxy_controller::type::ai ) ? side_proxy_controller::type::human : side_proxy_controller::type::ai; }
|
|
void toggle_idle() { info_.proxy_controller = (info_.proxy_controller == side_proxy_controller::type::idle) ? side_proxy_controller::type::human : side_proxy_controller::type::idle; }
|
|
|
|
const std::string& team_name() const { return info_.team_name; }
|
|
const t_string &user_team_name() const { return info_.user_team_name; }
|
|
void change_team(const std::string &name, const t_string &user_name);
|
|
|
|
const std::string& flag() const { return info_.flag; }
|
|
const std::string& flag_icon() const { return info_.flag_icon; }
|
|
|
|
/** @note Call display::reinit_flag_for_side() after calling this */
|
|
void set_flag(const std::string& flag) { info_.flag = flag; }
|
|
void set_flag_icon(const std::string& flag_icon) { info_.flag_icon = flag_icon; }
|
|
|
|
const std::string& side_name() const { return info_.side_name.empty() ? info_.current_player : info_.side_name.str(); }
|
|
t_string side_name_tstr() const { return info_.side_name.empty() ? t_string(info_.current_player) : info_.side_name; }
|
|
void set_side_name(const t_string& new_name) {info_.side_name = new_name == info_.current_player ? "" : new_name;}
|
|
const std::string& faction() const { return info_.faction; }
|
|
const t_string& faction_name() const { return info_.faction_name; }
|
|
//Returns true if the hex is shrouded/fogged for this side, or
|
|
//any other ally with shared vision.
|
|
bool shrouded(const map_location& loc) const;
|
|
bool fogged(const map_location& loc) const;
|
|
|
|
bool uses_shroud() const { return shroud_.enabled(); }
|
|
bool uses_fog() const { return fog_.enabled(); }
|
|
bool fog_or_shroud() const { return uses_shroud() || uses_fog(); }
|
|
bool clear_shroud(const map_location& loc) { return shroud_.clear(loc.wml_x(),loc.wml_y()); }
|
|
void place_shroud(const map_location& loc) { shroud_.place(loc.wml_x(),loc.wml_y()); }
|
|
bool clear_fog(const map_location& loc) { return fog_.clear(loc.wml_x(),loc.wml_y()); }
|
|
void reshroud() { shroud_.reset(); }
|
|
void refog() { fog_.reset(); }
|
|
void set_shroud(bool shroud) { shroud_.set_enabled(shroud); }
|
|
void set_fog(bool fog) { fog_.set_enabled(fog); }
|
|
std::string shroud_data() const { return shroud_.write(); }
|
|
|
|
/** Merge a WML shroud map with the shroud data of this player. */
|
|
void merge_shroud_map_data(const std::string& shroud_data) { shroud_.merge(shroud_data); }
|
|
|
|
bool knows_about_team(std::size_t index) const;
|
|
/** Records hexes that were cleared of fog via WML. */
|
|
void add_fog_override(const std::set<map_location> &hexes) { fog_clearer_.insert(hexes.begin(), hexes.end()); }
|
|
/** Removes the record of hexes that were cleared of fog via WML. */
|
|
void remove_fog_override(const std::set<map_location> &hexes);
|
|
|
|
bool auto_shroud_updates() const { return auto_shroud_updates_; }
|
|
void set_auto_shroud_updates(bool value) { auto_shroud_updates_ = value; }
|
|
bool get_disallow_observers() const {return info_.disallow_observers; }
|
|
bool no_leader() const { return info_.no_leader; }
|
|
defeat_condition::type defeat_cond() const { return info_.defeat_cond; }
|
|
void set_defeat_condition(defeat_condition::type value) { info_.defeat_cond = value; }
|
|
/** sets the defeat condition if @param value is a valid defeat condition, otherwise nothing happes. */
|
|
void set_defeat_condition_string(const std::string& value) { info_.defeat_cond = defeat_condition::get_enum(value).value_or(info_.defeat_cond); }
|
|
void have_leader(bool value=true) { info_.no_leader = !value; }
|
|
bool hidden() const { return info_.hidden; }
|
|
void set_hidden(bool value) { info_.hidden=value; }
|
|
bool persistent() const { return info_.persistent; }
|
|
void set_persistent(bool value) { info_.persistent = value; }
|
|
void set_lost(bool value=true) { info_.lost = value; }
|
|
bool lost() const { return info_.lost; }
|
|
|
|
void set_carryover_percentage(int value) { info_.carryover_percentage = value; }
|
|
int carryover_percentage() const { return info_.carryover_percentage; }
|
|
void set_carryover_add(bool value) { info_.carryover_add = value; }
|
|
bool carryover_add() const { return info_.carryover_add; }
|
|
void set_carryover_bonus(double value) { info_.carryover_bonus = value; }
|
|
double carryover_bonus() const { return info_.carryover_bonus; }
|
|
void set_carryover_gold(int value) { info_.carryover_gold = value; }
|
|
int carryover_gold() const { return info_.carryover_gold; }
|
|
config& variables() { return info_.variables; }
|
|
const config& variables() const { return info_.variables; }
|
|
|
|
bool no_turn_confirmation() const { return info_.no_turn_confirmation; }
|
|
void set_no_turn_confirmation(bool value) { info_.no_turn_confirmation = value; }
|
|
|
|
//function which, when given a 1-based side will return the color used by that side.
|
|
static const color_range get_side_color_range(int side);
|
|
|
|
static color_t get_side_color(int side);
|
|
static color_t get_minimap_color(int side);
|
|
|
|
static std::string get_side_color_id(unsigned side);
|
|
static const t_string get_side_color_name_for_UI(unsigned side);
|
|
static std::string get_side_color_id_from_config(const config& cfg);
|
|
static std::string get_side_highlight_pango(int side);
|
|
|
|
void log_recruitable() const;
|
|
|
|
/** clear the shroud, fog, and enemies cache for all teams*/
|
|
static void clear_caches();
|
|
|
|
/** get the whiteboard planned actions for this team */
|
|
std::shared_ptr<wb::side_actions> get_side_actions() const { return planned_actions_; }
|
|
|
|
config to_config() const;
|
|
|
|
bool share_maps() const { return info_.share_vision != team_shared_vision::type::none ; }
|
|
bool share_view() const { return info_.share_vision == team_shared_vision::type::all; }
|
|
team_shared_vision::type share_vision() const { return info_.share_vision; }
|
|
|
|
void set_share_vision(const std::string& vision_status) {
|
|
info_.share_vision = team_shared_vision::get_enum(vision_status).value_or(team_shared_vision::type::all);
|
|
clear_caches();
|
|
}
|
|
|
|
void set_share_vision(team_shared_vision::type vision_status) {
|
|
info_.share_vision = vision_status;
|
|
clear_caches();
|
|
}
|
|
|
|
void handle_legacy_share_vision(const config& cfg)
|
|
{
|
|
info_.handle_legacy_share_vision(cfg);
|
|
}
|
|
std::string allied_human_teams() const;
|
|
|
|
bool chose_random() const
|
|
{
|
|
return info_.chose_random;
|
|
}
|
|
|
|
private:
|
|
|
|
const std::vector<const shroud_map*>& ally_shroud(const std::vector<team>& teams) const;
|
|
const std::vector<const shroud_map*>& ally_fog(const std::vector<team>& teams) const;
|
|
|
|
int gold_;
|
|
std::set<map_location> villages_;
|
|
|
|
shroud_map shroud_, fog_;
|
|
/** Stores hexes that have been cleared of fog via WML. */
|
|
std::set<map_location> fog_clearer_;
|
|
|
|
bool auto_shroud_updates_;
|
|
|
|
team_info info_;
|
|
|
|
mutable int countdown_time_;
|
|
int action_bonus_count_;
|
|
|
|
recall_list_manager recall_list_;
|
|
std::string last_recruit_;
|
|
|
|
private:
|
|
void calculate_enemies(std::size_t index) const;
|
|
bool calculate_is_enemy(std::size_t index) const;
|
|
mutable boost::dynamic_bitset<> enemies_;
|
|
|
|
mutable std::vector<const shroud_map*> ally_shroud_, ally_fog_;
|
|
|
|
/**
|
|
* Whiteboard planned actions for this team.
|
|
*/
|
|
std::shared_ptr<wb::side_actions> planned_actions_;
|
|
};
|
|
|
|
//function which will validate a side. Throws game::game_error
|
|
//if the side is invalid
|
|
void validate_side(int side); //throw game::game_error
|