bool play_controller::hotkey_handler::execute_command(const hotkey::hotkey_command& cmd, int index) { hotkey::HOTKEY_COMMAND command = cmd.id; if(index >= 0) { unsigned i = static_cast<unsigned>(index); if(i < savenames_.size() && !savenames_[i].empty()) { // Load the game by throwing load_game_exception throw game::load_game_exception(savenames_[i],false,false,false,"",true); } else if ( i < wml_commands_.size() && wml_commands_[i] ) { if (!wml_command_pager_->capture(*wml_commands_[i])) { wml_commands_[i]->fire_event(mouse_handler_.get_last_hex(), gamestate().gamedata_); } else { //relaunch the menu show_menu(gui()->get_theme().context_menu()->items(),last_context_menu_x_,last_context_menu_y_,true, *gui()); } return true; } } int prefixlen = wml_menu_hotkey_prefix.length(); if(command == hotkey::HOTKEY_WML && cmd.command.compare(0, prefixlen, wml_menu_hotkey_prefix) == 0) { std::string name = cmd.command.substr(prefixlen); const map_location& hex = mouse_handler_.get_last_hex(); gamestate().gamedata_.get_wml_menu_items().fire_item(name, hex, gamestate().gamedata_, gamestate(), gamestate().board_.units_); /// @todo Shouldn't the function return at this point? } return command_executor::execute_command(cmd, index); }
void play_controller::check_time_over() { const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_); if(!time_left) { LOG_NG << "firing time over event...\n"; set_scontext_synced_base sync; pump().fire("time_over"); LOG_NG << "done firing time over event...\n"; // If turns are added while handling 'time over' event. if (gamestate().tod_manager_.is_time_left()) { return; } if(gui_->video().non_interactive()) { LOG_AIT << "time over (draw)\n"; ai_testing::log_draw(); } check_victory(); if (is_regular_game_end()) { return; } end_level_data e; e.proceed_to_next_level = false; e.is_victory = false; set_end_level_data(e); } }
void playsingle_controller::init_gui(){ LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks()) << "\n"; play_controller::init_gui(); // Scroll to the starting position of the first team. If there is a // human team, use that team; otherwise use team 1. If the map defines // a starting position for the selected team, scroll to that tile. Note // this often does not matter since many scenario start with messages, // which will usually scroll to the speaker. Also note that the map // does not necessarily define the starting positions. While usually // best to use the map, the scenarion may explicitly set the positions, // overriding those found in the map (if any). { int scroll_team = gamestate().first_human_team_ + 1; if (scroll_team == 0) { scroll_team = 1; } map_location loc(gamestate().board_.map().starting_position(scroll_team)); if ((loc.x >= 0) && (loc.y >= 0)) { gui_->scroll_to_tile(loc, game_display::WARP); } } update_locker lock_display(gui_->video(), is_skipping_replay()); gui_->draw(); get_hotkey_command_executor()->set_button_state(); events::raise_draw_event(); }
void playsingle_controller::play_scenario_main_loop() { LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks()) << "\n"; // Avoid autosaving after loading, but still // allow the first turn to have an autosave. ai_testing::log_game_start(); if(gamestate().board_.teams().empty()) { ERR_NG << "Playing game with 0 teams." << std::endl; } while(true) { try { play_turn(); if (is_regular_game_end()) { turn_data_.send_data(); return; } gamestate_->player_number_ = 1; } catch(const reset_gamestate_exception& ex) { // // TODO: // // The MP replay feature still doesn't work properly (causes OOS) // because: // // 1) The undo stack is not reset along with the gamestate (fixed). // 2) The server_request_number_ is not reset along with the // gamestate (fixed). // 3) chat and other unsynced actions are inserted in the middle of // the replay bringing the replay_pos in unorder (fixed). // 4) untracked changes in side controllers are lost when resetting // gamestate (fixed). // 5) The game should have a stricter check for whether the loaded // game is actually a parent of this game. // 6) If an action was undone after a game was saved it can cause // OOS if the undone action is in the snapshot of the saved // game (luckily this is never the case for autosaves). // boost::dynamic_bitset<> local_players; local_players.resize(gamestate().board_.teams().size(), true); //Preserve side controllers, becasue we won't get the side controoller updates again when replaying. for(size_t i = 0; i < local_players.size(); ++i) { local_players[i] = gamestate().board_.teams()[i].is_local(); } reset_gamestate(*ex.level, (*ex.level)["replay_pos"]); for(size_t i = 0; i < local_players.size(); ++i) { resources::gameboard->teams()[i].set_local(local_players[i]); } play_scenario_init(); replay_.reset(new replay_controller(*this, false, ex.level)); if(ex.start_replay) { replay_->play_replay(); } } } //end for loop }
void play_controller::tab() { gui::TEXTBOX_MODE mode = menu_handler_.get_textbox().mode(); std::set<std::string> dictionary; switch(mode) { case gui::TEXTBOX_SEARCH: { for (const unit& u : gamestate().board_.units()) { const map_location& loc = u.get_location(); if(!gui_->fogged(loc) && !(gamestate().board_.teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc, gui_->get_disp_context()))) dictionary.insert(u.name()); } //TODO List map labels break; } case gui::TEXTBOX_COMMAND: { std::vector<std::string> commands = menu_handler_.get_commands_list(); dictionary.insert(commands.begin(), commands.end()); // no break here, we also want player names from the next case } case gui::TEXTBOX_MESSAGE: { for (const team& t : gamestate().board_.teams()) { if(!t.is_empty()) dictionary.insert(t.current_player()); } // Add observers for (const std::string& o : gui_->observers()) { dictionary.insert(o); } // Add nicks who whispered you for (const std::string& w : gui_->get_chat_manager().whisperers()) { dictionary.insert(w); } // Add nicks from friendlist const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend"); for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) { dictionary.insert((*iter).first); } //Exclude own nick from tab-completion. //NOTE why ? dictionary.erase(preferences::login()); break; } default: ERR_DP << "unknown textbox mode" << std::endl; } //switch(mode) menu_handler_.get_textbox().tab(dictionary); }
void replay_savegame::write_game(config_writer &out) { savegame::write_game(out); gamestate().write_carryover(out); out.write_child("replay_start", gamestate().replay_start()); out.write_child("replay", gamestate().replay_data); }
void ingame_savegame::create_filename() { std::stringstream stream; const std::string ellipsed_name = font::make_text_ellipsis(gamestate().classification().label, font::SIZE_NORMAL, 200); stream << ellipsed_name << " " << _("Turn") << " " << gamestate().get_starting_pos()["turn_at"]; set_filename(stream.str()); }
void ingame_savegame::write_game(config_writer &out) { log_scope("write_game"); savegame::write_game(out); gamestate().write_carryover(out); out.write_child("snapshot",snapshot()); out.write_child("replay_start", gamestate().replay_start()); out.write_child("replay", gamestate().replay_data); }
void menu_handle(DARNIT_KEYS *keys, DARNIT_MOUSE *mouse) { if(keys->a) gamestate(GAMESTATE_GAME); else if(keys->b) gamestate(GAMESTATE_HIGHSCORE); else if(keys->x) gamestate(GAMESTATE_INSTRUCTIONS); else if(keys->y) gamestate(GAMESTATE_QUIT); }
void autosave_savegame::create_filename() { std::string filename; if (gamestate().classification().label.empty()) filename = _("Auto-Save"); else filename = gamestate().classification().label + "-" + _("Auto-Save") + gamestate().get_starting_pos()["turn_at"]; set_filename(filename); }
void playsingle_controller::check_objectives() { if (!gamestate().board_.teams().empty()) { const team &t = gamestate().board_.teams()[gui_->viewing_team()]; if (!is_regular_game_end() && !is_browsing() && t.objectives_changed()) { show_objectives(); } } }
void replay_savegame::write_game(config_writer &out) { savegame::write_game(out); gamestate().write_carryover(out); out.write_child("replay_start", gamestate().replay_start()); out.open_child("replay"); gamestate().get_replay().write(out); out.close_child("replay"); }
void ingame_savegame::write_game(config_writer &out) { log_scope("write_game"); savegame::write_game(out); gamestate().write_carryover(out); out.write_child("snapshot",gamestate().get_starting_pos()); out.write_child("replay_start", gamestate().replay_start()); out.open_child("replay"); gamestate().get_replay().write(out); out.close_child("replay"); }
void scenariostart_savegame::before_save() { //Add the player section to the starting position so we can get the correct recall list //when loading the replay later on // if there is no scenario information in the starting pos, add the (persistent) sides from the snapshot // else do nothing, as persistence information was already added at the end of the previous scenario if (gamestate().starting_pos["id"].empty()) { foreach(const config &snapshot_side, gamestate().snapshot.child_range("side")) { //add all side tags (assuming they only contain carryover information) gamestate().starting_pos.add_child("side", snapshot_side); } }
int play_controller::find_last_visible_team() const { assert(current_side() <= int(gamestate().board_.teams().size())); const int num_teams = gamestate().board_.teams().size(); const bool is_observer = this->is_observer(); for(int i = 0; i < num_teams; i++) { const int team_num = modulo(current_side() - i, num_teams, 1); if(is_team_visible(team_num, is_observer)) { return team_num; } } return 0; }
void playsingle_controller::init_gui(){ LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks()) << "\n"; play_controller::init_gui(); if(gamestate().first_human_team_ != -1) { gui_->scroll_to_tile(gamestate().board_.map().starting_position(gamestate().first_human_team_ + 1), game_display::WARP); } gui_->scroll_to_tile(gamestate().board_.map().starting_position(1), game_display::WARP); update_locker lock_display(gui_->video(), is_skipping_replay()); gui_->draw(); get_hotkey_command_executor()->set_button_state(); events::raise_draw_event(); }
void play_controller::play_turn() { whiteboard_manager_->on_gamestate_change(); gui_->new_turn(); gui_->invalidate_game_status(); events::raise_draw_event(); LOG_NG << "turn: " << turn() << "\n"; if(gui_->video().non_interactive()) { LOG_AIT << "Turn " << turn() << ":" << std::endl; } for (; gamestate_->player_number_ <= int(gamestate().board_.teams().size()); ++gamestate_->player_number_) { // If a side is empty skip over it. if (current_team().is_empty()) { continue; } init_side_begin(); if(gamestate_->init_side_done()) { // This is the case in a reloaded game where the side was initialized before saving the game. init_side_end(); } ai_testing::log_turn_start(current_side()); play_side(); if(is_regular_game_end()) { return; } finish_side_turn(); if(is_regular_game_end()) { return; } if(gui_->video().non_interactive()) { LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages" << std::endl; ai_testing::log_turn_end(current_side()); } } // If the loop exits due to the last team having been processed. gamestate_->player_number_ = gamestate().board_.teams().size(); finish_turn(); // Time has run out check_time_over(); }
void gameover_handle(DARNIT_KEYS *keys, DARNIT_MOUSE *mouse) { if (highscore_is_new(score)) { d_render_begin(); if (d_menu_loop(highscore.name) != -1) { if (*highscore.name_buff < 0x20) sprintf(highscore.name_buff, "arne."); highscore_add(score, highscore.name_buff); gamestate(GAMESTATE_HIGHSCORE); } d_render_end(); } else if(keys->start) gamestate(GAMESTATE_MENU); }
int main(int argc, char **argv) { DARNIT_KEYS keys; DARNIT_MOUSE mouse; //particle_emitter_new(30, 5000, 8000, 10000, 255, 0, 0, PARTICLE_TYPE_PULSE, 400, 240, 0, -100, 100); //particle_emitter_new(30, 5000, 8000, 10000, 255, 255, 0, PARTICLE_TYPE_PULSE, 400, 240, 0, -100, 100); init(); gamestate(GAMESTATE_MENU); while(_gamestate!=GAMESTATE_QUIT) { keys=d_keys_get(); mouse=d_mouse_get(); if(handle[_gamestate]) handle[_gamestate](&keys, &mouse); d_render_begin(); if(render[_gamestate]) render[_gamestate](); d_render_end(); d_loop(); } d_quit(); return 0; }
void playmp_controller::maybe_linger() { // mouse_handler expects at least one team for linger mode to work. assert(is_regular_game_end()); if (!get_end_level_data_const().transient.linger_mode || gamestate().board_.teams().empty() || gui_->video().faked()) { const bool has_next_scenario = !gamestate().gamedata_.next_scenario().empty() && gamestate().gamedata_.next_scenario() != "null"; if(!is_host() && has_next_scenario) { // If we continue without lingering we need to // make sure the host uploads the next scenario // before we attempt to download it. wait_for_upload(); } } else { linger(); } }
void play_controller::process_keyup_event(const SDL_Event& event) { // 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 <= '9') { const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0; if(new_path_turns != mouse_handler_.get_path_turns()) { mouse_handler_.set_path_turns(new_path_turns); const unit_map::iterator u = mouse_handler_.selected_unit(); if(u.valid()) { // if it's not the unit's turn, we reset its moves unit_movement_resetter move_reset(*u, u->side() != current_side()); mouse_handler_.set_current_paths(pathfind::paths(*u, false, true, gamestate().board_.teams_[gui_->viewing_team()], mouse_handler_.get_path_turns())); gui_->highlight_reach(mouse_handler_.current_paths()); } else { mouse_handler_.select_hex(mouse_handler_.get_selected_hex(), false, false, false); } } } else if (event.key.keysym.sym == SDLK_TAB) { static CKey keys; if (!keys[SDLK_TAB]) { whiteboard_manager_->set_invert_behavior(false); } } }
void playsingle_controller::enable_replay(bool is_unit_test) { replay_.reset(new replay_controller(*this, gamestate().has_human_sides(), boost::shared_ptr<config>( new config(saved_game_.replay_start())), boost::bind(&playsingle_controller::on_replay_end, this, is_unit_test))); if(is_unit_test) { replay_->play_replay(); } }
void play_controller::play_side() { //check for team-specific items in the scenario gui_->parse_team_overlays(); do { update_viewing_player(); { save_blocker blocker; maybe_do_init_side(); if(is_regular_game_end()) { return; } } // This flag can be set by derived classes (in overridden functions). player_type_changed_ = false; statistics::reset_turn_stats(gamestate().board_.teams()[current_side() - 1].save_id()); play_side_impl(); if(is_regular_game_end()) { return; } } while (player_type_changed_); // Keep looping if the type of a team (human/ai/networked) // has changed mid-turn sync_end_turn(); }
void play_controller::fire_start() { gamestate().gamedata_.set_phase(game_data::START); pump().fire("start"); // start event may modify start turn with WML, reflect any changes. gamestate().gamedata_.get_variable("turn_number") = int(turn()); check_objectives(); // prestart and start events may modify the initial gold amount, // reflect any changes. for (team& tm : gamestate().board_.teams_) { tm.set_start_gold(tm.gold()); } gamestate_->init_side_done() = false; gamestate().gamedata_.set_phase(game_data::PLAY); }
bool play_controller::hotkey_handler::in_context_menu(hotkey::HOTKEY_COMMAND command) const { switch(command) { // Only display these if the mouse is over a castle or keep tile case hotkey::HOTKEY_RECRUIT: case hotkey::HOTKEY_REPEAT_RECRUIT: case hotkey::HOTKEY_RECALL: { // last_hex_ is set by mouse_events::mouse_motion const map_location & last_hex = mouse_handler_.get_last_hex(); const int viewing_side = gui()->viewing_side(); // A quick check to save us having to create the future map and // possibly loop through all units. if ( !play_controller_.get_map_const().is_keep(last_hex) && !play_controller_.get_map_const().is_castle(last_hex) ) return false; wb::future_map future; /* lasts until method returns. */ return gamestate().side_can_recruit_on(viewing_side, last_hex); } default: return true; } }
void play_controller::finish_side_turn() { whiteboard_manager_->on_finish_side_turn(current_side()); { //Block for set_scontext_synced set_scontext_synced sync(1); // Ending the turn commits all moves. undo_stack().clear(); gamestate().board_.end_turn(current_side()); const std::string turn_num = std::to_string(turn()); const std::string side_num = std::to_string(current_side()); // Clear shroud, in case units had been slowed for the turn. actions::clear_shroud(current_side()); pump().fire("side_turn_end"); pump().fire("side_"+ side_num + "_turn_end"); pump().fire("side_turn_" + turn_num + "_end"); pump().fire("side_" + side_num + "_turn_" + turn_num + "_end"); // This is where we refog, after all of a side's events are done. actions::recalculate_fog(current_side()); check_victory(); sync.do_final_checkup(); } mouse_handler_.deselect_hex(); resources::gameboard->unit_id_manager().reset_fake(); gamestate_->init_side_done() = false; }
void playsingle_controller::play_side_impl() { if (!skip_next_turn_) { end_turn_ = END_TURN_NONE; } if(replay_.get() != NULL) { REPLAY_RETURN res = replay_->play_side_impl(); if(res == REPLAY_FOUND_END_TURN) { end_turn_ = END_TURN_SYNCED; } if (player_type_changed_) { replay_.reset(); } } else if((current_team().is_local_human() && current_team().is_proxy_human())) { LOG_NG << "is human...\n"; // If a side is dead end the turn, but play at least side=1's // turn in case all sides are dead if (gamestate().board_.side_units(current_side()) == 0 && !(gamestate().board_.units().size() == 0 && current_side() == 1)) { end_turn_ = END_TURN_REQUIRED; } before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_human_turn(); } if ( !player_type_changed_ && !is_regular_game_end()) { after_human_turn(); } LOG_NG << "human finished turn...\n"; } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) { play_ai_turn(); } else if(current_team().is_network()) { play_network_turn(); } else if(current_team().is_local_human() && current_team().is_idle()) { end_turn_enable(false); do_idle_notification(); before_human_turn(); if (end_turn_ == END_TURN_NONE) { play_idle_loop(); } } else { // we should have skipped over empty controllers before so this shouldn't be possible ERR_NG << "Found invalid side controller " << current_team().controller().to_string() << " (" << current_team().proxy_controller().to_string() << ") for side " << current_team().side() << "\n"; } }
void play_controller::show_objectives() const { const team& t = gamestate().board_.teams()[gui_->viewing_team()]; static const std::string no_objectives(_("No objectives available")); std::string objectives = t.objectives(); gui2::show_transient_message(gui_->video(), get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true); t.reset_objectives_changed(); }
void play_controller::fire_prestart() { // pre-start events must be executed before any GUI operation, // as those may cause the display to be refreshed. update_locker lock_display(gui_->video()); gamestate().gamedata_.set_phase(game_data::PRESTART); // Fire these right before prestart events, to catch only the units sides // have started with. for (const unit& u : gamestate().board_.units()) { pump().fire("unit_placed", map_location(u.get_location())); } pump().fire("prestart"); // prestart event may modify start turn with WML, reflect any changes. gamestate().gamedata_.get_variable("turn_number") = int(turn()); }
void playsingle_controller::maybe_linger() { // mouse_handler expects at least one team for linger mode to work. assert(is_regular_game_end()); if (get_end_level_data_const().transient.linger_mode && !gamestate().board_.teams().empty()) { linger(); } }