TiborB has proposed merging lp:~widelands-dev/widelands/seafaring-ai into lp:widelands.
Requested reviews: Widelands Developers (widelands-dev) For more details, see: https://code.launchpad.net/~widelands-dev/widelands/seafaring-ai/+merge/242271 Hi, after some time this is ready for review, read the branch info for more info. Tested a lot as AI-only games, I would welcome a human players tests and feedback. -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/seafaring-ai into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h' --- src/ai/ai_help_structs.h 2014-10-30 20:24:57 +0000 +++ src/ai/ai_help_structs.h 2014-11-19 21:19:43 +0000 @@ -32,6 +32,8 @@ #include "logic/player.h" #include "logic/world/terrain_description.h" #include "logic/world/world.h" +#include "logic/ship.h" +#include <unordered_set> namespace Widelands { @@ -227,10 +229,10 @@ int16_t military_stationed_; // stationed (manned) military buildings nearby int16_t military_unstationed_; - // some buildings must be postponed bit - int32_t prohibited_till_; - // and then some must be forced - int32_t forced_after_; + bool is_portspace_; + // bool portspace_nearby_; // to prohibit buildings near a portspace Not needed? + bool port_nearby_; // to increase priority if a port is nearby, + // especially for new colonies std::vector<uint8_t> consumers_nearby_; std::vector<uint8_t> producers_nearby_; @@ -261,7 +263,9 @@ military_loneliness_(1000), military_in_constr_nearby_(0), military_presence_(0), - military_stationed_(0) { + military_stationed_(0), + is_portspace_(false), + port_nearby_(false) { } }; @@ -310,12 +314,14 @@ bool plants_trees_; bool recruitment_; // is "producing" workers? bool is_buildable_; - bool need_trees_; // lumberjack = true - bool need_stones_; // quarry = true - bool mines_water_; // wells - bool need_water_; // fisher, fish_breeder = true - bool is_hunter_; // need to identify hunters - bool is_fisher_; // need to identify fishers + bool need_trees_; // lumberjack = true + bool need_stones_; // quarry = true + bool mines_water_; // wells + bool need_water_; // fisher, fish_breeder = true + bool is_hunter_; // need to identify hunters + bool is_fisher_; // need to identify fishers + bool is_port_; + bool is_shipyard_; bool space_consumer_; // farm, vineyard... = true bool expansion_type_; // military building used that can be used to control area bool fighting_type_; // military building built near enemies @@ -327,7 +333,6 @@ int32_t mines_; // type of resource it mines_ uint16_t mines_percent_; // % of res it can mine - uint32_t current_stats_; std::vector<int16_t> inputs_; @@ -372,6 +377,25 @@ bool enemies_nearby_; }; +struct TrainingSiteObserver { + Widelands::TrainingSite* site; + BuildingObserver* bo; +}; + +struct WarehouseSiteObserver { + Widelands::Warehouse* site; + BuildingObserver* bo; +}; + +struct ShipObserver { + Widelands::Ship* ship; + Widelands::Coords expedition_start_point_; + std::unordered_set<uint32_t> visited_spots_; + bool island_circ_direction = true; // a ship circumvents all island in the same direction + bool waiting_for_command_ = false; + int32_t last_command_time = 0; +}; + struct WareObserver { uint8_t producers_; uint8_t consumers_; === modified file 'src/ai/defaultai.cc' --- src/ai/defaultai.cc 2014-11-03 06:45:32 +0000 +++ src/ai/defaultai.cc 2014-11-19 21:19:43 +0000 @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful,n * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. @@ -31,7 +31,9 @@ #include "base/macros.h" #include "economy/economy.h" #include "economy/flag.h" +#include "economy/portdock.h" #include "economy/road.h" +#include "economy/wares_queue.h" #include "logic/constructionsite.h" #include "logic/findbob.h" #include "logic/findimmovable.h" @@ -39,15 +41,19 @@ #include "logic/map.h" #include "logic/militarysite.h" #include "logic/player.h" +#include "logic/playercommand.h" #include "logic/productionsite.h" +#include "logic/ship.h" #include "logic/trainingsite.h" #include "logic/tribe.h" #include "logic/warehouse.h" #include "logic/world/world.h" #include "profile/profile.h" + // Building of new military buildings can be restricted constexpr int kPushExpansion = 1; + constexpr int kResourcesOrDefense = 2; constexpr int kDefenseOnly = 3; constexpr int kNoNewMilitary = 4; @@ -59,6 +65,10 @@ // building of the same building can be started after 25s at earliest constexpr int kBuildingMinInterval = 25 * 1000; constexpr int kMinBFCheckInterval = 5 * 1000; +constexpr int kShipCheckInterval = 5 * 1000; +constexpr int kMarineDecisionInterval = 20 * 1000; +constexpr int kTrainingSitesCheckInterval = 30 * 1000; + // Some buildings have to be built close to borders and their // priority might be decreased below 0, so this is to // compensate @@ -81,6 +91,7 @@ num_constructionsites_(0), num_milit_constructionsites(0), num_prod_constructionsites(0), + num_ports(0), next_road_due_(2000), next_stats_update_due_(30000), next_construction_due_(1000), @@ -88,9 +99,12 @@ next_productionsite_check_due_(0), next_mine_check_due_(0), next_militarysite_check_due_(0), + next_ship_check_due(5 * 60 * 1000), + next_marine_decisions_due(5 * 60 * 1000), next_attack_consideration_due_(300000), - next_helpersites_check_due_(180000), + next_trainingsites_check_due_(15 * 60 * 1000), next_bf_check_due_(1000), + next_wares_review_due_(5 * 60 * 1000), // review inhibit_road_building_(0), time_of_last_construction_(0), enemy_last_seen_(-2 * 60 * 1000), @@ -103,15 +117,17 @@ resource_necessity_water_needed_(false), unstationed_milit_buildings_(0), military_last_dismantle_(0), - military_last_build_(-60 * 1000), - last_attack_target_( + military_last_build_(-60 * 1000), + last_attack_target_( std::numeric_limits<uint16_t>::max(), std::numeric_limits<uint16_t>::max()), next_attack_waittime_(10), + seafaring_economy(false), + colony_scan_area_(35), spots_(0) { // Subscribe to NoteFieldPossession. field_possession_subscriber_ = - Notifications::subscribe<NoteFieldPossession>([this](const NoteFieldPossession& note) { + Notifications::subscribe<NoteFieldPossession>([this](const NoteFieldPossession& note) { if (note.player != player_) { return; } @@ -137,12 +153,47 @@ outofresource_subscriber_ = Notifications::subscribe<NoteProductionSiteOutOfResources>( [this](const NoteProductionSiteOutOfResources& note) { if (note.ps->owner().player_number() != player_->player_number()) { + // if (note.ps->owner().player_number() != pid) { return; } out_of_resources_site(*note.ps); }); + + // Subscribe to ShipNotes. + shipnotes_subscriber_ = + Notifications::subscribe<NoteShipMessage>([this](const NoteShipMessage& note) { + if (note.ship->get_owner()->player_number() != player_->player_number()) { + // if (note.ship->get_owner()->player_number() != pid) { + return; + } + + if (note.message == NoteShipMessage::Message::GAINED) { + marineTaskQueue_.push_back(STOPSHIPYARD); + + allships.push_back(ShipObserver()); + allships.back().ship = note.ship; + if (game().get_gametime() % 2 == 0) { + allships.back().island_circ_direction = true; + } else { + allships.back().island_circ_direction = false; + } + + } else if (note.message == NoteShipMessage::Message::LOST) { + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) + if (i->ship == note.ship) { + allships.erase(i); + break; + } + } else if (note.message == NoteShipMessage::Message::WAITINGFORCOMMAND) { + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) + if (i->ship == note.ship) { + i->waiting_for_command_ = true; + break; + } + } + }); } DefaultAI::~DefaultAI() { @@ -169,7 +220,7 @@ */ void DefaultAI::think() { - if (tribe_ == nullptr) { + if (tribe_ == nullptr || player_ == nullptr) { late_initialization(); } @@ -238,6 +289,14 @@ return; } + if (check_ships(gametime)) { + return; + } + + if (marine_main_decisions(gametime)) { + return; + } + // Check the mines and consider upgrading or destroying one if (check_mines_(gametime)) { return; @@ -249,6 +308,10 @@ return; } + if (check_trainingsites(gametime)) { + return; + } + // improve existing roads! // main part of this improvment is creation 'shortcut roads' // this includes also connection of new buildings @@ -257,6 +320,12 @@ m_mineable_changed = true; return; } + + // once in 15 minutes we increase(or decrease) targets for wares + if (next_wares_review_due_ <= gametime) { + next_wares_review_due_ = gametime + 15 * 60 * 1000; + review_wares_targets(gametime); + } } /** @@ -317,6 +386,7 @@ bo.mountain_conqueror_ = bh.is_mountain_conqueror(); bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds + bo.is_port_ = bld.get_isport(); if (char const* const s = bh.get_renews_map_resource()) { bo.production_hint_ = tribe_->safe_ware_index(s); } @@ -362,6 +432,13 @@ } else { bo.is_fisher_ = false; } + + if (building_name == "shipyard") { + bo.is_shipyard_ = true; + } else { + bo.is_shipyard_ = false; + } + continue; } @@ -394,6 +471,11 @@ if (typeid(bld) == typeid(TrainingSiteDescr)) { bo.type = BuildingObserver::TRAININGSITE; + const TrainingSiteDescr& train = + ref_cast<TrainingSiteDescr const, BuildingDescr const>(bld); + for (const WareAmount& temp_input : train.inputs()) { + bo.inputs_.push_back(temp_input.first); + } continue; } @@ -593,6 +675,40 @@ } } + if (!field.is_portspace_) { // if we know it no need to do it once more + if (player_->get_buildcaps(field.coords) & BUILDCAPS_PORT) { + field.is_portspace_ = true; + seafaring_economy = true; + // blocking fields in vicinity + MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(field.coords), 3)); + do { + // const Coords coords = map.get_fcoords(*(mr.location().field)); + const int32_t hash = coords_hash(map.get_fcoords(*(mr.location().field))); + if (port_reserved_coords.count(hash) == 0) + port_reserved_coords.insert(hash); + } while (mr.advance(map)); + } else { + field.is_portspace_ = false; + } + } + + // testing if a port is nearby, such field will get a priority boost + uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); + for (std::list<WarehouseSiteObserver>::iterator wh_iter = warehousesites.begin(); + wh_iter != warehousesites.end(); + ++wh_iter) { + const uint16_t actual_distance = + map.calc_distance(field.coords, wh_iter->site->get_position()); + if (nearest_distance > actual_distance) { + nearest_distance = actual_distance; + } + } + if (nearest_distance < 15) { + field.port_nearby_ = true; + } else { + field.port_nearby_ = false; + } + // collect information about resources in the area std::vector<ImmovableFound> immovables; // Search in a radius of range @@ -901,6 +1017,7 @@ // (there should be no upgrade when there are not two buildings of the same type) // - algorigthm is trying to take into account actual utlization of buildings // (the one shown in GUI/game is not reliable, it calculates own statistics) +// * military buildings have own strategy, split into two situations:spot // * military buildings have own strategy, split into two situations: // - there is no enemy // - there is an enemy @@ -913,7 +1030,7 @@ uint32_t consumers_nearby_count = 0; std::vector<int32_t> spots_avail; spots_avail.resize(4); - // uint16_t const pn = player_number(); + Map& map = game().map(); for (int32_t i = 0; i < 4; ++i) spots_avail.at(i) = 0; @@ -1019,7 +1136,8 @@ } // testing big military buildings, whether critical construction - // material is (not) needed + // material is available (at lest in amount of + // 2/3 of default target amount) for (uint32_t j = 0; j < buildings_.size(); ++j) { BuildingObserver& bo = buildings_.at(j); @@ -1037,19 +1155,13 @@ bo.build_material_shortage_ = false; - for (EconomyObserver* observer : economies) { - // Don't check if the economy has no warehouse. - if (observer->economy.warehouses().empty()) { - continue; - } - - for (uint32_t m = 0; m < bo.critical_built_mat_.size(); ++m) { - WareIndex wt(static_cast<size_t>(bo.critical_built_mat_.at(m))); - - if (observer->economy.needs_ware(wt)) { - bo.build_material_shortage_ = true; - continue; - } + // checking we have enough critical material on stock + for (uint32_t m = 0; m < bo.critical_built_mat_.size(); ++m) { + WareIndex wt(static_cast<size_t>(bo.critical_built_mat_.at(m))); + // using default ware quantity, not the best solution but will do + if (get_warehoused_stock(wt) < + tribe_->get_ware_descr(wt)->default_target_quantity() * 2 / 3) { + bo.build_material_shortage_ = true; } } } @@ -1065,10 +1177,6 @@ ++i) { BuildableField* const bf = *i; - // if 'buildable field' update is overdue for more then 8 seconds - // (= bf has not been updated for about 15 seconds) - // skip the bf in evaluation, bacause information - // contained in bf are too old if (bf->next_update_due_ < gametime - 8000) { continue; } @@ -1108,6 +1216,13 @@ continue; } + // testing for reserved ports + if (!bo.is_port_) { + if (port_reserved_coords.count(coords_hash(bf->coords)) > 0) { + continue; + } + } + if (time(nullptr) % 3 == 0 && bo.total_count() > 0) { continue; } // add randomnes and ease AI @@ -1405,6 +1520,10 @@ } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 && bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) { prio += 10; + } else if (bo.is_shipyard_ && seafaring_economy) { + ; + } else if (bo.is_shipyard_ && !seafaring_economy) { + continue; } else if (!output_is_needed) { continue; } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) { @@ -1444,7 +1563,13 @@ prio -= bf->space_consumers_nearby_ * 3; } - if (!bo.inputs_.empty()) { + else if (bo.is_shipyard_) { + // for now AI builds only one shipyard + if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) { + prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_; + } + + } else if (!bo.inputs_.empty()) { if (bo.total_count() == 0) { prio += max_needed_preciousness + kDefaultPrioBoost; } @@ -1560,9 +1685,19 @@ } else if (bo.type == BuildingObserver::WAREHOUSE) { // exclude spots on border - if (bf->near_border_) { - continue; - } + if (bf->near_border_ && !bo.is_port_) { + continue; + } + + if (!bf->is_portspace_ && bo.is_port_) { + continue; + } + + if (bo.cnt_under_construction_ > 0) { + continue; + } + + bool warehouse_needed = false; // Build one warehouse for ~every 35 productionsites and mines_. // Militarysites are slightly important as well, to have a bigger @@ -1572,22 +1707,53 @@ static_cast<int32_t>(numof_warehouses_) && bo.cnt_under_construction_ == 0) { prio = 20; - } - - // take care about borders and enemies + warehouse_needed = true; + } + + // but generally we prefer ports + if (bo.is_port_) { + prio += 10; + } + + // special boost for first port + if (bo.is_port_ && bo.total_count() == 0 && productionsites.size() > 5 && + !bf->enemy_nearby_ && bf->is_portspace_ && seafaring_economy) { + prio += kDefaultPrioBoost + productionsites.size(); + warehouse_needed = true; + } + + if (!warehouse_needed) { + continue; + } + + // iterating over current warehouses and testing a distance + // getting distance to nearest warehouse and adding it to a score + uint16_t nearest_distance = std::numeric_limits<uint16_t>::max(); + for (std::list<WarehouseSiteObserver>::iterator wh_iter = warehousesites.begin(); + wh_iter != warehousesites.end(); + ++wh_iter) { + const uint16_t actual_distance = + map.calc_distance(bf->coords, wh_iter->site->get_position()); + if (nearest_distance > actual_distance) { + nearest_distance = actual_distance; + } + } + + // take care about and enemies if (bf->enemy_nearby_) { prio /= 2; } - if (bf->unowned_land_nearby_) { + if (bf->unowned_land_nearby_ && !bo.is_port_) { prio /= 2; } - // TODO(unknown): introduce check that there is no warehouse nearby - // to prevent too close placing - } else if (bo.type == BuildingObserver::TRAININGSITE) { + if (virtual_mines < 5) { + continue; + } + // exclude spots on border if (bf->near_border_) { continue; @@ -1598,7 +1764,7 @@ } // build after 20 production sites and then after each 50 production site - if (static_cast<int32_t>((productionsites.size() + 30) / 50) > bo.total_count() && + if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() && bo.cnt_under_construction_ == 0) { prio = 4 + kDefaultPrioBoost; } @@ -1621,11 +1787,24 @@ continue; } + // testing also vicinity + if (!bo.is_port_) { + const int32_t hash = bf->coords.x << 16 | bf->coords.y; + if (port_reserved_coords.count(hash) > 0) { + continue; + } + } + // Prefer road side fields prio += bf->preferred_ ? 1 : 0; // don't waste good land for small huts prio -= (maxsize - bo.desc->get_size()) * 5; + // prefer vicinity of ports (with exemption of warehouses) + if (bf->port_nearby_ && bo.type == BuildingObserver::MILITARYSITE) { + prio *= 2; + } + if (prio > proposed_priority) { best_building = &bo; proposed_priority = prio; @@ -1762,7 +1941,7 @@ block_time = 25 * 1000; block_area = 6; } - Map& map = game().map(); + // Map& map = game().map(); MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(proposed_coords), block_area)); do { @@ -1968,7 +2147,39 @@ // Increasing the failed_connection_tries counter // At the same time it indicates a time an economy is without a warehouse EconomyObserver* eco = get_economy_observer(flag.economy()); + + // there are two special situations which have a bit different treatment + bool is_remote_port_csite = false; + bool stationed_military = false; if (flag.get_economy()->warehouses().empty()) { + // first very special case - lonesome port (in the phase of constructionsite) + // obviously it has no warehouse/road network to connect to + if (upcast(ConstructionSite const, constructionsite, flag.get_building())) { + BuildingObserver& bo = get_building_observer( + constructionsite->building().name().c_str()); // constructionsite->building(); + if (bo.is_port_ && + remote_ports_coords.count(coords_hash(flag.get_building()->get_position())) > 0) { + is_remote_port_csite = true; + } + } + + // second exemption is when a military buiding was conquered, it + // might be just too far from near connected builging + if (Building* b = flag.get_building()) { + if (upcast(MilitarySite, militb, b)) { + if (militb->present_soldiers().size() > 0) { + stationed_military = true; + // also increasing checkradius a bit + checkradius += 4; + } + } + } + } + + if (is_remote_port_csite || + (stationed_military && game().get_gametime() % 10 > 0)) { // counter disabled + ; + } else if (flag.get_economy()->warehouses().empty()) { eco->failed_connection_tries += 1; } else { eco->failed_connection_tries = 0; @@ -2014,7 +2225,7 @@ for (const Coords& reachable_coords : reachable) { - // first make sure there is an immovable (shold be, but still) + // first make sure there is an immovable (should be, but still) if (upcast(PlayerImmovable const, player_immovable, map[reachable_coords].get_immovable())) { // if it is the road, make a flag there @@ -2536,6 +2747,233 @@ return changed; } +// This function scans current situation with shipyards, ports, ships, ongoing expeditions +// and makes two decisions: +// - build a ship +// - start preparation for expedition +bool DefaultAI::marine_main_decisions(int32_t const gametime) { + if (gametime < next_marine_decisions_due) { + return false; + } + next_marine_decisions_due += kMarineDecisionInterval; + + if (!seafaring_economy) { + return false; + } + + // getting some base statistics + player_ = game().get_player(player_number()); + uint16_t ports_count = 0; + uint16_t shipyards_count = 0; + uint16_t working_shipyards_count = 0; + uint16_t expeditions_in_prep = 0; + uint16_t expeditions_in_progress = 0; + uint16_t terittories_count = 1; + bool idle_shipyard_stocked = false; + + // goes over all wareouses (these includes ports) + for (std::list<WarehouseSiteObserver>::iterator wh_iter = warehousesites.begin(); + wh_iter != warehousesites.end(); + ++wh_iter) { + + if (wh_iter->bo->is_port_) { + ports_count += 1; + if (Widelands::PortDock* pd = wh_iter->site->get_portdock()) { + if (pd->expedition_started()) { + expeditions_in_prep += 1; + } + } else { + log(" there is a port without portdock at %3dx%3d?\n", + wh_iter->site->get_position().x, + wh_iter->site->get_position().y); + } + } + } + + // goes over productionsites and gets status of shipyards + for (std::list<ProductionSiteObserver>::iterator ps_iter = productionsites.begin(); + ps_iter != productionsites.end(); + ++ps_iter) { + + if (ps_iter->bo->is_shipyard_) { + shipyards_count += 1; + if (!ps_iter->site->is_stopped()) { + working_shipyards_count += 1; + } + // counting stocks + uint8_t stocked_wares = 0; + std::vector<WaresQueue*> const warequeues = ps_iter->site->warequeues(); + size_t const nr_warequeues = warequeues.size(); + for (size_t i = 0; i < nr_warequeues; ++i) { + stocked_wares += warequeues[i]->get_filled(); + } + if (stocked_wares == 16 && ps_iter->site->is_stopped()) { + idle_shipyard_stocked = true; + } + } + } + + // and now over ships + for (std::list<ShipObserver>::iterator sp_iter = allships.begin(); sp_iter != allships.end(); + ++sp_iter) { + if (sp_iter->ship->state_is_expedition()) { + expeditions_in_progress += 1; + } + } + + // we must verify that all remote ports are still ours (and exists at all) + bool still_ours; + for (std::unordered_set<uint32_t>::iterator ports_iter = remote_ports_coords.begin(); + ports_iter != remote_ports_coords.end(); + ++ports_iter) { + still_ours = false; + FCoords fcoords = game().map().get_fcoords(coords_unhash(*ports_iter)); + if (fcoords.field->get_owned_by() == player_number()) { + if (upcast(PlayerImmovable, imm, fcoords.field->get_immovable())) { + still_ours = true; + } + } + + if (!still_ours) { + remote_ports_coords.erase(*ports_iter); + break; + } + } + terittories_count += remote_ports_coords.size(); + + enum {NEEDSHIP = 0, ENOUGHSHIPS = 1, DONOTHING = 2 }; + + // now we must compare ports vs ships to decide if new ship is needed or new expedition can start + uint8_t enough_ships = DONOTHING; + if (static_cast<float>(allships.size()) > + static_cast<float>((terittories_count - 1) * 0.6 + ports_count * 0.75)) { + enough_ships = ENOUGHSHIPS; + } else if (static_cast<float>(allships.size()) < + static_cast<float>((terittories_count - 1) * 0.6 + ports_count * 0.75)) { + enough_ships = NEEDSHIP; + } + + // building a ship? if yes, find a shipyard and order it to build a ship + if (shipyards_count > 0 && enough_ships == NEEDSHIP && idle_shipyard_stocked && + ports_count > 0) { + + for (std::list<ProductionSiteObserver>::iterator ps_iter = productionsites.begin(); + ps_iter != productionsites.end(); + ++ps_iter) { + + if (ps_iter->bo->is_shipyard_ && ps_iter->site->can_start_working() && + ps_iter->site->is_stopped()) { + // make sure it is fully stocked + // counting stocks + uint8_t stocked_wares = 0; + std::vector<WaresQueue*> const warequeues = ps_iter->site->warequeues(); + size_t const nr_warequeues = warequeues.size(); + for (size_t i = 0; i < nr_warequeues; ++i) { + stocked_wares += warequeues[i]->get_filled(); + } + if (stocked_wares < 16) { + continue; + } + + game().send_player_start_stop_building(*ps_iter->site); + return true; + } + } + } + + // starting an expedition? if yes, find a port and order it to start an expedition + if (ports_count > 0 && enough_ships == ENOUGHSHIPS && expeditions_in_prep == 0 && + expeditions_in_progress == 0) { + // we need to find a port + for (std::list<WarehouseSiteObserver>::iterator wh_iter = warehousesites.begin(); + wh_iter != warehousesites.end(); + ++wh_iter) { + + if (wh_iter->bo->is_port_) { + game().send_player_start_or_cancel_expedition(*wh_iter->site); + return true; + } + } + } + return true; +} + +// This identifies ships that are waiting for command +bool DefaultAI::check_ships(int32_t const gametime) { + if (gametime < next_ship_check_due) { + return false; + } + + next_ship_check_due += kShipCheckInterval; + + if (!seafaring_economy) { + return false; + } + + if (!allships.empty()) { + // iterating over ships and executing what is needed + for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) { + + // only two states need an attention + if ((i->ship->get_ship_state() == Widelands::Ship::EXP_WAITING || + i->ship->get_ship_state() == Widelands::Ship::EXP_FOUNDPORTSPACE) && + !i->waiting_for_command_) { + if (gametime - i->last_command_time > 180 * 1000) { + i->waiting_for_command_ = true; + log(" %1d: last command for ship at %3dx%3d was %3d seconds ago, something wrong " + "here?...\n", + player_number(), + i->ship->get_position().x, + i->ship->get_position().y, + (gametime - i->last_command_time) / 1000); + } + } + // only two states need an attention + if (!(i->ship->get_ship_state() == Widelands::Ship::EXP_WAITING || + i->ship->get_ship_state() == Widelands::Ship::EXP_FOUNDPORTSPACE) && + i->waiting_for_command_) { + } + // if ships is waiting for command + if (i->waiting_for_command_) { + expedition_management(*i); + } + } + } + + // processing marineTaskQueue_ + while (!marineTaskQueue_.empty()) { + if (marineTaskQueue_.back() == STOPSHIPYARD) { + // iterate over all production sites searching for shipyard + for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin(); + site != productionsites.end(); + ++site) { + if (site->bo->is_shipyard_) { + if (!site->site->is_stopped()) { + game().send_player_start_stop_building(*site->site); + } + } + } + } + + if (marineTaskQueue_.back() == REPRIORITIZE) { + for (std::list<ProductionSiteObserver>::iterator site = productionsites.begin(); + site != productionsites.end(); + ++site) { + if (site->bo->is_shipyard_) { + for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) { + game().send_player_set_ware_priority( + *site->site, wwWARE, site->bo->inputs_.at(k), HIGH_PRIORITY); + } + } + } + } + + marineTaskQueue_.pop_back(); + } + + return true; +} + /** * checks the first mine in list, takes care if it runs out of * resources and finally reenqueues it at the end of the list. @@ -2709,6 +3147,51 @@ return count; } +// counts produced output in warehouses (only) +// perhaps it wil might replace get_stocklevel +// if multiple outputs, it returns lowest value +uint32_t DefaultAI::get_warehoused_stock(WareIndex wt) { + uint32_t count = 0; + + for (std::list<WarehouseSiteObserver>::iterator i = warehousesites.begin(); + i != warehousesites.end(); + ++i) { + count += i->site->get_wares().stock(wt); + } + + return count; +} + +// this function only manipulates with trainingsites' inputs priority +// decreases it when too many unoccupied military buildings +bool DefaultAI::check_trainingsites(int32_t gametime) { + if (next_trainingsites_check_due_ > gametime) { + return false; + } + if (!trainingsites.empty()) { + next_trainingsites_check_due_ = gametime + kTrainingSitesCheckInterval; + } else { + next_trainingsites_check_due_ = gametime + 3 * kTrainingSitesCheckInterval; + } + + uint8_t new_priority = DEFAULT_PRIORITY; + if (unstationed_milit_buildings_ > 2) { + new_priority = LOW_PRIORITY; + } else { + new_priority = DEFAULT_PRIORITY; + } + for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin(); + site != trainingsites.end(); + ++site) { + + for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) { + game().send_player_set_ware_priority( + *site->site, wwWARE, site->bo->inputs_.at(k), new_priority); + } + } + return true; +} + /** * Updates the first military building in list and reenques it at the end of * the list afterwards. If a militarysite is in secure area but holds more than @@ -3002,6 +3485,203 @@ } } +// this scores spot for potential colony +uint8_t DefaultAI::spot_scoring(Widelands::Coords candidate_spot) { + + uint8_t score = 0; + uint16_t mineable_fields_count = 0; + Map& map = game().map(); + // first making sure there are no other players nearby + std::list<uint32_t> queue; + std::unordered_set<uint32_t> done; + queue.push_front(coords_hash(candidate_spot)); + while (!queue.empty()) { + + // if already processed + if (done.count(queue.front()) > 0) { + queue.pop_front(); + continue; + } + + done.insert(queue.front()); + + Coords tmp_coords = coords_unhash(queue.front()); + + // if beyond range + if (map.calc_distance(candidate_spot, tmp_coords) > colony_scan_area_) { + continue; + } + + Field* f = map.get_fcoords(tmp_coords).field; + + // if owned by someone: + if (f->get_owned_by() > 0) { + // just return 0 as score + return 0; + } + + // not interested if not walkable + if (!(f->nodecaps() & MOVECAPS_WALK)) { + continue; + } + + // increase mines counter + if (f->nodecaps() & BUILDCAPS_MINE) { + mineable_fields_count += 1; + }; + + // add neighbours to a queue (duplicates are no problem) + // to relieve AI/CPU we skip every second field in each direction + // obstacles are usually wider then one field + for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { + Coords neigh_coords1; + map.get_neighbour(tmp_coords, dir, &neigh_coords1); + Coords neigh_coords2; + map.get_neighbour(neigh_coords1, dir, &neigh_coords2); + queue.push_front(coords_hash(neigh_coords2)); + } + } + + // if the island is too small + if (done.size() < 50) { + return 0; + } + + // if we are here we put score + score = 1; + if (mineable_fields_count > 0) { + score += 1; + } + + // here we check for surface stones + trees + std::vector<ImmovableFound> immovables; + // Search in a radius of range + map.find_immovables(Area<FCoords>(map.get_fcoords(candidate_spot), 10), &immovables); + + int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite"); + uint16_t stones = 0; + int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree"); + uint16_t trees = 0; + + for (uint32_t j = 0; j < immovables.size(); ++j) { + if (immovables.at(j).object->has_attribute(stone_attr)) { + ++stones; + } + if (immovables.at(j).object->has_attribute(tree_attr)) { + ++trees; + } + } + if (stones > 1) { + score += 1; + } + if (trees > 1) { + score += 1; + } + + return score; +} + +// this is called whenever ship received a notification that requires +// navigation decisions (these notifiation are processes not in 'real time') +void DefaultAI::expedition_management(ShipObserver& so) { + + Map& map = game().map(); + const int32_t gametime = game().get_gametime(); + + // first we put current spot into visited_spots_ + bool first_time_here = false; + if (so.visited_spots_.count(coords_hash(so.ship->get_position())) == 0) { + first_time_here = true; + so.visited_spots_.insert(coords_hash(so.ship->get_position())); + } + + // If we have portspace following options are avaiable: + // 1. Build a port there + if (so.ship->exp_port_spaces()->size() > 0) { // making sure we have possible portspaces + + // we score the place + const uint8_t spot_score = spot_scoring(so.ship->exp_port_spaces()->front()); + + if ((gametime / 10) % 8 < spot_score) { // we build a port here + const Coords last_portspace = so.ship->exp_port_spaces()->front(); + remote_ports_coords.insert(coords_hash(last_portspace)); + game().send_player_ship_construct_port(*so.ship, so.ship->exp_port_spaces()->front()); + so.last_command_time = gametime; + so.waiting_for_command_ = false; + // blocking the area for some time to save AI from idle attempts to built there + // buildings + // TODO(anybody): how long it takes to build a port? + // I used 5 minutes + MapRegion<Area<FCoords>> mr( + game().map(), Area<FCoords>(map.get_fcoords(so.ship->exp_port_spaces()->front()), 8)); + do { + BlockedField blocked2( + map.get_fcoords(*(mr.location().field)), gametime + 5 * 60 * 1000); + blocked_fields.push_back(blocked2); + } while (mr.advance(map)); + + return; + } + + // decreasing colony_scan_area_ + if (colony_scan_area_ > 15 && gametime % 10 == 0) { + colony_scan_area_ -= 1; + } + } + + // if we are here, port was not ordered above + // 2. Go on with expedition + + if (first_time_here) { + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); + so.last_command_time = gametime; + so.waiting_for_command_ = false; + + // we was here but to add randomnes we might continue with expedition + } else if (gametime % 2 == 0) { + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); + so.last_command_time = gametime; + so.waiting_for_command_ = false; + } else { + // get swimable directions + std::vector<Direction> possible_directions; + for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { + + // testing distance of 8 fields + // this would say there is an 'open sea' there + Widelands::FCoords tmp_fcoords = map.get_fcoords(so.ship->get_position()); + for (int8_t i = 0; i < 8; ++i) { + tmp_fcoords = map.get_neighbour(tmp_fcoords, dir); + if (tmp_fcoords.field->nodecaps() & MOVECAPS_SWIM) { + if (i == 7) { + possible_directions.push_back(dir); + break; // not needed but..... + } + } else { + break; + } + } + } + + // we test if there is open sea + if (possible_directions.size() == 0) { + // 2.A No there is no open sea + game().send_player_ship_explore_island(*so.ship, so.island_circ_direction); + so.last_command_time = gametime; + so.waiting_for_command_ = false; + ; + } else { + // 2.B Yes, pick one of avaiable directions + const Direction final_direction = + possible_directions.at(gametime % possible_directions.size()); + game().send_player_ship_scout_direction(*so.ship, final_direction); + so.last_command_time = gametime; + so.waiting_for_command_ = false; + } + } + return; +} + // this is called whenever we gain a new building void DefaultAI::gain_building(Building& b) { BuildingObserver& bo = get_building_observer(b.descr().name().c_str()); @@ -3031,6 +3711,10 @@ productionsites.back().unoccupied_till_ = game().get_gametime(); productionsites.back().stats_zero_ = 0; productionsites.back().no_resources_count = 0; + if (bo.is_shipyard_) { + marineTaskQueue_.push_back(STOPSHIPYARD); + marineTaskQueue_.push_back(REPRIORITIZE); + } for (uint32_t i = 0; i < bo.outputs_.size(); ++i) ++wares.at(bo.outputs_.at(i)).producers_; @@ -3055,8 +3739,19 @@ militarysites.back().checks = bo.desc->get_size(); militarysites.back().enemies_nearby_ = true; + } else if (bo.type == BuildingObserver::TRAININGSITE) { + trainingsites.push_back(TrainingSiteObserver()); + trainingsites.back().site = &ref_cast<TrainingSite, Building>(b); + trainingsites.back().bo = &bo; + } else if (bo.type == BuildingObserver::WAREHOUSE) { ++numof_warehouses_; + warehousesites.push_back(WarehouseSiteObserver()); + warehousesites.back().site = &ref_cast<Warehouse, Building>(b); + warehousesites.back().bo = &bo; + if (bo.is_port_) { + ++num_ports; + } } } } @@ -3123,9 +3818,31 @@ break; } } + } else if (bo.type == BuildingObserver::TRAININGSITE) { + + for (std::list<TrainingSiteObserver>::iterator i = trainingsites.begin(); + i != trainingsites.end(); + ++i) { + if (i->site == &b) { + trainingsites.erase(i); + break; + } + } } else if (bo.type == BuildingObserver::WAREHOUSE) { assert(numof_warehouses_ > 0); --numof_warehouses_; + if (bo.is_port_) { + --num_ports; + } + + for (std::list<WarehouseSiteObserver>::iterator i = warehousesites.begin(); + i != warehousesites.end(); + ++i) { + if (i->site == &b) { + warehousesites.erase(i); + break; + } + } } } @@ -3411,6 +4128,35 @@ } } +// This runs once in 15 minutes, and adjust wares targets based on number of +// productionsites and ports +void DefaultAI::review_wares_targets(int32_t const gametime) { + + player_ = game().get_player(player_number()); + tribe_ = &player_->tribe(); + + // to avoid floats real multiplicator is multiplicator/10 + uint16_t multiplicator = 10; + if ((productionsites.size() + num_ports * 5) > 50) { + multiplicator = (productionsites.size() + num_ports * 5) / 5; + } + + for (EconomyObserver* observer : economies) { + WareIndex nritems = observer->economy.owner().tribe().get_nrwares(); + for (Widelands::WareIndex id = 0; id < nritems; ++id) { + // const Economy::TargetQuantity & tq = observer->economy.ware_target_quantity(id); + const uint16_t default_target = tribe_->get_ware_descr(id)->default_target_quantity(); + + game().send_player_command(*new Widelands::CmdSetWareTargetQuantity( + gametime, + player_number(), + player_->get_economy_number(&observer->economy), + id, + default_target * multiplicator / 10)); + } + } +} + // This is used for profiling, so usually this is not used :) void DefaultAI::print_land_stats() { // this will just print statistics of land size === modified file 'src/ai/defaultai.h' --- src/ai/defaultai.h 2014-10-17 19:13:39 +0000 +++ src/ai/defaultai.h 2014-11-19 21:19:43 +0000 @@ -27,6 +27,8 @@ #include "ai/computer_player.h" #include "base/i18n.h" #include "logic/immovable.h" +#include "logic/ship.h" +#include <unordered_set> namespace Widelands { struct Road; @@ -130,6 +132,18 @@ bool construct_building(int32_t); + uint32_t coords_hash(Widelands::Coords coords) { + uint32_t hash = coords.x << 16 | coords.y; + return hash; + } + + Widelands::Coords coords_unhash(uint32_t hash) { + Widelands::Coords coords; + coords.x = hash >> 16; // is cast needed here??? + coords.y = hash; + return coords; + } + // all road management is invoked by function improve_roads() // if needed it calls create_shortcut_road() with a flag from which // new road should be considered (or is needed) @@ -139,18 +153,25 @@ bool dispensable_road_test(const Widelands::Road&); bool check_economies(); bool check_productionsites(int32_t); + bool check_trainingsites(int32_t); bool check_mines_(int32_t); bool check_militarysites(int32_t); + bool marine_main_decisions(int32_t); + bool check_ships(int32_t); uint32_t get_stocklevel_by_hint(size_t); uint32_t get_stocklevel(BuildingObserver&); + uint32_t get_warehoused_stock(Widelands::WareIndex wt); uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_ void check_helpersites(int32_t); + void review_wares_targets(int32_t); int32_t recalc_with_border_range(const BuildableField&, int32_t); int32_t calculate_need_for_ps(BuildingObserver&, int32_t); void consider_productionsite_influence(BuildableField&, Widelands::Coords, const BuildingObserver&); + // considering wood, stones, mines, water, fishes for candidate for colonization (new port) + uint8_t spot_scoring(Widelands::Coords candidate_spot); EconomyObserver* get_economy_observer(Widelands::Economy&); BuildingObserver& get_building_observer(char const*); @@ -159,6 +180,8 @@ void lose_immovable(const Widelands::PlayerImmovable&); void gain_building(Widelands::Building&); void lose_building(const Widelands::Building&); + void expedition_management(ShipObserver&); + // bool pick_farest_portspace(Widelands::Ship&); void out_of_resources_site(const Widelands::ProductionSite&); bool check_supply(const BuildingObserver&); @@ -181,10 +204,14 @@ uint32_t num_constructionsites_; uint32_t num_milit_constructionsites; uint32_t num_prod_constructionsites; + uint32_t num_ports; std::list<Widelands::FCoords> unusable_fields; std::list<BuildableField*> buildable_fields; std::list<BlockedField> blocked_fields; + std::unordered_set<uint32_t> port_reserved_coords; + // to distinquish which ports are on home teritory and which one are remote + std::unordered_set<uint32_t> remote_ports_coords; std::list<MineableField*> mineable_fields; std::list<Widelands::Flag const*> new_flags; std::list<Widelands::Coords> flags_to_be_removed; @@ -193,6 +220,9 @@ std::list<ProductionSiteObserver> productionsites; std::list<ProductionSiteObserver> mines_; std::list<MilitarySiteObserver> militarysites; + std::list<WarehouseSiteObserver> warehousesites; + std::list<TrainingSiteObserver> trainingsites; + std::list<ShipObserver> allships; std::vector<WareObserver> wares; @@ -203,9 +233,12 @@ int32_t next_productionsite_check_due_; int32_t next_mine_check_due_; int32_t next_militarysite_check_due_; + int32_t next_ship_check_due; + int32_t next_marine_decisions_due; int32_t next_attack_consideration_due_; - int32_t next_helpersites_check_due_; + int32_t next_trainingsites_check_due_; int32_t next_bf_check_due_; + int32_t next_wares_review_due_; int32_t inhibit_road_building_; int32_t time_of_last_construction_; int32_t enemy_last_seen_; @@ -232,13 +265,20 @@ Widelands::Coords last_attack_target_; // flag to abuilding (position) that was attacked last time int32_t next_attack_waittime_; // second till the next attack consideration - int32_t spots_; // sum of buildable fields + bool seafaring_economy; // false by default, until first port space is found + uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory + // it decreases with failed scans + int32_t spots_; // sum of buildable fields + + enum {REPRIORITIZE, STOPSHIPYARD, STARTSHIPYARD}; + std::vector<int16_t> marineTaskQueue_; std::unique_ptr<Notifications::Subscriber<Widelands::NoteFieldPossession>> field_possession_subscriber_; std::unique_ptr<Notifications::Subscriber<Widelands::NoteImmovable>> immovable_subscriber_; std::unique_ptr<Notifications::Subscriber<Widelands::NoteProductionSiteOutOfResources>> outofresource_subscriber_; + std::unique_ptr<Notifications::Subscriber<Widelands::NoteShipMessage>> shipnotes_subscriber_; }; #endif // end of include guard: WL_AI_DEFAULTAI_H === modified file 'src/economy/portdock.cc' --- src/economy/portdock.cc 2014-09-20 09:37:47 +0000 +++ src/economy/portdock.cc 2014-11-19 21:19:43 +0000 @@ -21,6 +21,8 @@ #include <memory> +#include <boost/format.hpp> + #include "base/deprecated.h" #include "base/log.h" #include "economy/fleet.h" @@ -46,20 +48,16 @@ return g_portdock_descr; } - PortdockDescr::PortdockDescr(char const* const _name, char const* const _descname) - : - MapObjectDescr(MapObjectType::PORTDOCK, _name, _descname) -{ + : MapObjectDescr(MapObjectType::PORTDOCK, _name, _descname) { } -PortDock::PortDock(Warehouse* wh) : - PlayerImmovable(g_portdock_descr), - m_fleet(nullptr), - m_warehouse(wh), - m_need_ship(false), - m_expedition_ready(false) -{ +PortDock::PortDock(Warehouse* wh) + : PlayerImmovable(g_portdock_descr), + m_fleet(nullptr), + m_warehouse(wh), + m_need_ship(false), + m_expedition_ready(false) { } PortDock::~PortDock() { @@ -76,8 +74,7 @@ * * @note This only works properly when called before @ref init */ -void PortDock::add_position(Coords where) -{ +void PortDock::add_position(Coords where) { m_dockpoints.push_back(where); } @@ -90,29 +87,23 @@ * * @warning This should only be called via @ref Fleet itself. */ -void PortDock::set_fleet(Fleet * fleet) -{ +void PortDock::set_fleet(Fleet* fleet) { m_fleet = fleet; } -int32_t PortDock::get_size() const -{ +int32_t PortDock::get_size() const { return SMALL; } -bool PortDock::get_passable() const -{ +bool PortDock::get_passable() const { return true; } -PortDock::PositionList PortDock::get_positions - (const EditorGameBase &) const -{ +PortDock::PositionList PortDock::get_positions(const EditorGameBase&) const { return m_dockpoints; } -Flag & PortDock::base_flag() -{ +Flag& PortDock::base_flag() { return m_warehouse->base_flag(); } @@ -120,8 +111,7 @@ * Return the dock that has the given flag as its base, or 0 if no dock of our fleet * has the given flag. */ -PortDock * PortDock::get_dock(Flag & flag) const -{ +PortDock* PortDock::get_dock(Flag& flag) const { if (m_fleet) return m_fleet->get_dock(flag); return nullptr; @@ -133,8 +123,7 @@ * Called by @ref Warehouse::set_economy, and responsible for forwarding the * change to @ref Fleet. */ -void PortDock::set_economy(Economy * e) -{ +void PortDock::set_economy(Economy* e) { if (e == get_economy()) return; @@ -152,18 +141,14 @@ m_expedition_bootstrap->set_economy(e); } - -void PortDock::draw - (const EditorGameBase &, RenderTarget &, const FCoords&, const Point&) -{ +void PortDock::draw(const EditorGameBase&, RenderTarget&, const FCoords&, const Point&) { // do nothing } -void PortDock::init(EditorGameBase & egbase) -{ +void PortDock::init(EditorGameBase& egbase) { PlayerImmovable::init(egbase); - for (const Coords& coords: m_dockpoints) { + for (const Coords& coords : m_dockpoints) { set_position(egbase, coords); } @@ -174,18 +159,21 @@ * Create our initial singleton @ref Fleet. The fleet code ensures * that we merge with a larger fleet when possible. */ -void PortDock::init_fleet(EditorGameBase & egbase) -{ - Fleet * fleet = new Fleet(owner()); +void PortDock::init_fleet(EditorGameBase& egbase) { + Fleet* fleet = new Fleet(owner()); fleet->add_port(egbase, this); fleet->init(egbase); // Note: the Fleet calls our set_fleet automatically } -void PortDock::cleanup(EditorGameBase & egbase) -{ +void PortDock::cleanup(EditorGameBase& egbase) { + + Warehouse* wh = nullptr; + if (egbase.objects().object_still_available(m_warehouse)) { - // Transfer all our wares into the warehouse. + + wh = m_warehouse; + if (upcast(Game, game, &egbase)) { for (ShippingItem& shipping_item : m_waiting) { WareInstance* ware; @@ -199,6 +187,7 @@ } } } + m_waiting.clear(); m_warehouse->m_portdock = nullptr; } @@ -212,7 +201,7 @@ if (m_fleet) m_fleet->remove_port(egbase, this); - for (const Coords& coords: m_dockpoints) { + for (const Coords& coords : m_dockpoints) { unset_position(egbase, coords); } @@ -222,13 +211,26 @@ } PlayerImmovable::cleanup(egbase); + + if (wh) { + if (upcast(Game, game, &egbase)) { + if (game->is_loaded()) { + Player& player = owner(); + log("Message: Portdock lost, trying to restore it (player %d)\n", + player.player_number()); + wh->restore_portdock_or_destroy(egbase); + return; + } + } + // this is not a (running) game, destroying the port + wh->destroy(egbase); + } } /** * Add the flags of all ports that can be reached via this dock. */ -void PortDock::add_neighbours(std::vector<RoutingNodeNeighbour> & neighbours) -{ +void PortDock::add_neighbours(std::vector<RoutingNodeNeighbour>& neighbours) { if (m_fleet && m_fleet->active()) m_fleet->add_neighbours(*this, neighbours); } @@ -236,8 +238,7 @@ /** * The given @p ware enters the dock, waiting to be transported away. */ -void PortDock::add_shippingitem(Game & game, WareInstance & ware) -{ +void PortDock::add_shippingitem(Game& game, WareInstance& ware) { m_waiting.push_back(ShippingItem(ware)); ware.set_location(game, this); ware.update(game); @@ -247,15 +248,14 @@ * The given @p ware, which is assumed to be inside the dock, has updated * its route. */ -void PortDock::update_shippingitem(Game & game, WareInstance & ware) -{ +void PortDock::update_shippingitem(Game& game, WareInstance& ware) { for (std::vector<ShippingItem>::iterator item_iter = m_waiting.begin(); - item_iter != m_waiting.end(); - ++item_iter) { + item_iter != m_waiting.end(); + ++item_iter) { if (item_iter->m_object.serial() == ware.serial()) { _update_shippingitem(game, item_iter); - return; + return; } } } @@ -263,8 +263,7 @@ /** * The given @p worker enters the dock, waiting to be transported away. */ -void PortDock::add_shippingitem(Game & game, Worker & worker) -{ +void PortDock::add_shippingitem(Game& game, Worker& worker) { m_waiting.push_back(ShippingItem(worker)); worker.set_location(this); update_shippingitem(game, worker); @@ -274,24 +273,22 @@ * The given @p worker, which is assumed to be inside the dock, has * updated its route. */ -void PortDock::update_shippingitem(Game & game, Worker & worker) -{ +void PortDock::update_shippingitem(Game& game, Worker& worker) { for (std::vector<ShippingItem>::iterator item_iter = m_waiting.begin(); - item_iter != m_waiting.end(); - ++item_iter) { + item_iter != m_waiting.end(); + ++item_iter) { if (item_iter->m_object.serial() == worker.serial()) { _update_shippingitem(game, item_iter); - return; + return; } } } -void PortDock::_update_shippingitem(Game & game, std::vector<ShippingItem>::iterator it) -{ +void PortDock::_update_shippingitem(Game& game, std::vector<ShippingItem>::iterator it) { it->update_destination(game, *this); - PortDock * dst = it->get_destination(game); + PortDock* dst = it->get_destination(game); assert(dst != this); // Destination might have vanished or be in another economy altogether. @@ -312,8 +309,7 @@ * A ship has arrived at the dock. Clear all items designated for this dock, * and load the ship. */ -void PortDock::ship_arrived(Game & game, Ship & ship) -{ +void PortDock::ship_arrived(Game& game, Ship& ship) { std::vector<ShippingItem> items_brought_by_ship; ship.withdraw_items(game, *this, items_brought_by_ship); @@ -330,7 +326,8 @@ // Load the ship std::vector<Worker*> workers; std::vector<WareInstance*> wares; - m_expedition_bootstrap->get_waiting_workers_and_wares(game, owner().tribe(), &workers, &wares); + m_expedition_bootstrap->get_waiting_workers_and_wares( + game, owner().tribe(), &workers, &wares); for (Worker* worker : workers) { ship.add_item(game, ShippingItem(*worker)); @@ -375,8 +372,7 @@ m_fleet->update(game); } -void PortDock::set_need_ship(Game & game, bool need) -{ +void PortDock::set_need_ship(Game& game, bool need) { molog("set_need_ship(%s)\n", need ? "true" : "false"); if (need == m_need_ship) @@ -393,13 +389,12 @@ /** * Return the number of wares or workers of the given type that are waiting at the dock. */ -uint32_t PortDock::count_waiting(WareWorker waretype, WareIndex wareindex) -{ +uint32_t PortDock::count_waiting(WareWorker waretype, WareIndex wareindex) { uint32_t count = 0; for (ShippingItem& shipping_item : m_waiting) { - WareInstance * ware; - Worker * worker; + WareInstance* ware; + Worker* worker; shipping_item.get(owner().egbase(), &ware, &worker); if (waretype == wwWORKER) { @@ -414,7 +409,6 @@ return count; } - /// \returns whether an expedition was started or is even ready bool PortDock::expedition_started() { return (m_expedition_bootstrap.get() != nullptr) || m_expedition_ready; @@ -425,19 +419,18 @@ assert(!m_expedition_bootstrap); m_expedition_bootstrap.reset(new ExpeditionBootstrap(this)); m_expedition_bootstrap->start(); - } ExpeditionBootstrap* PortDock::expedition_bootstrap() { return m_expedition_bootstrap.get(); } -void PortDock::expedition_bootstrap_complete(Game & game) { +void PortDock::expedition_bootstrap_complete(Game& game) { m_expedition_ready = true; get_fleet()->update(game); } -void PortDock::cancel_expedition(Game & game) { +void PortDock::cancel_expedition(Game& game) { // Reset m_expedition_ready = false; @@ -445,38 +438,34 @@ m_expedition_bootstrap.reset(nullptr); } - -void PortDock::log_general_info(const EditorGameBase & egbase) -{ +void PortDock::log_general_info(const EditorGameBase& egbase) { PlayerImmovable::log_general_info(egbase); Coords pos(m_warehouse->get_position()); - molog - ("PortDock for warehouse %u (at %i,%i) in fleet %u, need_ship: %s, waiting: %" PRIuS "\n", - m_warehouse ? m_warehouse->serial() : 0, pos.x, pos.y, - m_fleet ? m_fleet->serial() : 0, - m_need_ship ? "true" : "false", - m_waiting.size()); + molog("PortDock for warehouse %u (at %i,%i) in fleet %u, need_ship: %s, waiting: %" PRIuS "\n", + m_warehouse ? m_warehouse->serial() : 0, + pos.x, + pos.y, + m_fleet ? m_fleet->serial() : 0, + m_need_ship ? "true" : "false", + m_waiting.size()); for (ShippingItem& shipping_item : m_waiting) { - molog - (" IT %u, destination %u\n", - shipping_item.m_object.serial(), - shipping_item.m_destination_dock.serial()); + molog(" IT %u, destination %u\n", + shipping_item.m_object.serial(), + shipping_item.m_destination_dock.serial()); } } #define PORTDOCK_SAVEGAME_VERSION 3 -PortDock::Loader::Loader() : m_warehouse(0) -{ +PortDock::Loader::Loader() : m_warehouse(0) { } -void PortDock::Loader::load(FileRead & fr, uint8_t version) -{ +void PortDock::Loader::load(FileRead& fr, uint8_t version) { PlayerImmovable::Loader::load(fr); - PortDock & pd = get<PortDock>(); + PortDock& pd = get<PortDock>(); m_warehouse = fr.unsigned_32(); uint16_t nrdockpoints = fr.unsigned_16(); @@ -507,11 +496,10 @@ } } -void PortDock::Loader::load_pointers() -{ +void PortDock::Loader::load_pointers() { PlayerImmovable::Loader::load_pointers(); - PortDock & pd = get<PortDock>(); + PortDock& pd = get<PortDock>(); pd.m_warehouse = &mol().get<Warehouse>(m_warehouse); pd.m_waiting.resize(m_waiting.size()); @@ -520,11 +508,10 @@ } } -void PortDock::Loader::load_finish() -{ +void PortDock::Loader::load_finish() { PlayerImmovable::Loader::load_finish(); - PortDock & pd = get<PortDock>(); + PortDock& pd = get<PortDock>(); if (pd.m_warehouse->get_portdock() != &pd) { log("Inconsistent PortDock <> Warehouse link\n"); @@ -537,9 +524,7 @@ pd.init_fleet(egbase()); } -MapObject::Loader * PortDock::load - (EditorGameBase & egbase, MapObjectLoader & mol, FileRead & fr) -{ +MapObject::Loader* PortDock::load(EditorGameBase& egbase, MapObjectLoader& mol, FileRead& fr) { std::unique_ptr<Loader> loader(new Loader); try { @@ -551,15 +536,14 @@ loader->load(fr, version); } else throw GameDataError("unknown/unhandled version %u", version); - } catch (const std::exception & e) { + } catch (const std::exception& e) { throw wexception("loading portdock: %s", e.what()); } return loader.release(); } -void PortDock::save(EditorGameBase & egbase, MapObjectSaver & mos, FileWrite & fw) -{ +void PortDock::save(EditorGameBase& egbase, MapObjectSaver& mos, FileWrite& fw) { fw.unsigned_8(HeaderPortDock); fw.unsigned_8(PORTDOCK_SAVEGAME_VERSION); @@ -567,7 +551,7 @@ fw.unsigned_32(mos.get_object_file_index(*m_warehouse)); fw.unsigned_16(m_dockpoints.size()); - for (const Coords& coords: m_dockpoints) { + for (const Coords& coords : m_dockpoints) { write_coords_32(&fw, coords); } @@ -583,4 +567,4 @@ fw.unsigned_8(m_expedition_ready ? 1 : 0); } -} // namespace Widelands +} // namespace Widelands === modified file 'src/logic/ship.cc' --- src/logic/ship.cc 2014-09-20 09:37:47 +0000 +++ src/logic/ship.cc 2014-11-19 21:19:43 +0000 @@ -49,24 +49,24 @@ namespace Widelands { -ShipDescr::ShipDescr - (const char * given_name, const char * gdescname, - const std::string & directory, Profile & prof, Section & global_s, - const TribeDescr & gtribe) - : - BobDescr(MapObjectType::SHIP, given_name, gdescname, >ribe) -{ - { // global options - Section & idle_s = prof.get_safe_section("idle"); +ShipDescr::ShipDescr(const char* given_name, + const char* gdescname, + const std::string& directory, + Profile& prof, + Section& global_s, + const TribeDescr& gtribe) + : BobDescr(MapObjectType::SHIP, given_name, gdescname, >ribe) { + { // global options + Section& idle_s = prof.get_safe_section("idle"); add_animation("idle", g_gr->animations().load(directory, idle_s)); } m_sail_anims.parse(*this, directory, prof, "sail"); - Section * sinking_s = prof.get_section("sinking"); + Section* sinking_s = prof.get_section("sinking"); if (sinking_s) add_animation("sinking", g_gr->animations().load(directory, *sinking_s)); - m_capacity = global_s.get_natural("capacity", 20); + m_capacity = global_s.get_natural("capacity", 20); m_vision_range = global_s.get_natural("vision_range", 7); } @@ -74,18 +74,12 @@ return MOVECAPS_SWIM; } -Bob & ShipDescr::create_object() const { +Bob& ShipDescr::create_object() const { return *new Ship(*this); } - -Ship::Ship(const ShipDescr & gdescr) : - Bob(gdescr), - m_window(nullptr), - m_fleet(nullptr), - m_economy(nullptr), - m_ship_state(TRANSPORT) -{ +Ship::Ship(const ShipDescr& gdescr) + : Bob(gdescr), m_window(nullptr), m_fleet(nullptr), m_economy(nullptr), m_ship_state(TRANSPORT) { } Ship::~Ship() { @@ -104,13 +98,14 @@ return m_fleet; } -void Ship::init_auto_task(Game & game) { +void Ship::init_auto_task(Game& game) { start_task_ship(game); } -void Ship::init(EditorGameBase & egbase) { +void Ship::init(EditorGameBase& egbase) { Bob::init(egbase); init_fleet(egbase); + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::GAINED)); } /** @@ -118,15 +113,15 @@ * The fleet code will automatically merge us into a larger * fleet, if one is reachable. */ -void Ship::init_fleet(EditorGameBase & egbase) { +void Ship::init_fleet(EditorGameBase& egbase) { assert(get_owner() != nullptr); - Fleet * fleet = new Fleet(*get_owner()); + Fleet* fleet = new Fleet(*get_owner()); fleet->add_ship(this); fleet->init(egbase); // fleet calls the set_fleet function appropriately } -void Ship::cleanup(EditorGameBase & egbase) { +void Ship::cleanup(EditorGameBase& egbase) { if (m_fleet) { m_fleet->remove_ship(egbase, this); } @@ -136,34 +131,32 @@ m_items.pop_back(); } + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::LOST)); + Bob::cleanup(egbase); } /** * This function is to be called only by @ref Fleet. */ -void Ship::set_fleet(Fleet * fleet) { +void Ship::set_fleet(Fleet* fleet) { m_fleet = fleet; } -void Ship::wakeup_neighbours(Game & game) { +void Ship::wakeup_neighbours(Game& game) { FCoords position = get_position(); Area<FCoords> area(position, 1); - std::vector<Bob *> ships; + std::vector<Bob*> ships; game.map().find_bobs(area, &ships, FindBobShip()); - for - (std::vector<Bob *>::const_iterator it = ships.begin(); - it != ships.end(); ++it) - { + for (std::vector<Bob*>::const_iterator it = ships.begin(); it != ships.end(); ++it) { if (*it == this) continue; - static_cast<Ship *>(*it)->ship_wakeup(game); + static_cast<Ship*>(*it)->ship_wakeup(game); } } - /** * Standard behaviour of ships. * @@ -174,20 +167,20 @@ static_cast<Bob::Ptr>(&Ship::ship_update), nullptr, nullptr, - true // unique task + true // unique task }; -void Ship::start_task_ship(Game & game) { +void Ship::start_task_ship(Game& game) { push_task(game, taskShip); top_state().ivar1 = 0; } -void Ship::ship_wakeup(Game & game) { +void Ship::ship_wakeup(Game& game) { if (get_state(taskShip)) send_signal(game, "wakeup"); } -void Ship::ship_update(Game & game, Bob::State & state) { +void Ship::ship_update(Game& game, Bob::State& state) { // Handle signals std::string signal = get_signal(); if (!signal.empty()) { @@ -211,44 +204,43 @@ } switch (m_ship_state) { - case TRANSPORT: - if (ship_update_transport(game, state)) - return; - break; - case EXP_FOUNDPORTSPACE: - case EXP_SCOUTING: - case EXP_WAITING: - ship_update_expedition(game, state); - break; - case EXP_COLONIZING: - break; - case SINK_REQUEST: - if (descr().is_animation_known("sinking")) { - m_ship_state = SINK_ANIMATION; - start_task_idle(game, descr().get_animation("sinking"), 3000); - return; - } - log("Oh no... this ship has no sinking animation :(!\n"); - // fall trough - case SINK_ANIMATION: - // The sink animation has been played, so finally remove the ship from the map - pop_task(game); - remove(game); - return; - default: - assert(false); // never here + case TRANSPORT: + if (ship_update_transport(game, state)) + return; + break; + case EXP_FOUNDPORTSPACE: + case EXP_SCOUTING: + case EXP_WAITING: + ship_update_expedition(game, state); + break; + case EXP_COLONIZING: + break; + case SINK_REQUEST: + if (descr().is_animation_known("sinking")) { + m_ship_state = SINK_ANIMATION; + start_task_idle(game, descr().get_animation("sinking"), 3000); + return; + } + log("Oh no... this ship has no sinking animation :(!\n"); + // fall trough + case SINK_ANIMATION: + // The sink animation has been played, so finally remove the ship from the map + pop_task(game); + remove(game); + return; + default: + assert(false); // never here } // if the real update function failed (e.g. nothing to transport), the ship goes idle ship_update_idle(game, state); } - /// updates a ships tasks in transport mode \returns false if failed to update tasks -bool Ship::ship_update_transport(Game & game, Bob::State &) { - Map & map = game.map(); +bool Ship::ship_update_transport(Game& game, Bob::State&) { + Map& map = game.map(); - PortDock * dst = get_destination(game); + PortDock* dst = get_destination(game); if (!dst) { molog("ship_update: No destination anymore.\n"); if (m_items.empty()) @@ -277,7 +269,7 @@ molog("ship_update: Go to dock %u\n", dst->serial()); - PortDock * lastdock = m_lastdock.get(game); + PortDock* lastdock = m_lastdock.get(game); if (lastdock && lastdock != dst) { molog("ship_update: Have lastdock %u\n", lastdock->serial()); @@ -332,10 +324,9 @@ return true; } - /// updates a ships tasks in expedition mode -void Ship::ship_update_expedition(Game & game, Bob::State &) { - Map & map = game.map(); +void Ship::ship_update_expedition(Game& game, Bob::State&) { + Map& map = game.map(); assert(m_expedition); @@ -343,30 +334,30 @@ FCoords position = get_position(); for (Direction dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { m_expedition->swimable[dir - 1] = - map.get_neighbour(position, dir).field->nodecaps() & MOVECAPS_SWIM; + map.get_neighbour(position, dir).field->nodecaps() & MOVECAPS_SWIM; } if (m_ship_state == EXP_SCOUTING) { // Check surrounding fields for port buildspaces - std::unique_ptr<std::list<Coords> > temp_port_buildspaces(new std::list<Coords>()); - MapRegion<Area<Coords> > mr(map, Area<Coords>(position, descr().vision_range())); + std::unique_ptr<std::list<Coords>> temp_port_buildspaces(new std::list<Coords>()); + MapRegion<Area<Coords>> mr(map, Area<Coords>(position, descr().vision_range())); bool new_port_space = false; do { if (map.is_port_space(mr.location())) { FCoords fc = map.get_fcoords(mr.location()); - // Check whether the maximum theoretical possible NodeCap of the field is of the size big + // Check whether the maximum theoretical possible NodeCap of the field is of the size + // big // and whether it can theoretically be a port space - if - ((map.get_max_nodecaps(game.world(), fc) & BUILDCAPS_SIZEMASK) != BUILDCAPS_BIG - || - map.find_portdock(fc).empty()) - { + if ((map.get_max_nodecaps(game.world(), fc) & BUILDCAPS_SIZEMASK) != BUILDCAPS_BIG || + map.find_portdock(fc).empty()) { continue; } - // NOTE This is the place to handle enemy territory and "clearing a port space from the enemy". - // NOTE There is a simple check for the current land owner to avoid placement of ports into enemy + // NOTE This is the place to handle enemy territory and "clearing a port space from the + // enemy". + // NOTE There is a simple check for the current land owner to avoid placement of ports + // into enemy // NOTE territory, as "clearing" is not yet implemented. // NOTE further it checks, whether there is a Player_immovable on one of the fields. // TODO(unknown): handle this more gracefully concering opposing players @@ -378,7 +369,7 @@ invalid = true; continue; } - BaseImmovable * baim = coord.field->get_immovable(); + BaseImmovable* baim = coord.field->get_immovable(); if (baim) if (is_a(PlayerImmovable, baim)) { invalid = true; @@ -387,60 +378,60 @@ // Check all neighboured fields that will be used by the port switch (step) { - case 0: - map.get_ln(fc, &coord); - break; - case 1: - map.get_tln(fc, &coord); - break; - case 2: - map.get_trn(fc, &coord); - break; - case 3: - // Flag coordinate - map.get_brn(fc, &coord); - break; - default: - break; + case 0: + map.get_ln(fc, &coord); + break; + case 1: + map.get_tln(fc, &coord); + break; + case 2: + map.get_trn(fc, &coord); + break; + case 3: + // Flag coordinate + map.get_brn(fc, &coord); + break; + default: + break; } } // Now check whether there is a flag in the surroundings of the flag position FCoords neighb; map.get_ln(coord, &neighb); for (uint8_t step = 0; !invalid && step < 5; ++step) { - BaseImmovable * baim = neighb.field->get_immovable(); + BaseImmovable* baim = neighb.field->get_immovable(); if (baim) if (is_a(Flag, baim)) { invalid = true; continue; } - // Check all neighboured fields but not the one already checked for a PlayerImmovable. + // Check all neighboured fields but not the one already checked for a + // PlayerImmovable. switch (step) { - case 0: - map.get_bln(coord, &neighb); - break; - case 1: - map.get_brn(coord, &neighb); - break; - case 2: - map.get_rn(coord, &neighb); - break; - case 3: - map.get_trn(coord, &neighb); - break; - default: - break; + case 0: + map.get_bln(coord, &neighb); + break; + case 1: + map.get_brn(coord, &neighb); + break; + case 2: + map.get_rn(coord, &neighb); + break; + case 3: + map.get_trn(coord, &neighb); + break; + default: + break; } } if (invalid) continue; bool pbs_saved = false; - for - (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); - it != m_expedition->seen_port_buildspaces->end() && !pbs_saved; - ++it) - { + for (std::list<Coords>::const_iterator it = + m_expedition->seen_port_buildspaces->begin(); + it != m_expedition->seen_port_buildspaces->end() && !pbs_saved; + ++it) { // Check if the ship knows this port space already from its last check if (*it == mr.location()) { temp_port_buildspaces->push_back(mr.location()); @@ -462,10 +453,13 @@ send_message(game, "exp_port_space", msg_head, msg_body, "port.png"); } m_expedition->seen_port_buildspaces.swap(temp_port_buildspaces); + if (new_port_space) { + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::WAITINGFORCOMMAND)); + } } } -void Ship::ship_update_idle(Game & game, Bob::State & state) { +void Ship::ship_update_idle(Game& game, Bob::State& state) { if (state.ivar1) { // We've just completed one step, so give neighbours // a chance to move away first @@ -475,172 +469,182 @@ return; } - // If we are waiting for the next transport job, check if we should move away from ships and shores + // If we are waiting for the next transport job, check if we should move away from ships and + // shores switch (m_ship_state) { - case TRANSPORT: { - FCoords position = get_position(); - Map & map = game.map(); - unsigned int dirs[LAST_DIRECTION + 1]; - unsigned int dirmax = 0; - + case TRANSPORT: { + FCoords position = get_position(); + Map& map = game.map(); + unsigned int dirs[LAST_DIRECTION + 1]; + unsigned int dirmax = 0; + + for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { + FCoords node = dir ? map.get_neighbour(position, dir) : position; + dirs[dir] = node.field->nodecaps() & MOVECAPS_WALK ? 10 : 0; + + Area<FCoords> area(node, 0); + std::vector<Bob*> ships; + map.find_bobs(area, &ships, FindBobShip()); + + for (std::vector<Bob*>::const_iterator it = ships.begin(); it != ships.end(); ++it) { + if (*it == this) + continue; + + dirs[dir] += 3; + } + + dirmax = std::max(dirmax, dirs[dir]); + } + + if (dirmax) { + unsigned int prob[LAST_DIRECTION + 1]; + unsigned int totalprob = 0; + + // The probability for moving into a given direction is also + // affected by the "close" directions. for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { - FCoords node = dir ? map.get_neighbour(position, dir) : position; - dirs[dir] = node.field->nodecaps() & MOVECAPS_WALK ? 10 : 0; - - Area<FCoords> area(node, 0); - std::vector<Bob *> ships; - map.find_bobs(area, &ships, FindBobShip()); - - for (std::vector<Bob *>::const_iterator it = ships.begin(); it != ships.end(); ++it) { - if (*it == this) - continue; - - dirs[dir] += 3; - } - - dirmax = std::max(dirmax, dirs[dir]); - } - - if (dirmax) { - unsigned int prob[LAST_DIRECTION + 1]; - unsigned int totalprob = 0; - - // The probability for moving into a given direction is also - // affected by the "close" directions. - for (Direction dir = 0; dir <= LAST_DIRECTION; ++dir) { - prob[dir] = 10 * dirmax - 10 * dirs[dir]; - - if (dir > 0) { - unsigned int delta = std::min(prob[dir], dirs[(dir % 6) + 1] + dirs[1 + ((dir - 1) % 6)]); - prob[dir] -= delta; - } - - totalprob += prob[dir]; - } - - if (totalprob == 0) { - start_task_idle(game, descr().main_animation(), 1500); - return; - } - - unsigned int rnd = game.logic_rand() % totalprob; - Direction dir = 0; - while (rnd >= prob[dir]) { - rnd -= prob[dir]; - ++dir; - } - - if (dir == 0 || dir > LAST_DIRECTION) { - start_task_idle(game, descr().main_animation(), 1500); - return; - } - - FCoords neighbour = map.get_neighbour(position, dir); - if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { - start_task_idle(game, descr().main_animation(), 1500); - return; - } - - state.ivar1 = 1; - start_task_move(game, dir, descr().get_sail_anims(), false); - return; - } - // No desire to move around, so sleep - start_task_idle(game, descr().main_animation(), -1); + prob[dir] = 10 * dirmax - 10 * dirs[dir]; + + if (dir > 0) { + unsigned int delta = + std::min(prob[dir], dirs[(dir % 6) + 1] + dirs[1 + ((dir - 1) % 6)]); + prob[dir] -= delta; + } + + totalprob += prob[dir]; + } + + if (totalprob == 0) { + start_task_idle(game, descr().main_animation(), 1500); + return; + } + + unsigned int rnd = game.logic_rand() % totalprob; + Direction dir = 0; + while (rnd >= prob[dir]) { + rnd -= prob[dir]; + ++dir; + } + + if (dir == 0 || dir > LAST_DIRECTION) { + start_task_idle(game, descr().main_animation(), 1500); + return; + } + + FCoords neighbour = map.get_neighbour(position, dir); + if (!(neighbour.field->nodecaps() & MOVECAPS_SWIM)) { + start_task_idle(game, descr().main_animation(), 1500); + return; + } + + state.ivar1 = 1; + start_task_move(game, dir, descr().get_sail_anims(), false); return; } - case EXP_SCOUTING: { - if (m_expedition->island_exploration) { // Exploration of the island - if (exp_close_to_coast()) { - if (m_expedition->direction == 0) { - // Make sure we know the location of the coast and use it as initial direction we come from - m_expedition->direction = WALK_SE; - for (uint8_t secure = 0; exp_dir_swimable(m_expedition->direction); ++secure) { - assert(secure < 6); - m_expedition->direction = get_cw_neighbour(m_expedition->direction); - } - m_expedition->direction = get_backward_dir(m_expedition->direction); - // Save the position - this is where we start - m_expedition->exploration_start = get_position(); - } else { - // Check whether the island was completely surrounded - if (get_position() == m_expedition->exploration_start) { - std::string msg_head = _("Island Circumnavigated"); - std::string msg_body = _("An expedition ship sailed around its" - " island without any events."); - send_message(game, "exp_island", msg_head, msg_body, - "ship_explore_island_cw.png"); - m_ship_state = EXP_WAITING; - return start_task_idle(game, descr().main_animation(), 1500); - } + // No desire to move around, so sleep + start_task_idle(game, descr().main_animation(), -1); + return; + } + case EXP_SCOUTING: { + if (m_expedition->island_exploration) { // Exploration of the island + if (exp_close_to_coast()) { + if (m_expedition->direction == 0) { + // Make sure we know the location of the coast and use it as initial direction we + // come from + m_expedition->direction = WALK_SE; + for (uint8_t secure = 0; exp_dir_swimable(m_expedition->direction); ++secure) { + assert(secure < 6); + m_expedition->direction = get_cw_neighbour(m_expedition->direction); } - // The ship is supposed to follow the coast as close as possible, therefore the check for - // a swimable field begins at the neighbour field of the direction we came from. m_expedition->direction = get_backward_dir(m_expedition->direction); - if (m_expedition->clockwise) { - do { - m_expedition->direction = get_ccw_neighbour(m_expedition->direction); - } while (!exp_dir_swimable(m_expedition->direction)); - } else { - do { - m_expedition->direction = get_cw_neighbour(m_expedition->direction); - } while (!exp_dir_swimable(m_expedition->direction)); - } - state.ivar1 = 1; - return start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); - } else { - // The ship got the command to scout around an island, but is not close to any island - // Most likely the command was send as the ship was on an exploration and just leaving - // the island - therefore we try to find the island again. - FCoords position = get_position(); - Map & map = game.map(); - for (uint8_t dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { - FCoords neighbour = map.get_neighbour(position, dir); - for (uint8_t sur = FIRST_DIRECTION; sur <= LAST_DIRECTION; ++sur) - if (!(map.get_neighbour(neighbour, sur).field->nodecaps() & MOVECAPS_SWIM)) { - // Okay we found the next coast, so now the ship should go there. - // However, we do neither save the position as starting position, nor do we save - // the direction we currently go. So the ship can start exploring normally - state.ivar1 = 1; - return start_task_move(game, dir, descr().get_sail_anims(), false); - } - } - // if we are here, it seems something really strange happend. - log("WARNING: ship was not able to start exploration. Entering WAIT mode."); - m_ship_state = EXP_WAITING; - return start_task_idle(game, descr().main_animation(), 1500); - } - } else { // scouting towards a specific direction - if (exp_dir_swimable(m_expedition->direction)) { - // the scouting direction is still free to move - state.ivar1 = 1; - start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); - return; - } - // coast reached + // Save the position - this is where we start + m_expedition->exploration_start = get_position(); + } else { + // Check whether the island was completely surrounded + if (get_position() == m_expedition->exploration_start) { + std::string msg_head = _("Island Circumnavigated"); + std::string msg_body = _("An expedition ship sailed around its" + " island without any events."); + send_message( + game, "exp_island", msg_head, msg_body, "ship_explore_island_cw.png"); + m_ship_state = EXP_WAITING; + Notifications::publish( + NoteShipMessage(this, NoteShipMessage::Message::WAITINGFORCOMMAND)); + return start_task_idle(game, descr().main_animation(), 1500); + } + } + // The ship is supposed to follow the coast as close as possible, therefore the check + // for + // a swimable field begins at the neighbour field of the direction we came from. + m_expedition->direction = get_backward_dir(m_expedition->direction); + if (m_expedition->clockwise) { + do { + m_expedition->direction = get_ccw_neighbour(m_expedition->direction); + } while (!exp_dir_swimable(m_expedition->direction)); + } else { + do { + m_expedition->direction = get_cw_neighbour(m_expedition->direction); + } while (!exp_dir_swimable(m_expedition->direction)); + } + state.ivar1 = 1; + return start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); + } else { + // The ship got the command to scout around an island, but is not close to any island + // Most likely the command was send as the ship was on an exploration and just leaving + // the island - therefore we try to find the island again. + FCoords position = get_position(); + Map& map = game.map(); + for (uint8_t dir = FIRST_DIRECTION; dir <= LAST_DIRECTION; ++dir) { + FCoords neighbour = map.get_neighbour(position, dir); + for (uint8_t sur = FIRST_DIRECTION; sur <= LAST_DIRECTION; ++sur) + if (!(map.get_neighbour(neighbour, sur).field->nodecaps() & MOVECAPS_SWIM)) { + // Okay we found the next coast, so now the ship should go there. + // However, we do neither save the position as starting position, nor do we + // save + // the direction we currently go. So the ship can start exploring normally + state.ivar1 = 1; + return start_task_move(game, dir, descr().get_sail_anims(), false); + } + } + // if we are here, it seems something really strange happend. + log("WARNING: ship was not able to start exploration. Entering WAIT mode."); m_ship_state = EXP_WAITING; - start_task_idle(game, descr().main_animation(), 1500); - // Send a message to the player, that a new coast was reached - std::string msg_head = _("Coast Reached"); - std::string msg_body = - _("An expedition ship reached a coast and is waiting for further commands."); - send_message(game, "exp_coast", msg_head, msg_body, "ship_explore_island_cw.png"); + return start_task_idle(game, descr().main_animation(), 1500); + } + } else { // scouting towards a specific direction + if (exp_dir_swimable(m_expedition->direction)) { + // the scouting direction is still free to move + state.ivar1 = 1; + start_task_move(game, m_expedition->direction, descr().get_sail_anims(), false); return; } + // coast reached + m_ship_state = EXP_WAITING; + start_task_idle(game, descr().main_animation(), 1500); + // Send a message to the player, that a new coast was reached + std::string msg_head = _("Coast Reached"); + std::string msg_body = + _("An expedition ship reached a coast and is waiting for further commands."); + send_message(game, "exp_coast", msg_head, msg_body, "ship_explore_island_cw.png"); + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::WAITINGFORCOMMAND)); + return; } - case EXP_COLONIZING: { - assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty()); - BaseImmovable * baim = game.map()[m_expedition->seen_port_buildspaces->front()].get_immovable(); - assert(baim); + } + case EXP_COLONIZING: { + assert(m_expedition->seen_port_buildspaces && !m_expedition->seen_port_buildspaces->empty()); + BaseImmovable* baim = + game.map()[m_expedition->seen_port_buildspaces->front()].get_immovable(); + if (baim) { upcast(ConstructionSite, cs, baim); for (int i = m_items.size() - 1; i >= 0; --i) { - WareInstance * ware; - Worker * worker; + WareInstance* ware; + Worker* worker; m_items.at(i).get(game, &ware, &worker); if (ware) { - // no, we don't transfer the wares, we create new ones out of air and remove the old ones ;) - WaresQueue & wq = cs->waresqueue(ware->descr_index()); + // no, we don't transfer the wares, we create new ones out of air and remove the old + // ones ;) + WaresQueue& wq = cs->waresqueue(ware->descr_index()); const uint32_t max = wq.get_max_fill(); const uint32_t cur = wq.get_filled(); assert(max > cur); @@ -654,35 +658,56 @@ worker->set_location(cs); worker->set_position(game, cs->get_position()); worker->reset_tasks(game); - PartiallyFinishedBuilding::request_builder_callback - (game, *cs->get_builder_request(), worker->descr().worker_index(), worker, *cs); + PartiallyFinishedBuilding::request_builder_callback( + game, *cs->get_builder_request(), worker->descr().worker_index(), worker, *cs); m_items.resize(i); } } - if (m_items.empty()) { - m_ship_state = TRANSPORT; // That's it, expedition finished - - init_fleet(game); - m_expedition.reset(nullptr); - - if (upcast(InteractiveGameBase, igb, game.get_ibase())) - refresh_window(*igb); + } else { // it seems that port constructionsite has dissapeared + // Send a message to the player, that a port constructionsite is gone + std::string msg_head = _("New port constructionsite is gone"); + std::string msg_body = _("Unloading of wares failed, expedition is cancelled now."); + send_message(game, "exp_port_space", msg_head, msg_body, "port.png"); + send_signal(game, "cancel_expedition"); + } + + if (m_items.empty() || !baim) { // we are done, either way + m_ship_state = TRANSPORT; // That's it, expedition finished + + // Bring us back into a fleet and a economy. + init_fleet(game); + + // for case that there are any workers left on board + // (applicable when port construction space is lost) + Worker* worker; + for (ShippingItem& item : m_items) { + item.get(game, nullptr, &worker); + if (worker) { + worker->reset_tasks(game); + worker->start_task_shipping(game, nullptr); + } } - return start_task_idle(game, descr().main_animation(), 1500); // unload the next item - } - - default: { - // wait for input - start_task_idle(game, descr().main_animation(), 1500); - return; - } + + m_expedition.reset(nullptr); + + if (upcast(InteractiveGameBase, igb, game.get_ibase())) + refresh_window(*igb); + return start_task_idle(game, descr().main_animation(), 1500); + } + } + + default: { + // wait for input + start_task_idle(game, descr().main_animation(), 1500); + return; + } } // never here - assert (false); + assert(false); } -void Ship::set_economy(Game & game, Economy * e) { +void Ship::set_economy(Game& game, Economy* e) { // Do not check here that the economy actually changed, because on loading // we rely that wares really get reassigned our economy. @@ -697,23 +722,23 @@ * * @note This is supposed to be called only from the scheduling code of @ref Fleet. */ -void Ship::set_destination(Game & game, PortDock & pd) { +void Ship::set_destination(Game& game, PortDock& pd) { molog("set_destination to %u (currently %" PRIuS " items)\n", pd.serial(), m_items.size()); m_destination = &pd; send_signal(game, "wakeup"); } -void Ship::add_item(Game & game, const ShippingItem & item) { +void Ship::add_item(Game& game, const ShippingItem& item) { assert(m_items.size() < descr().get_capacity()); m_items.push_back(item); m_items.back().set_location(game, this); } -void Ship::withdraw_items(Game & game, PortDock & pd, std::vector<ShippingItem> & items) { +void Ship::withdraw_items(Game& game, PortDock& pd, std::vector<ShippingItem>& items) { uint32_t dst = 0; for (uint32_t src = 0; src < m_items.size(); ++src) { - PortDock * destination = m_items[src].get_destination(game); + PortDock* destination = m_items[src].get_destination(game); if (!destination || destination == &pd) { items.push_back(m_items[src]); } else { @@ -726,8 +751,8 @@ /** * Find a path to the dock @p pd and follow it without using precomputed paths. */ -void Ship::start_task_movetodock(Game & game, PortDock & pd) { - Map & map = game.map(); +void Ship::start_task_movetodock(Game& game, PortDock& pd) { + Map& map = game.map(); StepEvalAStar se(pd.get_warehouse()->get_position()); se.m_swim = true; se.m_conservative = false; @@ -753,7 +778,7 @@ } /// Prepare everything for the coming exploration -void Ship::start_task_expedition(Game & game) { +void Ship::start_task_expedition(Game& game) { // Now we are waiting m_ship_state = EXP_WAITING; // Initialize a new, yet empty expedition @@ -773,8 +798,8 @@ set_economy(game, m_expedition->economy.get()); for (int i = m_items.size() - 1; i >= 0; --i) { - WareInstance * ware; - Worker * worker; + WareInstance* ware; + Worker* worker; m_items.at(i).get(game, &ware, &worker); if (worker) { worker->reset_tasks(game); @@ -788,11 +813,12 @@ const std::string msg_head = _("Expedition Ready"); const std::string msg_body = _("An expedition ship is waiting for your commands."); send_message(game, "exp_ready", msg_head, msg_body, "start_expedition.png"); + Notifications::publish(NoteShipMessage(this, NoteShipMessage::Message::WAITINGFORCOMMAND)); } /// Initializes / changes the direction of scouting to @arg direction /// @note only called via player command -void Ship::exp_scout_direction(Game &, uint8_t direction) { +void Ship::exp_scout_direction(Game&, uint8_t direction) { assert(m_expedition); m_ship_state = EXP_SCOUTING; m_expedition->direction = direction; @@ -801,7 +827,7 @@ /// Initializes the construction of a port at @arg c /// @note only called via player command -void Ship::exp_construct_port (Game &, const Coords& c) { +void Ship::exp_construct_port(Game&, const Coords& c) { assert(m_expedition); BuildingIndex port_idx = get_owner()->tribe().safe_building_index("port"); get_owner()->force_csite(c, port_idx); @@ -810,7 +836,7 @@ /// Initializes / changes the direction the island exploration in @arg clockwise direction /// @note only called via player command -void Ship::exp_explore_island (Game &, bool clockwise) { +void Ship::exp_explore_island(Game&, bool clockwise) { assert(m_expedition); m_ship_state = EXP_SCOUTING; m_expedition->clockwise = clockwise; @@ -820,9 +846,10 @@ /// Cancels a currently running expedition /// @note only called via player command -void Ship::exp_cancel (Game & game) { +void Ship::exp_cancel(Game& game) { // Running colonization has the highest priority before cancelation // + cancelation only works if an expedition is actually running + if ((m_ship_state == EXP_COLONIZING) || !state_is_expedition()) return; send_signal(game, "cancel_expedition"); @@ -834,7 +861,7 @@ // Theres nothing to be done for wares - they already changed // economy with us and the warehouse will make sure that they are // getting used. - Worker * worker; + Worker* worker; for (ShippingItem& item : m_items) { item.get(game, nullptr, &worker); if (worker) { @@ -859,7 +886,7 @@ /// Sinks the ship /// @note only called via player command -void Ship::sink_ship (Game & game) { +void Ship::sink_ship(Game& game) { // Running colonization has the highest priority + a sink request is only valid once if (!state_is_sinkable()) return; @@ -869,25 +896,22 @@ close_window(); } -void Ship::log_general_info(const EditorGameBase & egbase) -{ +void Ship::log_general_info(const EditorGameBase& egbase) { Bob::log_general_info(egbase); - molog - ("Fleet: %u, destination: %u, lastdock: %u, carrying: %" PRIuS "\n", - m_fleet? m_fleet->serial() : 0, - m_destination.serial(), m_lastdock.serial(), - m_items.size()); + molog("Fleet: %u, destination: %u, lastdock: %u, carrying: %" PRIuS "\n", + m_fleet ? m_fleet->serial() : 0, + m_destination.serial(), + m_lastdock.serial(), + m_items.size()); for (const ShippingItem& shipping_item : m_items) { - molog - (" IT %u, destination %u\n", - shipping_item.m_object.serial(), - shipping_item.m_destination_dock.serial()); + molog(" IT %u, destination %u\n", + shipping_item.m_object.serial(), + shipping_item.m_destination_dock.serial()); } } - /** * Send a message to the owning player. * @@ -895,31 +919,31 @@ * * \param msgsender a computer-readable description of why the message was sent * \param title user-visible title of the message - * \param description user-visible message body, will be placed in an appropriate rich-text paragraph + * \param description user-visible message body, will be placed in an appropriate rich-text + *paragraph * \param picture picture name relative to the pics directory */ -void Ship::send_message - (Game & game, const std::string & msgsender, - const std::string & title, const std::string & description, - const std::string & picture) -{ +void Ship::send_message(Game& game, + const std::string& msgsender, + const std::string& title, + const std::string& description, + const std::string& picture) { std::string rt_description; if (picture.size() > 3) { - rt_description = "<rt image=pics/"; + rt_description = "<rt image=pics/"; rt_description += picture; rt_description += "><p font-size=14 font-face=DejaVuSerif>"; } else - rt_description = "<rt><p font-size=14 font-face=DejaVuSerif>"; + rt_description = "<rt><p font-size=14 font-face=DejaVuSerif>"; rt_description += description; rt_description += "</p></rt>"; - Message * msg = new Message - (msgsender, game.get_gametime(), title, rt_description, get_position(), m_serial); + Message* msg = + new Message(msgsender, game.get_gametime(), title, rt_description, get_position(), m_serial); get_owner()->add_message(game, *msg); } - /* ============================== @@ -930,20 +954,16 @@ #define SHIP_SAVEGAME_VERSION 4 -Ship::Loader::Loader() : - m_lastdock(0), - m_destination(0) -{ +Ship::Loader::Loader() : m_lastdock(0), m_destination(0) { } -const Bob::Task * Ship::Loader::get_task(const std::string & name) -{ - if (name == "shipidle" || name == "ship") return &taskShip; +const Bob::Task* Ship::Loader::get_task(const std::string& name) { + if (name == "shipidle" || name == "ship") + return &taskShip; return Bob::Loader::get_task(name); } -void Ship::Loader::load(FileRead & fr, uint8_t version) -{ +void Ship::Loader::load(FileRead& fr, uint8_t version) { Bob::Loader::load(fr); if (version >= 2) { @@ -952,15 +972,8 @@ m_ship_state = fr.unsigned_8(); // Expedition specific data - if - (m_ship_state == EXP_SCOUTING - || - m_ship_state == EXP_WAITING - || - m_ship_state == EXP_FOUNDPORTSPACE - || - m_ship_state == EXP_COLONIZING) - { + if (m_ship_state == EXP_SCOUTING || m_ship_state == EXP_WAITING || + m_ship_state == EXP_FOUNDPORTSPACE || m_ship_state == EXP_COLONIZING) { m_expedition.reset(new Expedition()); // Currently seen port build spaces m_expedition->seen_port_buildspaces.reset(new std::list<Coords>()); @@ -992,11 +1005,10 @@ } } -void Ship::Loader::load_pointers() -{ +void Ship::Loader::load_pointers() { Bob::Loader::load_pointers(); - Ship & ship = get<Ship>(); + Ship& ship = get<Ship>(); if (m_lastdock) ship.m_lastdock = &mol().get<PortDock>(m_lastdock); @@ -1009,11 +1021,10 @@ } } -void Ship::Loader::load_finish() -{ +void Ship::Loader::load_finish() { Bob::Loader::load_finish(); - Ship & ship = get<Ship>(); + Ship& ship = get<Ship>(); // restore the state the ship is in ship.m_ship_state = m_ship_state; @@ -1023,7 +1034,8 @@ ship.m_expedition.swap(m_expedition); ship.m_expedition->economy.reset(new Economy(*ship.get_owner())); ship.m_economy = ship.m_expedition->economy.get(); - } else assert(m_ship_state == TRANSPORT); + } else + assert(m_ship_state == TRANSPORT); // Workers load code set their economy to the economy of their location // (which is a PlayerImmovable), that means that workers on ships do not get @@ -1034,10 +1046,7 @@ ship.set_economy(ref_cast<Game>(egbase()), ship.m_economy); } - -MapObject::Loader * Ship::load - (EditorGameBase & egbase, MapObjectLoader & mol, FileRead & fr) -{ +MapObject::Loader* Ship::load(EditorGameBase& egbase, MapObjectLoader& mol, FileRead& fr) { std::unique_ptr<Loader> loader(new Loader); try { @@ -1047,32 +1056,28 @@ if (1 <= version && version <= SHIP_SAVEGAME_VERSION) { std::string owner = fr.c_string(); std::string name = fr.c_string(); - const ShipDescr * descr = nullptr; + const ShipDescr* descr = nullptr; egbase.manually_load_tribe(owner); - if (const TribeDescr * tribe = egbase.get_tribe(owner)) - descr = dynamic_cast<const ShipDescr *> - (tribe->get_bob_descr(name)); + if (const TribeDescr* tribe = egbase.get_tribe(owner)) + descr = dynamic_cast<const ShipDescr*>(tribe->get_bob_descr(name)); if (!descr) - throw GameDataError - ("undefined ship %s/%s", owner.c_str(), name.c_str()); + throw GameDataError("undefined ship %s/%s", owner.c_str(), name.c_str()); loader->init(egbase, mol, descr->create_object()); loader->load(fr, version); } else throw GameDataError("unknown/unhandled version %u", version); - } catch (const std::exception & e) { + } catch (const std::exception& e) { throw wexception("loading ship: %s", e.what()); } return loader.release(); } -void Ship::save - (EditorGameBase & egbase, MapObjectSaver & mos, FileWrite & fw) -{ +void Ship::save(EditorGameBase& egbase, MapObjectSaver& mos, FileWrite& fw) { fw.unsigned_8(HeaderShip); fw.unsigned_8(SHIP_SAVEGAME_VERSION); @@ -1089,11 +1094,9 @@ // currently seen port buildspaces assert(m_expedition->seen_port_buildspaces); fw.unsigned_8(m_expedition->seen_port_buildspaces->size()); - for - (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); - it != m_expedition->seen_port_buildspaces->end(); - ++it) - { + for (std::list<Coords>::const_iterator it = m_expedition->seen_port_buildspaces->begin(); + it != m_expedition->seen_port_buildspaces->end(); + ++it) { write_coords_32(&fw, *it); } // swimability of the directions @@ -1118,4 +1121,4 @@ } } -} // namespace Widelands +} // namespace Widelands === modified file 'src/logic/ship.h' --- src/logic/ship.h 2014-09-10 08:55:04 +0000 +++ src/logic/ship.h 2014-11-19 21:19:43 +0000 @@ -37,6 +37,19 @@ struct Fleet; class PortDock; +struct NoteShipMessage { + CAN_BE_SEND_AS_NOTE(NoteId::ShipMessage) + + Ship* ship; + + enum class Message {LOST, GAINED, WAITINGFORCOMMAND}; + Message message; + + NoteShipMessage(Ship* const init_ship, Message const init_message) + : ship(init_ship), message(init_message) { + } +}; + struct ShipDescr : BobDescr { ShipDescr (char const * name, char const * descname, === modified file 'src/logic/warehouse.cc' --- src/logic/warehouse.cc 2014-09-30 05:41:55 +0000 +++ src/logic/warehouse.cc 2014-11-19 21:19:43 +0000 @@ -51,7 +51,7 @@ namespace Widelands { -namespace { +namespace { static const uint32_t WORKER_WITHOUT_COST_SPAWN_INTERVAL = 2500; @@ -69,19 +69,17 @@ } // namespace -WarehouseSupply::~WarehouseSupply() -{ +WarehouseSupply::~WarehouseSupply() { if (m_economy) { - log - ("WarehouseSupply::~WarehouseSupply: Warehouse %u still belongs to " - "an economy", - m_warehouse->serial()); + log("WarehouseSupply::~WarehouseSupply: Warehouse %u still belongs to " + "an economy", + m_warehouse->serial()); set_economy(nullptr); } // We're removed from the Economy. Therefore, the wares can simply // be cleared out. The global inventory will be okay. - m_wares .clear(); + m_wares.clear(); m_workers.clear(); } @@ -97,10 +95,8 @@ m_workers.set_nrwares(i); } - /// Add and remove our wares and the Supply to the economies as necessary. -void WarehouseSupply::set_economy(Economy * const e) -{ +void WarehouseSupply::set_economy(Economy* const e) { if (e == m_economy) return; @@ -127,87 +123,74 @@ } } - /// Add wares and update the economy. -void WarehouseSupply::add_wares(WareIndex const id, uint32_t const count) -{ +void WarehouseSupply::add_wares(WareIndex const id, uint32_t const count) { if (!count) return; - if (m_economy) // No economies in the editor + if (m_economy) // No economies in the editor m_economy->add_wares(id, count); m_wares.add(id, count); } - /// Remove wares and update the economy. -void WarehouseSupply::remove_wares(WareIndex const id, uint32_t const count) -{ +void WarehouseSupply::remove_wares(WareIndex const id, uint32_t const count) { if (!count) return; m_wares.remove(id, count); - if (m_economy) // No economies in the editor + if (m_economy) // No economies in the editor m_economy->remove_wares(id, count); } - /// Add workers and update the economy. -void WarehouseSupply::add_workers(WareIndex const id, uint32_t const count) -{ +void WarehouseSupply::add_workers(WareIndex const id, uint32_t const count) { if (!count) return; - if (m_economy) // No economies in the editor + if (m_economy) // No economies in the editor m_economy->add_workers(id, count); m_workers.add(id, count); } - /** * Remove workers and update the economy. * Comments see add_workers */ -void WarehouseSupply::remove_workers(WareIndex const id, uint32_t const count) -{ +void WarehouseSupply::remove_workers(WareIndex const id, uint32_t const count) { if (!count) return; m_workers.remove(id, count); - if (m_economy) // No economies in the editor + if (m_economy) // No economies in the editor m_economy->remove_workers(id, count); } /// Return the position of the Supply, i.e. the owning Warehouse. -PlayerImmovable * WarehouseSupply::get_position(Game &) {return m_warehouse;} - +PlayerImmovable* WarehouseSupply::get_position(Game&) { + return m_warehouse; +} /// Warehouse supplies are never active. -bool WarehouseSupply::is_active() const {return false;} +bool WarehouseSupply::is_active() const { + return false; +} -bool WarehouseSupply::has_storage() const -{ +bool WarehouseSupply::has_storage() const { return true; } -void WarehouseSupply::get_ware_type(WareWorker & /* type */, WareIndex & /* ware */) const -{ - throw wexception - ("WarehouseSupply::get_ware_type: calling this is nonsensical"); +void WarehouseSupply::get_ware_type(WareWorker& /* type */, WareIndex& /* ware */) const { + throw wexception("WarehouseSupply::get_ware_type: calling this is nonsensical"); } -void WarehouseSupply::send_to_storage(Game &, Warehouse * /* wh */) -{ +void WarehouseSupply::send_to_storage(Game&, Warehouse* /* wh */) { throw wexception("WarehouseSupply::send_to_storage: should never be called"); } -uint32_t WarehouseSupply::nr_supplies - (const Game & game, const Request & req) const -{ +uint32_t WarehouseSupply::nr_supplies(const Game& game, const Request& req) const { if (req.get_type() == wwWORKER) - return - m_warehouse->count_workers - (game, req.get_index(), req.get_requirements()); + return m_warehouse->count_workers(game, req.get_index(), req.get_requirements()); // Calculate how many wares can be sent out - it might be that we need them // ourselves. E.g. for hiring new soldiers. @@ -217,18 +200,16 @@ // of *this* warehouse + 1 (+1 is important, as else the ware would directly // be taken back to the warehouse as the request of the warehouse would be // highered and would have the same value as the original request) - int32_t const y = - x + (req.get_priority(0) / 100) - - (m_warehouse->get_priority(wwWARE, req.get_index()) / 100) - 1; + int32_t const y = x + (req.get_priority(0) / 100) - + (m_warehouse->get_priority(wwWARE, req.get_index()) / 100) - 1; // But the number should never be higher than the number of wares available if (y > x) return x; return (x > 0) ? x : 0; } - /// Launch a ware. -WareInstance & WarehouseSupply::launch_ware(Game & game, const Request & req) { +WareInstance& WarehouseSupply::launch_ware(Game& game, const Request& req) { if (req.get_type() != wwWARE) throw wexception("WarehouseSupply::launch_ware: called for non-ware request"); if (!m_wares.stock(req.get_index())) @@ -238,29 +219,26 @@ } /// Launch a ware as worker. -Worker & WarehouseSupply::launch_worker(Game & game, const Request & req) -{ - return - m_warehouse->launch_worker - (game, req.get_index(), req.get_requirements()); +Worker& WarehouseSupply::launch_worker(Game& game, const Request& req) { + return m_warehouse->launch_worker(game, req.get_index(), req.get_requirements()); } - /* ============================== Warehouse Building ============================== */ - /// Warehouse Descr -WarehouseDescr::WarehouseDescr - (char const* const _name, char const* const _descname, - const std::string& directory, Profile& prof, Section& global_s, const TribeDescr& _tribe) - : BuildingDescr(MapObjectType::WAREHOUSE, _name, _descname, directory, prof, global_s, _tribe), - m_conquers (0), - m_heal_per_second (0) -{ +WarehouseDescr::WarehouseDescr(char const* const _name, + char const* const _descname, + const std::string& directory, + Profile& prof, + Section& global_s, + const TribeDescr& _tribe) + : BuildingDescr(MapObjectType::WAREHOUSE, _name, _descname, directory, prof, global_s, _tribe), + m_conquers(0), + m_heal_per_second(0) { m_heal_per_second = global_s.get_safe_int("heal_per_second"); if ((m_conquers = prof.get_safe_section("global").get_positive("conquers", 0))) m_workarea_info[m_conquers].insert(descname() + " conquer"); @@ -272,25 +250,21 @@ ============================== */ -Warehouse::Warehouse(const WarehouseDescr & warehouse_descr) : - Building(warehouse_descr), - m_supply(new WarehouseSupply(this)), - m_next_military_act(0), - m_portdock(nullptr) -{ +Warehouse::Warehouse(const WarehouseDescr& warehouse_descr) + : Building(warehouse_descr), + m_supply(new WarehouseSupply(this)), + m_next_military_act(0), + m_portdock(nullptr) { uint8_t nr_worker_types_without_cost = - warehouse_descr.tribe().worker_types_without_cost().size(); - m_next_worker_without_cost_spawn = - new uint32_t[nr_worker_types_without_cost]; + warehouse_descr.tribe().worker_types_without_cost().size(); + m_next_worker_without_cost_spawn = new uint32_t[nr_worker_types_without_cost]; for (int i = 0; i < nr_worker_types_without_cost; ++i) { m_next_worker_without_cost_spawn[i] = never(); } m_next_stock_remove_act = 0; } - -Warehouse::~Warehouse() -{ +Warehouse::~Warehouse() { delete m_supply; delete[] m_next_worker_without_cost_spawn; } @@ -299,22 +273,19 @@ * Try to bring the given \ref PlannedWorkers up to date with our game data. * Return \c false if \p pw cannot be salvaged. */ -bool Warehouse::_load_finish_planned_worker(PlannedWorkers & pw) -{ +bool Warehouse::_load_finish_planned_worker(PlannedWorkers& pw) { if (pw.index == INVALID_INDEX || !(pw.index < m_supply->get_workers().get_nrwareids())) return false; - const WorkerDescr * w_desc = descr().tribe().get_worker_descr(pw.index); + const WorkerDescr* w_desc = descr().tribe().get_worker_descr(pw.index); if (!w_desc || !w_desc->is_buildable()) return false; - const WorkerDescr::Buildcost & cost = w_desc->buildcost(); + const WorkerDescr::Buildcost& cost = w_desc->buildcost(); uint32_t idx = 0; - for - (WorkerDescr::Buildcost::const_iterator cost_it = cost.begin(); - cost_it != cost.end(); ++cost_it, ++idx) - { + for (WorkerDescr::Buildcost::const_iterator cost_it = cost.begin(); cost_it != cost.end(); + ++cost_it, ++idx) { WareWorker type; WareIndex ware; ware = descr().tribe().ware_index(cost_it->first); @@ -326,13 +297,10 @@ return false; if (idx < pw.requests.size()) { - if - (pw.requests[idx]->get_type() == type && - pw.requests[idx]->get_index() == ware) + if (pw.requests[idx]->get_type() == type && pw.requests[idx]->get_index() == ware) continue; - std::vector<Request *>::iterator req_it = - pw.requests.begin() + idx + 1; + std::vector<Request*>::iterator req_it = pw.requests.begin() + idx + 1; while (req_it != pw.requests.end()) { if ((*req_it)->get_type() == type && (*req_it)->get_index() == ware) break; @@ -345,19 +313,16 @@ } } - log - ("_load_finish_planned_worker: old savegame: " - "need to create new request for '%s'\n", - cost_it->first.c_str()); - pw.requests.insert - (pw.requests.begin() + idx, - new Request(*this, ware, &Warehouse::request_cb, type)); + log("_load_finish_planned_worker: old savegame: " + "need to create new request for '%s'\n", + cost_it->first.c_str()); + pw.requests.insert( + pw.requests.begin() + idx, new Request(*this, ware, &Warehouse::request_cb, type)); } while (pw.requests.size() > idx) { - log - ("_load_finish_planned_worker: old savegame: " - "removing outdated request.\n"); + log("_load_finish_planned_worker: old savegame: " + "removing outdated request.\n"); delete pw.requests.back(); pw.requests.pop_back(); } @@ -365,32 +330,30 @@ return true; } -void Warehouse::load_finish(EditorGameBase & egbase) { +void Warehouse::load_finish(EditorGameBase& egbase) { Building::load_finish(egbase); uint32_t next_spawn = never(); - const std::vector<WareIndex> & worker_types_without_cost = - descr().tribe().worker_types_without_cost(); + const std::vector<WareIndex>& worker_types_without_cost = + descr().tribe().worker_types_without_cost(); for (uint8_t i = worker_types_without_cost.size(); i;) { WareIndex const worker_index = worker_types_without_cost.at(--i); - if - (owner().is_worker_type_allowed(worker_index) && - m_next_worker_without_cost_spawn[i] == static_cast<uint32_t>(never())) - { + if (owner().is_worker_type_allowed(worker_index) && + m_next_worker_without_cost_spawn[i] == static_cast<uint32_t>(never())) { if (next_spawn == static_cast<uint32_t>(never())) - next_spawn = - schedule_act - (ref_cast<Game, EditorGameBase>(egbase), - WORKER_WITHOUT_COST_SPAWN_INTERVAL); + next_spawn = schedule_act( + ref_cast<Game, EditorGameBase>(egbase), WORKER_WITHOUT_COST_SPAWN_INTERVAL); m_next_worker_without_cost_spawn[i] = next_spawn; - log - ("WARNING: player %u is allowed to create worker type %s but his " - "%s %u at (%i, %i) does not have a next_spawn time set for that " - "worker type; setting it to %u\n", - owner().player_number(), - descr().tribe().get_worker_descr(worker_index)->descname().c_str(), - descr().descname().c_str(), serial(), get_position().x, get_position().y, - next_spawn); + log("WARNING: player %u is allowed to create worker type %s but his " + "%s %u at (%i, %i) does not have a next_spawn time set for that " + "worker type; setting it to %u\n", + owner().player_number(), + descr().tribe().get_worker_descr(worker_index)->descname().c_str(), + descr().descname().c_str(), + serial(), + get_position().x, + get_position().y, + next_spawn); } } @@ -408,13 +371,12 @@ } } -void Warehouse::init(EditorGameBase & egbase) -{ +void Warehouse::init(EditorGameBase& egbase) { Building::init(egbase); - WareIndex const nr_wares = descr().tribe().get_nrwares (); + WareIndex const nr_wares = descr().tribe().get_nrwares(); WareIndex const nr_workers = descr().tribe().get_nrworkers(); - m_supply->set_nrwares (nr_wares); + m_supply->set_nrwares(nr_wares); m_supply->set_nrworkers(nr_workers); m_ware_policy.resize(nr_wares, SP_Normal); @@ -423,17 +385,15 @@ // Even though technically, a warehouse might be completely empty, // we let warehouse see always for simplicity's sake (since there's // almost always going to be a carrier inside, that shouldn't hurt). - Player & player = owner(); + Player& player = owner(); if (upcast(Game, game, &egbase)) { - player.see_area - (Area<FCoords> - (egbase.map().get_fcoords(get_position()), descr().vision_range())); + player.see_area( + Area<FCoords>(egbase.map().get_fcoords(get_position()), descr().vision_range())); { - uint32_t const act_time = schedule_act - (*game, WORKER_WITHOUT_COST_SPAWN_INTERVAL); - const std::vector<WareIndex> & worker_types_without_cost = - descr().tribe().worker_types_without_cost(); + uint32_t const act_time = schedule_act(*game, WORKER_WITHOUT_COST_SPAWN_INTERVAL); + const std::vector<WareIndex>& worker_types_without_cost = + descr().tribe().worker_types_without_cost(); for (size_t i = 0; i < worker_types_without_cost.size(); ++i) { if (owner().is_worker_type_allowed(worker_types_without_cost.at(i))) { @@ -444,30 +404,25 @@ // m_next_military_act is not touched in the loading code. Is only needed // if there warehous is created in the game? I assume it's for the // conquer_radius thing - m_next_military_act = - schedule_act - (ref_cast<Game, EditorGameBase>(egbase), 1000); - - m_next_stock_remove_act = - schedule_act - (ref_cast<Game, EditorGameBase>(egbase), 4000); - - log("Message: adding (wh) (%s) %i \n", to_string(descr().type()).c_str(), player.player_number()); - send_message - (ref_cast<Game, EditorGameBase>(egbase), - "warehouse", - descr().descname(), - (boost::format(_("A new %s was added to your economy.")) - % descr().descname().c_str()).str(), - true); - } + m_next_military_act = schedule_act(ref_cast<Game, EditorGameBase>(egbase), 1000); + + m_next_stock_remove_act = schedule_act(ref_cast<Game, EditorGameBase>(egbase), 4000); + + log("Message: adding (wh) (%s) %i \n", + to_string(descr().type()).c_str(), + player.player_number()); + send_message(ref_cast<Game, EditorGameBase>(egbase), + "warehouse", + descr().descname(), + (boost::format(_("A new %s was added to your economy.")) % + descr().descname().c_str()).str(), + true); + } if (uint32_t const conquer_radius = descr().get_conquers()) - egbase.conquer_area - (PlayerArea<Area<FCoords> > - (player.player_number(), - Area<FCoords> - (egbase.map().get_fcoords(get_position()), conquer_radius))); + egbase.conquer_area(PlayerArea<Area<FCoords>>( + player.player_number(), + Area<FCoords>(egbase.map().get_fcoords(get_position()), conquer_radius))); if (descr().get_isport()) init_portdock(egbase); @@ -477,14 +432,15 @@ * Find a contiguous set of water fields close to the port for docking * and initialize the @ref PortDock instance. */ -void Warehouse::init_portdock(EditorGameBase & egbase) -{ +void Warehouse::init_portdock(EditorGameBase& egbase) { molog("Setting up port dock fields\n"); - Map & map = egbase.map(); + Map& map = egbase.map(); std::vector<Coords> dock = map.find_portdock(get_position()); if (dock.empty()) { - log("Attempting to setup port without neighboring water.\n"); + log("Attempting to setup port without neighboring water (coords: %3dx%3d).\n", + get_position().x, + get_position().y); return; } @@ -502,16 +458,30 @@ m_portdock->set_economy(get_economy()); } -void Warehouse::destroy(EditorGameBase & egbase) -{ +void Warehouse::destroy(EditorGameBase& egbase) { Building::destroy(egbase); } +// if the port still exists and we are in game we first try to restore the portdock +void Warehouse::restore_portdock_or_destroy(EditorGameBase& egbase) { + // re-init the portdock + Warehouse::init_portdock(egbase); + if (!m_portdock) { + log(" Portdock lost, removing the port now (coords: %3dx%3d)\n", + get_position().x, + get_position().y); + Building::destroy(egbase); + } +} + /// Destroy the warehouse. -void Warehouse::cleanup(EditorGameBase & egbase) -{ +void Warehouse::cleanup(EditorGameBase& egbase) { + + // storing object of the portdock if exists + PortDock* pd = nullptr; + if (egbase.objects().object_still_available(m_portdock)) { - m_portdock->remove(egbase); + pd = m_portdock; m_portdock = nullptr; } @@ -536,21 +506,32 @@ Map& map = egbase.map(); if (const uint32_t conquer_radius = descr().get_conquers()) - egbase.unconquer_area - (PlayerArea<Area<FCoords> > - (owner().player_number(), - Area<FCoords>(map.get_fcoords(get_position()), conquer_radius)), - m_defeating_player); + egbase.unconquer_area( + PlayerArea<Area<FCoords>>(owner().player_number(), + Area<FCoords>(map.get_fcoords(get_position()), conquer_radius)), + m_defeating_player); // Unsee the area that we started seeing in init() - Player & player = owner(); - player.unsee_area - (Area<FCoords>(map.get_fcoords(get_position()), descr().vision_range())); + Player& player = owner(); + player.unsee_area(Area<FCoords>(map.get_fcoords(get_position()), descr().vision_range())); + + log("Message: removing %s (player %i)\n", + to_string(descr().type()).c_str(), + player.player_number()); + send_message(ref_cast<Game, EditorGameBase>(egbase), + "warehouse", + descr().descname(), + (boost::format(_("A %s was destroyed.")) % descr().descname().c_str()).str(), + true); Building::cleanup(egbase); + + // if there was a portdock, removing it now + if (pd) { + pd->remove(egbase); + } } - /// Act regularly to create workers of buildable types without cost. According /// to intelligence, this is some highly advanced technology. Not only do the /// settlers have no problems with birth control, they do not even need anybody @@ -558,12 +539,11 @@ /// what the hell are they doing, killing useless tribesmen! The Borg? Or just /// like Soylent Green? Or maybe I should just stop writing comments that late /// at night ;-) -void Warehouse::act(Game & game, uint32_t const data) -{ +void Warehouse::act(Game& game, uint32_t const data) { uint32_t const gametime = game.get_gametime(); { - const std::vector<WareIndex> & worker_types_without_cost = - owner().tribe().worker_types_without_cost(); + const std::vector<WareIndex>& worker_types_without_cost = + owner().tribe().worker_types_without_cost(); for (size_t i = worker_types_without_cost.size(); i;) if (m_next_worker_without_cost_spawn[--i] <= gametime) { WareIndex const id = worker_types_without_cost.at(i); @@ -581,8 +561,7 @@ remove_workers(id, 1); } - m_next_worker_without_cost_spawn[i] = - schedule_act(game, tdelta); + m_next_worker_without_cost_spawn[i] = schedule_act(game, tdelta); } else m_next_worker_without_cost_spawn[i] = never(); } @@ -593,19 +572,15 @@ WareIndex const ware = descr().tribe().safe_worker_index("soldier"); if (m_incorporated_workers.count(ware)) { - WorkerList & soldiers = m_incorporated_workers[ware]; + WorkerList& soldiers = m_incorporated_workers[ware]; uint32_t total_heal = descr().get_heal_per_second(); // Using an explicit iterator, as we plan to erase some // of those guys - for - (WorkerList::iterator it = soldiers.begin(); - it != soldiers.end(); - ++it) - { + for (WorkerList::iterator it = soldiers.begin(); it != soldiers.end(); ++it) { // This is a safe cast: we know only soldiers can land in this // slot in the incorporated array - Soldier * soldier = static_cast<Soldier *>(*it); + Soldier* soldier = static_cast<Soldier*>(*it); // Soldier dead ... if (!soldier || soldier->get_current_hitpoints() == 0) { @@ -618,7 +593,6 @@ soldier->heal(total_heal); continue; } - } } m_next_military_act = schedule_act(game, 1000); @@ -639,11 +613,9 @@ Building::act(game, data); } - /// Transfer our registration to the new economy. -void Warehouse::set_economy(Economy * const e) -{ - Economy * const old = get_economy(); +void Warehouse::set_economy(Economy* const e) { + Economy* const old = get_economy(); if (old == e) return; @@ -657,7 +629,7 @@ Building::set_economy(e); for (const PlannedWorkers& pw : m_planned_workers) { - for (Request * req : pw.requests) { + for (Request* req : pw.requests) { req->set_economy(e); } } @@ -669,82 +641,62 @@ e->add_warehouse(*this); } - -const WareList & Warehouse::get_wares() const -{ +const WareList& Warehouse::get_wares() const { return m_supply->get_wares(); } - -const WareList & Warehouse::get_workers() const -{ +const WareList& Warehouse::get_workers() const { return m_supply->get_workers(); } -PlayerImmovable::Workers Warehouse::get_incorporated_workers() -{ +PlayerImmovable::Workers Warehouse::get_incorporated_workers() { PlayerImmovable::Workers all_workers; for (const std::pair<WareIndex, WorkerList>& worker_pair : m_incorporated_workers) { - for (Worker * worker : worker_pair.second) { + for (Worker* worker : worker_pair.second) { all_workers.push_back(worker); } } return all_workers; } - /// Magically create wares in this warehouse. Updates the economy accordingly. -void Warehouse::insert_wares(WareIndex const id, uint32_t const count) -{ +void Warehouse::insert_wares(WareIndex const id, uint32_t const count) { m_supply->add_wares(id, count); } - /// Magically destroy wares. -void Warehouse::remove_wares(WareIndex const id, uint32_t const count) -{ +void Warehouse::remove_wares(WareIndex const id, uint32_t const count) { m_supply->remove_wares(id, count); } - /// Magically create workers in this warehouse. Updates the economy accordingly. -void Warehouse::insert_workers(WareIndex const id, uint32_t const count) -{ +void Warehouse::insert_workers(WareIndex const id, uint32_t const count) { m_supply->add_workers(id, count); } - /// Magically destroy workers. -void Warehouse::remove_workers(WareIndex const id, uint32_t const count) -{ +void Warehouse::remove_workers(WareIndex const id, uint32_t const count) { m_supply->remove_workers(id, count); } - - /// Launch a carrier to fetch an ware from our flag. -bool Warehouse::fetch_from_flag(Game & game) -{ +bool Warehouse::fetch_from_flag(Game& game) { WareIndex const carrierid = descr().tribe().safe_worker_index("carrier"); - if (!m_supply->stock_workers(carrierid)) // XXX yep, let's cheat + if (!m_supply->stock_workers(carrierid)) // XXX yep, let's cheat insert_workers(carrierid, 1); - launch_worker(game, carrierid, Requirements()).start_task_fetchfromflag - (game); + launch_worker(game, carrierid, Requirements()).start_task_fetchfromflag(game); return true; } - /** * \return the number of workers that we can launch satisfying the given * requirements. */ -uint32_t Warehouse::count_workers - (const Game & /* game */, WareIndex ware, const Requirements & req) -{ +uint32_t Warehouse::count_workers(const Game& /* game */, WareIndex ware, const Requirements& req) { uint32_t sum = 0; do { @@ -752,7 +704,7 @@ // NOTE: This code lies about the TrainingAttributes of non-instantiated workers. if (m_incorporated_workers.count(ware)) { - for (Worker * worker : m_incorporated_workers[ware]) { + for (Worker* worker : m_incorporated_workers[ware]) { if (!req.check(*worker)) { // This is one of the workers in our sum. // But he is too stupid for this job @@ -769,9 +721,7 @@ /// Start a worker of a given type. The worker will /// be assigned a job by the caller. -Worker & Warehouse::launch_worker - (Game & game, WareIndex ware, const Requirements & req) -{ +Worker& Warehouse::launch_worker(Game& game, WareIndex ware, const Requirements& req) { do { if (m_supply->stock_workers(ware)) { uint32_t unincorporated = m_supply->stock_workers(ware); @@ -784,15 +734,15 @@ remove_no_longer_existing_workers(game, &m_incorporated_workers[ware]); WorkerList& incorporated_workers = m_incorporated_workers[ware]; - for (std::vector<Worker *>::iterator worker_iter = incorporated_workers.begin(); - worker_iter != incorporated_workers.end(); ++worker_iter) - { + for (std::vector<Worker*>::iterator worker_iter = incorporated_workers.begin(); + worker_iter != incorporated_workers.end(); + ++worker_iter) { Worker* worker = *worker_iter; --unincorporated; if (req.check(*worker)) { - worker->reset_tasks(game); // forget everything you did - worker->set_location(this); // back in a economy + worker->reset_tasks(game); // forget everything you did + worker->set_location(this); // back in a economy incorporated_workers.erase(worker_iter); m_supply->remove_workers(ware, 1); @@ -807,7 +757,7 @@ // Create a new one // NOTE: This code lies about the TrainingAttributes of the new worker m_supply->remove_workers(ware, 1); - const WorkerDescr & workerdescr = *descr().tribe().get_worker_descr(ware); + const WorkerDescr& workerdescr = *descr().tribe().get_worker_descr(ware); return workerdescr.create(game, owner(), this, m_position); } } @@ -820,13 +770,10 @@ } } while (ware != INVALID_INDEX); - throw wexception - ("Warehouse::launch_worker: worker does not actually exist"); + throw wexception("Warehouse::launch_worker: worker does not actually exist"); } - -void Warehouse::incorporate_worker(EditorGameBase & egbase, Worker* w) -{ +void Warehouse::incorporate_worker(EditorGameBase& egbase, Worker* w) { assert(w != nullptr); assert(w->get_owner() == &owner()); @@ -852,10 +799,10 @@ // Incorporate the worker if (!m_incorporated_workers.count(worker_index)) - m_incorporated_workers[worker_index] = std::vector<Worker *>(); + m_incorporated_workers[worker_index] = std::vector<Worker*>(); m_incorporated_workers[worker_index].push_back(w); - w->set_location(nullptr); // no longer in an economy + w->set_location(nullptr); // no longer in an economy if (upcast(Game, game, &egbase)) { // Bind the worker into this house, hide him on the map. @@ -866,9 +813,9 @@ /// Create an instance of a ware and make sure it gets /// carried out of the warehouse. -WareInstance & Warehouse::launch_ware(Game & game, WareIndex const ware_index) { +WareInstance& Warehouse::launch_ware(Game& game, WareIndex const ware_index) { // Create the ware - WareInstance & ware = *new WareInstance(ware_index, descr().tribe().get_ware_descr(ware_index)); + WareInstance& ware = *new WareInstance(ware_index, descr().tribe().get_ware_descr(ware_index)); ware.init(game); do_launch_ware(game, ware); @@ -877,15 +824,13 @@ return ware; } - /// Get a carrier to actually move this ware out of the warehouse. -void Warehouse::do_launch_ware(Game & game, WareInstance & ware) -{ +void Warehouse::do_launch_ware(Game& game, WareInstance& ware) { // Create a carrier WareIndex const carrierid = descr().tribe().worker_index("carrier"); - const WorkerDescr & workerdescr = *descr().tribe().get_worker_descr(carrierid); + const WorkerDescr& workerdescr = *descr().tribe().get_worker_descr(carrierid); - Worker & worker = workerdescr.create(game, owner(), this, m_position); + Worker& worker = workerdescr.create(game, owner(), this, m_position); // Yup, this is cheating. if (m_supply->stock_workers(carrierid)) @@ -895,23 +840,15 @@ worker.start_task_dropoff(game, ware); } - -void Warehouse::incorporate_ware(EditorGameBase & egbase, WareInstance* ware) -{ +void Warehouse::incorporate_ware(EditorGameBase& egbase, WareInstance* ware) { m_supply->add_wares(ware->descr_index(), 1); ware->destroy(egbase); } - /// Called when a transfer for one of the idle Requests completes. -void Warehouse::request_cb - (Game & game, - Request &, - WareIndex const ware, - Worker * const w, - PlayerImmovable & target) -{ - Warehouse & wh = ref_cast<Warehouse, PlayerImmovable>(target); +void Warehouse::request_cb( + Game& game, Request&, WareIndex const ware, Worker* const w, PlayerImmovable& target) { + Warehouse& wh = ref_cast<Warehouse, PlayerImmovable>(target); if (w) { w->schedule_incorporate(game); @@ -928,38 +865,35 @@ /** * Receive a ware from a transfer that was not associated to a \ref Request. */ -void Warehouse::receive_ware(Game & /* game */, WareIndex ware) -{ +void Warehouse::receive_ware(Game& /* game */, WareIndex ware) { m_supply->add_wares(ware, 1); } /** * Receive a worker from a transfer that was not associated to a \ref Request. */ -void Warehouse::receive_worker(Game & game, Worker & worker) -{ +void Warehouse::receive_worker(Game& game, Worker& worker) { worker.schedule_incorporate(game); } -Building & WarehouseDescr::create_object() const { +Building& WarehouseDescr::create_object() const { return *new Warehouse(*this); } - -bool Warehouse::can_create_worker(Game &, WareIndex const worker) const { +bool Warehouse::can_create_worker(Game&, WareIndex const worker) const { if (!(worker < m_supply->get_workers().get_nrwareids())) - throw wexception - ("worker type %d does not exists (max is %d)", - worker, m_supply->get_workers().get_nrwareids()); + throw wexception("worker type %d does not exists (max is %d)", + worker, + m_supply->get_workers().get_nrwareids()); - const WorkerDescr & w_desc = *descr().tribe().get_worker_descr(worker); + const WorkerDescr& w_desc = *descr().tribe().get_worker_descr(worker); assert(&w_desc); if (!w_desc.is_buildable()) return false; // see if we have the resources for (const std::pair<std::string, uint8_t>& buildcost : w_desc.buildcost()) { - const std::string & input_name = buildcost.first; + const std::string& input_name = buildcost.first; WareIndex id_w = descr().tribe().ware_index(input_name); if (id_w != INVALID_INDEX) { if (m_supply->stock_wares(id_w) < buildcost.second) @@ -968,27 +902,26 @@ if (m_supply->stock_workers(id_w) < buildcost.second) return false; } else - throw wexception - ("worker type %s needs \"%s\" to be built but that is neither " - "a ware type nor a worker type defined in the tribe %s", - w_desc.descname().c_str(), input_name.c_str(), - descr().tribe().name().c_str()); + throw wexception("worker type %s needs \"%s\" to be built but that is neither " + "a ware type nor a worker type defined in the tribe %s", + w_desc.descname().c_str(), + input_name.c_str(), + descr().tribe().name().c_str()); } return true; } - -void Warehouse::create_worker(Game & game, WareIndex const worker) { - assert(can_create_worker (game, worker)); - - const WorkerDescr & w_desc = *descr().tribe().get_worker_descr(worker); +void Warehouse::create_worker(Game& game, WareIndex const worker) { + assert(can_create_worker(game, worker)); + + const WorkerDescr& w_desc = *descr().tribe().get_worker_descr(worker); for (const std::pair<std::string, uint8_t>& buildcost : w_desc.buildcost()) { - const std::string & input = buildcost.first; + const std::string& input = buildcost.first; WareIndex const id_ware = descr().tribe().ware_index(input); if (id_ware != INVALID_INDEX) { - remove_wares (id_ware, buildcost.second); - //update statistic accordingly + remove_wares(id_ware, buildcost.second); + // update statistic accordingly owner().ware_consumed(id_ware, buildcost.second); } else remove_workers(descr().tribe().safe_worker_index(input), buildcost.second); @@ -1010,8 +943,7 @@ * Return the number of workers of the given type that we plan to * create in this warehouse. */ -uint32_t Warehouse::get_planned_workers(Game & /* game */, WareIndex index) const -{ +uint32_t Warehouse::get_planned_workers(Game& /* game */, WareIndex index) const { for (const PlannedWorkers& pw : m_planned_workers) { if (pw.index == index) return pw.amount; @@ -1025,23 +957,21 @@ * * This is the current stock plus any incoming transfers. */ -std::vector<uint32_t> Warehouse::calc_available_for_worker - (Game & /* game */, WareIndex index) const -{ - const WorkerDescr & w_desc = *descr().tribe().get_worker_descr(index); +std::vector<uint32_t> Warehouse::calc_available_for_worker(Game& /* game */, + WareIndex index) const { + const WorkerDescr& w_desc = *descr().tribe().get_worker_descr(index); std::vector<uint32_t> available; for (const std::pair<std::string, uint8_t>& buildcost : w_desc.buildcost()) { - const std::string & input_name = buildcost.first; + const std::string& input_name = buildcost.first; WareIndex id_w = descr().tribe().ware_index(input_name); if (id_w != INVALID_INDEX) { available.push_back(get_wares().stock(id_w)); } else if ((id_w = descr().tribe().worker_index(input_name)) != INVALID_INDEX) { available.push_back(get_workers().stock(id_w)); } else - throw wexception - ("Economy::_create_requested_worker: buildcost inconsistency '%s'", - input_name.c_str()); + throw wexception( + "Economy::_create_requested_worker: buildcost inconsistency '%s'", input_name.c_str()); } for (const PlannedWorkers& pw : m_planned_workers) { @@ -1057,14 +987,12 @@ return available; } - /** * Set the amount of workers we plan to create * of the given \p index to \p amount. */ -void Warehouse::plan_workers(Game & game, WareIndex index, uint32_t amount) -{ - PlannedWorkers * pw = nullptr; +void Warehouse::plan_workers(Game& game, WareIndex index, uint32_t amount) { + PlannedWorkers* pw = nullptr; for (PlannedWorkers& planned_worker : m_planned_workers) { if (planned_worker.index == index) { @@ -1082,22 +1010,17 @@ pw->index = index; pw->amount = 0; - const WorkerDescr & w_desc = *descr().tribe().get_worker_descr(pw->index); + const WorkerDescr& w_desc = *descr().tribe().get_worker_descr(pw->index); for (const std::pair<std::string, uint8_t>& buildcost : w_desc.buildcost()) { - const std::string & input_name = buildcost.first; + const std::string& input_name = buildcost.first; WareIndex id_w = descr().tribe().ware_index(input_name); if (id_w != INVALID_INDEX) { - pw->requests.push_back - (new Request - (*this, id_w, &Warehouse::request_cb, wwWARE)); + pw->requests.push_back(new Request(*this, id_w, &Warehouse::request_cb, wwWARE)); } else if ((id_w = descr().tribe().worker_index(input_name)) != INVALID_INDEX) { - pw->requests.push_back - (new Request - (*this, id_w, &Warehouse::request_cb, wwWORKER)); + pw->requests.push_back(new Request(*this, id_w, &Warehouse::request_cb, wwWORKER)); } else - throw wexception - ("plan_workers: bad buildcost '%s'", input_name.c_str()); + throw wexception("plan_workers: bad buildcost '%s'", input_name.c_str()); } } @@ -1109,10 +1032,8 @@ * See if we can create the workers of the given plan, * and update requests accordingly. */ -void Warehouse::_update_planned_workers - (Game & game, Warehouse::PlannedWorkers & pw) -{ - const WorkerDescr & w_desc = *descr().tribe().get_worker_descr(pw.index); +void Warehouse::_update_planned_workers(Game& game, Warehouse::PlannedWorkers& pw) { + const WorkerDescr& w_desc = *descr().tribe().get_worker_descr(pw.index); while (pw.amount && can_create_worker(game, pw.index)) { create_worker(game, pw.index); @@ -1121,7 +1042,7 @@ uint32_t idx = 0; for (const std::pair<std::string, uint8_t>& buildcost : w_desc.buildcost()) { - const std::string & input_name = buildcost.first; + const std::string& input_name = buildcost.first; uint32_t supply; WareIndex id_w = descr().tribe().ware_index(input_name); @@ -1130,14 +1051,12 @@ } else if ((id_w = descr().tribe().worker_index(input_name)) != INVALID_INDEX) { supply = m_supply->stock_workers(id_w); } else - throw wexception - ("_update_planned_workers: bad buildcost '%s'", input_name.c_str()); + throw wexception("_update_planned_workers: bad buildcost '%s'", input_name.c_str()); if (supply >= pw.amount * buildcost.second) pw.requests[idx]->set_count(0); else - pw.requests[idx]->set_count - (pw.amount * buildcost.second - supply); + pw.requests[idx]->set_count(pw.amount * buildcost.second - supply); ++idx; } @@ -1153,8 +1072,7 @@ * Needs to be called periodically, because some necessary supplies might arrive * due to idle transfers instead of by explicit request. */ -void Warehouse::_update_all_planned_workers(Game & game) -{ +void Warehouse::_update_all_planned_workers(Game& game) { uint32_t idx = 0; while (idx < m_planned_workers.size()) { _update_planned_workers(game, m_planned_workers[idx]); @@ -1168,51 +1086,35 @@ } } -void Warehouse::enable_spawn - (Game & game, uint8_t const worker_types_without_cost_index) -{ - assert - (m_next_worker_without_cost_spawn[worker_types_without_cost_index] - == - static_cast<uint32_t>(never())); +void Warehouse::enable_spawn(Game& game, uint8_t const worker_types_without_cost_index) { + assert(m_next_worker_without_cost_spawn[worker_types_without_cost_index] == + static_cast<uint32_t>(never())); m_next_worker_without_cost_spawn[worker_types_without_cost_index] = - schedule_act(game, WORKER_WITHOUT_COST_SPAWN_INTERVAL); + schedule_act(game, WORKER_WITHOUT_COST_SPAWN_INTERVAL); } -void Warehouse::disable_spawn(uint8_t const worker_types_without_cost_index) -{ - assert - (m_next_worker_without_cost_spawn[worker_types_without_cost_index] - != - static_cast<uint32_t>(never())); +void Warehouse::disable_spawn(uint8_t const worker_types_without_cost_index) { + assert(m_next_worker_without_cost_spawn[worker_types_without_cost_index] != + static_cast<uint32_t>(never())); m_next_worker_without_cost_spawn[worker_types_without_cost_index] = never(); } - -bool Warehouse::can_attack() -{ +bool Warehouse::can_attack() { return descr().get_conquers() > 0; } -void Warehouse::aggressor(Soldier & enemy) -{ +void Warehouse::aggressor(Soldier& enemy) { if (!descr().get_conquers()) return; - Game & game = ref_cast<Game, EditorGameBase>(owner().egbase()); - Map & map = game.map(); - if - (enemy.get_owner() == &owner() || - enemy.get_battle() || - descr().get_conquers() - <= - map.calc_distance(enemy.get_position(), get_position())) + Game& game = ref_cast<Game, EditorGameBase>(owner().egbase()); + Map& map = game.map(); + if (enemy.get_owner() == &owner() || enemy.get_battle() || + descr().get_conquers() <= map.calc_distance(enemy.get_position(), get_position())) return; - if - (game.map().find_bobs - (Area<FCoords>(map.get_fcoords(base_flag().get_position()), 2), - nullptr, - FindBobEnemySoldier(&owner()))) + if (game.map().find_bobs(Area<FCoords>(map.get_fcoords(base_flag().get_position()), 2), + nullptr, + FindBobEnemySoldier(&owner()))) return; WareIndex const soldier_index = descr().tribe().worker_index("soldier"); @@ -1221,20 +1123,17 @@ if (!count_workers(game, soldier_index, noreq)) return; - Soldier & defender = - ref_cast<Soldier, Worker>(launch_worker(game, soldier_index, noreq)); + Soldier& defender = ref_cast<Soldier, Worker>(launch_worker(game, soldier_index, noreq)); defender.start_task_defense(game, false); } -bool Warehouse::attack(Soldier & enemy) -{ - Game & game = ref_cast<Game, EditorGameBase>(owner().egbase()); +bool Warehouse::attack(Soldier& enemy) { + Game& game = ref_cast<Game, EditorGameBase>(owner().egbase()); WareIndex const soldier_index = descr().tribe().worker_index("soldier"); Requirements noreq; if (count_workers(game, soldier_index, noreq)) { - Soldier & defender = - ref_cast<Soldier, Worker>(launch_worker(game, soldier_index, noreq)); + Soldier& defender = ref_cast<Soldier, Worker>(launch_worker(game, soldier_index, noreq)); defender.start_task_defense(game, true); enemy.send_signal(game, "sleep"); return true; @@ -1245,45 +1144,36 @@ return false; } -void Warehouse::PlannedWorkers::cleanup() -{ +void Warehouse::PlannedWorkers::cleanup() { while (!requests.empty()) { delete requests.back(); requests.pop_back(); } } -Warehouse::StockPolicy Warehouse::get_ware_policy(WareIndex ware) const -{ +Warehouse::StockPolicy Warehouse::get_ware_policy(WareIndex ware) const { assert(ware < m_ware_policy.size()); return m_ware_policy[ware]; } -Warehouse::StockPolicy Warehouse::get_worker_policy(WareIndex ware) const -{ +Warehouse::StockPolicy Warehouse::get_worker_policy(WareIndex ware) const { assert(ware < m_worker_policy.size()); return m_worker_policy[ware]; } -Warehouse::StockPolicy Warehouse::get_stock_policy - (WareWorker waretype, WareIndex wareindex) const -{ +Warehouse::StockPolicy Warehouse::get_stock_policy(WareWorker waretype, WareIndex wareindex) const { if (waretype == wwWORKER) return get_worker_policy(wareindex); else return get_ware_policy(wareindex); } - -void Warehouse::set_ware_policy(WareIndex ware, Warehouse::StockPolicy policy) -{ +void Warehouse::set_ware_policy(WareIndex ware, Warehouse::StockPolicy policy) { assert(ware < m_ware_policy.size()); m_ware_policy[ware] = policy; } -void Warehouse::set_worker_policy - (WareIndex ware, Warehouse::StockPolicy policy) -{ +void Warehouse::set_worker_policy(WareIndex ware, Warehouse::StockPolicy policy) { assert(ware < m_worker_policy.size()); m_worker_policy[ware] = policy; } @@ -1292,8 +1182,7 @@ * Check if there are remaining wares with stock policy \ref SP_Remove, * and remove one of them if appropriate. */ -void Warehouse::check_remove_stock(Game & game) -{ +void Warehouse::check_remove_stock(Game& game) { if (base_flag().current_wares() < base_flag().total_capacity() / 2) { for (WareIndex ware = 0; ware < m_ware_policy.size(); ++ware) { if (get_ware_policy(ware) != SP_Remove || !get_wares().stock(ware)) @@ -1308,7 +1197,7 @@ if (get_worker_policy(widx) != SP_Remove || !get_workers().stock(widx)) continue; - Worker & worker = launch_worker(game, widx, Requirements()); + Worker& worker = launch_worker(game, widx, Requirements()); worker.start_task_leavebuilding(game, true); break; } @@ -1324,36 +1213,34 @@ /* * SoldierControl implementations */ -std::vector<Soldier *> Warehouse::present_soldiers() const -{ - std::vector<Soldier *> rv; +std::vector<Soldier*> Warehouse::present_soldiers() const { + std::vector<Soldier*> rv; WareIndex const ware = descr().tribe().safe_worker_index("soldier"); IncorporatedWorkers::const_iterator sidx = m_incorporated_workers.find(ware); if (sidx != m_incorporated_workers.end()) { - const WorkerList & soldiers = sidx->second; + const WorkerList& soldiers = sidx->second; - for (Worker * temp_soldier: soldiers) { - rv.push_back(static_cast<Soldier *>(temp_soldier)); + for (Worker* temp_soldier : soldiers) { + rv.push_back(static_cast<Soldier*>(temp_soldier)); } } return rv; } -int Warehouse::incorporate_soldier(EditorGameBase & egbase, Soldier & soldier) { +int Warehouse::incorporate_soldier(EditorGameBase& egbase, Soldier& soldier) { incorporate_worker(egbase, &soldier); return 0; } -int Warehouse::outcorporate_soldier(EditorGameBase & /* egbase */, Soldier & soldier) { +int Warehouse::outcorporate_soldier(EditorGameBase& /* egbase */, Soldier& soldier) { WareIndex const ware = descr().tribe().safe_worker_index("soldier"); if (m_incorporated_workers.count(ware)) { - WorkerList & soldiers = m_incorporated_workers[ware]; + WorkerList& soldiers = m_incorporated_workers[ware]; - WorkerList::iterator i = std::find - (soldiers.begin(), soldiers.end(), &soldier); + WorkerList::iterator i = std::find(soldiers.begin(), soldiers.end(), &soldier); soldiers.erase(i); m_supply->remove_workers(ware, 1); @@ -1366,13 +1253,10 @@ return 0; } -void Warehouse::log_general_info(const EditorGameBase & egbase) -{ +void Warehouse::log_general_info(const EditorGameBase& egbase) { Building::log_general_info(egbase); if (descr().get_isport()) molog("Port dock: %u\n", m_portdock ? m_portdock->serial() : 0); } - - } === modified file 'src/logic/warehouse.h' --- src/logic/warehouse.h 2014-09-19 12:54:54 +0000 +++ src/logic/warehouse.h 2014-11-19 21:19:43 +0000 @@ -136,6 +136,8 @@ void destroy(EditorGameBase &) override; + void restore_portdock_or_destroy(EditorGameBase &); + void act(Game & game, uint32_t data) override; void set_economy(Economy *) override; === modified file 'src/main.cc' --- src/main.cc 2014-09-18 18:46:22 +0000 +++ src/main.cc 2014-11-19 21:19:43 +0000 @@ -1,3 +1,40 @@ +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> +#include <unistd.h> /* * Copyright (C) 2002-2004, 2008-2009 by the Widelands Development Team * === modified file 'src/notifications/note_ids.h' --- src/notifications/note_ids.h 2014-09-10 19:54:01 +0000 +++ src/notifications/note_ids.h 2014-11-19 21:19:43 +0000 @@ -32,6 +32,7 @@ FieldPossession, FieldTransformed, ProductionSiteOutOfResources, + ShipMessage, }; #endif // end of include guard: WL_NOTIFICATIONS_NOTE_IDS_H
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp