New replay functionality.
|
@ -45,7 +45,7 @@ height=768
|
|||
|
||||
[main_map]
|
||||
id=main-map
|
||||
rect="=,+0,+882,768"
|
||||
rect="=,+0,+882,742"
|
||||
xanchor=left
|
||||
yanchor=top
|
||||
[/main_map]
|
||||
|
@ -61,14 +61,14 @@ height=768
|
|||
[panel]
|
||||
id=middle-right-panel
|
||||
image=misc/rightside-bg.png
|
||||
rect="=,+0,=,+421"
|
||||
rect="=,+0,=,+395"
|
||||
xanchor=right
|
||||
yanchor=top
|
||||
[/panel]
|
||||
[panel]
|
||||
id=bottom-right-panel
|
||||
image=misc/rightside-bottom.png
|
||||
rect="=,+0,=,768"
|
||||
rect="=,+0,=,742"
|
||||
xanchor=right
|
||||
yanchor=bottom
|
||||
[/panel]
|
||||
|
@ -428,6 +428,92 @@ height=768
|
|||
yanchor=fixed
|
||||
[/unit_status]
|
||||
[/status]
|
||||
|
||||
[panel]
|
||||
id=bottom-panel
|
||||
image=misc/top-bg.png
|
||||
ref=main-map
|
||||
rect="=,742,1024,768"
|
||||
xanchor=left
|
||||
yanchor=bottom
|
||||
[/panel]
|
||||
|
||||
[label]
|
||||
id=replay-label
|
||||
text= _ "Replay"
|
||||
ref=bottom-panel
|
||||
rect="=+3,=+1,+60,=-4"
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/label]
|
||||
[menu]
|
||||
id=button-playreplay
|
||||
type=image
|
||||
image=play
|
||||
title= _ "Play"
|
||||
items=playreplay
|
||||
rect="+2,=-1,+23,="
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=button-stopreplay
|
||||
type=image
|
||||
image=pause
|
||||
title= _ "Stop"
|
||||
items=stopreplay
|
||||
rect="+2,=,+23,="
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=button-resetreplay
|
||||
type=image
|
||||
image=stop
|
||||
title= _ "Reset"
|
||||
items=resetreplay
|
||||
rect="+2,=,+23,="
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=button-nextturn
|
||||
type=image
|
||||
image=fast-fwd
|
||||
title= _ "Next Turn"
|
||||
items=replaynextturn
|
||||
rect="+2,=,+23,="
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=button-nextside
|
||||
type=image
|
||||
image=frame-fwd
|
||||
title= _ "Next Side"
|
||||
items=replaynextside
|
||||
rect="+2,=,+60,="
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=check-shroud
|
||||
type=checkbox
|
||||
title= _ "Shroud"
|
||||
items=replayswitchshroud
|
||||
rect="+15,=,+60,+10"
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[menu]
|
||||
id=check-fog
|
||||
type=checkbox
|
||||
title= _ "Fog"
|
||||
items=replayswitchfog
|
||||
rect="+15,=,+60,+10"
|
||||
xanchor=fixed
|
||||
yanchor=bottom
|
||||
[/menu]
|
||||
[/resolution]
|
||||
|
||||
[partialresolution]
|
||||
|
|
BIN
images/buttons/fast-fwd-active.png
Normal file
After Width: | Height: | Size: 816 B |
BIN
images/buttons/fast-fwd-pressed.png
Normal file
After Width: | Height: | Size: 823 B |
BIN
images/buttons/fast-fwd.png
Normal file
After Width: | Height: | Size: 896 B |
BIN
images/buttons/frame-fwd-active.png
Normal file
After Width: | Height: | Size: 821 B |
BIN
images/buttons/frame-fwd-pressed.png
Normal file
After Width: | Height: | Size: 819 B |
BIN
images/buttons/frame-fwd.png
Normal file
After Width: | Height: | Size: 918 B |
BIN
images/buttons/pause-active.png
Normal file
After Width: | Height: | Size: 728 B |
BIN
images/buttons/pause-pressed.png
Normal file
After Width: | Height: | Size: 742 B |
BIN
images/buttons/pause.png
Normal file
After Width: | Height: | Size: 852 B |
BIN
images/buttons/play-active.png
Normal file
After Width: | Height: | Size: 823 B |
BIN
images/buttons/play-pressed.png
Normal file
After Width: | Height: | Size: 827 B |
BIN
images/buttons/play.png
Normal file
After Width: | Height: | Size: 908 B |
BIN
images/buttons/stop-active.png
Normal file
After Width: | Height: | Size: 843 B |
BIN
images/buttons/stop-pressed.png
Normal file
After Width: | Height: | Size: 860 B |
BIN
images/buttons/stop.png
Normal file
After Width: | Height: | Size: 954 B |
|
@ -1078,10 +1078,12 @@ void attack(display& gui, const gamemap& map,
|
|||
d->second.get_experience(defenderxp);
|
||||
}
|
||||
|
||||
gui.invalidate_unit();
|
||||
gui.invalidate(attacker);
|
||||
gui.invalidate(defender);
|
||||
gui.draw(true,true);
|
||||
if (!recorder.is_skipping()){
|
||||
gui.invalidate_unit();
|
||||
gui.invalidate(attacker);
|
||||
gui.invalidate(defender);
|
||||
gui.draw(true,true);
|
||||
}
|
||||
}
|
||||
|
||||
int village_owner(const gamemap::location& loc, const std::vector<team>& teams)
|
||||
|
@ -1342,7 +1344,7 @@ void calculate_healing(display& disp, const gamestatus& status, const gamemap& m
|
|||
|
||||
unit& u = units.find(loc)->second;
|
||||
|
||||
const bool show_healing = !disp.turbo() && !recorder.skipping() &&
|
||||
const bool show_healing = !disp.turbo() && !recorder.is_skipping() &&
|
||||
!disp.fogged(loc.x,loc.y) &&
|
||||
(!u.invisible(map.underlying_terrain(map[h->first.x][h->first.y]),
|
||||
status.get_time_of_day().lawful_bonus,h->first,units,teams) ||
|
||||
|
|
169
src/config.cpp
|
@ -17,14 +17,19 @@
|
|||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "config.hpp"
|
||||
#include "log.hpp"
|
||||
#include "wassert.hpp"
|
||||
#include "game_errors.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "log.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "unit.hpp"
|
||||
#include "util.hpp"
|
||||
#include "wassert.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#define ERR_CF LOG_STREAM(err, config)
|
||||
#define LOG_NG LOG_STREAM(info, engine)
|
||||
|
||||
config::config(const config& cfg)
|
||||
{
|
||||
|
@ -567,6 +572,166 @@ bool operator!=(const config& a, const config& b)
|
|||
return !operator==(a,b);
|
||||
}
|
||||
|
||||
std::string get_unique_saveid(const config& cfg, std::set<std::string>& seen_save_ids){
|
||||
std::string save_id = cfg["save_id"];
|
||||
|
||||
if(save_id.empty()) {
|
||||
save_id=cfg["description"];
|
||||
}
|
||||
|
||||
//make sure the 'save_id' is unique
|
||||
while(seen_save_ids.count(save_id)) {
|
||||
save_id += "_";
|
||||
}
|
||||
|
||||
return save_id;
|
||||
}
|
||||
|
||||
void get_player_info(const config& cfg, game_state& gamestate, std::string save_id, std::vector<team>& teams, const config& level, const game_data& gameinfo, gamemap& map, unit_map& units){
|
||||
player_info *player = NULL;
|
||||
|
||||
if(cfg["controller"] == "human" ||
|
||||
cfg["controller"] == "network" ||
|
||||
cfg["persistent"] == "1") {
|
||||
player = gamestate.get_player(save_id);
|
||||
|
||||
if(player == NULL && !save_id.empty()) {
|
||||
player = &gamestate.players[save_id];
|
||||
}
|
||||
}
|
||||
|
||||
LOG_NG << "initializing team...\n";
|
||||
|
||||
std::string gold = cfg["gold"];
|
||||
if(gold.empty())
|
||||
gold = "100";
|
||||
|
||||
LOG_NG << "found gold: '" << gold << "'\n";
|
||||
|
||||
int ngold = lexical_cast_default<int>(gold);
|
||||
if(player != NULL && player->gold >= ngold) {
|
||||
ngold = player->gold;
|
||||
}
|
||||
|
||||
LOG_NG << "set gold to '" << ngold << "'\n";
|
||||
|
||||
teams.push_back(team(cfg,ngold));
|
||||
|
||||
//update/fix the recall list for this side, by setting the
|
||||
//"side" of each unit in it to be the "side" of the player.
|
||||
int side = lexical_cast_default<int>(cfg["side"], 1);
|
||||
if(player != NULL) {
|
||||
for(std::vector<unit>::iterator it = player->available_units.begin(); it != player->available_units.end(); ++it) {
|
||||
it->set_side(side);
|
||||
}
|
||||
}
|
||||
|
||||
//if this team has no objectives, set its objectives to the
|
||||
//level-global "objectives"
|
||||
if(teams.back().objectives().empty())
|
||||
teams.back().set_objectives(level["objectives"]);
|
||||
|
||||
//if this side tag describes the leader of the side
|
||||
if(cfg["no_leader"] != "yes" && cfg["controller"] != "null") {
|
||||
unit new_unit(gameinfo, cfg);
|
||||
|
||||
//search the recall list for leader units, and if there is
|
||||
//one, use it in place of the config-described unit
|
||||
if(player != NULL) {
|
||||
for(std::vector<unit>::iterator it = player->available_units.begin(); it != player->available_units.end(); ++it) {
|
||||
if(it->can_recruit()) {
|
||||
new_unit = *it;
|
||||
player->available_units.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//see if the side specifies its location. Otherwise start it at the map-given
|
||||
//starting position
|
||||
const std::string& has_loc = cfg["x"];
|
||||
gamemap::location start_pos(cfg);
|
||||
|
||||
if(has_loc.empty()) {
|
||||
start_pos = map.starting_position(side);
|
||||
LOG_NG << "initializing side '" << cfg["side"] << "' at "
|
||||
<< start_pos << '\n';
|
||||
}
|
||||
|
||||
if(map.empty()) {
|
||||
throw game::load_game_failed("Map not found");
|
||||
}
|
||||
|
||||
if(!start_pos.valid() && new_unit.side() == 1) {
|
||||
throw game::load_game_failed("No starting position for side 1");
|
||||
}
|
||||
|
||||
if(start_pos.valid()) {
|
||||
new_unit.new_turn();
|
||||
units.insert(std::pair<gamemap::location,unit>(
|
||||
map.starting_position(new_unit.side()), new_unit));
|
||||
}
|
||||
}
|
||||
|
||||
//if the game state specifies units that can be recruited for the player
|
||||
//then add them
|
||||
if(player != NULL && player->can_recruit.empty() == false) {
|
||||
std::copy(player->can_recruit.begin(),player->can_recruit.end(),
|
||||
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
|
||||
}
|
||||
|
||||
if(player != NULL) {
|
||||
player->can_recruit = teams.back().recruits();
|
||||
}
|
||||
|
||||
//if there are additional starting units on this side
|
||||
const config::child_list& starting_units = cfg.get_children("unit");
|
||||
for(config::child_list::const_iterator su = starting_units.begin(); su != starting_units.end(); ++su) {
|
||||
unit new_unit(gameinfo,**su);
|
||||
|
||||
new_unit.set_side(side);
|
||||
|
||||
const std::string& x = (**su)["x"];
|
||||
const std::string& y = (**su)["y"];
|
||||
|
||||
const gamemap::location loc(**su);
|
||||
if(x.empty() || y.empty() || !map.on_board(loc)) {
|
||||
if(player) {
|
||||
player->available_units.push_back(new_unit);
|
||||
}
|
||||
} else {
|
||||
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
|
||||
LOG_NG << "inserting unit for side " << new_unit.side() << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_first_human_team(const config::child_list::const_iterator& cfg, const config::child_list& unit_cfg){
|
||||
int result = -1;
|
||||
const std::string& controller = (**cfg)["controller"];
|
||||
if (controller == preferences::client_type() && (**cfg)["description"] == preferences::login()) {
|
||||
result = cfg - unit_cfg.begin();
|
||||
} else if(result == -1 && ((**cfg)["controller"] == "human" || (**cfg)["persistent"] == "1")) {
|
||||
result = cfg - unit_cfg.begin();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const config* get_theme(const config& game_config, std::string theme_name){
|
||||
const config* theme_cfg = NULL;
|
||||
if(theme_name != "") {
|
||||
theme_cfg = game_config.find_child("theme","name",theme_name);
|
||||
}
|
||||
|
||||
if(theme_cfg == NULL) {
|
||||
theme_cfg = game_config.find_child("theme","name",preferences::theme());
|
||||
}
|
||||
if (theme_cfg == NULL){
|
||||
theme_cfg = new config();
|
||||
}
|
||||
return theme_cfg;
|
||||
}
|
||||
|
||||
//#define TEST_CONFIG
|
||||
|
||||
#ifdef TEST_CONFIG
|
||||
|
|
|
@ -13,9 +13,11 @@
|
|||
#ifndef CONFIG_HPP_INCLUDED
|
||||
#define CONFIG_HPP_INCLUDED
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "map.hpp"
|
||||
#include "tstring.hpp"
|
||||
|
||||
//This module defines the interface to Wesnoth Markup Language (WML).
|
||||
|
@ -27,6 +29,10 @@
|
|||
//throughout the game.
|
||||
|
||||
class t_string;
|
||||
class unit;
|
||||
struct game_data;
|
||||
struct game_state;
|
||||
class team;
|
||||
|
||||
typedef std::map<std::string,t_string> string_map;
|
||||
|
||||
|
@ -144,4 +150,9 @@ private:
|
|||
bool operator==(const config& a, const config& b);
|
||||
bool operator!=(const config& a, const config& b);
|
||||
|
||||
std::string get_unique_saveid(const config& cfg, std::set<std::string>& seen_save_ids);
|
||||
int get_first_human_team(const config::child_list::const_iterator& cfg, const config::child_list& unit_cfg);
|
||||
void get_player_info(const config& cfg, game_state& gamestate, std::string save_id, std::vector<team>& teams, const config& level, const game_data& gameinfo, gamemap& map, std::map<gamemap::location,unit>& units);
|
||||
const config* get_theme(const config& game_config, std::string theme_name);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "halo.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "image.hpp"
|
||||
#include "widgets/image_button.hpp"
|
||||
#include "language.hpp"
|
||||
#include "log.hpp"
|
||||
#include "marked-up_text.hpp"
|
||||
|
@ -33,6 +34,7 @@
|
|||
#include "sdl_utils.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "team.hpp"
|
||||
#include "theme.hpp"
|
||||
#include "tooltips.hpp"
|
||||
#include "unit_display.hpp"
|
||||
#include "util.hpp"
|
||||
|
@ -541,6 +543,15 @@ void display::scroll_to_tiles(int x1, int y1, int x2, int y2,
|
|||
}
|
||||
}
|
||||
|
||||
void display::scroll_to_leader(unit_map& units, int side){
|
||||
const unit_map::iterator leader = find_leader(units,side);
|
||||
|
||||
if(leader != units_.end()) {
|
||||
const hotkey::basic_handler key_events_handler(this);
|
||||
scroll_to_tile(leader->first.x,leader->first.y);
|
||||
}
|
||||
}
|
||||
|
||||
void display::bounds_check_position()
|
||||
{
|
||||
const int min_zoom = MinZoom(map_,map_area());
|
||||
|
@ -2167,7 +2178,7 @@ void display::create_buttons()
|
|||
|
||||
const std::vector<theme::menu>& buttons = theme_.menus();
|
||||
for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
|
||||
gui::button b(screen_,i->title(),gui::button::TYPE_PRESS,i->image());
|
||||
gui::button b(screen_,i->title(),string_to_button_type(i->type()),i->image());
|
||||
const SDL_Rect& loc = i->location(screen_area());
|
||||
b.set_location(loc.x,loc.y);
|
||||
|
||||
|
@ -2179,6 +2190,13 @@ void display::create_buttons()
|
|||
}
|
||||
}
|
||||
|
||||
gui::button::TYPE display::string_to_button_type(std::string type){
|
||||
gui::button::TYPE res = gui::button::TYPE_PRESS;
|
||||
if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
|
||||
else if (type == "image") { res = gui::button::TYPE_IMAGE; }
|
||||
return res;
|
||||
}
|
||||
|
||||
void display::add_observer(const std::string& name)
|
||||
{
|
||||
observers_.insert(name);
|
||||
|
|
|
@ -107,6 +107,9 @@ public:
|
|||
void scroll_to_tiles(int x1, int y1, int x2, int y2,
|
||||
SCROLL_TYPE scroll_type=SCROLL, bool check_fogged=true);
|
||||
|
||||
//scrolls to the leader of a certain side. This will normally be the playing team.
|
||||
void scroll_to_leader(unit_map& units, int side);
|
||||
|
||||
//invalidates entire screen, including all tiles and sidebar.
|
||||
void redraw_everything();
|
||||
|
||||
|
@ -198,6 +201,8 @@ private:
|
|||
|
||||
void draw_halo_on_tile(int x, int y);
|
||||
|
||||
gui::button::TYPE string_to_button_type(std::string type);
|
||||
|
||||
// void draw_tile_adjacent(int x, int y, image::TYPE image_type, ADJACENT_TERRAIN_TYPE type);
|
||||
|
||||
|
||||
|
|
27
src/game.cpp
|
@ -109,6 +109,7 @@ public:
|
|||
|
||||
enum RELOAD_GAME_DATA { RELOAD_DATA, NO_RELOAD_DATA };
|
||||
void play_game(RELOAD_GAME_DATA reload=RELOAD_DATA);
|
||||
void play_replay();
|
||||
|
||||
private:
|
||||
game_controller(const game_controller&);
|
||||
|
@ -662,7 +663,7 @@ bool game_controller::load_game()
|
|||
}
|
||||
recorder = replay(state_.replay_data);
|
||||
recorder.start_replay();
|
||||
recorder.set_skip(0);
|
||||
recorder.set_skip(false);
|
||||
|
||||
std::cerr << "has snapshot: " << (state_.snapshot.child("side") ? "yes" : "no") << "\n";
|
||||
|
||||
|
@ -674,7 +675,7 @@ bool game_controller::load_game()
|
|||
std::cerr << "replaying (start of scenario)\n";
|
||||
} else {
|
||||
std::cerr << "skipping...\n";
|
||||
recorder.set_skip(-1);
|
||||
recorder.set_skip(false);
|
||||
}
|
||||
} else {
|
||||
// We have a snapshot. But does the user want to see a replay?
|
||||
|
@ -1468,6 +1469,21 @@ void game_controller::play_game(RELOAD_GAME_DATA reload)
|
|||
|
||||
} //end anon namespace
|
||||
|
||||
void game_controller::play_replay()
|
||||
{
|
||||
const binary_paths_manager bin_paths_manager(game_config_);
|
||||
|
||||
try {
|
||||
::play_replay(disp(),state_,game_config_,units_data_,video_);
|
||||
|
||||
} catch(game::load_game_exception& e) {
|
||||
|
||||
//this will make it so next time through the title screen loop, this game is loaded
|
||||
loaded_game_ = e.game;
|
||||
loaded_game_show_replay_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
int play_game(int argc, char** argv)
|
||||
{
|
||||
const int start_ticks = SDL_GetTicks();
|
||||
|
@ -1715,7 +1731,12 @@ int play_game(int argc, char** argv)
|
|||
continue;
|
||||
}
|
||||
|
||||
game.play_game(should_reload);
|
||||
if (recorder.at_end()){
|
||||
game.play_game(should_reload);
|
||||
}
|
||||
else{
|
||||
game.play_replay();
|
||||
}
|
||||
ntip = -1; // Change tip when a game is played
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "util.hpp"
|
||||
#include "video.hpp"
|
||||
#include "wesconfig.h"
|
||||
#include "wml_separators.hpp"
|
||||
#include "SDL.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -77,6 +78,13 @@ const struct {
|
|||
{ hotkey::HOTKEY_LABEL_TERRAIN, "labelterrain", N_("Set Label"), false },
|
||||
{ hotkey::HOTKEY_SHOW_ENEMY_MOVES, "showenemymoves", N_("Show Enemy Moves"), false },
|
||||
{ hotkey::HOTKEY_BEST_ENEMY_MOVES, "bestenemymoves", N_("Best Possible Enemy Moves"), false },
|
||||
{ hotkey::HOTKEY_PLAY_REPLAY, "playreplay", N_("Play"), false },
|
||||
{ hotkey::HOTKEY_RESET_REPLAY, "resetreplay", N_("Reset"), false },
|
||||
{ hotkey::HOTKEY_STOP_REPLAY, "stopreplay", N_("Stop"), false },
|
||||
{ hotkey::HOTKEY_REPLAY_NEXT_TURN, "replaynextturn", N_("Next Turn"), false },
|
||||
{ hotkey::HOTKEY_REPLAY_NEXT_SIDE, "replaynextside", N_("Next Side"), false },
|
||||
{ hotkey::HOTKEY_REPLAY_SHROUD, "replayswitchshroud", N_("Shroud"), false },
|
||||
{ hotkey::HOTKEY_REPLAY_FOG, "replayswitchfog", N_("Fog"), false },
|
||||
|
||||
{ hotkey::HOTKEY_EDIT_SET_TERRAIN, "editsetterrain", N_("Set Terrain"),true },
|
||||
{ hotkey::HOTKEY_EDIT_QUIT, "editquit", N_("Quit Editor"),true },
|
||||
|
@ -650,12 +658,63 @@ void execute_command(display& disp, HOTKEY_COMMAND command, command_executor* ex
|
|||
if(executor)
|
||||
executor->change_language();
|
||||
break;
|
||||
case HOTKEY_PLAY_REPLAY:
|
||||
if (executor)
|
||||
executor->play_replay();
|
||||
break;
|
||||
case HOTKEY_RESET_REPLAY:
|
||||
if (executor)
|
||||
executor->reset_replay();
|
||||
break;
|
||||
case HOTKEY_STOP_REPLAY:
|
||||
if (executor)
|
||||
executor->stop_replay();
|
||||
break;
|
||||
case HOTKEY_REPLAY_NEXT_TURN:
|
||||
if (executor)
|
||||
executor->replay_next_turn();
|
||||
break;
|
||||
case HOTKEY_REPLAY_NEXT_SIDE:
|
||||
if (executor)
|
||||
executor->replay_next_side();
|
||||
break;
|
||||
case HOTKEY_REPLAY_SHROUD:
|
||||
if (executor)
|
||||
executor->replay_switch_shroud();
|
||||
break;
|
||||
case HOTKEY_REPLAY_FOG:
|
||||
if (executor)
|
||||
executor->replay_switch_fog();
|
||||
break;
|
||||
default:
|
||||
std::cerr << "command_executor: unknown command number " << command << ", ignoring.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void command_executor::show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu, display& gui)
|
||||
{
|
||||
std::vector<std::string> items = items_arg;
|
||||
if (can_execute_command(hotkey::get_hotkey(items.front()).get_id())){
|
||||
//if just one item is passed in, that means we should execute that item
|
||||
if(items.size() == 1 && items_arg.size() == 1) {
|
||||
hotkey::execute_command(gui,hotkey::get_hotkey(items.front()).get_id(),this);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> menu = get_menu_images(items);
|
||||
|
||||
static const std::string style = "menu2";
|
||||
const int res = gui::show_dialog(gui,NULL,"","",
|
||||
gui::MESSAGE,&menu,NULL,"",NULL,-1,NULL,NULL,xloc,yloc,&style);
|
||||
if (size_t(res) >= items.size())
|
||||
return;
|
||||
|
||||
const hotkey::HOTKEY_COMMAND cmd = hotkey::get_hotkey(items[res]).get_id();
|
||||
hotkey::execute_command(gui,cmd,this);
|
||||
}
|
||||
}
|
||||
|
||||
std::string command_executor::get_menu_image(hotkey::HOTKEY_COMMAND command) const {
|
||||
switch(get_action_state(command)) {
|
||||
case ACTION_ON: return game_config::checked_menu_image;
|
||||
|
@ -664,4 +723,31 @@ std::string command_executor::get_menu_image(hotkey::HOTKEY_COMMAND command) con
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> command_executor::get_menu_images(const std::vector<std::string>& items){
|
||||
std::vector<std::string> result;
|
||||
bool has_image = false;
|
||||
|
||||
for(std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i) {
|
||||
const hotkey::hotkey_item hk = hotkey::get_hotkey(*i);
|
||||
|
||||
std::stringstream str;
|
||||
//see if this menu item has an associated image
|
||||
std::string img(get_menu_image(hk.get_id()));
|
||||
if(img.empty() == false) {
|
||||
has_image = true;
|
||||
str << IMAGE_PREFIX << img << COLUMN_SEPARATOR;
|
||||
}
|
||||
|
||||
str << hk.get_description() << COLUMN_SEPARATOR << hk.get_name();
|
||||
|
||||
result.push_back(str.str());
|
||||
}
|
||||
//If any of the menu items have an image, create an image column
|
||||
if(has_image)
|
||||
for(std::vector<std::string>::iterator i = result.begin(); i != result.end(); ++i)
|
||||
if(*(i->begin()) != IMAGE_PREFIX)
|
||||
i->insert(i->begin(), COLUMN_SEPARATOR);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ enum HOTKEY_COMMAND {
|
|||
HOTKEY_DELAY_SHROUD, HOTKEY_UPDATE_SHROUD, HOTKEY_CONTINUE_MOVE,
|
||||
HOTKEY_SEARCH, HOTKEY_SPEAK_ALLY, HOTKEY_SPEAK_ALL, HOTKEY_HELP,
|
||||
HOTKEY_CHAT_LOG, HOTKEY_LANGUAGE,
|
||||
HOTKEY_PLAY_REPLAY, HOTKEY_RESET_REPLAY, HOTKEY_STOP_REPLAY, HOTKEY_REPLAY_NEXT_TURN,
|
||||
HOTKEY_REPLAY_NEXT_SIDE, HOTKEY_REPLAY_SHROUD, HOTKEY_REPLAY_FOG,
|
||||
|
||||
//editing specific commands
|
||||
HOTKEY_EDIT_SET_TERRAIN,
|
||||
|
@ -163,6 +165,13 @@ public:
|
|||
virtual void show_chat_log() {}
|
||||
virtual void user_command() {}
|
||||
virtual void change_language() {}
|
||||
virtual void play_replay() {}
|
||||
virtual void reset_replay() {}
|
||||
virtual void stop_replay() {}
|
||||
virtual void replay_next_turn() {}
|
||||
virtual void replay_next_side() {}
|
||||
virtual void replay_switch_shroud() {}
|
||||
virtual void replay_switch_fog() {}
|
||||
|
||||
// Map editor stuff.
|
||||
virtual void edit_set_terrain() {}
|
||||
|
@ -190,6 +199,10 @@ public:
|
|||
virtual ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const { return ACTION_STATELESS; }
|
||||
//Returns the appropriate menu image. Checkable items will get a checked/unchecked image.
|
||||
std::string get_menu_image(hotkey::HOTKEY_COMMAND command) const;
|
||||
//Returns a vector of images for a given menu
|
||||
std::vector<std::string> get_menu_images(const std::vector<std::string>& items_arg);
|
||||
|
||||
void show_menu(const std::vector<std::string>& items_arg, int xloc, int yloc, bool context_menu, display& gui);
|
||||
|
||||
virtual bool can_execute_command(HOTKEY_COMMAND command) const = 0;
|
||||
};
|
||||
|
|
226
src/mouse_events.cpp
Normal file
|
@ -0,0 +1,226 @@
|
|||
#include "mouse_events.hpp"
|
||||
|
||||
#include "cursor.hpp"
|
||||
|
||||
namespace events{
|
||||
|
||||
mouse_handler::mouse_handler(display* gui, std::vector<team>& teams, unit_map& units, gamemap& map,
|
||||
gamestatus& status, const game_data& gameinfo):
|
||||
gui_(gui), teams_(teams), units_(units), map_(map), status_(status), gameinfo_(gameinfo)
|
||||
{
|
||||
minimap_scrolling_ = false;
|
||||
last_nearest_ = gamemap::location::NORTH;
|
||||
last_second_nearest_ = gamemap::location::NORTH;
|
||||
enemy_paths_ = false;
|
||||
browse_ = false;
|
||||
path_turns_ = 0;
|
||||
}
|
||||
|
||||
void mouse_handler::mouse_motion(const SDL_MouseMotionEvent& event, const int player_number)
|
||||
{
|
||||
mouse_motion(event.x,event.y, player_number);
|
||||
}
|
||||
|
||||
void mouse_handler::mouse_motion(int x, int y, const int player_number)
|
||||
{
|
||||
if(minimap_scrolling_) {
|
||||
//if the game is run in a window, we could miss a LMB/MMB up event
|
||||
// if it occurs outside our window.
|
||||
// thus, we need to check if the LMB/MMB is still down
|
||||
minimap_scrolling_ = ((SDL_GetMouseState(NULL,NULL) & (SDL_BUTTON(1) | SDL_BUTTON(2))) != 0);
|
||||
if(minimap_scrolling_) {
|
||||
const gamemap::location& loc = (*gui_).minimap_location_on(x,y);
|
||||
if(loc.valid()) {
|
||||
if(loc != last_hex_) {
|
||||
last_hex_ = loc;
|
||||
(*gui_).scroll_to_tile(loc.x,loc.y,display::WARP,false);
|
||||
}
|
||||
} else {
|
||||
// clicking outside of the minimap will end minimap scrolling
|
||||
minimap_scrolling_ = false;
|
||||
}
|
||||
}
|
||||
if(minimap_scrolling_) return;
|
||||
}
|
||||
|
||||
gamemap::location::DIRECTION nearest_hex = gamemap::location::NDIRECTIONS;
|
||||
gamemap::location::DIRECTION second_nearest_hex = gamemap::location::NDIRECTIONS;
|
||||
const gamemap::location new_hex = (*gui_).hex_clicked_on(x,y,&nearest_hex,&second_nearest_hex);
|
||||
|
||||
if(new_hex != last_hex_ || nearest_hex != last_nearest_ || second_nearest_hex != last_second_nearest_) {
|
||||
if(new_hex.valid() == false) {
|
||||
current_route_.steps.clear();
|
||||
(*gui_).set_route(NULL);
|
||||
}
|
||||
|
||||
(*gui_).highlight_hex(new_hex);
|
||||
|
||||
|
||||
//see if we should show the normal cursor, the movement cursor, or
|
||||
//the attack cursor
|
||||
|
||||
const unit_map::const_iterator selected_unit = find_unit(selected_hex_, player_number);
|
||||
const unit_map::const_iterator mouseover_unit = find_unit(new_hex, player_number);
|
||||
|
||||
gamemap::location attack_from;
|
||||
if(selected_unit != units_.end() && mouseover_unit != units_.end()) {
|
||||
attack_from = current_unit_attacks_from(new_hex, nearest_hex, second_nearest_hex, player_number);
|
||||
}
|
||||
|
||||
team& current_team = teams_[player_number - 1];
|
||||
if(selected_unit != units_.end() && (current_paths_.routes.count(new_hex) ||
|
||||
attack_from.valid())) {
|
||||
if(mouseover_unit == units_.end()) {
|
||||
cursor::set(cursor::MOVE);
|
||||
} else if(current_team.is_enemy(mouseover_unit->second.side()) && !mouseover_unit->second.stone()) {
|
||||
cursor::set(cursor::ATTACK);
|
||||
} else {
|
||||
cursor::set(cursor::NORMAL);
|
||||
}
|
||||
} else {
|
||||
cursor::set(cursor::NORMAL);
|
||||
}
|
||||
|
||||
if(enemy_paths_) {
|
||||
enemy_paths_ = false;
|
||||
current_paths_ = paths();
|
||||
(*gui_).set_paths(NULL);
|
||||
}
|
||||
|
||||
const gamemap::location& dest = attack_from.valid() ? attack_from : new_hex;
|
||||
const unit_map::const_iterator dest_un = find_unit(dest, player_number);
|
||||
if(dest == selected_hex_ || dest_un != units_.end()) {
|
||||
current_route_.steps.clear();
|
||||
(*gui_).set_route(NULL);
|
||||
} else if(!current_paths_.routes.empty() && map_.on_board(selected_hex_) &&
|
||||
map_.on_board(new_hex)) {
|
||||
|
||||
unit_map::const_iterator un = find_unit(selected_hex_, player_number);
|
||||
|
||||
if((new_hex != last_hex_ || attack_from.valid()) && un != units_.end() && !un->second.stone()) {
|
||||
const shortest_path_calculator calc(un->second,current_team,
|
||||
visible_units(player_number),teams_,map_,status_);
|
||||
const bool can_teleport = un->second.type().teleports();
|
||||
|
||||
const std::set<gamemap::location>* teleports = NULL;
|
||||
|
||||
std::set<gamemap::location> allowed_teleports;
|
||||
if(can_teleport) {
|
||||
allowed_teleports = vacant_villages(current_team.villages(),units_);
|
||||
teleports = &allowed_teleports;
|
||||
if(current_team.villages().count(un->first))
|
||||
allowed_teleports.insert(un->first);
|
||||
}
|
||||
|
||||
current_route_ = a_star_search(selected_hex_, dest, 10000.0, &calc, map_.x(), map_.y(), teleports);
|
||||
|
||||
current_route_.move_left = route_turns_to_complete(un->second,map_,current_route_);
|
||||
|
||||
if(!browse_) {
|
||||
(*gui_).set_route(¤t_route_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unit_map::const_iterator un = find_unit(new_hex, player_number);
|
||||
|
||||
if(un != units_.end() && /* un->second.side() != player_number && */
|
||||
current_paths_.routes.empty() && !(*gui_).fogged(un->first.x,un->first.y)) {
|
||||
//Quick-Hack because of problems with passing a constant Reference
|
||||
unit un2 = un->second;
|
||||
unit_movement_resetter move_reset(un2);
|
||||
|
||||
const bool ignore_zocs = un->second.type().is_skirmisher();
|
||||
const bool teleport = un->second.type().teleports();
|
||||
current_paths_ = paths(map_,status_,gameinfo_,units_,new_hex,teams_,
|
||||
ignore_zocs,teleport,path_turns_);
|
||||
(*gui_).set_paths(¤t_paths_);
|
||||
enemy_paths_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
last_hex_ = new_hex;
|
||||
last_nearest_ = nearest_hex;
|
||||
last_second_nearest_ = second_nearest_hex;
|
||||
}
|
||||
|
||||
unit_map::const_iterator mouse_handler::find_unit(const gamemap::location& hex, const int player_number)
|
||||
{
|
||||
if ((*gui_).fogged(hex.x,hex.y)) {
|
||||
return units_.end();
|
||||
}
|
||||
|
||||
return find_visible_unit(units_,hex,map_,status_.get_time_of_day().lawful_bonus,teams_,viewing_team(player_number));
|
||||
}
|
||||
|
||||
gamemap::location mouse_handler::current_unit_attacks_from(const gamemap::location& loc, const gamemap::location::DIRECTION preferred, const gamemap::location::DIRECTION second_preferred, const int player_number)
|
||||
{
|
||||
const unit_map::const_iterator current = find_unit(selected_hex_, player_number);
|
||||
if(current == units_.end() || current->second.side() != player_number) {
|
||||
return gamemap::location();
|
||||
}
|
||||
|
||||
team& current_team = teams_[player_number - 1];
|
||||
const unit_map::const_iterator enemy = find_unit(loc, player_number);
|
||||
if(enemy == units_.end() || current_team.is_enemy(enemy->second.side()) == false) {
|
||||
return gamemap::location();
|
||||
}
|
||||
|
||||
int best_rating = 100;//smaller is better
|
||||
gamemap::location res;
|
||||
gamemap::location adj[6];
|
||||
get_adjacent_tiles(loc,adj);
|
||||
for(size_t n = 0; n != 6; ++n) {
|
||||
if(map_.on_board(adj[n]) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(adj[n] == selected_hex_) {
|
||||
return selected_hex_;
|
||||
}
|
||||
|
||||
if(find_unit(adj[n], player_number) != units_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(current_paths_.routes.count(adj[n])) {
|
||||
static const size_t NDIRECTIONS = gamemap::location::NDIRECTIONS;
|
||||
int difference = abs(int(preferred - n));
|
||||
if(difference > NDIRECTIONS/2) {
|
||||
difference = NDIRECTIONS - difference;
|
||||
}
|
||||
int second_difference = abs(int(second_preferred - n));
|
||||
if(second_difference > NDIRECTIONS/2) {
|
||||
second_difference = NDIRECTIONS - second_difference;
|
||||
}
|
||||
const int rating = difference * 2 + (second_difference > difference);
|
||||
if(rating < best_rating || res.valid() == false) {
|
||||
best_rating = rating;
|
||||
res = adj[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
unit_map& mouse_handler::visible_units(const int player_number)
|
||||
{
|
||||
if(viewing_team(player_number).uses_shroud() == false && viewing_team(player_number).uses_fog() == false) {
|
||||
LOG_STREAM(info, engine) << "all units are visible...\n";
|
||||
return units_;
|
||||
}
|
||||
|
||||
visible_units_.clear();
|
||||
for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if((*gui_).fogged(i->first.x,i->first.y) == false) {
|
||||
visible_units_.insert(*i);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_STREAM(info, engine) << "number of visible units: " << visible_units_.size() << "\n";
|
||||
|
||||
return visible_units_;
|
||||
}
|
||||
|
||||
}
|
49
src/mouse_events.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef MOUSE_EVENTS_H_INCLUDED
|
||||
#define MOUSE_EVENTS_H_INCLUDED
|
||||
|
||||
#include "global.hpp"
|
||||
|
||||
#include "display.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "unit.hpp"
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
namespace events{
|
||||
|
||||
class mouse_handler{
|
||||
public:
|
||||
mouse_handler(display* gui, std::vector<team>& teams, unit_map& units, gamemap& map, gamestatus& status, const game_data& gameinfo);
|
||||
void mouse_motion(const SDL_MouseMotionEvent& event, const int player_number);
|
||||
void set_gui(display* gui) { gui_ = gui; }
|
||||
private:
|
||||
team& viewing_team(const int player_number) { return teams_[(*gui_).viewing_team()]; }
|
||||
|
||||
void mouse_motion(int x, int y, const int player_number);
|
||||
gamemap::location current_unit_attacks_from(const gamemap::location& loc, const gamemap::location::DIRECTION preferred, const gamemap::location::DIRECTION second_preferred, const int player_number);
|
||||
unit_map::const_iterator find_unit(const gamemap::location& hex, const int player_number);
|
||||
unit_map& visible_units(const int player_number);
|
||||
|
||||
display* gui_;
|
||||
std::vector<team>& teams_;
|
||||
unit_map& units_;
|
||||
gamemap& map_;
|
||||
gamestatus& status_;
|
||||
const game_data& gameinfo_;
|
||||
|
||||
bool minimap_scrolling_;
|
||||
gamemap::location last_hex_;
|
||||
gamemap::location selected_hex_;
|
||||
gamemap::location::DIRECTION last_nearest_, last_second_nearest_;
|
||||
paths::route current_route_;
|
||||
paths current_paths_;
|
||||
bool enemy_paths_;
|
||||
bool browse_;
|
||||
mutable unit_map visible_units_;
|
||||
int path_turns_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -16,6 +16,7 @@
|
|||
#include "marked-up_text.hpp"
|
||||
#include "minimap.hpp"
|
||||
#include "multiplayer_lobby.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "wassert.hpp"
|
||||
#include "wml_separators.hpp"
|
||||
#include "game_config.hpp"
|
||||
|
@ -495,6 +496,7 @@ void lobby::process_event()
|
|||
}
|
||||
|
||||
if(quit_game_.pressed()) {
|
||||
recorder.set_skip(false);
|
||||
set_result(QUIT);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ void level_to_gamestate(config& level, game_state& state)
|
|||
LOG_NW << "setting replay\n";
|
||||
recorder = replay(replay_data_store);
|
||||
if(!recorder.empty()) {
|
||||
recorder.set_skip(-1);
|
||||
recorder.set_skip(false);
|
||||
}
|
||||
|
||||
level.clear_children("replay");
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "multiplayer.hpp"
|
||||
#include "multiplayer_wait.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "util.hpp"
|
||||
#include "video.hpp"
|
||||
|
@ -249,6 +250,10 @@ void wait::join_game(bool observe)
|
|||
|
||||
network::send_data(response);
|
||||
}
|
||||
else{
|
||||
//const int res = gui::show_dialog(disp(),NULL,_("Skip replay"),_("Do you want to skip the replay?"),gui::YES_NO);
|
||||
//recorder.set_skip(res == 0);
|
||||
}
|
||||
|
||||
generate_menu();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "config.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "replay_controller.hpp"
|
||||
#include "log.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "dialogs.hpp"
|
||||
|
@ -49,6 +50,92 @@ typedef std::map<std::string, player_controller> controller_map;
|
|||
|
||||
}
|
||||
|
||||
void play_replay(display& disp, game_state& state, const config& game_config,
|
||||
const game_data& units_data, CVideo& video,
|
||||
io_type_t io_type)
|
||||
{
|
||||
std::string type = state.campaign_type;
|
||||
if(type.empty())
|
||||
type = "scenario";
|
||||
|
||||
config const* scenario = NULL;
|
||||
|
||||
//'starting_pos' will contain the position we start the game from.
|
||||
config starting_pos;
|
||||
|
||||
recorder.set_save_info(state);
|
||||
|
||||
//see if we load the scenario from the scenario data -- if there is
|
||||
//no snapshot data available from a save, or if the user has selected
|
||||
//to view the replay from scratch
|
||||
if(state.snapshot.child("side") == NULL || !recorder.at_end()) {
|
||||
//if the starting state is specified, then use that,
|
||||
//otherwise get the scenario data and start from there.
|
||||
if(state.starting_pos.empty() == false) {
|
||||
LOG_G << "loading starting position...\n";
|
||||
starting_pos = state.starting_pos;
|
||||
scenario = &starting_pos;
|
||||
} else {
|
||||
LOG_G << "loading scenario: '" << state.scenario << "'\n";
|
||||
scenario = game_config.find_child(type,"id",state.scenario);
|
||||
LOG_G << "scenario found: " << (scenario != NULL ? "yes" : "no") << "\n";
|
||||
}
|
||||
} else {
|
||||
LOG_G << "loading snapshot...\n";
|
||||
//load from a save-snapshot.
|
||||
starting_pos = state.snapshot;
|
||||
scenario = &starting_pos;
|
||||
state = read_game(units_data, &state.snapshot);
|
||||
}
|
||||
|
||||
controller_map controllers;
|
||||
|
||||
while(scenario != NULL) {
|
||||
//If we are a multiplayer client, tweak the controllers
|
||||
const config::child_list& story = scenario->get_children("story");
|
||||
const std::string current_scenario = state.scenario;
|
||||
|
||||
bool save_game_after_scenario = true;
|
||||
|
||||
try {
|
||||
// preserve old label eg. replay
|
||||
if (state.label.empty())
|
||||
state.label = (*scenario)["name"];
|
||||
|
||||
LEVEL_RESULT res = play_replay_level(units_data,game_config,scenario,video,state,story);
|
||||
|
||||
state.snapshot = config();
|
||||
recorder.clear();
|
||||
state.replay_data.clear();
|
||||
|
||||
} catch(game::load_game_failed& e) {
|
||||
gui::show_error_message(disp, _("The game could not be loaded: ") + e.message);
|
||||
} catch(game::game_error& e) {
|
||||
gui::show_error_message(disp, _("Error while playing the game: ") + e.message);
|
||||
} catch(gamemap::incorrect_format_exception& e) {
|
||||
gui::show_error_message(disp, std::string(_("The game map could not be loaded: ")) + e.msg_);
|
||||
}
|
||||
|
||||
//if the scenario hasn't been set in-level, set it now.
|
||||
if(state.scenario == current_scenario)
|
||||
state.scenario = (*scenario)["next_scenario"];
|
||||
|
||||
scenario = game_config.find_child(type,"id",state.scenario);
|
||||
|
||||
//update the replay start
|
||||
if(scenario != NULL) {
|
||||
//FIXME: this should only be done if the scenario was not tweaked.
|
||||
state.starting_pos = *scenario;
|
||||
}
|
||||
|
||||
recorder.set_save_info(state);
|
||||
}
|
||||
|
||||
if (!state.scenario.empty() && state.scenario != "null") {
|
||||
gui::show_error_message(disp, _("Unknown scenario: '") + state.scenario + '\'');
|
||||
}
|
||||
}
|
||||
|
||||
LEVEL_RESULT play_game(display& disp, game_state& state, const config& game_config,
|
||||
const game_data& units_data, CVideo& video,
|
||||
io_type_t io_type)
|
||||
|
|
|
@ -34,5 +34,9 @@ LEVEL_RESULT play_game(display& disp, game_state& state, const config& game_conf
|
|||
io_type_t io_type=IO_NONE);
|
||||
|
||||
|
||||
void play_replay(display& disp, game_state& state, const config& game_config,
|
||||
const game_data& units_data, CVideo& video,
|
||||
io_type_t io_type=IO_NONE);
|
||||
|
||||
#endif // PLAYCAMPAIGN_H_INCLUDED
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
#define LOG_NG LOG_STREAM(info, engine)
|
||||
|
||||
namespace {
|
||||
namespace play{
|
||||
int placing_score(const config& side, const gamemap& map, const gamemap::location& pos)
|
||||
{
|
||||
int positions = 0, liked = 0;
|
||||
|
@ -109,17 +109,6 @@ namespace {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_observer(const std::vector<team>& teams)
|
||||
{
|
||||
for(std::vector<team>::const_iterator i = teams.begin(); i != teams.end(); ++i) {
|
||||
if(i->is_human() || i->is_persistent()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
||||
|
@ -198,7 +187,7 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
|
||||
std::vector<team> teams;
|
||||
|
||||
const teams_manager team_manager(teams);
|
||||
teams_manager team_manager(teams);
|
||||
|
||||
int first_human_team = -1;
|
||||
|
||||
|
@ -206,7 +195,7 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
|
||||
if(lvl["modify_placing"] == "true") {
|
||||
LOG_NG << "modifying placing...\n";
|
||||
place_sides_in_preferred_locations(map,unit_cfg);
|
||||
play::place_sides_in_preferred_locations(map,unit_cfg);
|
||||
}
|
||||
|
||||
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
|
||||
|
@ -215,187 +204,19 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
std::set<std::string> seen_save_ids;
|
||||
|
||||
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
|
||||
std::string save_id = (**ui)["save_id"];
|
||||
|
||||
if(save_id.empty()) {
|
||||
save_id=(**ui)["description"];
|
||||
}
|
||||
|
||||
//make sure the 'save_id' is unique
|
||||
while(seen_save_ids.count(save_id)) {
|
||||
save_id += "_";
|
||||
}
|
||||
|
||||
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
|
||||
seen_save_ids.insert(save_id);
|
||||
|
||||
player_info *player = NULL;
|
||||
|
||||
if((**ui)["controller"] == "human" ||
|
||||
(**ui)["controller"] == "network" ||
|
||||
(**ui)["persistent"] == "1") {
|
||||
player = state_of_game.get_player(save_id);
|
||||
|
||||
if(player == NULL && !save_id.empty()) {
|
||||
player = &state_of_game.players[save_id];
|
||||
}
|
||||
if (first_human_team == -1){
|
||||
first_human_team = get_first_human_team(ui, unit_cfg);
|
||||
}
|
||||
|
||||
LOG_NG << "initializing team...\n";
|
||||
|
||||
const std::string& controller = (**ui)["controller"];
|
||||
if (controller == preferences::client_type() && (**ui)["description"] == preferences::login()) {
|
||||
first_human_team = ui - unit_cfg.begin();
|
||||
} else if(first_human_team == -1 && ((**ui)["controller"] == "human" || (**ui)["persistent"] == "1")) {
|
||||
first_human_team = ui - unit_cfg.begin();
|
||||
}
|
||||
|
||||
std::string gold = (**ui)["gold"];
|
||||
if(gold.empty())
|
||||
gold = "100";
|
||||
|
||||
LOG_NG << "found gold: '" << gold << "'\n";
|
||||
|
||||
int ngold = lexical_cast_default<int>(gold);
|
||||
if(player != NULL && player->gold >= ngold) {
|
||||
ngold = player->gold;
|
||||
}
|
||||
|
||||
LOG_NG << "set gold to '" << ngold << "'\n";
|
||||
|
||||
teams.push_back(team(**ui,ngold));
|
||||
|
||||
//update/fix the recall list for this side, by setting the
|
||||
//"side" of each unit in it to be the "side" of the player.
|
||||
int side = lexical_cast_default<int>((**ui)["side"], 1);
|
||||
if(player != NULL) {
|
||||
for(std::vector<unit>::iterator it = player->available_units.begin(); it != player->available_units.end(); ++it) {
|
||||
it->set_side(side);
|
||||
}
|
||||
}
|
||||
|
||||
//if this team has no objectives, set its objectives to the
|
||||
//level-global "objectives"
|
||||
if(teams.back().objectives().empty())
|
||||
teams.back().set_objectives((*level)["objectives"]);
|
||||
|
||||
//if this side tag describes the leader of the side
|
||||
if((**ui)["no_leader"] != "yes" && (**ui)["controller"] != "null") {
|
||||
unit new_unit(gameinfo, **ui);
|
||||
|
||||
//search the recall list for leader units, and if there is
|
||||
//one, use it in place of the config-described unit
|
||||
if(player != NULL) {
|
||||
for(std::vector<unit>::iterator it = player->available_units.begin(); it != player->available_units.end(); ++it) {
|
||||
if(it->can_recruit()) {
|
||||
new_unit = *it;
|
||||
player->available_units.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//see if the side specifies its location. Otherwise start it at the map-given
|
||||
//starting position
|
||||
const std::string& has_loc = (**ui)["x"];
|
||||
gamemap::location start_pos(**ui);
|
||||
|
||||
if(has_loc.empty()) {
|
||||
start_pos = map.starting_position(side);
|
||||
LOG_NG << "initializing side '" << (**ui)["side"] << "' at "
|
||||
<< start_pos << '\n';
|
||||
}
|
||||
|
||||
if(map.empty()) {
|
||||
throw game::load_game_failed("Map not found");
|
||||
}
|
||||
|
||||
if(!start_pos.valid() && new_unit.side() == 1) {
|
||||
throw game::load_game_failed("No starting position for side 1");
|
||||
}
|
||||
|
||||
if(start_pos.valid()) {
|
||||
new_unit.new_turn();
|
||||
units.insert(std::pair<gamemap::location,unit>(
|
||||
map.starting_position(new_unit.side()), new_unit));
|
||||
}
|
||||
}
|
||||
|
||||
//if the game state specifies units that can be recruited for the player
|
||||
//then add them
|
||||
if(player != NULL && player->can_recruit.empty() == false) {
|
||||
std::copy(player->can_recruit.begin(),player->can_recruit.end(),
|
||||
std::inserter(teams.back().recruits(),teams.back().recruits().end()));
|
||||
}
|
||||
|
||||
if(player != NULL) {
|
||||
player->can_recruit = teams.back().recruits();
|
||||
}
|
||||
|
||||
//if there are additional starting units on this side
|
||||
const config::child_list& starting_units = (*ui)->get_children("unit");
|
||||
for(config::child_list::const_iterator su = starting_units.begin(); su != starting_units.end(); ++su) {
|
||||
unit new_unit(gameinfo,**su);
|
||||
|
||||
new_unit.set_side(side);
|
||||
|
||||
const std::string& x = (**su)["x"];
|
||||
const std::string& y = (**su)["y"];
|
||||
|
||||
const gamemap::location loc(**su);
|
||||
if(x.empty() || y.empty() || !map.on_board(loc)) {
|
||||
if(player) {
|
||||
player->available_units.push_back(new_unit);
|
||||
}
|
||||
} else {
|
||||
units.insert(std::pair<gamemap::location,unit>(loc,new_unit));
|
||||
LOG_NG << "inserting unit for side " << new_unit.side() << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
get_player_info(**ui, state_of_game, save_id, teams, lvl, gameinfo, map, units);
|
||||
}
|
||||
|
||||
// Add all recruitable units as encountered so that information
|
||||
// about them are displayed to the user in the help system.
|
||||
for (std::vector<team>::const_iterator help_team_it = teams.begin();
|
||||
help_team_it != teams.end(); help_team_it++) {
|
||||
LOG_NG << "Adding help units for team '" << help_team_it->name() << "'\n";
|
||||
const std::set<std::string> &recruitable = help_team_it->recruits();
|
||||
std::set<std::string> &enc_units = preferences::encountered_units();
|
||||
LOG_NG << "Adding recruitable units: \n";
|
||||
for (std::set<std::string>::const_iterator it = recruitable.begin();
|
||||
it != recruitable.end(); it++) {
|
||||
LOG_NG << *it << std::endl;
|
||||
}
|
||||
LOG_NG << "Added all recruitable units\n";
|
||||
std::copy(recruitable.begin(), recruitable.end(),
|
||||
std::inserter(enc_units, enc_units.begin()));
|
||||
}
|
||||
preferences::encounter_recruitable_units(teams);
|
||||
preferences::encounter_start_units(units);
|
||||
preferences::encounter_recallable_units(state_of_game);
|
||||
preferences::encounter_map_terrain(map);
|
||||
|
||||
// Add all units that exist at the start to the encountered units so
|
||||
// that information about them are displayed to the user in the help
|
||||
// system.
|
||||
for (unit_map::const_iterator help_unit_it = units.begin();
|
||||
help_unit_it != units.end(); help_unit_it++) {
|
||||
const std::string name = help_unit_it->second.type().id();
|
||||
preferences::encountered_units().insert(name);
|
||||
}
|
||||
|
||||
// Add all units that are recallable as encountred units.
|
||||
for(std::map<std::string, player_info>::iterator pi = state_of_game.players.begin(); pi!=state_of_game.players.end(); ++pi) {
|
||||
for(std::vector<unit>::iterator help_recall_it = pi->second.available_units.begin(); help_recall_it != pi->second.available_units.end(); help_recall_it++) {
|
||||
preferences::encountered_units().insert(help_recall_it->type().id());
|
||||
}
|
||||
}
|
||||
|
||||
// Add all terrains on the map as encountered terrains.
|
||||
for (int map_x = 0; map_x < map.x(); map_x++) {
|
||||
for (int map_y = 0; map_y < map.y(); map_y++) {
|
||||
const gamemap::TERRAIN t = map.get_terrain(gamemap::location(map_x, map_y));
|
||||
std::string s;
|
||||
s += t;
|
||||
preferences::encountered_terrains().insert(s);
|
||||
}
|
||||
}
|
||||
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
const config* theme_cfg = NULL;
|
||||
|
@ -430,7 +251,7 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
game_events::manager events_manager(*level,gui,map,units,teams,
|
||||
state_of_game,status,gameinfo);
|
||||
|
||||
if(recorder.skipping() == false) {
|
||||
if(!recorder.is_skipping()) {
|
||||
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
|
||||
|
||||
show_intro(gui,**story_i, *level);
|
||||
|
@ -514,8 +335,8 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
if(first_time) {
|
||||
const hotkey::basic_handler key_events_handler(&gui);
|
||||
|
||||
LOG_NG << "first_time..." << (recorder.skipping() ? "skipping" : "no skip") << "\n";
|
||||
update_locker lock_display(gui.video(),recorder.skipping());
|
||||
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
|
||||
update_locker lock_display(gui.video(),recorder.is_skipping());
|
||||
events::raise_draw_event();
|
||||
gui.draw();
|
||||
for(std::vector<team>::iterator t = teams.begin(); t != teams.end(); ++t) {
|
||||
|
@ -546,7 +367,7 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
continue;
|
||||
}
|
||||
|
||||
if(is_observer(teams)) {
|
||||
if(team_manager.is_observer()) {
|
||||
gui.set_team(size_t(player_number-1));
|
||||
}
|
||||
|
||||
|
@ -591,13 +412,7 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& game_config,
|
|||
|
||||
clear_shroud(gui,status,map,gameinfo,units,teams,player_number-1);
|
||||
|
||||
//scroll the map to the leader
|
||||
const unit_map::iterator leader = find_leader(units,player_number);
|
||||
|
||||
if(leader != units.end() && !recorder.skipping()) {
|
||||
const hotkey::basic_handler key_events_handler(&gui);
|
||||
gui.scroll_to_tile(leader->first.x,leader->first.y);
|
||||
}
|
||||
gui.scroll_to_leader(units, player_number);
|
||||
|
||||
if(replaying) {
|
||||
const hotkey::basic_handler key_events_handler(&gui);
|
||||
|
@ -738,8 +553,8 @@ redo_turn:
|
|||
event_stream << status.turn();
|
||||
|
||||
{
|
||||
LOG_NG << "turn event..." << (recorder.skipping() ? "skipping" : "no skip") << "\n";
|
||||
update_locker lock_display(gui.video(),recorder.skipping());
|
||||
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
|
||||
update_locker lock_display(gui.video(),recorder.is_skipping());
|
||||
const std::string turn_num = event_stream.str();
|
||||
state_of_game.set_variable("turn_number",turn_num);
|
||||
game_events::fire("turn " + turn_num);
|
||||
|
@ -748,7 +563,7 @@ redo_turn:
|
|||
} //end for loop
|
||||
|
||||
} catch(end_level_exception& end_level) {
|
||||
bool obs = is_observer(teams);
|
||||
bool obs = team_manager.is_observer();
|
||||
if (end_level.result == DEFEAT || end_level.result == VICTORY) {
|
||||
// if we're a player, and the result is victory/defeat, then send a message to notify
|
||||
// the server of the reason for the game ending
|
||||
|
|
|
@ -42,4 +42,8 @@ LEVEL_RESULT play_level(const game_data& gameinfo, const config& terrain_config,
|
|||
game_state& state_of_game,
|
||||
const std::vector<config*>& story);
|
||||
|
||||
namespace play{
|
||||
void place_sides_in_preferred_locations(gamemap& map, const config::child_list& sides);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1171,43 +1171,7 @@ void turn_info::show_menu(const std::vector<std::string>& items_arg, int xloc, i
|
|||
if(items.empty())
|
||||
return;
|
||||
|
||||
//if just one item is passed in, that means we should execute that item
|
||||
if(items.size() == 1 && items_arg.size() == 1) {
|
||||
hotkey::execute_command(gui_,hotkey::get_hotkey(items.front()).get_id(),this);
|
||||
return;
|
||||
}
|
||||
|
||||
bool has_image = false;
|
||||
std::vector<std::string> menu;
|
||||
for(std::vector<std::string>::const_iterator i = items.begin(); i != items.end(); ++i) {
|
||||
const hotkey::hotkey_item hk = hotkey::get_hotkey(*i);
|
||||
|
||||
std::stringstream str;
|
||||
//see if this menu item has an associated image
|
||||
std::string img(get_menu_image(hk.get_id()));
|
||||
if(img.empty() == false) {
|
||||
has_image = true;
|
||||
str << IMAGE_PREFIX << img << COLUMN_SEPARATOR;
|
||||
}
|
||||
|
||||
str << hk.get_description() << COLUMN_SEPARATOR << hk.get_name();
|
||||
|
||||
menu.push_back(str.str());
|
||||
}
|
||||
//If any of the menu items have an image, create an image column
|
||||
if(has_image)
|
||||
for(std::vector<std::string>::iterator i = menu.begin(); i != menu.end(); ++i)
|
||||
if(*(i->begin()) != IMAGE_PREFIX)
|
||||
i->insert(i->begin(), COLUMN_SEPARATOR);
|
||||
|
||||
static const std::string style = "menu2";
|
||||
const int res = gui::show_dialog(gui_,NULL,"","",
|
||||
gui::MESSAGE,&menu,NULL,"",NULL,-1,NULL,NULL,xloc,yloc,&style);
|
||||
if (size_t(res) >= items.size())
|
||||
return;
|
||||
|
||||
const hotkey::HOTKEY_COMMAND cmd = hotkey::get_hotkey(items[res]).get_id();
|
||||
hotkey::execute_command(gui_,cmd,this);
|
||||
command_executor::show_menu(items_arg, xloc, yloc, context_menu, gui_);
|
||||
}
|
||||
|
||||
bool turn_info::unit_in_cycle(unit_map::const_iterator it) const
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "config.hpp"
|
||||
#include "cursor.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "log.hpp"
|
||||
|
@ -777,4 +778,40 @@ void save_hotkeys() {
|
|||
hotkey::save_hotkeys(prefs);
|
||||
}
|
||||
|
||||
void encounter_recruitable_units(std::vector<team>& teams){
|
||||
for (std::vector<team>::iterator help_team_it = teams.begin();
|
||||
help_team_it != teams.end(); help_team_it++) {
|
||||
help_team_it->log_recruitable();
|
||||
std::copy(help_team_it->recruits().begin(), help_team_it->recruits().end(),
|
||||
std::inserter(encountered_units_set, encountered_units_set.begin()));
|
||||
}
|
||||
}
|
||||
|
||||
void encounter_start_units(unit_map& units){
|
||||
for (unit_map::const_iterator help_unit_it = units.begin();
|
||||
help_unit_it != units.end(); help_unit_it++) {
|
||||
const std::string name = help_unit_it->second.type().id();
|
||||
encountered_units_set.insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
void encounter_recallable_units(game_state& gamestate){
|
||||
for(std::map<std::string, player_info>::iterator pi = gamestate.players.begin(); pi!=gamestate.players.end(); ++pi) {
|
||||
for(std::vector<unit>::iterator help_recall_it = pi->second.available_units.begin(); help_recall_it != pi->second.available_units.end(); help_recall_it++) {
|
||||
encountered_units_set.insert(help_recall_it->type().id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void encounter_map_terrain(gamemap& map){
|
||||
for (int map_x = 0; map_x < map.x(); map_x++) {
|
||||
for (int map_y = 0; map_y < map.y(); map_y++) {
|
||||
const gamemap::TERRAIN t = map.get_terrain(gamemap::location(map_x, map_y));
|
||||
std::string s;
|
||||
s += t;
|
||||
preferences::encountered_terrains().insert(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
class config;
|
||||
class display;
|
||||
class team;
|
||||
struct game_state;
|
||||
|
||||
#include "unit.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
@ -190,6 +194,18 @@ namespace preferences {
|
|||
// proxies for preferences_dialog
|
||||
void load_hotkeys();
|
||||
void save_hotkeys();
|
||||
|
||||
// Add all recruitable units as encountered so that information
|
||||
// about them are displayed to the user in the help system.
|
||||
void encounter_recruitable_units(std::vector<team>& teams);
|
||||
// Add all units that exist at the start to the encountered units so
|
||||
// that information about them are displayed to the user in the help
|
||||
// system.
|
||||
void encounter_start_units(unit_map& units);
|
||||
// Add all units that are recallable as encountred units.
|
||||
void encounter_recallable_units(game_state& gamestate);
|
||||
// Add all terrains on the map as encountered terrains.
|
||||
void encounter_map_terrain(gamemap& map);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "actions.hpp"
|
||||
#include "ai_interface.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "game_config.hpp"
|
||||
#include "game_events.hpp"
|
||||
#include "log.hpp"
|
||||
|
@ -184,20 +185,14 @@ const game_state& replay::get_save_info() const
|
|||
return saveInfo_;
|
||||
}
|
||||
|
||||
void replay::set_skip(int turns_to_skip)
|
||||
void replay::set_skip(bool skip)
|
||||
{
|
||||
skip_ = turns_to_skip;
|
||||
skip_ = skip;
|
||||
}
|
||||
|
||||
void replay::next_skip()
|
||||
bool replay::is_skipping() const
|
||||
{
|
||||
if(skip_ > 0)
|
||||
--skip_;
|
||||
}
|
||||
|
||||
bool replay::skipping() const
|
||||
{
|
||||
return at_end() == false && skip_ != 0;
|
||||
return at_end() == false && skip_;
|
||||
}
|
||||
|
||||
void replay::save_game(const std::string& label, const config& snapshot,
|
||||
|
@ -542,12 +537,14 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
replay& replayer = (obj != NULL) ? *obj : recorder;
|
||||
|
||||
clear_shroud(disp,state,map,gameinfo,units,teams,team_num-1);
|
||||
disp.recalculate_minimap();
|
||||
if (!replayer.is_skipping()){
|
||||
clear_shroud(disp,state,map,gameinfo,units,teams,team_num-1);
|
||||
disp.recalculate_minimap();
|
||||
}
|
||||
|
||||
const set_random_generator generator_setter(&replayer);
|
||||
|
||||
update_locker lock_update(disp.video(),replayer.skipping());
|
||||
update_locker lock_update(disp.video(),replayer.is_skipping());
|
||||
|
||||
//a list of units that have promoted from the last attack
|
||||
std::deque<gamemap::location> advancing_units;
|
||||
|
@ -591,7 +588,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
//if there is nothing more in the records
|
||||
if(cfg == NULL) {
|
||||
replayer.set_skip(0);
|
||||
replayer.set_skip(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -602,19 +599,23 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
const std::string& team_name = (*child)["team_name"];
|
||||
if(team_name == "" || teams[disp.viewing_team()].team_name() == team_name) {
|
||||
if(preferences::message_bell()) {
|
||||
if(!replayer.skipping())
|
||||
if(!replayer.is_skipping())
|
||||
sound::play_sound(game_config::sounds::receive_message);
|
||||
}
|
||||
|
||||
const int side = lexical_cast_default<int>((*child)["side"].c_str(),1);
|
||||
disp.add_chat_message((*child)["description"],side,(*child)["message"],
|
||||
team_name == "" ? display::MESSAGE_PUBLIC : display::MESSAGE_PRIVATE);
|
||||
if (!replayer.is_skipping()){
|
||||
disp.add_chat_message((*child)["description"],side,(*child)["message"],
|
||||
team_name == "" ? display::MESSAGE_PUBLIC : display::MESSAGE_PRIVATE);
|
||||
}
|
||||
}
|
||||
} else if((child = cfg->child("label")) != NULL) {
|
||||
const gamemap::location loc(*child);
|
||||
const std::string& text = (*child)["text"];
|
||||
|
||||
disp.labels().set_label(loc,text);
|
||||
if (!replayer.is_skipping()){
|
||||
disp.labels().set_label(loc,text);
|
||||
}
|
||||
}
|
||||
|
||||
else if((child = cfg->child("rename")) != NULL) {
|
||||
|
@ -633,8 +634,6 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
//if there is an end turn directive
|
||||
else if(cfg->child("end_turn") != NULL) {
|
||||
replayer.next_skip();
|
||||
|
||||
child = cfg->child("verify");
|
||||
if(child != NULL) {
|
||||
verify_units(*child);
|
||||
|
@ -686,8 +685,14 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
current_team.spend_gold(u_type->second.cost());
|
||||
LOG_NW << "-> " << (current_team.gold()) << "\n";
|
||||
fix_shroud = true;
|
||||
}
|
||||
fix_shroud = !replayer.is_skipping() && true;
|
||||
FILE* fUnit;
|
||||
fUnit = fopen( "debug.txt", "a+" );
|
||||
std::string strDescription = new_unit.description();
|
||||
fprintf(fUnit, "Recruit: %i, %s: %i, %i\n", state.turn(), strDescription.c_str(), new_unit.experience(), new_unit.max_experience());
|
||||
fprintf(fUnit, "\n");
|
||||
fclose(fUnit);
|
||||
}
|
||||
|
||||
else if((child = cfg->child("recall")) != NULL) {
|
||||
player_info* player = state_of_game.get_player(current_team.save_id());
|
||||
|
@ -712,7 +717,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
ERR_NW << "illegal recall\n";
|
||||
if (!game_config::ignore_replay_errors) throw replay::error();
|
||||
}
|
||||
fix_shroud = true;
|
||||
fix_shroud = !replayer.is_skipping() && true;
|
||||
}
|
||||
|
||||
else if((child = cfg->child("disband")) != NULL) {
|
||||
|
@ -782,7 +787,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
rt->second.steps.push_back(dst);
|
||||
|
||||
if(!replayer.skipping() && unit_display::unit_visible_on_path(disp,map,rt->second.steps,current_unit,state.get_time_of_day(),units,teams)) {
|
||||
if(!replayer.is_skipping() && unit_display::unit_visible_on_path(disp,map,rt->second.steps,current_unit,state.get_time_of_day(),units,teams)) {
|
||||
disp.set_paths(&paths_list);
|
||||
|
||||
disp.scroll_to_tiles(src.x,src.y,dst.x,dst.y);
|
||||
|
@ -790,9 +795,13 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
units.erase(u);
|
||||
|
||||
if(!replayer.skipping()) {
|
||||
if(!replayer.is_skipping()) {
|
||||
unit_display::move_unit(disp,map,rt->second.steps,current_unit,state.get_time_of_day(),units,teams);
|
||||
}
|
||||
else{
|
||||
//unit location needs to be updated
|
||||
current_unit.set_goto(*(rt->second.steps.end() - 1));
|
||||
}
|
||||
|
||||
current_unit.set_movement(rt->second.move_left);
|
||||
u = units.insert(std::pair<gamemap::location,unit>(dst,current_unit)).first;
|
||||
|
@ -804,7 +813,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
}
|
||||
}
|
||||
|
||||
if(!replayer.skipping()) {
|
||||
if(!replayer.is_skipping()) {
|
||||
disp.draw_tile(dst.x,dst.y);
|
||||
disp.update_display();
|
||||
}
|
||||
|
@ -815,11 +824,19 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
game_events::fire("sighted",dst);
|
||||
}
|
||||
|
||||
fix_shroud = true;
|
||||
fix_shroud = !replayer.is_skipping() && true;
|
||||
FILE* fUnit;
|
||||
fUnit = fopen( "debug.txt", "a+" );
|
||||
if (u != units.end()){
|
||||
std::string strDescription = u->second.description();
|
||||
int iMaxExperience = u->second.max_experience();
|
||||
fprintf(fUnit, "Move: %i, %s: von %i,%i nach %i,%i\n", state.turn(), strDescription.c_str(), src.x, src.y, dst.x, dst.y);
|
||||
}
|
||||
fprintf(fUnit, "\n");
|
||||
fclose(fUnit);
|
||||
}
|
||||
|
||||
else if((child = cfg->child("attack")) != NULL) {
|
||||
|
||||
const config* const destination = child->child("destination");
|
||||
const config* const source = child->child("source");
|
||||
|
||||
|
@ -877,7 +894,24 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
if(advancing_units.empty()) {
|
||||
check_victory(units,teams);
|
||||
}
|
||||
fix_shroud = true;
|
||||
fix_shroud = !replayer.is_skipping() && true;
|
||||
|
||||
FILE* fUnit;
|
||||
fUnit = fopen( "debug.txt", "a+" );
|
||||
if (u != units.end()){
|
||||
std::string strDescription = u->second.description();
|
||||
int iExperience = u->second.experience();
|
||||
int iMaxExperience = u->second.max_experience();
|
||||
fprintf(fUnit, "Attacker: %i, %s, %i, %i\n", state.turn(), strDescription.c_str(), iExperience, iMaxExperience);
|
||||
}
|
||||
if (tgt != units.end()){
|
||||
std::string strDescription = tgt->second.description();
|
||||
int iExperience = tgt->second.experience();
|
||||
int iMaxExperience = tgt->second.max_experience();
|
||||
fprintf(fUnit, "Defender: %i, %s, %i, %i\n", state.turn(), strDescription.c_str(), iExperience, iMaxExperience);
|
||||
}
|
||||
fprintf(fUnit, "\n");
|
||||
fclose(fUnit);
|
||||
} else {
|
||||
ERR_NW << "unrecognized action\n";
|
||||
if (!game_config::ignore_replay_errors) throw replay::error();
|
||||
|
@ -885,7 +919,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
|
||||
//Check if we should refresh the shroud, and redraw the minimap/map tiles.
|
||||
//This is needed for shared vision to work properly.
|
||||
if(fix_shroud && clear_shroud(disp,state,map,gameinfo,units,teams,team_num-1)) {
|
||||
if(fix_shroud && clear_shroud(disp,state,map,gameinfo,units,teams,team_num-1) && !recorder.is_skipping()) {
|
||||
disp.recalculate_minimap();
|
||||
disp.invalidate_all();
|
||||
}
|
||||
|
|
|
@ -38,9 +38,8 @@ public:
|
|||
void set_save_info(const game_state& save);
|
||||
const game_state& get_save_info() const;
|
||||
|
||||
void set_skip(int turns_to_skip);
|
||||
void next_skip();
|
||||
bool skipping() const;
|
||||
void set_skip(bool skip);
|
||||
bool is_skipping() const;
|
||||
|
||||
void save_game(const std::string& label, const config& snapshot,
|
||||
const config& starting_pos, bool include_replay = true);
|
||||
|
@ -119,7 +118,7 @@ private:
|
|||
|
||||
game_state saveInfo_;
|
||||
|
||||
int skip_;
|
||||
bool skip_;
|
||||
};
|
||||
|
||||
replay& get_replay_source();
|
||||
|
|
627
src/replay_controller.cpp
Normal file
|
@ -0,0 +1,627 @@
|
|||
#include "global.hpp"
|
||||
|
||||
#include "ai_interface.hpp"
|
||||
#include "cursor.hpp"
|
||||
#include "dialogs.hpp"
|
||||
#include "events.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "game_errors.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "game_events.hpp"
|
||||
#include "halo.hpp"
|
||||
#include "help.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "intro.hpp"
|
||||
#include "log.hpp"
|
||||
#include "mapgen.hpp"
|
||||
#include "map_create.hpp"
|
||||
#include "network.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "playturn.hpp"
|
||||
#include "preferences.hpp"
|
||||
#include "preferences_display.hpp"
|
||||
#include "random.hpp"
|
||||
#include "replay.hpp"
|
||||
#include "replay_controller.hpp"
|
||||
#include "scoped_resource.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "tooltips.hpp"
|
||||
#include "unit_display.hpp"
|
||||
#include "util.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#define LOG_NG LOG_STREAM(info, engine)
|
||||
|
||||
LEVEL_RESULT play_replay_level(const game_data& gameinfo, const config& game_config,
|
||||
const config* level, CVideo& video, game_state& state_of_game,
|
||||
const std::vector<config*>& story)
|
||||
{
|
||||
try{
|
||||
const int ticks = SDL_GetTicks();
|
||||
const int num_turns = atoi((*level)["turns"].c_str());
|
||||
replay_controller replaycontroller(*level, gameinfo, state_of_game, ticks, num_turns, game_config);
|
||||
replaycontroller.initialize(video, story);
|
||||
|
||||
//replay event-loop
|
||||
for (;;){
|
||||
replaycontroller.replay_slice();
|
||||
}
|
||||
}
|
||||
catch(end_level_exception&){
|
||||
}
|
||||
|
||||
return LEVEL_CONTINUE;
|
||||
}
|
||||
|
||||
replay_controller::replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
|
||||
const int ticks, const int num_turns, const config& game_config) :
|
||||
level_(level), gameinfo_(gameinfo), gamestate_(state_of_game), gamestate_start_(state_of_game), ticks_(ticks),
|
||||
status_(level, num_turns), status_start_(level, num_turns), map_(game_config, level["map_data"]),
|
||||
game_config_(game_config), team_manager_(teams_), xp_modifier_(atoi(level["experience_modifier"].c_str())),
|
||||
mouse_handler_(gui_, teams_, units_, map_, status_, gameinfo)
|
||||
{
|
||||
player_number_ = 1;
|
||||
delay_ = 0;
|
||||
is_playing_ = false;
|
||||
current_turn_ = 1;
|
||||
loading_game_ = level["playing_team"].empty() == false;
|
||||
first_player_ = atoi(level_["playing_team"].c_str());
|
||||
if(first_player_ < 0 || first_player_ >= int(teams_.size())) {
|
||||
first_player_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<team>& replay_controller::get_teams(){
|
||||
return teams_;
|
||||
}
|
||||
|
||||
unit_map replay_controller::get_units(){
|
||||
return units_;
|
||||
}
|
||||
|
||||
display& replay_controller::get_gui(){
|
||||
return *gui_;
|
||||
}
|
||||
|
||||
gamemap& replay_controller::get_map(){
|
||||
return map_;
|
||||
}
|
||||
|
||||
const gamestatus& replay_controller::get_status(){
|
||||
return status_;
|
||||
}
|
||||
|
||||
const int replay_controller::get_player_number(){
|
||||
return player_number_;
|
||||
}
|
||||
|
||||
const bool replay_controller::is_loading_game(){
|
||||
return loading_game_;
|
||||
}
|
||||
|
||||
void replay_controller::reset_replay(){
|
||||
is_playing_ = false;
|
||||
current_turn_ = 1;
|
||||
recorder.start_replay();
|
||||
units_ = *(new unit_map(units_start_));
|
||||
status_ = *(new gamestatus(status_start_));
|
||||
gamestate_ = *(new game_state(gamestate_start_));
|
||||
teams_ = team_manager_.clone(teams_start_);
|
||||
(*gui_).invalidate_all();
|
||||
(*gui_).draw();
|
||||
}
|
||||
|
||||
void replay_controller::stop_replay(){
|
||||
is_playing_ = false;
|
||||
}
|
||||
|
||||
void replay_controller::replay_next_turn(){
|
||||
is_playing_ = true;
|
||||
play_turn();
|
||||
is_playing_ = false;
|
||||
}
|
||||
|
||||
void replay_controller::replay_next_side(){
|
||||
is_playing_ = true;
|
||||
play_side(player_number_ - 1);
|
||||
if (player_number_ > teams_.size()){
|
||||
player_number_ = 1;
|
||||
current_turn_++;
|
||||
}
|
||||
is_playing_ = false;
|
||||
}
|
||||
|
||||
void replay_controller::replay_switch_fog(){
|
||||
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
|
||||
t->set_fog(!t->uses_fog());
|
||||
}
|
||||
update_teams();
|
||||
update_gui();
|
||||
}
|
||||
|
||||
void replay_controller::replay_switch_shroud(){
|
||||
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
|
||||
t->set_shroud(!t->uses_shroud());
|
||||
}
|
||||
update_teams();
|
||||
update_gui();
|
||||
}
|
||||
|
||||
void replay_controller::initialize(CVideo& video, const std::vector<config*>& story){
|
||||
//if the recorder has no event, adds an "game start" event to the
|
||||
//recorder, whose only goal is to initialize the RNG
|
||||
if(recorder.empty()) {
|
||||
recorder.add_start();
|
||||
} else {
|
||||
recorder.pre_replay();
|
||||
}
|
||||
recorder.set_skip(false);
|
||||
replay_network_sender replay_sender(recorder);
|
||||
|
||||
const set_random_generator generator_setter(&recorder);
|
||||
|
||||
//guarantee the cursor goes back to 'normal' at the end of the level
|
||||
const cursor::setter cursor_setter(cursor::NORMAL);
|
||||
|
||||
const int ticks = SDL_GetTicks();
|
||||
LOG_NG << "in play_replay_level()...\n";
|
||||
|
||||
const statistics::scenario_context statistics_context(level_["name"]);
|
||||
|
||||
LOG_NG << "created objects... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
const verification_manager verify_manager(units_);
|
||||
|
||||
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
|
||||
|
||||
int first_human_team = -1;
|
||||
|
||||
const config::child_list& unit_cfg = level_.get_children("side");
|
||||
|
||||
if(level_["modify_placing"] == "true") {
|
||||
LOG_NG << "modifying placing...\n";
|
||||
play::place_sides_in_preferred_locations(map_,unit_cfg);
|
||||
}
|
||||
|
||||
LOG_NG << "initializing teams..." << unit_cfg.size() << "\n";;
|
||||
LOG_NG << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
std::set<std::string> seen_save_ids;
|
||||
|
||||
for(config::child_list::const_iterator ui = unit_cfg.begin(); ui != unit_cfg.end(); ++ui) {
|
||||
std::string save_id = get_unique_saveid(**ui, seen_save_ids);
|
||||
seen_save_ids.insert(save_id);
|
||||
if (first_human_team == -1){
|
||||
first_human_team = get_first_human_team(ui, unit_cfg);
|
||||
}
|
||||
get_player_info(**ui, gamestate_, save_id, teams_, level_, gameinfo_, map_, units_);
|
||||
}
|
||||
|
||||
preferences::encounter_recruitable_units(teams_);
|
||||
preferences::encounter_start_units(units_);
|
||||
preferences::encounter_recallable_units(gamestate_);
|
||||
preferences::encounter_map_terrain(map_);
|
||||
|
||||
LOG_NG << "initialized teams... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
|
||||
LOG_NG << "initializing display... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
const config* theme_cfg = get_theme(game_config_, level_["theme"]);
|
||||
gui_ = new display(units_,video,map_,status_,teams_,*theme_cfg, game_config_, level_);
|
||||
mouse_handler_.set_gui(gui_);
|
||||
theme::set_known_themes(&game_config_);
|
||||
|
||||
LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
if(first_human_team != -1) {
|
||||
gui_->set_team(first_human_team);
|
||||
}
|
||||
|
||||
const preferences::display_manager prefs_disp_manager(gui_);
|
||||
const tooltips::manager tooltips_manager(gui_->video());
|
||||
|
||||
//this *needs* to be created before the show_intro and show_map_scene
|
||||
//as that functions use the manager state_of_game
|
||||
game_events::manager events_manager(level_,*gui_,map_,units_,teams_,
|
||||
gamestate_,status_,gameinfo_);
|
||||
|
||||
if(recorder.is_skipping() == false) {
|
||||
for(std::vector<config*>::const_iterator story_i = story.begin(); story_i != story.end(); ++story_i) {
|
||||
|
||||
show_intro(*gui_,**story_i, level_);
|
||||
}
|
||||
}
|
||||
|
||||
halo_manager_ = new halo::manager(*gui_);
|
||||
gui_->labels().read(level_);
|
||||
LOG_NG << "c... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
const std::string& music = level_["music"];
|
||||
if(music != "") {
|
||||
sound::play_music(music);
|
||||
}
|
||||
|
||||
LOG_NG << "d... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
victory_conditions::set_victory_when_enemies_defeated(
|
||||
level_["victory_when_enemies_defeated"] != "no");
|
||||
|
||||
LOG_NG << "initializing events manager... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
help::help_manager help_manager(&game_config_, &gameinfo_, &map_);
|
||||
|
||||
//find a list of 'items' (i.e. overlays) on the level, and add them
|
||||
const config::child_list& overlays = level_.get_children("item");
|
||||
for(config::child_list::const_iterator overlay = overlays.begin(); overlay != overlays.end(); ++overlay) {
|
||||
gui_->add_overlay(gamemap::location(**overlay),(**overlay)["image"], (**overlay)["halo"]);
|
||||
}
|
||||
|
||||
gui_->begin_game();
|
||||
gui_->adjust_colours(0,0,0);
|
||||
|
||||
std::deque<config> data_backlog;
|
||||
|
||||
const hotkey::basic_handler key_events_handler(gui_);
|
||||
|
||||
LOG_NG << "first_time..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
|
||||
|
||||
update_locker lock_display((*gui_).video(),false);
|
||||
for(std::vector<team>::iterator t = teams_.begin(); t != teams_.end(); ++t) {
|
||||
clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,(t-teams_.begin()));
|
||||
t->set_fog(false);
|
||||
t->set_shroud(false);
|
||||
}
|
||||
|
||||
LOG_NG << "scrolling... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
gui_->scroll_to_leader(units_, player_number_);
|
||||
LOG_NG << "done scrolling... " << (SDL_GetTicks() - ticks) << "\n";
|
||||
|
||||
if(!loading_game_) {
|
||||
game_events::fire("start");
|
||||
gamestate_.set_variable("turn_number", "1");
|
||||
}
|
||||
|
||||
update_gui();
|
||||
|
||||
units_start_ = units_;
|
||||
teams_start_ = team_manager_.clone(teams_);
|
||||
}
|
||||
|
||||
void replay_controller::play_replay(){
|
||||
try{
|
||||
is_playing_ = true;
|
||||
|
||||
LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks_) << "\n";
|
||||
for(; !recorder.at_end() && is_playing_; first_player_ = 0) {
|
||||
play_turn();
|
||||
replay_slice();
|
||||
} //end for loop
|
||||
is_playing_ = false;
|
||||
}
|
||||
catch(end_level_exception&){
|
||||
}
|
||||
}
|
||||
|
||||
void replay_controller::play_turn(){
|
||||
//FixMe
|
||||
//This is a little bit ugly at the moment, since it really should happen only once in a game
|
||||
//Probably need to fix the unit xp_max calculation
|
||||
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
|
||||
|
||||
LOG_NG << "turn: " << current_turn_ << "\n";
|
||||
|
||||
while (player_number_ <= teams_.size()) {
|
||||
play_side(player_number_ - 1);
|
||||
}
|
||||
|
||||
std::stringstream event_stream;
|
||||
event_stream << status_.turn();
|
||||
|
||||
{
|
||||
LOG_NG << "turn event..." << (recorder.is_skipping() ? "skipping" : "no skip") << "\n";
|
||||
update_locker lock_display(gui_->video(),recorder.is_skipping());
|
||||
const std::string turn_num = event_stream.str();
|
||||
gamestate_.set_variable("turn_number",turn_num);
|
||||
game_events::fire("turn " + turn_num);
|
||||
game_events::fire("new turn");
|
||||
}
|
||||
|
||||
player_number_ = 1;
|
||||
current_turn_++;
|
||||
}
|
||||
|
||||
void replay_controller::play_side(int team_index){
|
||||
team& current_team = teams_[team_index];
|
||||
log_scope("player turn");
|
||||
|
||||
//FixMe
|
||||
//This is a little bit ugly at the moment, since it really should happen only once in a game
|
||||
//Probably need to fix the unit xp_max calculation
|
||||
const unit_type::experience_accelerator xp_mod(xp_modifier_ > 0 ? xp_modifier_ : 100);
|
||||
|
||||
//if a side is dead, don't do their turn
|
||||
if(current_team.is_empty() || team_units(units_,player_number_) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(team_manager_.is_observer()) {
|
||||
(*gui_).set_team(size_t(player_number_-1));
|
||||
}
|
||||
|
||||
std::stringstream player_number_str;
|
||||
player_number_str << player_number_;
|
||||
gamestate_.set_variable("side_number",player_number_str.str());
|
||||
|
||||
//fire side turn event only if real side change occurs not counting changes from void to a side
|
||||
if (team_index != first_player_ || current_turn_ > 1) {
|
||||
game_events::fire("side turn");
|
||||
}
|
||||
|
||||
//we want to work out if units for this player should get healed, and the
|
||||
//player should get income now. healing/income happen if it's not the first
|
||||
//turn of processing, or if we are loading a game, and this is not the
|
||||
//player it started with.
|
||||
const bool turn_refresh = current_turn_ > 1 || loading_game_ && team_index != first_player_;
|
||||
|
||||
if(turn_refresh) {
|
||||
for(unit_map::iterator i = units_.begin(); i != units_.end(); ++i) {
|
||||
if(i->second.side() == player_number_) {
|
||||
i->second.new_turn();
|
||||
}
|
||||
}
|
||||
|
||||
current_team.new_turn();
|
||||
|
||||
//if the expense is less than the number of villages owned,
|
||||
//then we don't have to pay anything at all
|
||||
const int expense = team_upkeep(units_,player_number_) -
|
||||
current_team.villages().size();
|
||||
if(expense > 0) {
|
||||
current_team.spend_gold(expense);
|
||||
}
|
||||
|
||||
calculate_healing((*gui_),status_,map_,units_,player_number_,teams_);
|
||||
}
|
||||
|
||||
current_team.set_time_of_day(int(status_.turn()),status_.get_time_of_day());
|
||||
|
||||
gui_->set_playing_team(size_t(player_number_-1));
|
||||
|
||||
if (!recorder.is_skipping()){
|
||||
clear_shroud(*gui_,status_,map_,gameinfo_,units_,teams_,player_number_-1);
|
||||
}
|
||||
|
||||
const hotkey::basic_handler key_events_handler(gui_);
|
||||
LOG_NG << "doing replay " << player_number_ << "\n";
|
||||
bool replaying;
|
||||
try {
|
||||
replaying = do_replay(*gui_,map_,gameinfo_,units_,teams_,
|
||||
player_number_,status_,gamestate_);
|
||||
} catch(replay::error&) {
|
||||
gui::show_dialog(*gui_,NULL,"",_("The file you have tried to load is corrupt"),gui::OK_ONLY);
|
||||
|
||||
replaying = false;
|
||||
}
|
||||
LOG_NG << "result of replay: " << (replaying?"true":"false") << "\n";
|
||||
|
||||
for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
|
||||
if(uit->second.side() == player_number_){
|
||||
uit->second.end_turn();
|
||||
}
|
||||
else{
|
||||
//this is necessary for replays in order to show possible movements
|
||||
uit->second.new_turn();
|
||||
}
|
||||
}
|
||||
|
||||
//This implements "delayed map sharing." It's meant as an alternative to shared vision.
|
||||
if(current_team.copy_ally_shroud()) {
|
||||
gui_->recalculate_minimap();
|
||||
gui_->invalidate_all();
|
||||
}
|
||||
|
||||
game_events::pump();
|
||||
|
||||
check_victory(units_,teams_);
|
||||
|
||||
player_number_++;
|
||||
|
||||
if (player_number_ > teams_.size()) {
|
||||
status_.next_turn();
|
||||
}
|
||||
game_events::fire("side turn");
|
||||
update_teams();
|
||||
update_gui();
|
||||
}
|
||||
|
||||
void replay_controller::update_teams(){
|
||||
int next_team = player_number_;
|
||||
if (next_team > teams_.size()) { next_team = 1; }
|
||||
if (teams_[next_team - 1].uses_fog()){
|
||||
gui_->set_team(next_team - 1);
|
||||
clear_shroud(*gui_, status_, map_, gameinfo_, units_, teams_, next_team - 1);
|
||||
}
|
||||
if (teams_[next_team - 1].uses_shroud()){
|
||||
gui_->set_team(next_team - 1);
|
||||
recalculate_fog(map_, status_, gameinfo_, units_, teams_, next_team - 1);
|
||||
}
|
||||
gui_->set_playing_team(next_team - 1);
|
||||
(*gui_).scroll_to_leader(units_, next_team);
|
||||
}
|
||||
|
||||
void replay_controller::update_gui(){
|
||||
(*gui_).recalculate_minimap();
|
||||
(*gui_).redraw_minimap();
|
||||
(*gui_).invalidate_all();
|
||||
events::raise_draw_event();
|
||||
(*gui_).draw();
|
||||
}
|
||||
|
||||
void replay_controller::replay_slice()
|
||||
{
|
||||
CKey key;
|
||||
|
||||
events::pump();
|
||||
events::raise_process_event();
|
||||
|
||||
events::raise_draw_event();
|
||||
|
||||
const theme::menu* const m = gui_->menu_pressed();
|
||||
if(m != NULL) {
|
||||
const SDL_Rect& menu_loc = m->location(gui_->screen_area());
|
||||
show_menu(m->items(),menu_loc.x+1,menu_loc.y + menu_loc.h + 1,false,*gui_);
|
||||
return;
|
||||
}
|
||||
|
||||
int mousex, mousey;
|
||||
SDL_GetMouseState(&mousex,&mousey);
|
||||
tooltips::process(mousex, mousey);
|
||||
|
||||
const int scroll_threshold = 5;
|
||||
|
||||
if(key[SDLK_UP] || mousey < scroll_threshold)
|
||||
gui_->scroll(0,-preferences::scroll_speed());
|
||||
|
||||
if(key[SDLK_DOWN] || mousey > gui_->y()-scroll_threshold)
|
||||
gui_->scroll(0,preferences::scroll_speed());
|
||||
|
||||
if(key[SDLK_LEFT] || mousex < scroll_threshold)
|
||||
gui_->scroll(-preferences::scroll_speed(),0);
|
||||
|
||||
if(key[SDLK_RIGHT] || mousex > gui_->x()-scroll_threshold)
|
||||
gui_->scroll(preferences::scroll_speed(),0);
|
||||
|
||||
gui_->draw();
|
||||
//FixMe
|
||||
//integrate changed objectives
|
||||
/*
|
||||
if(!browse_ && current_team().objectives_changed()) {
|
||||
dialogs::show_objectives(gui_, level_, current_team().objectives());
|
||||
current_team().reset_objectives_changed();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool replay_controller::can_execute_command(hotkey::HOTKEY_COMMAND command) const
|
||||
{
|
||||
switch(command) {
|
||||
|
||||
//commands we can always do
|
||||
case hotkey::HOTKEY_LEADER:
|
||||
case hotkey::HOTKEY_CYCLE_UNITS:
|
||||
case hotkey::HOTKEY_CYCLE_BACK_UNITS:
|
||||
case hotkey::HOTKEY_ZOOM_IN:
|
||||
case hotkey::HOTKEY_ZOOM_OUT:
|
||||
case hotkey::HOTKEY_ZOOM_DEFAULT:
|
||||
case hotkey::HOTKEY_FULLSCREEN:
|
||||
case hotkey::HOTKEY_SCREENSHOT:
|
||||
case hotkey::HOTKEY_ACCELERATED:
|
||||
case hotkey::HOTKEY_TOGGLE_GRID:
|
||||
case hotkey::HOTKEY_STATUS_TABLE:
|
||||
case hotkey::HOTKEY_MUTE:
|
||||
case hotkey::HOTKEY_PREFERENCES:
|
||||
case hotkey::HOTKEY_OBJECTIVES:
|
||||
case hotkey::HOTKEY_UNIT_LIST:
|
||||
case hotkey::HOTKEY_STATISTICS:
|
||||
case hotkey::HOTKEY_QUIT_GAME:
|
||||
case hotkey::HOTKEY_SEARCH:
|
||||
case hotkey::HOTKEY_HELP:
|
||||
case hotkey::HOTKEY_USER_CMD:
|
||||
case hotkey::HOTKEY_SAVE_GAME:
|
||||
case hotkey::HOTKEY_LOAD_GAME:
|
||||
case hotkey::HOTKEY_PLAY_REPLAY:
|
||||
case hotkey::HOTKEY_RESET_REPLAY:
|
||||
case hotkey::HOTKEY_STOP_REPLAY:
|
||||
case hotkey::HOTKEY_REPLAY_NEXT_TURN:
|
||||
case hotkey::HOTKEY_REPLAY_NEXT_SIDE:
|
||||
case hotkey::HOTKEY_REPLAY_FOG:
|
||||
case hotkey::HOTKEY_REPLAY_SHROUD:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void replay_controller::handle_event(const SDL_Event& event)
|
||||
{
|
||||
if(gui::in_dialog()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch(event.type) {
|
||||
case SDL_KEYDOWN:
|
||||
hotkey::key_event(*gui_,event.key,this);
|
||||
//FixMe
|
||||
//add textbox support
|
||||
/*
|
||||
//detect key press events, unless there is a textbox present on-screen
|
||||
//in which case the key press events should go only to it.
|
||||
if(textbox_.active() == false) {
|
||||
hotkey::key_event(gui_,event.key,this);
|
||||
} else if(event.key.keysym.sym == SDLK_ESCAPE) {
|
||||
close_textbox();
|
||||
} else if(event.key.keysym.sym == SDLK_TAB) {
|
||||
tab_textbox();
|
||||
} else if(event.key.keysym.sym == SDLK_RETURN) {
|
||||
enter_textbox();
|
||||
}
|
||||
*/
|
||||
//intentionally fall-through
|
||||
case SDL_KEYUP:
|
||||
//FixMe
|
||||
//add unit-turn support
|
||||
/*
|
||||
//if the user has pressed 1 through 9, we want to show how far
|
||||
//the unit can move in that many turns
|
||||
if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') {
|
||||
const int new_path_turns = (event.type == SDL_KEYDOWN) ?
|
||||
event.key.keysym.sym - '1' : 0;
|
||||
|
||||
if(new_path_turns != path_turns_) {
|
||||
path_turns_ = new_path_turns;
|
||||
|
||||
const unit_map::iterator u = selected_unit();
|
||||
|
||||
if(u != units_.end() && u->second.side() == team_num_) {
|
||||
const bool ignore_zocs = u->second.type().is_skirmisher();
|
||||
const bool teleport = u->second.type().teleports();
|
||||
current_paths_ = paths(map_,status_,gameinfo_,units_,u->first,
|
||||
teams_,ignore_zocs,teleport,
|
||||
path_turns_);
|
||||
gui_->set_paths(¤t_paths_);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
// ignore old mouse motion events in the event queue
|
||||
SDL_Event new_event;
|
||||
|
||||
if(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
|
||||
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0) {
|
||||
while(SDL_PeepEvents(&new_event,1,SDL_GETEVENT,
|
||||
SDL_EVENTMASK(SDL_MOUSEMOTION)) > 0);
|
||||
mouse_handler_.mouse_motion(new_event.motion, player_number_);
|
||||
} else {
|
||||
mouse_handler_.mouse_motion(event.motion, player_number_);
|
||||
}
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
//FixMe
|
||||
//add mouse Click support
|
||||
//mouse_press(event.button);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
92
src/replay_controller.hpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* $Id: replay_controller.hpp 7396 2005-07-02 21:37:20Z ott $ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
|
||||
Part of the Battle for Wesnoth Project http://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.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef REPLAY_CONTROLLER_H_INCLUDED
|
||||
#define REPLAY_CONTROLLER_H_INCLUDED
|
||||
|
||||
#include "display.hpp"
|
||||
#include "font.hpp"
|
||||
#include "gamestatus.hpp"
|
||||
#include "halo.hpp"
|
||||
#include "hotkeys.hpp"
|
||||
#include "mouse_events.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "wml_separators.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class replay_controller : public hotkey::command_executor, public events::handler
|
||||
{
|
||||
public:
|
||||
replay_controller(const config& level, const game_data& gameinfo, game_state& state_of_game,
|
||||
const int ticks, const int num_turns, const config& game_config);
|
||||
|
||||
void handle_event(const SDL_Event& event);
|
||||
void replay_slice();
|
||||
bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
|
||||
|
||||
std::vector<team>& get_teams();
|
||||
unit_map get_units();
|
||||
display& get_gui();
|
||||
gamemap& get_map();
|
||||
const gamestatus& get_status();
|
||||
const int get_player_number();
|
||||
const bool is_loading_game();
|
||||
|
||||
void initialize(CVideo& video, const std::vector<config*>& story);
|
||||
|
||||
//event handlers
|
||||
void play_replay();
|
||||
void reset_replay();
|
||||
void stop_replay();
|
||||
void replay_next_turn();
|
||||
void replay_next_side();
|
||||
void replay_switch_fog();
|
||||
void replay_switch_shroud();
|
||||
|
||||
std::vector<team> teams_, teams_start_;
|
||||
private:
|
||||
void play_turn();
|
||||
void play_side(int team_index);
|
||||
void update_teams();
|
||||
void update_gui();
|
||||
|
||||
const config& level_;
|
||||
const config& game_config_;
|
||||
const game_data& gameinfo_;
|
||||
game_state& gamestate_, gamestate_start_;
|
||||
display* gui_;
|
||||
teams_manager team_manager_;
|
||||
halo::manager* halo_manager_;
|
||||
font::floating_label_context* labels_manager_; //object that will make sure that labels are removed at the end of the scenario
|
||||
gamestatus status_, status_start_;
|
||||
gamemap map_;
|
||||
unit_map units_, units_start_;
|
||||
events::mouse_handler mouse_handler_;
|
||||
|
||||
const int ticks_;
|
||||
int player_number_;
|
||||
int first_player_;
|
||||
bool loading_game_;
|
||||
int delay_;
|
||||
bool is_playing_;
|
||||
int current_turn_;
|
||||
const int xp_modifier_;
|
||||
};
|
||||
|
||||
|
||||
LEVEL_RESULT play_replay_level(const game_data& gameinfo, const config& terrain_config,
|
||||
const config* level, CVideo& video,
|
||||
game_state& state_of_game,
|
||||
const std::vector<config*>& story);
|
||||
|
||||
#endif
|
27
src/team.cpp
|
@ -43,6 +43,25 @@ teams_manager::~teams_manager()
|
|||
teams = NULL;
|
||||
}
|
||||
|
||||
std::vector<team> teams_manager::clone(std::vector<team>& team_list){
|
||||
std::vector<team> result;
|
||||
for (std::vector<team>::iterator t = team_list.begin(); t != team_list.end(); t++){
|
||||
result.push_back(*t);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool teams_manager::is_observer()
|
||||
{
|
||||
for(std::vector<team>::const_iterator i = teams->begin(); i != teams->end(); ++i) {
|
||||
if(i->is_human() || i->is_persistent()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
team::target::target(const config& cfg)
|
||||
: criteria(cfg), value(atof(cfg["value"].c_str()))
|
||||
{
|
||||
|
@ -878,3 +897,11 @@ int team::get_side_colour_index(int side)
|
|||
return side;
|
||||
}
|
||||
|
||||
void team::log_recruitable(){
|
||||
LOG_NG << "Adding recruitable units: \n";
|
||||
for (std::set<std::string>::const_iterator it = info_.can_recruit.begin();
|
||||
it != info_.can_recruit.end(); it++) {
|
||||
LOG_NG << *it << std::endl;
|
||||
}
|
||||
LOG_NG << "Added all recruitable units\n";
|
||||
}
|
|
@ -183,6 +183,8 @@ public:
|
|||
void place_shroud(int x, int y) { shroud_.place(x+1,y+1); }
|
||||
bool clear_fog(int x, int y) { return fog_.clear(x+1,y+1); }
|
||||
void refog() { fog_.reset(); }
|
||||
void set_shroud(bool shroud) { shroud_.set_enabled(shroud); }
|
||||
void set_fog(bool fog) { fog_.set_enabled(fog); }
|
||||
|
||||
bool knows_about_team(size_t index) const;
|
||||
bool copy_ally_shroud();
|
||||
|
@ -200,6 +202,8 @@ public:
|
|||
static const SDL_Color& get_side_colour(int side);
|
||||
static int get_side_colour_index(int side);
|
||||
|
||||
void log_recruitable();
|
||||
|
||||
private:
|
||||
//Make these public if you need them, but look at knows_about_team(...) first.
|
||||
bool share_maps() const { return info_.share_maps; }
|
||||
|
@ -232,6 +236,9 @@ private:
|
|||
struct teams_manager {
|
||||
teams_manager(std::vector<team>& teams);
|
||||
~teams_manager();
|
||||
|
||||
std::vector<team> clone(std::vector<team>& team_list);
|
||||
bool is_observer();
|
||||
};
|
||||
|
||||
bool is_observer();
|
||||
|
|
|
@ -420,7 +420,7 @@ theme::menu::menu() : context_(false)
|
|||
|
||||
theme::menu::menu(const config& cfg) : object(cfg), context_(cfg["is_context_menu"] == "true"),
|
||||
title_(cfg["title"].str() + cfg["title_literal"].str()),
|
||||
image_(cfg["image"]),
|
||||
image_(cfg["image"]), type_(cfg["type"]),
|
||||
items_(utils::split(cfg["items"]))
|
||||
{}
|
||||
|
||||
|
@ -428,6 +428,8 @@ bool theme::menu::is_context() const { return context_; }
|
|||
|
||||
const std::string& theme::menu::title() const { return title_; }
|
||||
|
||||
const std::string& theme::menu::type() const { return type_; }
|
||||
|
||||
const std::string& theme::menu::image() const { return image_; }
|
||||
|
||||
const std::vector<std::string>& theme::menu::items() const { return items_; }
|
||||
|
|
|
@ -123,12 +123,14 @@ public:
|
|||
|
||||
const std::string& title() const;
|
||||
|
||||
const std::string& type() const;
|
||||
|
||||
const std::string& image() const;
|
||||
|
||||
const std::vector<std::string>& items() const;
|
||||
private:
|
||||
bool context_;
|
||||
std::string title_, image_;
|
||||
std::string title_, image_, type_;
|
||||
std::vector<std::string> items_;
|
||||
};
|
||||
|
||||
|
|
|
@ -897,7 +897,9 @@ namespace {
|
|||
|
||||
unit_type::experience_accelerator::experience_accelerator(int modifier) : old_value_(experience_modifier)
|
||||
{
|
||||
experience_modifier = (experience_modifier*modifier)/100;
|
||||
if (experience_modifier == 100){
|
||||
experience_modifier = (experience_modifier*modifier)/100;
|
||||
}
|
||||
}
|
||||
|
||||
unit_type::experience_accelerator::~experience_accelerator()
|
||||
|
|
|
@ -14,10 +14,9 @@
|
|||
#include "../global.hpp"
|
||||
|
||||
#include "button.hpp"
|
||||
#include "../font.hpp"
|
||||
#include "font.hpp"
|
||||
#include "../image.hpp"
|
||||
#include "../log.hpp"
|
||||
#include "../marked-up_text.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../video.hpp"
|
||||
#include "../wml_separators.hpp"
|
||||
|
@ -68,7 +67,9 @@ button::button(CVideo& video, const std::string& label, button::TYPE type,
|
|||
base_height_ = button_image->h;
|
||||
base_width_ = button_image->w;
|
||||
|
||||
set_label(label);
|
||||
if (type_ != TYPE_IMAGE){
|
||||
set_label(label);
|
||||
}
|
||||
|
||||
if(type == TYPE_PRESS) {
|
||||
image_.assign(scale_surface(button_image,location().w,location().h));
|
||||
|
@ -81,21 +82,35 @@ button::button(CVideo& video, const std::string& label, button::TYPE type,
|
|||
if (type == TYPE_CHECK)
|
||||
pressedActiveImage_.assign(scale_surface(pressed_active_image, button_image->w, button_image->h));
|
||||
}
|
||||
|
||||
if (type_ == TYPE_IMAGE){
|
||||
calculate_size();
|
||||
}
|
||||
}
|
||||
|
||||
void button::calculate_size()
|
||||
{
|
||||
if (type_ == TYPE_IMAGE){
|
||||
SDL_Rect loc_image = location();
|
||||
loc_image.h = image_->h;
|
||||
loc_image.w = image_->w;
|
||||
set_location(loc_image);
|
||||
return;
|
||||
}
|
||||
SDL_Rect const &loc = location();
|
||||
bool change_size = loc.h == 0 || loc.w == 0;
|
||||
|
||||
if (!change_size) {
|
||||
unsigned w = loc.w - (type_ == TYPE_PRESS ? horizontal_padding :
|
||||
checkbox_horizontal_padding + base_width_);
|
||||
label_ = font::make_text_ellipsis(label_, font_size, w);
|
||||
unsigned w = loc.w - (type_ == TYPE_PRESS ? horizontal_padding : checkbox_horizontal_padding + base_width_);
|
||||
if (type_ != TYPE_IMAGE){
|
||||
label_ = font::make_text_ellipsis(label_, font_size, w);
|
||||
}
|
||||
}
|
||||
|
||||
textRect_ = font::draw_text(NULL, screen_area(), font_size,
|
||||
font::BUTTON_COLOUR, label_, 0, 0);
|
||||
if (type_ != TYPE_IMAGE){
|
||||
textRect_ = font::draw_text(NULL, screen_area(), font_size,
|
||||
font::BUTTON_COLOUR, label_, 0, 0);
|
||||
}
|
||||
|
||||
if (!change_size)
|
||||
return;
|
||||
|
@ -191,7 +206,9 @@ void button::draw_contents()
|
|||
}
|
||||
|
||||
video().blit_surface(loc.x, loc.y, image);
|
||||
font::draw_text(&video(), clipArea, font_size, button_colour, label_, textx, texty);
|
||||
if (type_ != TYPE_IMAGE){
|
||||
font::draw_text(&video(), clipArea, font_size, button_colour, label_, textx, texty);
|
||||
}
|
||||
|
||||
update_rect(loc);
|
||||
}
|
||||
|
@ -235,15 +252,22 @@ void button::mouse_motion(SDL_MouseMotionEvent const &event)
|
|||
// the cursor is not over the widget
|
||||
if (state_ == PRESSED_ACTIVE)
|
||||
state_ = PRESSED;
|
||||
else if (type_ != TYPE_CHECK || state_ != PRESSED)
|
||||
else if (type_ != TYPE_CHECK && type_ != TYPE_IMAGE || state_ != PRESSED)
|
||||
state_ = NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
void button::mouse_down(SDL_MouseButtonEvent const &event)
|
||||
{
|
||||
if (hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT && type_ != TYPE_CHECK)
|
||||
state_ = PRESSED;
|
||||
if (hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT && type_ != TYPE_CHECK){
|
||||
if (type_ != TYPE_IMAGE){
|
||||
state_ = PRESSED;
|
||||
}
|
||||
else{
|
||||
if (state_ == PRESSED) { state_ = ACTIVE; }
|
||||
else { state_ = PRESSED; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void button::mouse_up(SDL_MouseButtonEvent const &event)
|
||||
|
@ -265,6 +289,9 @@ void button::mouse_up(SDL_MouseButtonEvent const &event)
|
|||
case TYPE_TURBO:
|
||||
state_ = ACTIVE;
|
||||
break;
|
||||
case TYPE_IMAGE:
|
||||
pressed_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ class button : public widget
|
|||
public:
|
||||
struct error {};
|
||||
|
||||
enum TYPE { TYPE_PRESS, TYPE_CHECK, TYPE_TURBO };
|
||||
enum TYPE { TYPE_PRESS, TYPE_CHECK, TYPE_TURBO, TYPE_IMAGE };
|
||||
|
||||
enum SPACE_CONSUMPTION { DEFAULT_SPACE, MINIMUM_SPACE };
|
||||
|
||||
|
|
216
src/widgets/image_button.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
/* $Id: button.cpp 7396 2005-07-02 21:37:20Z ott $ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
|
||||
Part of the Battle for Wesnoth Project http://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.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "../global.hpp"
|
||||
|
||||
#include "image_button.hpp"
|
||||
#include "../font.hpp"
|
||||
#include "../image.hpp"
|
||||
#include "../log.hpp"
|
||||
#include "../util.hpp"
|
||||
#include "../video.hpp"
|
||||
#include "../wml_separators.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
namespace gui {
|
||||
|
||||
const int font_size = font::SIZE_SMALL;
|
||||
const int horizontal_padding = font::SIZE_SMALL;
|
||||
const int checkbox_horizontal_padding = font::SIZE_SMALL / 2;
|
||||
const int vertical_padding = font::SIZE_SMALL / 2;
|
||||
|
||||
image_button::image_button(CVideo& video, std::string button_image_name, SPACE_CONSUMPTION spacing)
|
||||
: widget(video), image_(NULL), pressedImage_(NULL), activeImage_(NULL), pressedActiveImage_(NULL),
|
||||
button_(true), state_(NORMAL), enabled_(true), pressed_(false),
|
||||
spacing_(spacing), base_height_(0), base_width_(0)
|
||||
{
|
||||
if(button_image_name.empty()) {
|
||||
button_image_name = "button";
|
||||
}
|
||||
|
||||
const std::string button_image_file = "buttons/" + button_image_name + ".png";
|
||||
surface button_image(image::get_image(button_image_file,image::UNSCALED));
|
||||
surface pressed_image(image::get_image("buttons/" + button_image_name + "-pressed.png", image::UNSCALED));
|
||||
surface active_image(image::get_image("buttons/" + button_image_name + "-active.png", image::UNSCALED));
|
||||
surface pressed_active_image;
|
||||
|
||||
if (pressed_image.null())
|
||||
pressed_image.assign(button_image);
|
||||
|
||||
if (active_image.null())
|
||||
active_image.assign(button_image);
|
||||
|
||||
if (button_image.null())
|
||||
throw error();
|
||||
|
||||
base_height_ = button_image->h;
|
||||
base_width_ = button_image->w;
|
||||
|
||||
image_.assign(scale_surface(button_image,location().w,location().h));
|
||||
pressedImage_.assign(scale_surface(pressed_image,location().w,location().h));
|
||||
activeImage_.assign(scale_surface(active_image,location().w,location().h));
|
||||
}
|
||||
|
||||
void image_button::calculate_size()
|
||||
{
|
||||
SDL_Rect const &loc = location();
|
||||
bool change_size = loc.h == 0 || loc.w == 0;
|
||||
|
||||
if (!change_size) {
|
||||
unsigned w = loc.w - horizontal_padding;
|
||||
}
|
||||
|
||||
if (!change_size)
|
||||
return;
|
||||
|
||||
#ifdef USE_TINY_GUI
|
||||
set_height(textRect_.h+vertical_padding);
|
||||
#else
|
||||
set_height(maximum(textRect_.h+vertical_padding,base_height_));
|
||||
#endif
|
||||
#ifdef USE_TINY_GUI
|
||||
set_width(textRect_.w + horizontal_padding);
|
||||
#else
|
||||
if(spacing_ == MINIMUM_SPACE) {
|
||||
set_width(textRect_.w + horizontal_padding);
|
||||
} else {
|
||||
set_width(maximum(textRect_.w+horizontal_padding,base_width_));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void image_button::enable(bool new_val)
|
||||
{
|
||||
if (enabled_ != new_val) {
|
||||
enabled_ = new_val;
|
||||
state_ = NORMAL;
|
||||
pressed_ = false;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
bool image_button::enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void image_button::draw_contents()
|
||||
{
|
||||
surface image = image_;
|
||||
const int image_w = image_->w;
|
||||
|
||||
int offset = 0;
|
||||
switch(state_) {
|
||||
case ACTIVE:
|
||||
image = activeImage_;
|
||||
break;
|
||||
case PRESSED:
|
||||
image = pressedImage_;
|
||||
offset = 1;
|
||||
break;
|
||||
case PRESSED_ACTIVE:
|
||||
image = pressedActiveImage_;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_Rect const &clipArea = screen_area();
|
||||
SDL_Rect const &loc = location();
|
||||
const int texty = loc.y + loc.h / 2 - textRect_.h / 2 + offset;
|
||||
int textx;
|
||||
|
||||
textx = loc.x + image_w + checkbox_horizontal_padding / 2;
|
||||
|
||||
SDL_Color button_colour = font::BUTTON_COLOUR;
|
||||
|
||||
if (!enabled_) {
|
||||
image = greyscale_image(image);
|
||||
button_colour = font::STONED_COLOUR;
|
||||
}
|
||||
|
||||
video().blit_surface(loc.x, loc.y, image);
|
||||
|
||||
update_rect(loc);
|
||||
}
|
||||
|
||||
bool image_button::hit(int x, int y) const
|
||||
{
|
||||
return point_in_rect(x,y,location());
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool not_image(const std::string& str) { return !str.empty() && str[0] != IMAGE_PREFIX; }
|
||||
}
|
||||
|
||||
void image_button::mouse_motion(SDL_MouseMotionEvent const &event)
|
||||
{
|
||||
if (hit(event.x, event.y)) {
|
||||
// the cursor is over the widget
|
||||
if (state_ == NORMAL)
|
||||
state_ = ACTIVE;
|
||||
} else {
|
||||
// the cursor is not over the widget
|
||||
if (state_ != PRESSED)
|
||||
state_ = NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
void image_button::mouse_down(SDL_MouseButtonEvent const &event)
|
||||
{
|
||||
if (hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT)
|
||||
state_ = PRESSED;
|
||||
}
|
||||
|
||||
void image_button::mouse_up(SDL_MouseButtonEvent const &event)
|
||||
{
|
||||
if (!(hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT))
|
||||
return;
|
||||
// the user has stopped pressing the mouse left button while on the widget
|
||||
if (state_ == PRESSED) {
|
||||
state_ = ACTIVE;
|
||||
pressed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void image_button::handle_event(const SDL_Event& event)
|
||||
{
|
||||
if (hidden() || !enabled_)
|
||||
return;
|
||||
|
||||
STATE start_state = state_;
|
||||
|
||||
switch(event.type) {
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
mouse_down(event.button);
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
mouse_up(event.button);
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
mouse_motion(event.motion);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_state != state_)
|
||||
set_dirty(true);
|
||||
}
|
||||
|
||||
bool image_button::pressed()
|
||||
{
|
||||
return state_ == PRESSED || state_ == PRESSED_ACTIVE;
|
||||
}
|
||||
|
||||
}
|
76
src/widgets/image_button.hpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* $Id: button.hpp 7396 2005-07-02 21:37:20Z ott $ */
|
||||
/*
|
||||
Copyright (C) 2003 by David White <davidnwhite@verizon.net>
|
||||
Part of the Battle for Wesnoth Project http://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.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
#ifndef IMAGE_BUTTON_H_INCLUDED
|
||||
#define IMAGE_BUTTON_H_INCLUDED
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
#include "widget.hpp"
|
||||
|
||||
#include "../sdl_utils.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace gui {
|
||||
|
||||
class image_button : public widget
|
||||
{
|
||||
public:
|
||||
struct error {};
|
||||
|
||||
enum SPACE_CONSUMPTION { DEFAULT_SPACE, MINIMUM_SPACE };
|
||||
|
||||
image_button(CVideo& video, std::string button_image="", SPACE_CONSUMPTION spacing=DEFAULT_SPACE);
|
||||
|
||||
virtual ~image_button() {}
|
||||
|
||||
bool pressed();
|
||||
|
||||
void enable(bool new_val);
|
||||
bool enabled() const;
|
||||
|
||||
protected:
|
||||
virtual void handle_event(const SDL_Event& event);
|
||||
virtual void mouse_motion(const SDL_MouseMotionEvent& event);
|
||||
virtual void mouse_down(const SDL_MouseButtonEvent& event);
|
||||
virtual void mouse_up(const SDL_MouseButtonEvent& event);
|
||||
virtual void draw_contents();
|
||||
|
||||
private:
|
||||
|
||||
void calculate_size();
|
||||
|
||||
surface image_, pressedImage_, activeImage_, pressedActiveImage_;
|
||||
SDL_Rect textRect_;
|
||||
|
||||
bool button_;
|
||||
|
||||
enum STATE { UNINIT, NORMAL, ACTIVE, PRESSED, PRESSED_ACTIVE };
|
||||
STATE state_;
|
||||
|
||||
bool enabled_;
|
||||
|
||||
bool pressed_;
|
||||
|
||||
SPACE_CONSUMPTION spacing_;
|
||||
|
||||
int base_height_, base_width_;
|
||||
|
||||
bool hit(int x, int y) const;
|
||||
}; //end class button
|
||||
|
||||
}
|
||||
|
||||
#endif
|
26
wesnoth.dsp
|
@ -70,7 +70,7 @@ LINK32=link.exe
|
|||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "f:/wesnoth/src" /I "f:/SDL-1.2.7/include" /I "f:/SDL_image-1.2.3/include" /I "f:/SDL_mixer-1.2.5/include" /I "f:/SDL_net-1.2.5/include" /I "src/sdl_ttf" /I "f:/libintl-devel/include" /I "f:/intl/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "f:/wesnoth/src" /I "f:/SDL-1.2.7/include" /I "f:/SDL_image-1.2.3/include" /I "f:/SDL_mixer-1.2.5/include" /I "f:/SDL_net-1.2.5/include" /I "src/sdl_ttf" /I "f:/libintl-devel/include" /I "f:/intl/include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /FR /YX /FD /GZ /c
|
||||
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
|
@ -229,6 +229,10 @@ SOURCE=.\src\image.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\widgets\image_button.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\intro.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -289,6 +293,10 @@ SOURCE=.\src\mouse.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\mouse_events.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\multiplayer.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -377,6 +385,10 @@ SOURCE=.\src\replay.cpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\replay_controller.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\reports.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -637,6 +649,10 @@ SOURCE=.\src\image.hpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\widgets\image_button.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\intro.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -693,6 +709,10 @@ SOURCE=.\src\mouse.hpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\mouse_events.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\multiplayer.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
@ -785,6 +805,10 @@ SOURCE=.\src\replay.hpp
|
|||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\replay_controller.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=.\src\reports.hpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
|