New replay functionality.

This commit is contained in:
Jörg Hinrichs 2005-11-08 22:23:03 +00:00
parent 47a998a8eb
commit 0f7816c9ef
50 changed files with 2058 additions and 307 deletions

View file

@ -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]

Binary file not shown.

After

Width:  |  Height:  |  Size: 816 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

BIN
images/buttons/fast-fwd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 819 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

BIN
images/buttons/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

BIN
images/buttons/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 908 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

BIN
images/buttons/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

View file

@ -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) ||

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -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
View 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(&current_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(&current_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
View 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

View file

@ -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;
}

View file

@ -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");

View file

@ -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();
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}
}
}
}

View file

@ -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

View file

@ -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();
}

View file

@ -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
View 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(&current_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
View 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

View file

@ -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";
}

View file

@ -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();

View file

@ -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_; }

View file

@ -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_;
};

View file

@ -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()

View file

@ -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;
}
}

View file

@ -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 };

View 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;
}
}

View 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

View file

@ -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