void ScriptRunner::timerFired(Timer<ScriptRunner>* timer) { ActionLogScope log_scope("script runner timer"); ASSERT_UNUSED(timer, timer == &m_timer); RefPtr<Document> protect(m_document); Vector<PendingScript> scripts; scripts.swap(m_scriptsToExecuteSoon); size_t numInOrderScriptsToExecute = 0; for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].cachedScript()->isLoaded(); ++numInOrderScriptsToExecute) scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]); if (numInOrderScriptsToExecute) m_scriptsToExecuteInOrder.remove(0, numInOrderScriptsToExecute); size_t size = scripts.size(); for (size_t i = 0; i < size; ++i) { CachedScript* cachedScript = scripts[i].cachedScript(); RefPtr<Element> element = scripts[i].releaseElementAndClear(); ScriptElement* script = toScriptElement(element.get()); ActionLogFormat(ActionLog::READ_MEMORY, "ScriptRunner-%p-%p", static_cast<void*>(this), static_cast<void*>(script)); script->execute(cachedScript); m_document->decrementLoadEventDelayCount(); } }
void loadgame::load_multiplayer_game() { show_dialog(false, false); if (filename_.empty()) throw load_game_cancelled_exception(); std::string error_log; { cursor::setter cur(cursor::WAIT); log_scope("load_game"); manager::read_save_file(filename_, load_config_, &error_log); copy_era(load_config_); gamestate_ = game_state(load_config_); } if(!error_log.empty()) { gui2::show_error_message(gui_.video(), _("The file you have tried to load is corrupt: '") + error_log); throw load_game_cancelled_exception(); } if(gamestate_.classification().campaign_type != "multiplayer") { gui2::show_message(gui_.video(), "", _("This is not a multiplayer save")); throw load_game_cancelled_exception(); } check_version_compatibility(); }
bool loadgame::load_multiplayer_game() { show_dialog(false, false); if (filename_.empty()) return false; std::string error_log; { cursor::setter cur(cursor::WAIT); log_scope("load_game"); read_save_file(filename_, load_config_, &error_log); copy_era(load_config_); gamestate_.set_data(load_config_); } if(!error_log.empty()) { gui2::show_error_message(gui_.video(), _("The file you have tried to load is corrupt: '") + error_log); return false; } if(gamestate_.classification().campaign_type != game_classification::CAMPAIGN_TYPE::MULTIPLAYER) { gui2::show_transient_error_message(gui_.video(), _("This is not a multiplayer save.")); return false; } return check_version_compatibility(); }
void manager::load_summary(const std::string& name, config& cfg_summary, std::string* error_log){ log_scope("load_game_summary"); config cfg; read_save_file(name,cfg,error_log); ::extract_summary_from_config(cfg, cfg_summary); }
void save_index::write_save_index() { log_scope("write_save_index()"); try { scoped_ostream stream = ostream_file(get_save_index_file()); write(*stream, load()); } catch(io_exception& e) { ERR_SAVE << "error writing to save index file: '" << e.what() << "'\n"; } }
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 ResourceLoader::cancel(const ResourceError& error) { // If the load has already completed - succeeded, failed, or previously cancelled - do nothing. if (m_reachedTerminalState) return; ResourceError nonNullError = error.isNull() ? cancelledError() : error; // willCancel() and didFailToLoad() both call out to clients that might do // something causing the last reference to this object to go away. RefPtr<ResourceLoader> protector(this); // If we re-enter cancel() from inside willCancel(), we want to pick up from where we left // off without re-running willCancel() if (!m_calledWillCancel) { m_calledWillCancel = true; willCancel(nonNullError); } // If we re-enter cancel() from inside didFailToLoad(), we want to pick up from where we // left off without redoing any of this work. if (!m_cancelled) { m_cancelled = true; if (FormData* data = m_request.httpBody()) data->removeGeneratedFilesIfNeeded(); if (m_handle) m_handle->clearAuthentication(); m_documentLoader->cancelPendingSubstituteLoad(this); if (m_handle) { m_handle->cancel(); m_handle = 0; } if (m_options.sendLoadCallbacks == SendCallbacks && m_identifier && !m_notifiedLoadComplete) { ActionLogScope log_scope( String::format("cancel_fail:%s", m_request.url().lastPathComponent().ascii().data()).ascii().data()); frameLoader()->notifier()->didFailToLoad(this, nonNullError); } } // If cancel() completed from within the call to willCancel() or didFailToLoad(), // we don't want to redo didCancel() or releasesResources(). if (m_reachedTerminalState) return; didCancel(nonNullError); releaseResources(); }
void savegame::write_game(config_writer &out) { log_scope("write_game"); out.write_key_val("version", game_config::version); gamestate_.write_general_info(out); out.open_child("statistics"); statistics::write_stats(out); out.close_child("statistics"); }
void savegame::write_game(config_writer &out) { log_scope("write_game"); out.write_key_val("version", game_config::version); out.write_key_val("next_underlying_unit_id", lexical_cast<std::string>(n_unit::id_manager::instance().get_save_id())); gamestate_.write_general_info(out); out.open_child("statistics"); statistics::write_stats(out); out.close_child("statistics"); }
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 ResourceLoader::didFinishLoadingOnePart(double finishTime) { if (m_cancelled) return; ASSERT(!m_reachedTerminalState); if (m_notifiedLoadComplete) return; m_notifiedLoadComplete = true; if (m_options.sendLoadCallbacks == SendCallbacks) { // SRL: Log end of network response. ActionLogScope log_scope( String::format("finish_load:%s", m_request.url().lastPathComponent().ascii().data()).ascii().data()); frameLoader()->notifier()->didFinishLoad(this, finishTime); } }
void playsingle_controller::before_human_turn(bool save) { log_scope("player turn"); browse_ = false; linger_ = false; ai::manager::raise_turn_started(); if(save && level_result_ == NONE) { savegame::autosave_savegame save(gamestate_, *gui_, to_config(), preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } if(preferences::turn_bell() && level_result_ == NONE) { sound::play_bell(game_config::sounds::turn_bell); } }
possible_end_play_signal playsingle_controller::before_human_turn() { log_scope("player turn"); browse_ = false; linger_ = false; HANDLE_END_PLAY_SIGNAL( ai::manager::raise_turn_started() ); //This line throws exception from here: https://github.com/wesnoth/wesnoth/blob/ac96a2b91b3276e20b682210617cf87d1e0d366a/src/playsingle_controller.cpp#L954 if(do_autosaves_ && level_result_ == NONE) { savegame::autosave_savegame save(saved_game_, *gui_, to_config(), preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } if(preferences::turn_bell() && level_result_ == NONE) { sound::play_bell(game_config::sounds::turn_bell); } return boost::none; }
void playsingle_controller::before_human_turn() { log_scope("player turn"); assert(!linger_); if(end_turn_ != END_TURN_NONE || is_regular_game_end()) { return; } if(init_side_done_now_) { scoped_savegame_snapshot snapshot(*this); savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } if(preferences::turn_bell()) { sound::play_bell(game_config::sounds::turn_bell); } }
void help_browser::show_topic(const topic &t, bool save_in_history) { log_scope("show_topic"); if (save_in_history) { forward_topics_.clear(); if (shown_topic_ != NULL) { if (back_topics_.size() > max_history) { back_topics_.pop_front(); } back_topics_.push_back(shown_topic_); } } shown_topic_ = &t; text_area_.show_topic(t); menu_.select_topic(t); update_cursor(); }
void playsingle_controller::before_human_turn() { log_scope("player turn"); assert(!linger_); if(end_turn_ != END_TURN_NONE) { return; } //TODO: why do we need the next line? ai::manager::raise_turn_started(); if(init_side_done_now_ && !is_regular_game_end()) { update_savegame_snapshot(); savegame::autosave_savegame save(saved_game_, *gui_, preferences::save_compression_format()); save.autosave(game_config::disable_autosave, preferences::autosavemax(), preferences::INFINITE_AUTO_SAVES); } if(preferences::turn_bell() && !is_regular_game_end()) { sound::play_bell(game_config::sounds::turn_bell); } }
void ResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce) { // The following assertions are not quite valid here, since a subclass // might override didReceiveData in a way that invalidates them. This // happens with the steps listed in 3266216 // ASSERT(con == connection); // ASSERT(!m_reachedTerminalState); // Protect this in this delegate method since the additional processing can do // anything including possibly derefing this; one example of this is Radar 3266216. RefPtr<ResourceLoader> protector(this); addData(data, length, allAtOnce); // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. // However, with today's computers and networking speeds, this won't happen in practice. // Could be an issue with a giant local file. if (m_options.sendLoadCallbacks == SendCallbacks && m_frame) { ActionLogScope log_scope( String::format("recv_data:%s", m_request.url().lastPathComponent().ascii().data()).ascii().data()); frameLoader()->notifier()->didReceiveData(this, data, length, static_cast<int>(encodedDataLength)); } }
void terrain_builder::build_terrains() { log_scope("terrain_builder::build_terrains"); // Builds the terrain_by_type_ cache for(int x = -2; x <= map().w(); ++x) { for(int y = -2; y <= map().h(); ++y) { const map_location loc(x,y); const t_translation::t_terrain t = map().get_terrain(loc); terrain_by_type_[t].push_back(loc); } } int rule_index = 0; building_ruleset::const_iterator r; for(r = building_rules_.begin(); r != building_rules_.end(); ++r) { const building_rule& rule = r->second; // Find the constraint that contains the less terrain of all terrain rules. // We will keep a track of the matching terrains of this constraint // and later try to apply the rule only on them size_t min_size = INT_MAX; t_translation::t_list min_types; constraint_set::const_iterator min_constraint = rule.constraints.end(); for(constraint_set::const_iterator constraint = rule.constraints.begin(); constraint != rule.constraints.end(); ++constraint) { const t_translation::t_match& match = constraint->second.terrain_types_match; t_translation::t_list matching_types; size_t constraint_size = 0; for (terrain_by_type_map::iterator type_it = terrain_by_type_.begin(); type_it != terrain_by_type_.end(); ++type_it) { const t_translation::t_terrain t = type_it->first; if (terrain_matches(t, match)) { const size_t match_size = type_it->second.size(); constraint_size += match_size; if (constraint_size >= min_size) { break; // not a minimum, bail out } matching_types.push_back(t); } } if (constraint_size < min_size) { min_size = constraint_size; min_types = matching_types; min_constraint = constraint; if (min_size == 0) { // a constraint is never matched on this map // we break with a empty type list break; } } } //NOTE: if min_types is not empty, we have found a valid min_constraint; for(t_translation::t_list::const_iterator t = min_types.begin(); t != min_types.end(); ++t) { const std::vector<map_location>* locations = &terrain_by_type_[*t]; for(std::vector<map_location>::const_iterator itor = locations->begin(); itor != locations->end(); ++itor) { const map_location loc = itor->legacy_difference(min_constraint->second.loc); if(rule_matches(rule, loc, rule_index, min_constraint)) { apply_rule(rule, loc); } } } ++rule_index; } }
void show_hotkeys_dialog (display & disp, config *save_config) { log_scope ("show_hotkeys_dialog"); const events::event_context dialog_events_context; const int centerx = disp.w()/2; const int centery = disp.h()/2; const int width = 700; const int height = disp.video().gety() < 600 ? 380 : 500; const int xpos = centerx - width/2; const int ypos = centery - height/2; gui::button close_button (disp.video(), _("Close")); std::vector<gui::button*> buttons; buttons.push_back(&close_button); gui::dialog_frame f(disp.video(),_("Hotkey Settings"),gui::dialog_frame::default_style,true,&buttons); f.layout(xpos,ypos,width,height); f.draw(); SDL_Rect clip_rect = create_rect(0, 0, disp.w (), disp.h ()); SDL_Rect text_size = font::draw_text(NULL, clip_rect, font::SIZE_LARGE, font::NORMAL_COLOR,_("Press desired hotkey (Esc cancels)"), 0, 0); std::vector<std::string> menu_items; std::vector<hotkey::hotkey_item>& hotkeys = hotkey::get_hotkeys(); for(std::vector<hotkey::hotkey_item>::iterator i = hotkeys.begin(); i != hotkeys.end(); ++i) { if(i->hidden() || !i->is_in_active_scope()) continue; std::stringstream str,name; name << i->get_description(); str << name.str(); str << COLUMN_SEPARATOR; // This trick allows to display chars identical to markup characters str << font::NULL_MARKUP << i->get_name(); menu_items.push_back(str.str()); } std::ostringstream heading; heading << HEADING_PREFIX << _("Action") << COLUMN_SEPARATOR << _("Binding"); menu_items.push_back(heading.str()); gui::menu::basic_sorter sorter; sorter.set_alpha_sort(0).set_alpha_sort(1); gui::menu menu_(disp.video(), menu_items, false, height - font::relative_size(10), -1, &sorter, &gui::menu::bluebg_style); menu_.sort_by(0); menu_.reset_selection(); menu_.set_width(font::relative_size(500)); menu_.set_location(xpos + font::relative_size(10), ypos + font::relative_size(10)); gui::button change_button (disp.video(), _("Change Hotkey")); change_button.set_location(xpos + width - change_button.width () - font::relative_size(30),ypos + font::relative_size(30)); gui::button clear_button (disp.video(), _("Clear Hotkey")); clear_button.set_location(xpos + width - clear_button.width () - font::relative_size(30),ypos + font::relative_size(80)); // gui::button save_button (disp.video(), _("Save Hotkeys")); // save_button.set_location(xpos + width - save_button.width () - font::relative_size(30),ypos + font::relative_size(130)); escape_handler esc_hand; for(;;) { if (close_button.pressed() || esc_hand.escape_pressed()) { if (save_config == NULL) { save_hotkeys(); } else { hotkey::save_hotkeys(*save_config); } break; } if (change_button.pressed () || menu_.double_clicked()) { // Lets change this hotkey...... SDL_Rect dlgr = create_rect(centerx - text_size.w / 2 - 30 , centery - text_size.h / 2 - 16 , text_size.w + 60 , text_size.h + 32); surface_restorer restorer(&disp.video(),dlgr); gui::dialog_frame mini_frame(disp.video()); mini_frame.layout(centerx-text_size.w/2 - 20, centery-text_size.h/2 - 6, text_size.w+40, text_size.h+12); mini_frame.draw_background(); mini_frame.draw_border(); font::draw_text (&disp.video(), clip_rect, font::SIZE_LARGE,font::NORMAL_COLOR, _("Press desired hotkey (Esc cancels)"),centerx-text_size.w/2, centery-text_size.h/2); disp.update_display(); SDL_Event event; event.type = 0; int character = 0, keycode = 0, mod = 0; // Just to avoid warning int joystick = 0, button = 0, hat = 0, value = 0; const int any_mod = KMOD_CTRL | KMOD_ALT | KMOD_LMETA; while (event.type!=SDL_KEYDOWN && event.type!=SDL_JOYBUTTONDOWN && event.type!= SDL_JOYHATMOTION) SDL_PollEvent(&event); do { if (event.type==SDL_KEYDOWN) { keycode=event.key.keysym.sym; character=event.key.keysym.unicode; mod=event.key.keysym.mod; }; if (event.type==SDL_JOYBUTTONDOWN) { joystick = event.jbutton.which; button = event.jbutton.button; } if (event.type==SDL_JOYHATMOTION) { joystick = event.jhat.which; hat = event.jhat.hat; value = event.jhat.value; } SDL_PollEvent(&event); disp.flip(); disp.delay(10); } while (event.type!=SDL_KEYUP && event.type!=SDL_JOYBUTTONUP && event.type!=SDL_JOYHATMOTION); restorer.restore(); disp.update_display(); if (keycode == SDLK_ESCAPE && (mod & any_mod) == 0) { //cancel -- no action } else { const hotkey::hotkey_item& oldhk = hotkey::get_hotkey(character, keycode, (mod & KMOD_SHIFT) != 0, (mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0); hotkey::hotkey_item& newhk = hotkey::get_visible_hotkey(menu_.selection()); if(oldhk.get_id() != newhk.get_id() && !oldhk.null()) { std::stringstream msg; msg << " " << oldhk.get_description() << " : " << oldhk.get_name(); gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str()); } else { if (event.type == SDL_JOYHATMOTION) { const hotkey::hotkey_item& oldhkhat = hotkey::get_hotkey(joystick, hat, value); if(oldhkhat.get_id() != newhk.get_id() && !oldhkhat.null()) { std::stringstream msg; msg << " " << oldhkhat.get_description() << " : " << oldhkhat.get_name(); gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str()); } else { newhk.set_hat(joystick, hat, value); menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name()); } } else if (event.type == SDL_JOYBUTTONUP) { const hotkey::hotkey_item& oldhkbtn = hotkey::get_hotkey(button, joystick); if(oldhkbtn.get_id() != newhk.get_id() && !oldhkbtn.null()) { std::stringstream msg; msg << " " << oldhkbtn.get_description() << " : " << oldhkbtn.get_name(); gui2::show_transient_message(disp.video(),_("This hotkey is already in use."),msg.str()); } else { newhk.set_button(button, joystick); menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name()); } } else { newhk.set_key(character, keycode, (mod & KMOD_SHIFT) != 0, (mod & KMOD_CTRL) != 0, (mod & KMOD_ALT) != 0, (mod & KMOD_LMETA) != 0); menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name()); if ((newhk.get_id() == hotkey::HOTKEY_SCREENSHOT || newhk.get_id() == hotkey::HOTKEY_MAP_SCREENSHOT) && (mod & any_mod) == 0) { gui2::show_transient_message(disp.video(), _("Warning"), _("Screenshot hotkeys should be combined with the Control, Alt or Meta modifiers to avoid problems.")); } } } } } // if (save_button.pressed()) { // if (save_config == NULL) { // save_hotkeys(); // } else { // hotkey::save_hotkeys(*save_config); // } // } if (clear_button.pressed()) { // clear hotkey hotkey::hotkey_item& newhk = hotkey::get_visible_hotkey(menu_.selection()); newhk.clear_hotkey(); menu_.change_item(menu_.selection(), 1, font::NULL_MARKUP + newhk.get_name()); } menu_.process(); events::pump(); events::raise_process_event(); events::raise_draw_event(); disp.update_display(); disp.delay(10); } }
void play_controller::do_init_side() { set_scontext_synced sync; log_scope("player turn"); // In case we might end up calling sync:network during the side turn events, // and we don't want do_init_side to be called when a player drops. gamestate_->init_side_done() = true; init_side_done_now_ = true; const std::string turn_num = std::to_string(turn()); const std::string side_num = std::to_string(current_side()); gamestate().gamedata_.get_variable("side_number") = current_side(); // We might have skipped some sides because they were empty so it is not enough to check for side_num==1 if(!gamestate().tod_manager_.has_turn_event_fired()) { pump().fire("turn_" + turn_num); pump().fire("new_turn"); gamestate().tod_manager_.turn_event_fired(); } pump().fire("side_turn"); pump().fire("side_" + side_num + "_turn"); pump().fire("side_turn_" + turn_num); pump().fire("side_" + side_num + "_turn_" + turn_num); // 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. if (turn() > 1) { gamestate().board_.new_turn(current_side()); current_team().new_turn(); // If the expense is less than the number of villages owned // times the village support capacity, // then we don't have to pay anything at all int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support(); if(expense > 0) { current_team().spend_gold(expense); } calculate_healing(current_side(), !is_skipping_replay()); } // Prepare the undo stack. undo_stack().new_side_turn(current_side()); pump().fire("turn_refresh"); pump().fire("side_" + side_num + "_turn_refresh"); pump().fire("turn_" + turn_num + "_refresh"); pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh"); // Make sure vision is accurate. actions::clear_shroud(current_side(), true); init_side_end(); check_victory(); sync.do_final_checkup(); }
void unit_attack( const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,bool drain,std::string att_text) { game_display* disp = game_display::get_singleton(); if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::iterator att = units.find(a); assert(att != units.end()); unit& attacker = *att; const unit_map::iterator def = units.find(b); assert(def != units.end()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text ; if(damage) text = lexical_cast<std::string>(damage); if(!hit_text.empty()) { text.insert(text.begin(),hit_text.size()/2,' '); text = text + "\n" + hit_text; } std::string text_2 ; if(drain && damage) text_2 = lexical_cast<std::string>(std::min<int>(damage,defender.hitpoints())/2); if(!att_text.empty()) { text_2.insert(text_2.begin(),att_text.size()/2,' '); text_2 = text_2 + "\n" + att_text; } unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::KILL; } else if(damage > 0) { hit_type = unit_animation::HIT; }else { hit_type = unit_animation::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, display::rgb(0, 255, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (std::vector<std::pair<const config *, map_location> >::iterator itor = leaders.cfgs.begin(); itor != leaders.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator leader = units.find(itor->second); assert(leader != units.end()); leader->set_facing(itor->second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", itor->second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (std::vector<std::pair<const config *, map_location> >::iterator itor = helpers.cfgs.begin(); itor != helpers.cfgs.end(); ++itor) { if(itor->second == a) continue; if(itor->second == b) continue; unit_map::iterator helper = units.find(itor->second); assert(helper != units.end()); helper->set_facing(itor->second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", itor->second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; while(damage_left > 0 && !animator.would_end()) { int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }
void config_cache::read_cache(const std::string& file_path, config& cfg) { static const std::string extension = ".gz"; std::stringstream defines_string; defines_string << file_path; bool is_valid = true; for(const preproc_map::value_type& d : defines_map_) { // // Only WESNOTH_VERSION is allowed to be non-empty. // if((!d.second.value.empty() || !d.second.arguments.empty()) && d.first != "WESNOTH_VERSION") { is_valid = false; ERR_CACHE << "Invalid preprocessor define: " << d.first << '\n'; break; } defines_string << " " << d.first; } // Do cache check only if define map is valid and // caching is allowed. const std::string& cache_path = filesystem::get_cache_dir(); if(is_valid && !cache_path.empty()) { // Use a hash for a shorter display of the defines. const std::string fname = cache_path + "/" + cache_file_prefix_ + sha1_hash(defines_string.str()).display(); const std::string fname_checksum = fname + ".checksum" + extension; filesystem::file_tree_checksum dir_checksum; if(!force_valid_cache_ && !fake_invalid_cache_) { try { if(filesystem::file_exists(fname_checksum)) { config checksum_cfg; DBG_CACHE << "Reading checksum: " << fname_checksum << "\n"; read_file(fname_checksum, checksum_cfg); dir_checksum = filesystem::file_tree_checksum(checksum_cfg); } } catch(config::error&) { ERR_CACHE << "cache checksum is corrupt" << std::endl; } catch(filesystem::io_exception&) { ERR_CACHE << "error reading cache checksum" << std::endl; } } if(force_valid_cache_) { LOG_CACHE << "skipping cache validation (forced)\n"; } if(filesystem::file_exists(fname + extension) && (force_valid_cache_ || (dir_checksum == filesystem::data_tree_checksum()))) { LOG_CACHE << "found valid cache at '" << fname << extension << "' with defines_map " << defines_string.str() << "\n"; log_scope("read cache"); try { read_file(fname + extension,cfg); const std::string define_file = fname + ".define" + extension; if(filesystem::file_exists(define_file)) { config_cache_transaction::instance().add_define_file(define_file); } return; } catch(config::error& e) { ERR_CACHE << "cache " << fname << extension << " is corrupt. Loading from files: "<< e.message << std::endl; } catch(filesystem::io_exception&) { ERR_CACHE << "error reading cache " << fname << extension << ". Loading from files" << std::endl; } catch (boost::iostreams::gzip_error& e) { //read_file -> ... -> read_gz can throw this exception. ERR_CACHE << "cache " << fname << extension << " is corrupt. Error code: " << e.error() << std::endl; } } LOG_CACHE << "no valid cache found. Writing cache to '" << fname << extension << " with defines_map "<< defines_string.str() << "'\n"; // Now we need queued defines so read them to memory read_defines_queue(); preproc_map copy_map(make_copy_map()); read_configs(file_path, cfg, copy_map); add_defines_map_diff(copy_map); try { write_file(fname + extension, cfg); write_file(fname + ".define" + extension, copy_map); config checksum_cfg; filesystem::data_tree_checksum().write(checksum_cfg); write_file(fname_checksum, checksum_cfg); } catch(filesystem::io_exception&) { ERR_CACHE << "could not write to cache '" << fname << "'" << std::endl; } return; } LOG_CACHE << "Loading plain config instead of cache\n"; preproc_map copy_map(make_copy_map()); read_configs(file_path, cfg, copy_map); add_defines_map_diff(copy_map); }
std::string default_generate_map(size_t width, size_t height, size_t island_size, size_t island_off_center, size_t iterations, size_t hill_size, size_t max_lakes, size_t nvillages, size_t castle_size, size_t nplayers, bool roads_between_castles, std::map<map_location,std::string>* labels, const config& cfg) { log_scope("map generation"); // Odd widths are nasty VALIDATE(is_even(width), _("Random maps with an odd width aren't supported.")); int ticks = SDL_GetTicks(); // Find out what the 'flatland' on this map is, i.e. grassland. std::string flatland = cfg["default_flatland"]; if(flatland == "") { flatland = t_translation::write_terrain_code(t_translation::GRASS_LAND); } const t_translation::t_terrain grassland = t_translation::read_terrain_code(flatland); // We want to generate a map that is 9 times bigger // than the actual size desired. // Only the middle part of the map will be used, // but the rest is so that the map we end up using // can have a context (e.g. rivers flowing from // out of the map into the map, same for roads, etc.) width *= 3; height *= 3; LOG_NG << "generating height map...\n"; // Generate the height of everything. const height_map heights = generate_height_map(width,height,iterations,hill_size,island_size,island_off_center); LOG_NG << "done generating height map...\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); config naming = cfg.child_or_empty("naming"); // HACK: dummy names to satisfy unit_race requirements naming["id"] = "village_naming"; naming["plural_name"] = "villages"; // Make a dummy race for generating names const unit_race name_generator(naming); std::vector<terrain_height_mapper> height_conversion; BOOST_FOREACH(const config &h, cfg.child_range("height")) { height_conversion.push_back(terrain_height_mapper(h)); } terrain_map terrain(width, t_translation::t_list(height, grassland)); size_t x, y; for(x = 0; x != heights.size(); ++x) { for(y = 0; y != heights[x].size(); ++y) { for(std::vector<terrain_height_mapper>::const_iterator i = height_conversion.begin(); i != height_conversion.end(); ++i) { if(i->convert_terrain(heights[x][y])) { terrain[x][y] = i->convert_to(); break; } } } } std::map<int, t_translation::coordinate> starting_positions; LOG_NG << output_map(terrain, starting_positions); LOG_NG << "placed land forms\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); // Now that we have our basic set of flatland/hills/mountains/water, // we can place lakes and rivers on the map. // All rivers are sourced at a lake. // Lakes must be in high land - at least 'min_lake_height'. // (Note that terrain below a certain altitude may be made // into bodies of water in the code above - i.e. 'sea', // but these are not considered 'lakes', because // they are not sources of rivers). // // We attempt to place 'max_lakes' lakes. // Each lake will be placed at a random location, // if that random location meets the minimum terrain requirements for a lake. // We will also attempt to source a river from each lake. std::set<location> lake_locs; std::map<location, std::string> river_names, lake_names, road_names, bridge_names, mountain_names, forest_names, swamp_names; const size_t nlakes = max_lakes > 0 ? (rand()%max_lakes) : 0; for(size_t lake = 0; lake != nlakes; ++lake) { for(int tries = 0; tries != 100; ++tries) { const int x = rand()%width; const int y = rand()%height; if (heights[x][y] > cfg["min_lake_height"].to_int()) { std::vector<location> river = generate_river(heights, terrain, x, y, cfg["river_frequency"]); if(river.empty() == false && labels != NULL) { std::string base_name; LOG_NG << "generating name for river...\n"; const std::string& name = generate_name(name_generator,"river_name",&base_name); LOG_NG << "named river '" << name << "'\n"; size_t name_frequency = 20; for(std::vector<location>::const_iterator r = river.begin(); r != river.end(); ++r) { const map_location loc(r->x-width/3,r->y-height/3); if(((r - river.begin())%name_frequency) == name_frequency/2) { labels->insert(std::pair<map_location,std::string>(loc,name)); } river_names.insert(std::pair<location,std::string>(loc,base_name)); } LOG_NG << "put down river name...\n"; } LOG_NG << "generating lake...\n"; std::set<location> locs; bool res = generate_lake(terrain, x, y, cfg["lake_size"], locs); if(res && labels != NULL) { bool touches_other_lake = false; std::string base_name; const std::string& name = generate_name(name_generator,"lake_name",&base_name); std::set<location>::const_iterator i; // Only generate a name if the lake hasn't touched any other lakes, // so that we don't end up with one big lake with multiple names. for(i = locs.begin(); i != locs.end(); ++i) { if(lake_locs.count(*i) != 0) { touches_other_lake = true; // Reassign the name of this lake to be the same as the other lake const location loc(i->x-width/3,i->y-height/3); const std::map<location,std::string>::const_iterator other_name = lake_names.find(loc); if(other_name != lake_names.end()) { base_name = other_name->second; } } lake_locs.insert(*i); } if(!touches_other_lake) { const map_location loc(x-width/3,y-height/3); labels->erase(loc); labels->insert(std::pair<map_location,std::string>(loc,name)); } for(i = locs.begin(); i != locs.end(); ++i) { const location loc(i->x-width/3,i->y-height/3); lake_names.insert(std::pair<location, std::string>(loc, base_name)); } } break; } } } LOG_NG << "done generating rivers...\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); const size_t default_dimensions = 40*40*9; /* * Convert grassland terrain to other types of flat terrain. * * We generate a 'temperature map' which uses the height generation * algorithm to generate the temperature levels all over the map. Then we * can use a combination of height and terrain to divide terrain up into * more interesting types than the default. */ const height_map temperature_map = generate_height_map(width,height, cfg["temperature_iterations"].to_int() * width * height / default_dimensions, cfg["temperature_size"], 0, 0); LOG_NG << "generated temperature map...\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); std::vector<terrain_converter> converters; BOOST_FOREACH(const config &cv, cfg.child_range("convert")) { converters.push_back(terrain_converter(cv)); } LOG_NG << "created terrain converters\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); // Iterate over every flatland tile, and determine // what type of flatland it is, based on our [convert] tags. for(x = 0; x != width; ++x) { for(y = 0; y != height; ++y) { for(std::vector<terrain_converter>::const_iterator i = converters.begin(); i != converters.end(); ++i) { if(i->convert_terrain(terrain[x][y],heights[x][y],temperature_map[x][y])) { terrain[x][y] = i->convert_to(); break; } } } } LOG_NG << "placing villages...\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); /* * Place villages in a 'grid', to make placing fair, but with villages * displaced from their position according to terrain and randomness, to * add some variety. */ std::set<location> villages; LOG_NG << "placing castles...\n"; /** Try to find configuration for castles. */ const config &castle_config = cfg.child("castle"); if (!castle_config) { LOG_NG << "Could not find castle configuration\n"; return std::string(); } /* * Castle configuration tag contains a 'valid_terrain' attribute which is a * list of terrains that the castle may appear on. */ const t_translation::t_list list = t_translation::read_list(castle_config["valid_terrain"]); const is_valid_terrain terrain_tester(terrain, list); /* * Attempt to place castles at random. * * Once we have placed castles, we run a sanity check to make sure that the * castles are well-placed. If the castles are not well-placed, we try * again. Definition of 'well-placed' is if no two castles are closer than * 'min_distance' hexes from each other, and the castles appear on a * terrain listed in 'valid_terrain'. */ std::vector<location> castles; std::set<location> failed_locs; for(size_t player = 0; player != nplayers; ++player) { LOG_NG << "placing castle for " << player << "\n"; log_scope("placing castle"); const int min_x = width/3 + 3; const int min_y = height/3 + 3; const int max_x = (width/3)*2 - 4; const int max_y = (height/3)*2 - 4; int min_distance = castle_config["min_distance"]; location best_loc; int best_ranking = 0; for(int x = min_x; x != max_x; ++x) { for(int y = min_y; y != max_y; ++y) { const location loc(x,y); if(failed_locs.count(loc)) { continue; } const int ranking = rank_castle_location(x,y,terrain_tester,min_x,max_x,min_y,max_y,min_distance,castles,best_ranking); if(ranking <= 0) { failed_locs.insert(loc); } if(ranking > best_ranking) { best_ranking = ranking; best_loc = loc; } } } if(best_ranking == 0) { ERR_NG << "No castle location found, aborting.\n"; std::string error = _("No valid castle location found. Too many or too few mountain hexes? (please check the 'max hill size' parameter)"); throw mapgen_exception(error); } assert(std::find(castles.begin(), castles.end(), best_loc) == castles.end()); castles.push_back(best_loc); // Make sure the location can't get a second castle. failed_locs.insert(best_loc); } LOG_NG << "placing roads...\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); // Place roads. // We select two tiles at random locations on the borders // of the map, and try to build roads between them. int nroads = cfg["roads"]; if(roads_between_castles) { nroads += castles.size()*castles.size(); } std::set<location> bridges; road_path_calculator calc(terrain,cfg); for (int road = 0; road != nroads; ++road) { log_scope("creating road"); /* * We want the locations to be on the portion of the map we're actually * going to use, since roads on other parts of the map won't have any * influence, and doing it like this will be quicker. */ location src = random_point_at_side(width/3 + 2,height/3 + 2); location dst = random_point_at_side(width/3 + 2,height/3 + 2); src.x += width/3 - 1; src.y += height/3 - 1; dst.x += width/3 - 1; dst.y += height/3 - 1; if (roads_between_castles && road < int(castles.size() * castles.size())) { const size_t src_castle = road/castles.size(); const size_t dst_castle = road%castles.size(); if(src_castle >= dst_castle) { continue; } src = castles[src_castle]; dst = castles[dst_castle]; } // If the road isn't very interesting (on the same border), don't draw it. else if(src.x == dst.x || src.y == dst.y) { continue; } if (calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) { continue; } // Search a path out for the road pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, &calc, width, height); std::string road_base_name; const std::string& name = generate_name(name_generator, "road_name", &road_base_name); const int name_frequency = 20; int name_count = 0; bool on_bridge = false; // Draw the road. // If the search failed, rt.steps will simply be empty. for(std::vector<location>::const_iterator step = rt.steps.begin(); step != rt.steps.end(); ++step) { const int x = step->x; const int y = step->y; if(x < 0 || y < 0 || x >= static_cast<long>(width) || y >= static_cast<long>(height)) { continue; } // Find the configuration which tells us // what to convert this tile to, to make it into a road. if (const config &child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y]))) { // Convert to bridge means that we want to convert // depending upon the direction the road is going. // Typically it will be in a format like, // convert_to_bridge=\,|,/ // '|' will be used if the road is going north-south // '/' will be used if the road is going south west-north east // '\' will be used if the road is going south east-north west // The terrain will be left unchanged otherwise // (if there is no clear direction). const std::string &convert_to_bridge = child["convert_to_bridge"]; if(convert_to_bridge.empty() == false) { if(step == rt.steps.begin() || step+1 == rt.steps.end()) continue; const location& last = *(step-1); const location& next = *(step+1); location adj[6]; get_adjacent_tiles(*step,adj); int direction = -1; // If we are going north-south if((last == adj[0] && next == adj[3]) || (last == adj[3] && next == adj[0])) { direction = 0; } // If we are going south west-north east else if((last == adj[1] && next == adj[4]) || (last == adj[4] && next == adj[1])) { direction = 1; } // If we are going south east-north west else if((last == adj[2] && next == adj[5]) || (last == adj[5] && next == adj[2])) { direction = 2; } if(labels != NULL && on_bridge == false) { on_bridge = true; std::string bridge_base_name; const std::string& name = generate_name(name_generator, "bridge_name", &bridge_base_name); const location loc(x - width / 3, y-height/3); labels->insert(std::pair<map_location,std::string>(loc,name)); bridge_names.insert(std::pair<location,std::string>(loc, bridge_base_name)); //add to use for village naming bridges.insert(loc); } if(direction != -1) { const std::vector<std::string> items = utils::split(convert_to_bridge); if(size_t(direction) < items.size() && items[direction].empty() == false) { terrain[x][y] = t_translation::read_terrain_code(items[direction]); } continue; } } else { on_bridge = false; } // Just a plain terrain substitution for a road const std::string &convert_to = child["convert_to"]; if(convert_to.empty() == false) { const t_translation::t_terrain letter = t_translation::read_terrain_code(convert_to); if(labels != NULL && terrain[x][y] != letter && name_count++ == name_frequency && on_bridge == false) { labels->insert(std::pair<map_location,std::string>(map_location(x-width/3,y-height/3),name)); name_count = 0; } terrain[x][y] = letter; const location loc(x - width / 3, y - height / 3); //add to use for village naming road_names.insert(std::pair<location,std::string>(loc, road_base_name)); } } } LOG_NG << "looked at " << calc.calls << " locations\n"; } // Now that road drawing is done, we can plonk down the castles. for(std::vector<location>::const_iterator c = castles.begin(); c != castles.end(); ++c) { if(c->valid() == false) { continue; } const int x = c->x; const int y = c->y; const int player = c - castles.begin() + 1; const struct t_translation::coordinate coord(x, y); starting_positions.insert(std::pair<int, t_translation::coordinate>(player, coord)); terrain[x][y] = t_translation::HUMAN_KEEP; const int castles[13][2] = { {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1}, {-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2} }; for (size_t i = 0; i < castle_size - 1; i++) { terrain[x+castles[i][0]][y+castles[i][1]] = t_translation::HUMAN_CASTLE; } // Remove all labels under the castle tiles if(labels != NULL) { labels->erase(location(x-width/3,y-height/3)); for (size_t i = 0; i < castle_size - 1; i++) { labels->erase(location(x+castles[i][0]-width/3, y+castles[i][1]-height/3)); } } } LOG_NG << "placed castles\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); /*Random naming for landforms: mountains, forests, swamps, hills *we name these now that everything else is placed (as e.g., placing * roads could split a forest) */ for (x = width / 3; x < (width / 3)*2; x++) { for (y = height / 3; y < (height / 3) * 2;y++) { //check the terrain of the tile const location loc(x - width / 3, y - height / 3); const t_translation::t_terrain terr = terrain[x][y]; std::string name, base_name; std::set<std::string> used_names; if (t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) { //name every 15th mountain if ((rand()%15) == 0) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { name = generate_name(name_generator, "mountain_name", &base_name); } labels->insert(std::pair<map_location, std::string>(loc, name)); mountain_names.insert(std::pair<location, std::string>(loc, base_name)); } } else if (t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) { //if the forest tile is not named yet, name it const std::map<location, std::string>::const_iterator forest_name = forest_names.find(loc); if(forest_name == forest_names.end()) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { name = generate_name(name_generator, "forest_name", &base_name); } forest_names.insert(std::pair<location, std::string>(loc, base_name)); // name all connected forest tiles accordingly flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, width, height, 0, labels, name); } } else if (t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) { //if the swamp tile is not named yet, name it const std::map<location, std::string>::const_iterator swamp_name = swamp_names.find(loc); if(swamp_name == swamp_names.end()) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { name = generate_name(name_generator, "swamp_name", &base_name); } swamp_names.insert(std::pair<location, std::string>(loc, base_name)); // name all connected swamp tiles accordingly flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, width, height, 0, labels, name); } } } } if (nvillages > 0) { config naming_cfg = cfg.child_or_empty("village_naming"); // HACK: dummy names to satisfy unit_race requirements naming_cfg["id"] = "village_naming"; naming_cfg["plural_name"] = "villages"; const unit_race village_names_generator(naming_cfg); // First we work out the size of the x and y distance between villages const size_t tiles_per_village = ((width*height)/9)/nvillages; size_t village_x = 1, village_y = 1; // Alternate between incrementing the x and y value. // When they are high enough to equal or exceed the tiles_per_village, // then we have them to the value we want them at. while(village_x*village_y < tiles_per_village) { if(village_x < village_y) { ++village_x; } else { ++village_y; } } std::set<std::string> used_names; tcode_list_cache adj_liked_cache; for(size_t vx = 0; vx < width; vx += village_x) { LOG_NG << "village at " << vx << "\n"; for(size_t vy = rand()%village_y; vy < height; vy += village_y) { const size_t add_x = rand()%3; const size_t add_y = rand()%3; const size_t x = (vx + add_x) - 1; const size_t y = (vy + add_y) - 1; const map_location res = place_village(terrain,x,y,2,cfg,adj_liked_cache); if(res.x >= static_cast<long>(width) / 3 && res.x < static_cast<long>(width * 2) / 3 && res.y >= static_cast<long>(height) / 3 && res.y < static_cast<long>(height * 2) / 3) { const std::string str = t_translation::write_terrain_code(terrain[res.x][res.y]); if (const config &child = cfg.find_child("village", "terrain", str)) { const std::string &convert_to = child["convert_to"]; if(convert_to != "") { terrain[res.x][res.y] = t_translation::read_terrain_code(convert_to); villages.insert(res); if(labels != NULL && naming_cfg.empty() == false) { const map_location loc(res.x-width/3,res.y-height/3); map_location adj[6]; get_adjacent_tiles(loc,adj); std::string name_type = "village_name"; const t_translation::t_list field = t_translation::t_list(1, t_translation::GRASS_LAND), forest = t_translation::t_list(1, t_translation::FOREST), mountain = t_translation::t_list(1, t_translation::MOUNTAIN), hill = t_translation::t_list(1, t_translation::HILL); size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0; utils::string_map symbols; size_t n; for(n = 0; n != 6; ++n) { const std::map<location,std::string>::const_iterator road_name = road_names.find(adj[n]); if(road_name != road_names.end()) { symbols["road"] = road_name->second; name_type = "village_name_road"; break; } const std::map<location,std::string>::const_iterator river_name = river_names.find(adj[n]); if(river_name != river_names.end()) { symbols["river"] = river_name->second; name_type = "village_name_river"; const std::map<location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]); if(bridge_name != bridge_names.end()) { //we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too symbols["bridge"] = bridge_name->second; name_type = "village_name_river_bridge"; } break; } const std::map<location,std::string>::const_iterator forest_name = forest_names.find(adj[n]); if(forest_name != forest_names.end()) { symbols["forest"] = forest_name->second; name_type = "village_name_forest"; break; } const std::map<location,std::string>::const_iterator lake_name = lake_names.find(adj[n]); if(lake_name != lake_names.end()) { symbols["lake"] = lake_name->second; name_type = "village_name_lake"; break; } const std::map<location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]); if(mountain_name != mountain_names.end()) { symbols["mountain"] = mountain_name->second; name_type = "village_name_mountain"; break; } const std::map<location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]); if(swamp_name != swamp_names.end()) { symbols["swamp"] = swamp_name->second; name_type = "village_name_swamp"; break; } const t_translation::t_terrain terr = terrain[adj[n].x+width/3][adj[n].y+height/3]; if(std::count(field.begin(),field.end(),terr) > 0) { ++field_count; } else if(std::count(forest.begin(),forest.end(),terr) > 0) { ++forest_count; } else if(std::count(hill.begin(),hill.end(),terr) > 0) { ++hill_count; } else if(std::count(mountain.begin(),mountain.end(),terr) > 0) { ++mountain_count; } } if(n == 6) { if(field_count == 6) { name_type = "village_name_grassland"; } else if(forest_count >= 2) { name_type = "village_name_forest"; } else if(mountain_count >= 1) { name_type = "village_name_mountain_anonymous"; } else if(hill_count >= 2) { name_type = "village_name_hill"; } } std::string name; for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { name = generate_name(village_names_generator,name_type,NULL,&symbols); } used_names.insert(name); labels->insert(std::pair<map_location,std::string>(loc,name)); } } } } } } } LOG_NG << "placed villages\n"; LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks(); return output_map(terrain, starting_positions); }
void terrain_builder::parse_config(const config &cfg, bool local) { log_scope("terrain_builder::parse_config"); // Parses the list of building rules (BRs) foreach (const config &br, cfg.child_range("terrain_graphics")) { building_rule pbr; // Parsed Building rule pbr.local = local; // add_images_from_config(pbr.images, **br); if(!br["x"].empty() && !br["y"].empty()) pbr.location_constraints = map_location(atoi(br["x"].c_str()) - 1, atoi(br["y"].c_str()) - 1); pbr.probability = br["probability"].empty() ? -1 : atoi(br["probability"].c_str()); // Mapping anchor indices to anchor locations. anchormap anchors; // Parse the map= , if there is one (and fill the anchors list) parse_mapstring(br["map"], pbr, anchors, br); // Parses the terrain constraints (TCs) foreach (const config &tc, br.child_range("tile")) { // Adds the terrain constraint to the current built terrain's list // of terrain constraints, if it does not exist. map_location loc; if (!tc["x"].empty()) { loc.x = atoi(tc["x"].c_str()); } if (!tc["y"].empty()) { loc.y = atoi(tc["y"].c_str()); } if (!tc["loc"].empty()) { std::vector<std::string> sloc = utils::split(tc["loc"]); if(sloc.size() == 2) { loc.x = atoi(sloc[0].c_str()); loc.y = atoi(sloc[1].c_str()); } } if(loc.valid()) { add_constraints(pbr.constraints, loc, tc, br); } if (!tc["pos"].empty()) { int pos = atoi(tc["pos"].c_str()); if(anchors.find(pos) == anchors.end()) { WRN_NG << "Invalid anchor!\n"; continue; } std::pair<anchormap::const_iterator, anchormap::const_iterator> range = anchors.equal_range(pos); for(; range.first != range.second; ++range.first) { loc = range.first->second; add_constraints(pbr.constraints, loc, tc, br); } } } const std::vector<std::string> global_set_flag = utils::split(br["set_flag"]); const std::vector<std::string> global_no_flag = utils::split(br["no_flag"]); const std::vector<std::string> global_has_flag = utils::split(br["has_flag"]); for(constraint_set::iterator constraint = pbr.constraints.begin(); constraint != pbr.constraints.end(); ++constraint) { if(global_set_flag.size()) constraint->second.set_flag.insert(constraint->second.set_flag.end(), global_set_flag.begin(), global_set_flag.end()); if(global_no_flag.size()) constraint->second.no_flag.insert(constraint->second.no_flag.end(), global_no_flag.begin(), global_no_flag.end()); if(global_has_flag.size()) constraint->second.has_flag.insert(constraint->second.has_flag.end(), global_has_flag.begin(), global_has_flag.end()); } // Handles rotations const std::string &rotations = br["rotations"]; int precedence = lexical_cast_default<int>(br["precedence"],0); add_rotated_rules(building_rules_, pbr, precedence, rotations); } // Debug output for the terrain rules #if 0 std::cerr << "Built terrain rules: \n"; building_ruleset::const_iterator rule; for(rule = building_rules_.begin(); rule != building_rules_.end(); ++rule) { std::cerr << ">> New rule: image_background = " << "\n>> Location " << rule->second.location_constraints << "\n>> Probability " << rule->second.probability for(constraint_set::const_iterator constraint = rule->second.constraints.begin(); constraint != rule->second.constraints.end(); ++constraint) { std::cerr << ">>>> New constraint: location = (" << constraint->second.loc << "), terrain types = '" << t_translation::write_list(constraint->second.terrain_types_match.terrain) << "'\n"; std::vector<std::string>::const_iterator flag; for(flag = constraint->second.set_flag.begin(); flag != constraint->second.set_flag.end(); ++flag) { std::cerr << ">>>>>> Set_flag: " << *flag << "\n"; } for(flag = constraint->second.no_flag.begin(); flag != constraint->second.no_flag.end(); ++flag) { std::cerr << ">>>>>> No_flag: " << *flag << "\n"; } } } #endif }
void unit_attack(display * disp, game_board & board, const map_location& a, const map_location& b, int damage, const attack_type& attack, const attack_type* secondary_attack, int swing,std::string hit_text,int drain_amount,std::string att_text, const std::vector<std::string>* extra_hit_sounds) { if(!disp ||disp->video().update_locked() || disp->video().faked() || (disp->fogged(a) && disp->fogged(b)) || preferences::show_combat() == false) { return; } //const unit_map& units = disp->get_units(); disp->select_hex(map_location::null_location()); // scroll such that there is at least half a hex spacing around fighters disp->scroll_to_tiles(a,b,game_display::ONSCREEN,true,0.5,false); log_scope("unit_attack"); const unit_map::const_iterator att = board.units().find(a); assert(att.valid()); const unit& attacker = *att; const unit_map::iterator def = board.find_unit(b); assert(def.valid()); unit &defender = *def; int def_hitpoints = defender.hitpoints(); att->set_facing(a.get_relative_dir(b)); def->set_facing(b.get_relative_dir(a)); defender.set_facing(b.get_relative_dir(a)); unit_animator animator; unit_ability_list leaders = attacker.get_abilities("leadership"); unit_ability_list helpers = defender.get_abilities("resistance"); std::string text = number_and_text(damage, hit_text); std::string text_2 = number_and_text(abs(drain_amount), att_text); unit_animation::hit_type hit_type; if(damage >= defender.hitpoints()) { hit_type = unit_animation::hit_type::KILL; } else if(damage > 0) { hit_type = unit_animation::hit_type::HIT; }else { hit_type = unit_animation::hit_type::MISS; } animator.add_animation(&attacker, "attack", att->get_location(), def->get_location(), damage, true, text_2, (drain_amount >= 0) ? display::rgb(0, 255, 0) : display::rgb(255, 0, 0), hit_type, &attack, secondary_attack, swing); // note that we take an anim from the real unit, we'll use it later const unit_animation *defender_anim = def->anim_comp().choose_animation(*disp, def->get_location(), "defend", att->get_location(), damage, hit_type, &attack, secondary_attack, swing); animator.add_animation(&defender, defender_anim, def->get_location(), true, text , display::rgb(255, 0, 0)); for (const unit_ability & ability : leaders) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator leader = board.units().find(ability.second); assert(leader.valid()); leader->set_facing(ability.second.get_relative_dir(a)); animator.add_animation(&*leader, "leading", ability.second, att->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } for (const unit_ability & ability : helpers) { if(ability.second == a) continue; if(ability.second == b) continue; unit_map::const_iterator helper = board.units().find(ability.second); assert(helper.valid()); helper->set_facing(ability.second.get_relative_dir(b)); animator.add_animation(&*helper, "resistance", ability.second, def->get_location(), damage, true, "", 0, hit_type, &attack, secondary_attack, swing); } animator.start_animations(); animator.wait_until(0); int damage_left = damage; bool extra_hit_sounds_played = false; while(damage_left > 0 && !animator.would_end()) { if(!extra_hit_sounds_played && extra_hit_sounds != nullptr) { for (std::string hit_sound : *extra_hit_sounds) { sound::play_sound(hit_sound); } extra_hit_sounds_played = true; } int step_left = (animator.get_end_time() - animator.get_animation_time() )/50; if(step_left < 1) step_left = 1; int removed_hp = damage_left/step_left ; if(removed_hp < 1) removed_hp = 1; defender.take_hit(removed_hp); damage_left -= removed_hp; animator.wait_until(animator.get_animation_time_potential() +50); } animator.wait_for_end(); // pass the animation back to the real unit def->anim_comp().start_animation(animator.get_end_time(), defender_anim, true); reset_helpers(&*att, &*def); def->set_hitpoints(def_hitpoints); }
std::string default_map_generator_job::default_generate_map(generator_data data, std::map<map_location,std::string>* labels, const config& cfg) { log_scope("map generation"); // Odd widths are nasty VALIDATE(is_even(data.width), _("Random maps with an odd width aren't supported.")); // Try to find configuration for castles const config& castle_config = cfg.child("castle"); int ticks = SDL_GetTicks(); // We want to generate a map that is 9 times bigger than the actual size desired. // Only the middle part of the map will be used, but the rest is so that the map we // end up using can have a context (e.g. rivers flowing from out of the map into the map, // same for roads, etc.) data.width *= 3; data.height *= 3; config naming; if(cfg.has_child("naming")) { naming = game_config_.child("naming"); naming.append_attributes(cfg.child("naming")); } // If the [naming] child is empty, we cannot provide good names. std::map<map_location,std::string>* misc_labels = naming.empty() ? nullptr : labels; std::shared_ptr<name_generator> base_name_generator, river_name_generator, lake_name_generator, road_name_generator, bridge_name_generator, mountain_name_generator, forest_name_generator, swamp_name_generator; if(misc_labels != nullptr) { name_generator_factory base_generator_factory{ naming, {"male", "base", "bridge", "road", "river", "forest", "lake", "mountain", "swamp"} }; naming.get_old_attribute("base_names", "male_names", "[naming]male_names= is deprecated, use base_names= instead"); //Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented. base_name_generator = base_generator_factory.get_name_generator( (naming.has_attribute("base_names") || naming.has_attribute("base_name_generator")) ? "base" : "male" ); river_name_generator = base_generator_factory.get_name_generator("river"); lake_name_generator = base_generator_factory.get_name_generator("lake"); road_name_generator = base_generator_factory.get_name_generator("road"); bridge_name_generator = base_generator_factory.get_name_generator("bridge"); mountain_name_generator = base_generator_factory.get_name_generator("mountain"); forest_name_generator = base_generator_factory.get_name_generator("forest"); swamp_name_generator = base_generator_factory.get_name_generator("swamp"); } // Generate the height of everything. const height_map heights = generate_height_map(data.width, data.height, data.iterations, data.hill_size, data.island_size, data.island_off_center); LOG_NG << "Done generating height map. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); // Find out what the 'flatland' on this map is, i.e. grassland. std::string flatland = cfg["default_flatland"]; if(flatland.empty()) { flatland = t_translation::write_terrain_code(t_translation::GRASS_LAND); } const t_translation::terrain_code grassland = t_translation::read_terrain_code(flatland); std::vector<terrain_height_mapper> height_conversion; for(const config& h : cfg.child_range("height")) { height_conversion.emplace_back(h); } terrain_map terrain(data.width, data.height, grassland); for(size_t x = 0; x != heights.size(); ++x) { for(size_t y = 0; y != heights[x].size(); ++y) { for(auto i : height_conversion) { if(i.convert_terrain(heights[x][y])) { terrain[x][y] = i.convert_to(); break; } } } } t_translation::starting_positions starting_positions; LOG_NG << output_map(terrain, starting_positions); LOG_NG << "Placed landforms. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); /* Now that we have our basic set of flatland/hills/mountains/water, * we can place lakes and rivers on the map. * All rivers are sourced at a lake. * Lakes must be in high land - at least 'min_lake_height'. * (Note that terrain below a certain altitude may be made into bodies of water * in the code above - i.e. 'sea', but these are not considered 'lakes', * because they are not sources of rivers). * * We attempt to place 'max_lakes' lakes. * Each lake will be placed at a random location, if that random location meets theminimum * terrain requirements for a lake. We will also attempt to source a river from each lake. */ std::set<map_location> lake_locs; std::map<map_location, std::string> river_names, lake_names, road_names, bridge_names, mountain_names, forest_names, swamp_names; const size_t nlakes = data.max_lakes > 0 ? (rng_()%data.max_lakes) : 0; for(size_t lake = 0; lake != nlakes; ++lake) { for(int tries = 0; tries != 100; ++tries) { const int x = rng_()%data.width; const int y = rng_()%data.height; if(heights[x][y] <= cfg["min_lake_height"].to_int()) { continue; } std::vector<map_location> river = generate_river(heights, terrain, x, y, cfg["river_frequency"]); if(!river.empty() && misc_labels != nullptr) { const std::string base_name = base_name_generator->generate(); const std::string& name = river_name_generator->generate({{"base", base_name}}); LOG_NG << "Named river '" << name << "'\n"; size_t name_frequency = 20; for(std::vector<map_location>::const_iterator r = river.begin(); r != river.end(); ++r) { const map_location loc(r->x-data.width/3,r->y-data.height/3); if(((r - river.begin())%name_frequency) == name_frequency/2) { misc_labels->emplace(loc, name); } river_names.emplace(loc, base_name); } } LOG_NG << "Generating lake...\n"; std::set<map_location> locs; if(generate_lake(terrain, x, y, cfg["lake_size"], locs) && misc_labels != nullptr) { bool touches_other_lake = false; std::string base_name = base_name_generator->generate(); const std::string& name = lake_name_generator->generate({{"base", base_name}}); // Only generate a name if the lake hasn't touched any other lakes, // so that we don't end up with one big lake with multiple names. for(auto i : locs) { if(lake_locs.count(i) != 0) { touches_other_lake = true; // Reassign the name of this lake to be the same as the other lake const map_location loc(i.x-data.width/3,i.y-data.height/3); const std::map<map_location,std::string>::const_iterator other_name = lake_names.find(loc); if(other_name != lake_names.end()) { base_name = other_name->second; } } lake_locs.insert(i); } if(!touches_other_lake) { const map_location loc(x-data.width/3,y-data.height/3); misc_labels->erase(loc); misc_labels->emplace(loc, name); } for(auto i : locs) { const map_location loc(i.x-data.width/3,i.y-data.height/3); lake_names.emplace(loc, base_name); } } break; } } LOG_NG << "Generated rivers. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); const size_t default_dimensions = 40*40*9; /* * Convert grassland terrain to other types of flat terrain. * * We generate a 'temperature map' which uses the height generation * algorithm to generate the temperature levels all over the map. Then we * can use a combination of height and terrain to divide terrain up into * more interesting types than the default. */ const height_map temperature_map = generate_height_map(data.width,data.height, cfg["temperature_iterations"].to_int() * data.width * data.height / default_dimensions, cfg["temperature_size"], 0, 0); LOG_NG << "Generated temperature map. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); std::vector<terrain_converter> converters; for(const config& cv : cfg.child_range("convert")) { converters.emplace_back(cv); } LOG_NG << "Created terrain converters. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); // Iterate over every flatland tile, and determine what type of flatland it is, based on our [convert] tags. for(int x = 0; x != data.width; ++x) { for(int y = 0; y != data.height; ++y) { for(auto i : converters) { if(i.convert_terrain(terrain[x][y],heights[x][y],temperature_map[x][y])) { terrain[x][y] = i.convert_to(); break; } } } } LOG_NG << "Placing castles...\n"; /* * Attempt to place castles at random. * * After they are placed, we run a sanity check to make sure no two castles * are closer than 'min_distance' hexes apart, and that they appear on a * terrain listed in 'valid_terrain'. * * If not, we attempt to place them again. */ std::vector<map_location> castles; std::set<map_location> failed_locs; if(castle_config) { /* * Castle configuration tag contains a 'valid_terrain' attribute which is a * list of terrains that the castle may appear on. */ const t_translation::ter_list list = t_translation::read_list(castle_config["valid_terrain"]); const is_valid_terrain terrain_tester(terrain, list); for(int player = 0; player != data.nplayers; ++player) { LOG_NG << "placing castle for " << player << "\n"; lg::scope_logger inner_scope_logging_object__(lg::general(), "placing castle"); const int min_x = data.width/3 + 3; const int min_y = data.height/3 + 3; const int max_x = (data.width/3)*2 - 4; const int max_y = (data.height/3)*2 - 4; int min_distance = castle_config["min_distance"]; map_location best_loc; int best_ranking = 0; for(int x = min_x; x != max_x; ++x) { for(int y = min_y; y != max_y; ++y) { const map_location loc(x,y); if(failed_locs.count(loc)) { continue; } const int ranking = rank_castle_location(x, y, terrain_tester, min_x, max_x, min_y, max_y, min_distance, castles, best_ranking); if(ranking <= 0) { failed_locs.insert(loc); } if(ranking > best_ranking) { best_ranking = ranking; best_loc = loc; } } } if(best_ranking == 0) { ERR_NG << "No castle location found, aborting." << std::endl; const std::string error = _("No valid castle location found. Too many or too few mountain hexes? (please check the 'max hill size' parameter)"); throw mapgen_exception(error); } assert(std::find(castles.begin(), castles.end(), best_loc) == castles.end()); castles.push_back(best_loc); // Make sure the location can't get a second castle. failed_locs.insert(best_loc); } LOG_NG << "Placed castles. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; } LOG_NG << "Placing roads...\n"; ticks = SDL_GetTicks(); // Place roads. // We select two tiles at random locations on the borders of the map // and try to build roads between them. int nroads = cfg["roads"]; if(data.link_castles) { nroads += castles.size()*castles.size(); } std::set<map_location> bridges; road_path_calculator calc(terrain, cfg, rng_()); for(int road = 0; road != nroads; ++road) { lg::scope_logger another_inner_scope_logging_object__(lg::general(), "creating road"); /* * We want the locations to be on the portion of the map we're actually * going to use, since roads on other parts of the map won't have any * influence, and doing it like this will be quicker. */ map_location src = random_point_at_side(data.width/3 + 2,data.height/3 + 2); map_location dst = random_point_at_side(data.width/3 + 2,data.height/3 + 2); src.x += data.width/3 - 1; src.y += data.height/3 - 1; dst.x += data.width/3 - 1; dst.y += data.height/3 - 1; if(data.link_castles && road < int(castles.size() * castles.size())) { const size_t src_castle = road/castles.size(); const size_t dst_castle = road%castles.size(); if(src_castle >= dst_castle) { continue; } src = castles[src_castle]; dst = castles[dst_castle]; } else if(src.x == dst.x || src.y == dst.y) { // If the road isn't very interesting (on the same border), don't draw it. continue; } if(calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) { continue; } // Search a path out for the road pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, calc, data.width, data.height); const std::string& road_base_name = misc_labels != nullptr ? base_name_generator->generate() : ""; const std::string& road_name = misc_labels != nullptr ? road_name_generator->generate({{"base", road_base_name}}) : ""; const int name_frequency = 20; int name_count = 0; bool on_bridge = false; // Draw the road. // If the search failed, rt.steps will simply be empty. for(std::vector<map_location>::const_iterator step = rt.steps.begin(); step != rt.steps.end(); ++step) { const int x = step->x; const int y = step->y; if(x < 0 || y < 0 || x >= static_cast<long>(data.width) || y >= static_cast<long>(data.height)) { continue; } // Find the configuration which tells us what to convert this tile to, to make it into a road. const config& child = cfg.find_child("road_cost", "terrain", t_translation::write_terrain_code(terrain[x][y])); if(child.empty()){ continue; } /* Convert to bridge means that we want to convert depending on the direction of the road. * Typically it will be in a format like convert_to_bridge = \,|,/ * '|' will be used if the road is going north-south * '/' will be used if the road is going south west-north east * '\' will be used if the road is going south east-north west * The terrain will be left unchanged otherwise (if there is no clear direction). */ const std::string& convert_to_bridge = child["convert_to_bridge"]; if(!convert_to_bridge.empty()) { if(step == rt.steps.begin() || step+1 == rt.steps.end()) { continue; } const map_location& last = *(step-1); const map_location& next = *(step+1); map_location adj[6]; get_adjacent_tiles(*step,adj); int direction = -1; // If we are going north-south if((last == adj[0] && next == adj[3]) || (last == adj[3] && next == adj[0])) { direction = 0; } // If we are going south west-north east else if((last == adj[1] && next == adj[4]) || (last == adj[4] && next == adj[1])) { direction = 1; } // If we are going south east-north west else if((last == adj[2] && next == adj[5]) || (last == adj[5] && next == adj[2])) { direction = 2; } if(misc_labels != nullptr && !on_bridge) { on_bridge = true; std::string bridge_base_name = base_name_generator->generate(); const std::string& name = bridge_name_generator->generate({{"base", bridge_base_name}}); const map_location loc(x - data.width / 3, y-data.height/3); misc_labels->emplace(loc, name); bridge_names.emplace(loc, bridge_base_name); //add to use for village naming bridges.insert(loc); } if(direction != -1) { const std::vector<std::string> items = utils::split(convert_to_bridge); if(size_t(direction) < items.size() && !items[direction].empty()) { terrain[x][y] = t_translation::read_terrain_code(items[direction]); } continue; } } else { on_bridge = false; } // Just a plain terrain substitution for a road const std::string& convert_to = child["convert_to"]; if(!convert_to.empty()) { const t_translation::terrain_code letter = t_translation::read_terrain_code(convert_to); if(misc_labels != nullptr && terrain[x][y] != letter && name_count++ == name_frequency && !on_bridge) { misc_labels->emplace(map_location(x - data.width / 3, y - data.height / 3), road_name); name_count = 0; } terrain[x][y] = letter; if(misc_labels != nullptr) { const map_location loc(x - data.width / 3, y - data.height / 3); //add to use for village naming if(!road_base_name.empty()) road_names.emplace(loc, road_base_name); } } } } // Now that road drawing is done, we can plonk down the castles. for(std::vector<map_location>::const_iterator c = castles.begin(); c != castles.end(); ++c) { if(!c->valid()) { continue; } const int x = c->x; const int y = c->y; const int player = c - castles.begin() + 1; const t_translation::coordinate coord(x, y); starting_positions.insert(t_translation::starting_positions::value_type(std::to_string(player), coord)); terrain[x][y] = t_translation::HUMAN_KEEP; const int castle_array[13][2] { {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1}, {-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2} }; for(int i = 0; i < data.castle_size - 1; i++) { terrain[x+ castle_array[i][0]][y+ castle_array[i][1]] = t_translation::HUMAN_CASTLE; } // Remove all labels under the castle tiles if(labels != nullptr) { labels->erase(map_location(x-data.width/3,y-data.height/3)); for(int i = 0; i < data.castle_size - 1; i++) { labels->erase(map_location(x+ castle_array[i][0]-data.width/3, y+ castle_array[i][1]-data.height/3)); } } } LOG_NG << "Placed roads. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; ticks = SDL_GetTicks(); /* Random naming for landforms: mountains, forests, swamps, hills * we name these now that everything else is placed (as e.g., placing * roads could split a forest) */ if(misc_labels != nullptr) { for(int x = data.width / 3; x < (data.width / 3)*2; x++) { for(int y = data.height / 3; y < (data.height / 3) * 2;y++) { //check the terrain of the tile const map_location loc(x - data.width / 3, y - data.height / 3); const t_translation::terrain_code terr = terrain[x][y]; std::string name, base_name; std::set<std::string> used_names; if(t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) { //name every 15th mountain if((rng_() % 15) == 0) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { base_name = base_name_generator->generate(); name = mountain_name_generator->generate({{"base", base_name}}); } misc_labels->emplace(loc, name); mountain_names.emplace(loc, base_name); } } else if(t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) { // If the forest tile is not named yet, name it const std::map<map_location, std::string>::const_iterator forest_name = forest_names.find(loc); if(forest_name == forest_names.end()) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { base_name = base_name_generator->generate(); name = forest_name_generator->generate({{"base", base_name}}); } forest_names.emplace(loc, base_name); // name all connected forest tiles accordingly flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, data.width, data.height, 0, misc_labels, name); } } else if(t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) { // If the swamp tile is not named yet, name it const std::map<map_location, std::string>::const_iterator swamp_name = swamp_names.find(loc); if(swamp_name == swamp_names.end()) { for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { base_name = base_name_generator->generate(); name = swamp_name_generator->generate({{"base", base_name}}); } swamp_names.emplace(loc, base_name); // name all connected swamp tiles accordingly flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, data.width, data.height, 0, misc_labels, name); } } } } } LOG_NG << "Named landforms. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; LOG_NG << "Placing villages...\n"; ticks = SDL_GetTicks(); /* * Place villages in a 'grid', to make placing fair, but with villages * displaced from their position according to terrain and randomness, to * add some variety. */ std::set<map_location> villages; if(data.nvillages > 0) { // First we work out the size of the x and y distance between villages const size_t tiles_per_village = ((data.width*data.height)/9)/data.nvillages; size_t village_x = 1, village_y = 1; // Alternate between incrementing the x and y value. // When they are high enough to equal or exceed the tiles_per_village, // then we have them to the value we want them at. while(village_x*village_y < tiles_per_village) { if(village_x < village_y) { ++village_x; } else { ++village_y; } } std::set<std::string> used_names; tcode_list_cache adj_liked_cache; config village_naming = game_config_.child("village_naming"); if(cfg.has_child("village_naming")) { village_naming.append_attributes(cfg.child("village_naming")); } // If the [village_naming] child is empty, we cannot provide good names. std::map<map_location,std::string>* village_labels = village_naming.empty() ? nullptr : labels; for(int vx = 0; vx < data.width; vx += village_x) { LOG_NG << "village at " << vx << "\n"; for(int vy = rng_()%village_y; vy < data.height; vy += village_y) { const size_t add = rng_()%3; const size_t x = (vx + add) - 1; const size_t y = (vy + add) - 1; const map_location res = place_village(terrain, x, y, 2, cfg, adj_liked_cache); if(res.x < static_cast<long>(data.width ) / 3 || res.x >= static_cast<long>(data.width * 2) / 3 || res.y < static_cast<long>(data.height ) / 3 || res.y >= static_cast<long>(data.height * 2) / 3) { continue; } const std::string str = t_translation::write_terrain_code(terrain[res.x][res.y]); const std::string& convert_to = cfg.find_child("village", "terrain", str)["convert_to"].str(); if(convert_to.empty()) { continue; } terrain[res.x][res.y] = t_translation::read_terrain_code(convert_to); villages.insert(res); if(village_labels == nullptr) { continue; } name_generator_factory village_name_generator_factory{ village_naming, {"base", "male", "village", "lake", "river", "bridge", "grassland", "forest", "hill", "mountain", "mountain_anon", "road", "swamp"} }; village_naming.get_old_attribute("base_names", "male_names", "[village_naming]male_names= is deprecated, use base_names= instead"); //Due to the attribute detection feature of the factory we also support male_name_generator= but keep it undocumented. base_name_generator = village_name_generator_factory.get_name_generator( (village_naming.has_attribute("base_names") || village_naming.has_attribute("base_name_generator")) ? "base" : "male" ); const map_location loc(res.x-data.width/3,res.y-data.height/3); map_location adj[6]; get_adjacent_tiles(loc,adj); std::string name_type = "village"; const t_translation::ter_list field = t_translation::ter_list(1, t_translation::GRASS_LAND), forest = t_translation::ter_list(1, t_translation::FOREST), mountain = t_translation::ter_list(1, t_translation::MOUNTAIN), hill = t_translation::ter_list(1, t_translation::HILL); size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0; std::map<std::string,std::string> symbols; size_t n; for(n = 0; n != 6; ++n) { const std::map<map_location,std::string>::const_iterator road_name = road_names.find(adj[n]); if(road_name != road_names.end()) { symbols["road"] = road_name->second; name_type = "road"; break; } const std::map<map_location,std::string>::const_iterator river_name = river_names.find(adj[n]); if(river_name != river_names.end()) { symbols["river"] = river_name->second; name_type = "river"; const std::map<map_location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]); if(bridge_name != bridge_names.end()) { //we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too symbols["bridge"] = bridge_name->second; name_type = "river_bridge"; } break; } const std::map<map_location,std::string>::const_iterator forest_name = forest_names.find(adj[n]); if(forest_name != forest_names.end()) { symbols["forest"] = forest_name->second; name_type = "forest"; break; } const std::map<map_location,std::string>::const_iterator lake_name = lake_names.find(adj[n]); if(lake_name != lake_names.end()) { symbols["lake"] = lake_name->second; name_type = "lake"; break; } const std::map<map_location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]); if(mountain_name != mountain_names.end()) { symbols["mountain"] = mountain_name->second; name_type = "mountain"; break; } const std::map<map_location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]); if(swamp_name != swamp_names.end()) { symbols["swamp"] = swamp_name->second; name_type = "swamp"; break; } const t_translation::terrain_code terr = terrain[adj[n].x+data.width/3][adj[n].y+data.height/3]; if(std::count(field.begin(),field.end(),terr) > 0) { ++field_count; } else if(std::count(forest.begin(),forest.end(),terr) > 0) { ++forest_count; } else if(std::count(hill.begin(),hill.end(),terr) > 0) { ++hill_count; } else if(std::count(mountain.begin(),mountain.end(),terr) > 0) { ++mountain_count; } } if(n == 6) { if(field_count == 6) { name_type = "grassland"; } else if(forest_count >= 2) { name_type = "forest"; } else if(mountain_count >= 1) { name_type = "mountain_anon"; } else if(hill_count >= 2) { name_type = "hill"; } } std::string name; symbols["base"] = base_name_generator->generate(); std::shared_ptr<name_generator> village_name_generator = village_name_generator_factory.get_name_generator(name_type); for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) { name = village_name_generator->generate(symbols); } used_names.insert(name); village_labels->emplace(loc, name); } } } LOG_NG << "Placed villages. " << (SDL_GetTicks() - ticks) << " ticks elapsed" << "\n"; return output_map(terrain, starting_positions); }