diff --git a/libs/s25main/Replay.cpp b/libs/s25main/Replay.cpp index 17db28f78..fe291c42d 100644 --- a/libs/s25main/Replay.cpp +++ b/libs/s25main/Replay.cpp @@ -5,6 +5,7 @@ #include "Replay.h" #include "Savegame.h" #include "enum_cast.hpp" +#include "helpers/format.hpp" #include "network/PlayerGameCommands.h" #include "gameTypes/MapInfo.h" #include @@ -194,10 +195,25 @@ bool Replay::LoadHeader(const boost::filesystem::path& filepath) // TODO(Replay): Move before mapType to have it as early as possible. // Previously mapType was an unsigned short, i.e. in little endian the 2nd byte was always unused/zero subVersion_ = file_.ReadUnsignedChar(); + if(subVersion_ > currentReplayDataVersion) + { + lastErrorMsg = + helpers::format(_("Cannot play replay created with a more recent version (Current: %1%, Replay: %2%)"), + currentReplayDataVersion, subVersion_); + return false; + } + if(subVersion_ >= 1) gcVersion_ = file_.ReadUnsignedChar(); else gcVersion_ = 0; + if(gcVersion_ > gc::Deserializer::getCurrentVersion()) + { + lastErrorMsg = + helpers::format(_("Cannot play replay created with a more recent GC version (Current: %1%, Replay: %2%)"), + gc::Deserializer::getCurrentVersion(), gcVersion_); + return false; + } if(mapType_ == MapType::Savegame) { diff --git a/libs/s25main/buildings/BurnedWarehouse.cpp b/libs/s25main/buildings/BurnedWarehouse.cpp index 17818862f..b6f0db9ff 100644 --- a/libs/s25main/buildings/BurnedWarehouse.cpp +++ b/libs/s25main/buildings/BurnedWarehouse.cpp @@ -7,21 +7,22 @@ #include "EventManager.h" #include "GamePlayer.h" #include "SerializedGameData.h" -#include "WineLoader.h" #include "figures/nofPassiveWorker.h" +#include "helpers/Range.h" #include "pathfinding/PathConditionHuman.h" #include "random/Random.h" #include "world/GameWorld.h" +#include -/// Anzahl der Rausgeh-Etappen -const unsigned GO_OUT_PHASES = 10; -/// Länge zwischen zwei solchen Phasen -const unsigned PHASE_LENGTH = 2; +/// Number of "waves" of workers leaving +constexpr unsigned GO_OUT_PHASES = 10; +/// Time between those phases +constexpr unsigned PHASE_LENGTH = 2; BurnedWarehouse::BurnedWarehouse(const MapPoint pos, const unsigned char player, const PeopleArray& people) : noCoordBase(NodalObjectType::BurnedWarehouse, pos), player(player), go_out_phase(0), people(people) { - // Erstes Event anmelden + // First event GetEvMgr().AddEvent(this, PHASE_LENGTH, 0); } @@ -51,23 +52,19 @@ void BurnedWarehouse::HandleEvent(const unsigned /*id*/) { RTTR_Assert(go_out_phase != GO_OUT_PHASES); - std::array> possibleDirs; - unsigned possibleDirCt = 0; - - // Mögliche Richtungen zählen und speichern + // Determine valid directions for people + boost::container::static_vector> possibleDirs; PathConditionHuman pathChecker(*world); for(const auto dir : helpers::EnumRange{}) { if(pathChecker.IsNodeOk(world->GetNeighbour(pos, dir))) - possibleDirs[possibleDirCt++] = dir; + possibleDirs.push_back(dir); } - // GAR KEINE Richtungen? - if(possibleDirCt == 0) + if(possibleDirs.empty()) { - // Das ist traurig, dann muss die Titanic mit allen restlichen an Board leider untergehen + // No way out for figures -> all die and we can remove this object GetEvMgr().AddToKillList(world->RemoveFigure(pos, *this)); - // restliche Leute von der Inventur abziehen for(const auto i : helpers::enumRange()) world->GetPlayer(player).DecreaseInventoryJob(i, people[i]); @@ -76,60 +73,49 @@ void BurnedWarehouse::HandleEvent(const unsigned /*id*/) for(const auto job : helpers::enumRange()) { - // Anzahl ausrechnen, die in dieser Runde rausgeht + // In the last phase all remaining ones leave, else only some unsigned count; if(go_out_phase + 1 >= GO_OUT_PHASES) - count = people[job]; // Take all on last round + count = people[job]; else count = people[job] / (GO_OUT_PHASES - go_out_phase); - - if(count == 0 && wineaddon::isWineAddonJobType(job)) + if(count == 0) continue; - // Von der vorhandenen Abzahl abziehen + // Remove from inventory people[job] -= count; - // In Alle Richtungen verteilen - // Startrichtung zufällig bestimmen - unsigned start_dir = RANDOM_RAND(helpers::NumEnumValues_v); + // Distribute in all directions starting at a random one of the possible ones + const unsigned startIdx = (possibleDirs.size() <= 1u) ? 0 : RANDOM_RAND(possibleDirs.size()); + const unsigned numPeoplePerDir = count / possibleDirs.size(); - for(unsigned j = 0; j < possibleDirCt; ++j) + for(const unsigned j : helpers::range(possibleDirs.size())) { - // Aktuelle Richtung, die jetzt dran ist bestimmen - Direction dir = possibleDirs[j] + start_dir; - - // Anzahl jetzt für diese Richtung ausrechnen - unsigned numPeopleInDir = count / possibleDirCt; - // Bei letzter Richtung noch den übriggebliebenen Rest dazuaddieren - if(j + 1 == possibleDirCt) - numPeopleInDir += count % possibleDirCt; - - // Die Figuren schließlich rausschicken - for(unsigned z = 0; z < numPeopleInDir; ++z) + // Get current direction accounting for startIdx and hence possible wrap around + const unsigned idx = j + startIdx; + const Direction curDir = possibleDirs[idx < possibleDirs.size() ? idx : idx - possibleDirs.size()]; + // Take all in last direction + const auto curNumPeople = (j + 1u < possibleDirs.size()) ? numPeoplePerDir : count; + count -= curNumPeople; + for(const auto z : helpers::range(curNumPeople)) { - // Job erzeugen + // Create job and send moving into the current direction auto& figure = world->AddFigure(pos, std::make_unique(job, pos, player, nullptr)); - // Losrumirren in die jeweilige Richtung figure.StartWandering(GetObjId()); - figure.StartWalking(dir); + figure.StartWalking(curDir); } } } - // Nächste Runde + // Prepare next phase if any ++go_out_phase; - - // Nächste Runde anmelden bzw. sich selbst killen, wenn alle Runden erledigt sind if(go_out_phase == GO_OUT_PHASES) { - // fertig, sich selbst töten + // All done GetEvMgr().AddToKillList(world->RemoveFigure(pos, *this)); - // Prüfen, ob alle evakuiert wurden und keiner mehr an Board ist + // There shouldn't be any more for(unsigned int it : people) RTTR_Assert(it == 0); - } else - { - // Nächstes Event anmelden + } else // Not done yet GetEvMgr().AddEvent(this, PHASE_LENGTH, 0); - } } diff --git a/libs/s25main/figures/nofCharburner.cpp b/libs/s25main/figures/nofCharburner.cpp index 318c5cbba..4234f137b 100644 --- a/libs/s25main/figures/nofCharburner.cpp +++ b/libs/s25main/figures/nofCharburner.cpp @@ -88,7 +88,7 @@ void nofCharburner::WorkFinished() if(GetPointQuality(pos) != PointQuality::NotPossible) { // Delete previous elements - // Only environt objects and signs are allowed to be removed by the worker! + // Only environ objects and signs are allowed to be removed by the worker! // Otherwise just do nothing NodalObjectType noType = no->GetType(); @@ -105,7 +105,7 @@ void nofCharburner::WorkFinished() } /// Fragt abgeleitete Klasse, ob hier Platz bzw ob hier ein Baum etc steht, den z.B. der Holzfäller braucht -nofFarmhand::PointQuality nofCharburner::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofCharburner::GetPointQuality(const MapPoint pt, const bool isBeforeWork) const { noBase* no = world->GetNO(pt); @@ -117,11 +117,11 @@ nofFarmhand::PointQuality nofCharburner::GetPointQuality(const MapPoint pt) cons if(pileState == noCharburnerPile::State::Smoldering) return PointQuality::NotPossible; - // Wood stack which stell need resources? + // Wood stack which still need resources? if(pileState == noCharburnerPile::State::Wood) { - // Does it need resources and I don't have them hen starting new work (state = Waiting1)? - if(!workplace->WaresAvailable() && this->state == State::Waiting1) + // Only check for resources before going out + if(isBeforeWork && !workplace->WaresAvailable()) return PointQuality::NotPossible; else // Only second class, harvest all piles first before continue @@ -134,8 +134,8 @@ nofFarmhand::PointQuality nofCharburner::GetPointQuality(const MapPoint pt) cons } // Try to "plant" a new pile - // Still enough wares when starting new work (state = Waiting1)? - if(!workplace->WaresAvailable() && state == State::Waiting1) + // Enough wares when starting new work? + if(isBeforeWork && !workplace->WaresAvailable()) return PointQuality::NotPossible; // Der Platz muss frei sein diff --git a/libs/s25main/figures/nofCharburner.h b/libs/s25main/figures/nofCharburner.h index 8867be253..bdbd39ced 100644 --- a/libs/s25main/figures/nofCharburner.h +++ b/libs/s25main/figures/nofCharburner.h @@ -27,18 +27,16 @@ class nofCharburner : public nofFarmhand friend constexpr auto maxEnumValue(WareType) { return WareType::Grain; } private: - /// Malt den Arbeiter beim Arbeiten void DrawWorking(DrawPoint drawPt) override; - /// Id in jobs.bob or carrier.bob when carrying a ware + /// Has no ID in jobs.bob or carrier.bob [[noreturn]] unsigned short GetCarryID() const override; - /// Abgeleitete Klasse informieren, wenn sie anfängt zu arbeiten (Vorbereitungen) void WorkStarted() override; - /// Abgeleitete Klasse informieren, wenn fertig ist mit Arbeiten void WorkFinished() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; + using nofFarmhand::GetPointQuality; /// Inform derived class about the start of the whole working process (at the beginning when walking out of the /// house) diff --git a/libs/s25main/figures/nofFarmer.cpp b/libs/s25main/figures/nofFarmer.cpp index 3d2c2d176..4fbb2a47a 100644 --- a/libs/s25main/figures/nofFarmer.cpp +++ b/libs/s25main/figures/nofFarmer.cpp @@ -111,7 +111,7 @@ void nofFarmer::WorkFinished() } /// Returns the quality of this working point or determines if the worker can work here at all -nofFarmhand::PointQuality nofFarmer::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofFarmer::GetPointQuality(const MapPoint pt, bool /* isBeforeWork */) const { // Entweder gibts ein Getreidefeld, das wir abernten können... if(world->GetNO(pt)->GetType() == NodalObjectType::Grainfield) diff --git a/libs/s25main/figures/nofFarmer.h b/libs/s25main/figures/nofFarmer.h index 3ce73277d..87934e9ce 100644 --- a/libs/s25main/figures/nofFarmer.h +++ b/libs/s25main/figures/nofFarmer.h @@ -27,7 +27,8 @@ class nofFarmer : public nofFarmhand void WorkAborted() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; + using nofFarmhand::GetPointQuality; public: nofFarmer(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/libs/s25main/figures/nofFarmhand.cpp b/libs/s25main/figures/nofFarmhand.cpp index ce647aa15..c5230dbd7 100644 --- a/libs/s25main/figures/nofFarmhand.cpp +++ b/libs/s25main/figures/nofFarmhand.cpp @@ -43,14 +43,11 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) { case State::Work: { - // fertig mit Arbeiten --> dann müssen die "Folgen des Arbeitens" ausgeführt werden + // Done working -> Handle results of the work (step) WorkFinished(); - // Objekt wieder freigeben world->SetReserved(pos, false); - // Wieder nach Hause gehen StartWalkingHome(); - // Evtl. Sounds löschen if(was_sounding) { world->GetSoundMgr().stopSounds(*this); @@ -60,7 +57,7 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) break; case State::Waiting1: { - // Fertig mit warten --> anfangen zu arbeiten + // Start working after the initial wait period // Work radius const unsigned max_radius = [](Job job) { switch(job) @@ -77,9 +74,9 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) default: throw std::logic_error("Invalid job"); } }(job_); - // Additional radius delta r which is used when a point in radius r was found - // I.e. looks till radius r + delta r - const unsigned add_radius_when_found = [](Job job) { + // Number of additional radii in which points should be found + // I.e. 0 => Don't search for points further away than ones already found + const unsigned additionalRadiiToFind = [](Job job) { switch(job) { case Job::Woodcutter: @@ -95,50 +92,44 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) } }(job_); - bool points_found = false; - bool wait = false; - // Anzahl der Radien, wo wir gültige Punkte gefunden haben - unsigned radius_count = 0; + bool wait = false; // Whether waiting might make points available + // Number of radii in which points have been found + unsigned numFoundRadii = 0; helpers::EnumArray, PointQuality> available_points; for(MapCoord tx = world->GetXA(pos, Direction::West), r = 1; r <= max_radius; tx = world->GetXA(MapPoint(tx, pos.y), Direction::West), ++r) { - // Wurde ein Punkt in diesem Radius gefunden? - bool found_in_radius = false; + bool pointFound = false; - MapPoint t2(tx, pos.y); + MapPoint pt(tx, pos.y); for(const auto dir : helpers::enumRange(Direction::NorthEast)) { - for(MapCoord r2 = 0; r2 < r; t2 = world->GetNeighbour(t2, dir), ++r2) + for(MapCoord r2 = 0; r2 < r; pt = world->GetNeighbour(pt, dir), ++r2) { - if(IsPointAvailable(t2)) + const auto quality = GetPointQuality(pt, true); + if(quality != PointQuality::NotPossible && world->FindHumanPath(this->pos, pt, 20)) { - if(!world->GetNode(t2).reserved) + if(!world->GetNode(pt).reserved) { - available_points[GetPointQuality(t2)].push_back(MapPoint(t2)); - found_in_radius = true; - points_found = true; + available_points[quality].push_back(pt); + pointFound = true; } else if(job_ == Job::Stonemason) - { - // just wait a little bit longer wait = true; - } } } } - // Nur die zwei ADD_RADIUS_WHEN_FOUND Radien erst einmal nehmen - if(found_in_radius) + // Stop if we found enough radii with points + if(pointFound) { - if(radius_count++ == add_radius_when_found) + if(numFoundRadii++ == additionalRadiiToFind) break; } } - // Are there any objects at all? - if(points_found) + if(numFoundRadii > 0) { // Prefer points with lower class (better) for(auto& available_point : available_points) @@ -150,41 +141,35 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) } } - state = State::WalkToWorkpoint; - - // Wir arbeiten jetzt + // Start working workplace->is_working = true; workplace->StopNotWorking(); - // Punkt für uns reservieren + // Avoid others taking this point too world->SetReserved(dest, true); - // Anfangen zu laufen (erstmal aus dem Haus raus!) + // Walk out of the building first + state = State::WalkToWorkpoint; StartWalking(Direction::SouthEast); WalkingStarted(); - } else if(wait) - { - // We have to wait, since we do not know whether there are any unreachable or reserved points where - // there's more to get - current_ev = GetEvMgr().AddEvent(this, JOB_CONSTS[job_].wait1_length, 1); - - workplace->StartNotWorking(); } else { - switch(job_) + if(!wait) { - case Job::Stonemason: - case Job::Fisher: workplace->OnOutOfResources(); break; - case Job::Woodcutter: - world->GetNotifications().publish(BuildingNote( - BuildingNote::NoRessources, player, workplace->GetPos(), workplace->GetBuildingType())); - break; - default: break; + switch(job_) + { + case Job::Stonemason: + case Job::Fisher: workplace->OnOutOfResources(); break; + case Job::Woodcutter: + world->GetNotifications().publish(BuildingNote( + BuildingNote::NoRessources, player, workplace->GetPos(), workplace->GetBuildingType())); + break; + default: break; + } } - // Weiter warten, vielleicht gibts ja später wieder mal was + // Try to find something later current_ev = GetEvMgr().AddEvent(this, JOB_CONSTS[job_].wait1_length, 1); - workplace->StartNotWorking(); } } @@ -193,17 +178,6 @@ void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/) } } -bool nofFarmhand::IsPointAvailable(const MapPoint pt) const -{ - // Gibts an diesen Punkt überhaupt die nötigen Vorraussetzungen für den Beruf? - if(GetPointQuality(pt) != PointQuality::NotPossible) - { - // Gucken, ob ein Weg hinführt - return world->FindHumanPath(this->pos, pt, 20) != boost::none; - } else - return false; -} - void nofFarmhand::WalkToWorkpoint() { if(GetPointQuality(dest) == PointQuality::NotPossible) diff --git a/libs/s25main/figures/nofFarmhand.h b/libs/s25main/figures/nofFarmhand.h index 5f748250f..028811d0a 100644 --- a/libs/s25main/figures/nofFarmhand.h +++ b/libs/s25main/figures/nofFarmhand.h @@ -8,44 +8,46 @@ class SerializedGameData; class nobUsual; -/// Ein Landarbeiter geht raus aus seiner Hütte und arbeitet in "freier Natur" +/// Worker that walks out of his building and works at some point class nofFarmhand : public nofBuildingWorker { -protected: - /// Arbeitsziel, das der Arbeiter ansteuert - MapPoint dest; - +public: enum class PointQuality { - NotPossible, // Work is not possible at this position - Class1, /// Work is possible, points are prefered to other points - Class2, /// Work is possible, points are prefered to other points class 2 + NotPossible, /// Work is not possible at this position + Class1, /// Work is possible, points are preferred to other points + Class2, /// Work is possible, points are preferred to other points class 2 Class3 /// Work is possible, points are only chosen if there are no other class 1/2's }; + +protected: + /// Point the worker is going to work at + MapPoint dest; + friend constexpr auto maxEnumValue(PointQuality) { return PointQuality::Class3; } - /// Funktionen, die nur von der Basisklasse (noFigure) aufgerufen werden, wenn... + /// Called by base class (noFigure) after having walked to new point void WalkedDerived() override; - /// Arbeit musste wegen Arbeitsplatzverlust abgebrochen werden + /// Stop work as workplace becomes unavailable void WorkAborted() override; - /// Läuft zum Arbeitspunkt + /// Start walking to work point void WalkToWorkpoint(); - /// Trifft Vorbereitungen fürs nach Hause - Laufen + /// Get ready to work back to workplace void StartWalkingHome(); - /// Läuft wieder zu seiner Hütte zurück + /// Do next step for walking home void WalkHome(); - /// Inform derived class about the start of the whole working process (at the beginning when walking out of the - /// house) + /// Inform derived class about the start of the whole working process + /// (at the beginning when walking out of the house) virtual void WalkingStarted(); - /// Abgeleitete Klasse informieren, wenn sie anfängt zu arbeiten (Vorbereitungen) + /// Inform derived class when starting work at the work point virtual void WorkStarted() = 0; - /// Abgeleitete Klasse informieren, wenn fertig ist mit Arbeiten + /// Inform derived class when work at work point is done virtual void WorkFinished() = 0; - /// Zeichnen der Figur in sonstigen Arbeitslagen + /// Draw in other work-related states void DrawOtherStates(DrawPoint drawPt) override; public: @@ -55,8 +57,8 @@ class nofFarmhand : public nofBuildingWorker void Serialize(SerializedGameData& sgd) const override; void HandleDerivedEvent(unsigned id) override; - /// Findet heraus, ob der Beruf an diesem Punkt arbeiten kann - bool IsPointAvailable(MapPoint pt) const; - /// Returns the quality of this working point or determines if the worker can work here at all - virtual PointQuality GetPointQuality(MapPoint pt) const = 0; + /// Return the quality of this working point (including if the worker can work here at all) + /// isBeforeWork determines whether the evaluation happens before leaving for work or afterwards + virtual PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const = 0; + PointQuality GetPointQuality(MapPoint pt) const { return GetPointQuality(pt, false); } }; diff --git a/libs/s25main/figures/nofFisher.cpp b/libs/s25main/figures/nofFisher.cpp index 5277b37d6..5a4cf4c46 100644 --- a/libs/s25main/figures/nofFisher.cpp +++ b/libs/s25main/figures/nofFisher.cpp @@ -121,7 +121,7 @@ void nofFisher::WorkFinished() } /// Returns the quality of this working point or determines if the worker can work here at all -nofFarmhand::PointQuality nofFisher::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofFisher::GetPointQuality(const MapPoint pt, bool /* isBeforeWork */) const { // Der Punkt muss passierbar sein für Figuren if(!PathConditionHuman(*world).IsNodeOk(pt)) diff --git a/libs/s25main/figures/nofFisher.h b/libs/s25main/figures/nofFisher.h index c830bc1f4..ec85bba31 100644 --- a/libs/s25main/figures/nofFisher.h +++ b/libs/s25main/figures/nofFisher.h @@ -27,7 +27,7 @@ class nofFisher : public nofFarmhand void WorkFinished() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; public: nofFisher(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/libs/s25main/figures/nofForester.cpp b/libs/s25main/figures/nofForester.cpp index 5ea555d46..0c6aa4074 100644 --- a/libs/s25main/figures/nofForester.cpp +++ b/libs/s25main/figures/nofForester.cpp @@ -84,7 +84,7 @@ void nofForester::WorkFinished() } /// Returns the quality of this working point or determines if the worker can work here at all -nofFarmhand::PointQuality nofForester::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofForester::GetPointQuality(const MapPoint pt, bool /* isBeforeWork */) const { // Der Platz muss frei sein BlockingManner bm = world->GetNO(pt)->GetBM(); diff --git a/libs/s25main/figures/nofForester.h b/libs/s25main/figures/nofForester.h index 0e07717fe..8ee13295f 100644 --- a/libs/s25main/figures/nofForester.h +++ b/libs/s25main/figures/nofForester.h @@ -22,7 +22,7 @@ class nofForester : public nofFarmhand void WorkFinished() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; public: nofForester(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/libs/s25main/figures/nofStonemason.cpp b/libs/s25main/figures/nofStonemason.cpp index ed68eb6b1..9f6521e04 100644 --- a/libs/s25main/figures/nofStonemason.cpp +++ b/libs/s25main/figures/nofStonemason.cpp @@ -67,7 +67,7 @@ void nofStonemason::WorkFinished() } /// Returns the quality of this working point or determines if the worker can work here at all -nofFarmhand::PointQuality nofStonemason::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofStonemason::GetPointQuality(const MapPoint pt, bool /* isBeforeWork */) const { // An dieser Position muss es nur Stein geben return ((world->GetNO(pt)->GetType() == NodalObjectType::Granite) ? PointQuality::Class1 : diff --git a/libs/s25main/figures/nofStonemason.h b/libs/s25main/figures/nofStonemason.h index 21b7906d4..8de19a4c3 100644 --- a/libs/s25main/figures/nofStonemason.h +++ b/libs/s25main/figures/nofStonemason.h @@ -22,7 +22,7 @@ class nofStonemason : public nofFarmhand void WorkFinished() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; public: nofStonemason(MapPoint pos, unsigned char player, nobUsual* workplace); diff --git a/libs/s25main/figures/nofWinegrower.cpp b/libs/s25main/figures/nofWinegrower.cpp index 848e4cd21..3081269ec 100644 --- a/libs/s25main/figures/nofWinegrower.cpp +++ b/libs/s25main/figures/nofWinegrower.cpp @@ -114,7 +114,7 @@ void nofWinegrower::WorkFinished() world->RecalcBQAroundPoint(pos); } -nofFarmhand::PointQuality nofWinegrower::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofWinegrower::GetPointQuality(const MapPoint pt, const bool isBeforeWork) const { // Either a grapefield exists we can harvest... if(world->GetNO(pt)->GetType() == NodalObjectType::Grapefield) @@ -126,8 +126,8 @@ nofFarmhand::PointQuality nofWinegrower::GetPointQuality(const MapPoint pt) cons } else // or a free space, to place a new one { // Try to "plant" a new grapefield - // Still enough wares when starting new work (state = Waiting1)? - if(state == State::Waiting1 && !workplace->WaresAvailable()) + // Still enough wares when starting new work + if(isBeforeWork && !workplace->WaresAvailable()) return PointQuality::NotPossible; // Do not build on road diff --git a/libs/s25main/figures/nofWinegrower.h b/libs/s25main/figures/nofWinegrower.h index 4881b734d..144155a0f 100644 --- a/libs/s25main/figures/nofWinegrower.h +++ b/libs/s25main/figures/nofWinegrower.h @@ -32,7 +32,8 @@ class nofWinegrower : public nofFarmhand void WorkAborted() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; + using nofFarmhand::GetPointQuality; /// Inform derived class about the start of the whole working process (at the beginning when walking out of the /// house) @@ -41,7 +42,6 @@ class nofWinegrower : public nofFarmhand /// Draws the figure while returning home / entering the building (often carrying wares) void DrawWalkingWithWare(DrawPoint drawPt) override; /// Draws the vintner while walking - /// (overriding standard method of nofFarmhand) void DrawOtherStates(DrawPoint drawPt) override; bool AreWaresAvailable() const override; diff --git a/libs/s25main/figures/nofWoodcutter.cpp b/libs/s25main/figures/nofWoodcutter.cpp index 155572f9a..2d4adf4c0 100644 --- a/libs/s25main/figures/nofWoodcutter.cpp +++ b/libs/s25main/figures/nofWoodcutter.cpp @@ -86,7 +86,7 @@ void nofWoodcutter::WorkFinished() } /// Returns the quality of this working point or determines if the worker can work here at all -nofFarmhand::PointQuality nofWoodcutter::GetPointQuality(const MapPoint pt) const +nofFarmhand::PointQuality nofWoodcutter::GetPointQuality(const MapPoint pt, bool /* isBeforeWork */) const { // Gibt es hier an dieser Position einen Baum und ist dieser ausgewachsen? // außerdem keine Ananas fällen! diff --git a/libs/s25main/figures/nofWoodcutter.h b/libs/s25main/figures/nofWoodcutter.h index 8e61bf5f4..954930398 100644 --- a/libs/s25main/figures/nofWoodcutter.h +++ b/libs/s25main/figures/nofWoodcutter.h @@ -22,7 +22,7 @@ class nofWoodcutter : public nofFarmhand void WorkFinished() override; /// Returns the quality of this working point or determines if the worker can work here at all - PointQuality GetPointQuality(MapPoint pt) const override; + PointQuality GetPointQuality(MapPoint pt, bool isBeforeWork) const override; /// wird aufgerufen, wenn die Arbeit abgebrochen wird (von nofBuildingWorker aufgerufen) void WorkAborted() override; diff --git a/tests/s25Main/autoplay/main.cpp b/tests/s25Main/autoplay/main.cpp index 47810e264..8454eb222 100644 --- a/tests/s25Main/autoplay/main.cpp +++ b/tests/s25Main/autoplay/main.cpp @@ -114,6 +114,8 @@ BOOST_AUTO_TEST_CASE(Play200kReplay) // Map: Big Slaughter v2 // 7 x Hard KI // 2 KIs each in Teams 1-3, 1 in Team 4 + // Player KI without team ("WINTER" + F10) + // Default addon settings // 200k GFs run (+ a bit) const boost::filesystem::path replayPath = rttr::test::rttrBaseDir / "tests" / "testData" / "200kGFs.rpl"; playReplay(replayPath); @@ -122,8 +124,8 @@ BOOST_AUTO_TEST_CASE(Play200kReplay) BOOST_AUTO_TEST_CASE(PlaySeaReplay) { // Map: Island by Island - // 2 x Hard KI + Player KI - // No teams, Sea attacks enabled, ships fast + // 2 x Hard KI + Player KI ("WINTER" + F10) + // No teams, Sea attacks enabled (harbors block), ships fast // 300k GFs run (+ a bit) const boost::filesystem::path replayPath = rttr::test::rttrBaseDir / "tests" / "testData" / "SeaMap300kGfs.rpl"; playReplay(replayPath); diff --git a/tests/s25Main/integration/testFarmer.cpp b/tests/s25Main/integration/testFarmer.cpp index e9661d01e..a54573209 100644 --- a/tests/s25Main/integration/testFarmer.cpp +++ b/tests/s25Main/integration/testFarmer.cpp @@ -35,19 +35,22 @@ struct FarmerFixture : public WorldFixture BOOST_FIXTURE_TEST_CASE(FarmFieldPlanting, FarmerFixture) { initGameRNG(); + const auto isPointAvailable = [farmer = this->farmer](const MapPoint pt) { + return farmer->GetPointQuality(pt) != nofFarmhand::PointQuality::NotPossible; + }; std::vector radius1Pts = world.GetPointsInRadiusWithCenter(farmPt, 1); // First check points for validity // Cannot build directly next to farm for(MapPoint pt : radius1Pts) - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(pt)); + BOOST_TEST_REQUIRE(!isPointAvailable(pt)); // Can build everywhere but on road for(unsigned dir = 0; dir < 12; dir++) { if(dir == 11) - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, dir))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, dir))); else - BOOST_TEST_REQUIRE(farmer->IsPointAvailable(world.GetNeighbour2(farmPt, dir))); + BOOST_TEST_REQUIRE(isPointAvailable(world.GetNeighbour2(farmPt, dir))); } // Not on non-vital terrain DescIdx tUnvital(0); @@ -57,30 +60,30 @@ BOOST_FIXTURE_TEST_CASE(FarmFieldPlanting, FarmerFixture) break; } world.GetNodeWriteable(world.GetNeighbour2(farmPt, 3)).t1 = tUnvital; - BOOST_TEST_REQUIRE(farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 2))); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 3))); - BOOST_TEST_REQUIRE(farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 4))); + BOOST_TEST_REQUIRE(isPointAvailable(world.GetNeighbour2(farmPt, 2))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 3))); + BOOST_TEST_REQUIRE(isPointAvailable(world.GetNeighbour2(farmPt, 4))); world.GetNodeWriteable(world.GetNeighbour2(farmPt, 3)).t2 = tUnvital; - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 3))); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 4))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 3))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 4))); // Env obj is allowed world.SetNO(world.GetNeighbour2(farmPt, 5), new noEnvObject(world.GetNeighbour2(farmPt, 5), 0)); - BOOST_TEST_REQUIRE(farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 5))); + BOOST_TEST_REQUIRE(isPointAvailable(world.GetNeighbour2(farmPt, 5))); // On bld and next to it is not allowed world.SetBuildingSite(BuildingType::Watchtower, world.GetNeighbour2(farmPt, 6), 0); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 6))); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 7))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 6))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 7))); // On or next to grain field is not allowed const MapPoint grainFieldPos = world.GetNeighbour2(farmPt, 0); auto* grainField = new noGrainfield(grainFieldPos); world.SetNO(grainFieldPos, grainField); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(grainFieldPos)); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 1))); + BOOST_TEST_REQUIRE(!isPointAvailable(grainFieldPos)); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 1))); const MapPoint grainFieldPos2 = world.GetNeighbour(world.GetNeighbour2(farmPt, 2), Direction::NorthWest); world.SetNO(grainFieldPos2, new noGrainfield(grainFieldPos2)); - BOOST_TEST_REQUIRE(!farmer->IsPointAvailable(world.GetNeighbour2(farmPt, 0))); + BOOST_TEST_REQUIRE(!isPointAvailable(world.GetNeighbour2(farmPt, 0))); - // Test farmer behaviour + // Test farmer behavior for(unsigned i = 0; i < 2; i++) { // Let the farmer pick a field diff --git a/tests/testData/200kGFs.rpl b/tests/testData/200kGFs.rpl index 9841fe2d2..8ff510f76 100644 Binary files a/tests/testData/200kGFs.rpl and b/tests/testData/200kGFs.rpl differ diff --git a/tests/testData/SeaMap300kGfs.rpl b/tests/testData/SeaMap300kGfs.rpl index 6ca96f2c6..d6fc317ba 100644 Binary files a/tests/testData/SeaMap300kGfs.rpl and b/tests/testData/SeaMap300kGfs.rpl differ