• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 26160728046

20 May 2026 11:52AM UTC coverage: 50.29% (+0.006%) from 50.284%
26160728046

Pull #1941

github

web-flow
Merge c6021ce99 into 57b082981
Pull Request #1941: Adjust working ranges of forester, woodcutter and stonemason (3 Addons)

12 of 20 new or added lines in 4 files covered. (60.0%)

1 existing line in 1 file now uncovered.

23138 of 46009 relevant lines covered (50.29%)

43340.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.76
/libs/s25main/figures/nofFarmhand.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "nofFarmhand.h"
6
#include "EventManager.h"
7
#include "SerializedGameData.h"
8
#include "SoundManager.h"
9
#include "buildings/nobUsual.h"
10
#include "notifications/BuildingNote.h"
11
#include "random/Random.h"
12
#include "world/GameWorld.h"
13
#include "gameData/JobConsts.h"
14

15
#include "GlobalGameSettings.h"
16
#include "addons/AddonForesterReachRadius.h"
17
#include "addons/AddonWoodcutterReachRadius.h"
18
#include "addons/AddonStonemasonReachRadius.h"
19

20
nofFarmhand::nofFarmhand(const Job job, const MapPoint pos, const unsigned char player, nobUsual* workplace)
4✔
21
    : nofBuildingWorker(job, pos, player, workplace), dest(0, 0)
4✔
22
{}
4✔
23

24
void nofFarmhand::Serialize(SerializedGameData& sgd) const
×
25
{
26
    nofBuildingWorker::Serialize(sgd);
×
27

28
    helpers::pushPoint(sgd, dest);
×
29
}
×
30

31
nofFarmhand::nofFarmhand(SerializedGameData& sgd, const unsigned obj_id)
×
32
    : nofBuildingWorker(sgd, obj_id), dest(sgd.PopMapPoint())
×
33
{}
×
34

35
unsigned nofFarmhand::GetWorkRadius(const Job job)
25✔
36
{
37
    switch(job)
25✔
38
    {
39
        case Job::Carpenter: return 0;
×
40
        case Job::Hunter:
25✔
41
        case Job::Farmer:
42
        case Job::Winegrower: return 2;
25✔
43
        case Job::CharBurner: return 3;
×
44
        case Job::Woodcutter:
×
45
        {
NEW
46
            const unsigned sel = world->GetGGS().getSelection(AddonId::WOODCUTTER_REACH_RADIUS);
×
NEW
47
            return woodcutterRadiusValues[sel];
×
48
        }
NEW
49
        case Job::Forester:
×
50
        {
NEW
51
            const unsigned sel = world->GetGGS().getSelection(AddonId::FORESTER_REACH_RADIUS);
×
NEW
52
            return foresterRadiusValues[sel];
×
53
        }
54
        case Job::Fisher: return 7;
×
NEW
55
        case Job::Stonemason:
×
56
        {
NEW
57
            const unsigned sel = world->GetGGS().getSelection(AddonId::STONEMASON_REACH_RADIUS);
×
NEW
58
            return stonemasonRadiusValues[sel];
×
59
        }
UNCOV
60
        default: throw std::logic_error("Invalid job");
×
61
    }
62
}
63

64
void nofFarmhand::WalkedDerived()
21✔
65
{
66
    switch(state)
21✔
67
    {
68
        case State::WalkToWorkpoint: WalkToWorkpoint(); break;
13✔
69
        case State::WalkingHome: WalkHome(); break;
8✔
70
        default: break;
×
71
    }
72
}
21✔
73

74
void nofFarmhand::HandleDerivedEvent(const unsigned /*id*/)
29✔
75
{
76
    switch(state)
29✔
77
    {
78
        case State::Work:
5✔
79
        {
80
            // Done working -> Handle results of the work (step)
81
            WorkFinished();
5✔
82
            world->SetReserved(pos, false);
5✔
83
            StartWalkingHome();
5✔
84

85
            if(was_sounding)
5✔
86
            {
87
                world->GetSoundMgr().stopSounds(*this);
×
88
                was_sounding = false;
×
89
            }
90
        }
91
        break;
5✔
92
        case State::Waiting1:
24✔
93
        {
94
            // Start working after the initial wait period
95
            // Work radius
96
            const unsigned max_radius = GetWorkRadius(job_);
24✔
97
            // Number of additional radii in which points should be found
98
            // I.e. 0 => Don't search for points further away than ones already found
99
            const unsigned additionalRadiiToFind = [](Job job) {
24✔
100
                switch(job)
24✔
101
                {
102
                    case Job::Woodcutter:
24✔
103
                    case Job::Fisher:
104
                    case Job::Forester:
105
                    case Job::Carpenter:
106
                    case Job::Hunter:
107
                    case Job::Farmer:
108
                    case Job::CharBurner:
109
                    case Job::Winegrower: return 1;
24✔
110
                    case Job::Stonemason: return 0;
×
111
                    default: throw std::logic_error("Invalid job");
×
112
                }
113
            }(job_);
24✔
114

115
            bool wait = false; // Whether waiting might make points available
24✔
116
            // Number of radii in which points have been found
117
            unsigned numFoundRadii = 0;
24✔
118

119
            helpers::EnumArray<std::vector<MapPoint>, PointQuality> available_points;
48✔
120

121
            for(MapCoord tx = world->GetXA(pos, Direction::West), r = 1; r <= max_radius;
72✔
122
                tx = world->GetXA(MapPoint(tx, pos.y), Direction::West), ++r)
48✔
123
            {
124
                bool pointFound = false;
48✔
125

126
                MapPoint pt(tx, pos.y);
48✔
127
                for(const auto dir : helpers::enumRange(Direction::NorthEast))
768✔
128
                {
129
                    for(MapCoord r2 = 0; r2 < r; pt = world->GetNeighbour(pt, dir), ++r2)
720✔
130
                    {
131
                        const auto quality = GetPointQuality(pt, true);
432✔
132
                        if(quality != PointQuality::NotPossible && world->FindHumanPath(this->pos, pt, 20))
432✔
133
                        {
134
                            if(!world->GetNode(pt).reserved)
11✔
135
                            {
136
                                available_points[quality].push_back(pt);
11✔
137
                                pointFound = true;
11✔
138
                            } else if(job_ == Job::Stonemason)
×
139
                                wait = true;
×
140
                        }
141
                    }
142
                }
143

144
                // Stop if we found enough radii with points
145
                if(pointFound)
48✔
146
                {
147
                    if(numFoundRadii++ == additionalRadiiToFind)
5✔
148
                        break;
×
149
                }
150
            }
151

152
            if(numFoundRadii > 0)
24✔
153
            {
154
                // Prefer points with lower class (better)
155
                for(auto& available_point : available_points)
14✔
156
                {
157
                    if(!available_point.empty())
14✔
158
                    {
159
                        dest = RANDOM_ELEMENT(available_point);
5✔
160
                        break;
5✔
161
                    }
162
                }
163

164
                // Start working
165
                workplace->is_working = true;
5✔
166
                workplace->StopNotWorking();
5✔
167

168
                // Avoid others taking this point too
169
                world->SetReserved(dest, true);
5✔
170

171
                // Walk out of the building first
172
                state = State::WalkToWorkpoint;
5✔
173
                StartWalking(Direction::SouthEast);
5✔
174
                WalkingStarted();
5✔
175
            } else
176
            {
177
                if(!wait)
19✔
178
                {
179
                    switch(job_)
19✔
180
                    {
181
                        case Job::Stonemason:
×
182
                        case Job::Fisher: workplace->OnOutOfResources(); break;
×
183
                        case Job::Woodcutter:
×
184
                            world->GetNotifications().publish(BuildingNote(
×
185
                              BuildingNote::NoRessources, player, workplace->GetPos(), workplace->GetBuildingType()));
×
186
                            break;
×
187
                        default: break;
19✔
188
                    }
189
                }
190

191
                // Try to find something later
192
                current_ev = GetEvMgr().AddEvent(this, JOB_CONSTS[job_].wait1_length, 1);
19✔
193
                workplace->StartNotWorking();
19✔
194
            }
195
        }
196
        break;
24✔
197
        default: break;
×
198
    }
199
}
29✔
200

201
void nofFarmhand::WalkToWorkpoint()
13✔
202
{
203
    if(GetPointQuality(dest) == PointQuality::NotPossible)
13✔
204
    {
205
        // Point became invalid -> Abort and go home
206
        WorkAborted();
×
207
        StartWalkingHome();
×
208
    } else if(pos == dest)
13✔
209
    {
210
        // Arrived at target -> Start working
211
        state = State::Work;
5✔
212
        current_ev = GetEvMgr().AddEvent(this, JOB_CONSTS[job_].work_length, 1);
5✔
213
        WorkStarted();
5✔
214
    } else
215
    {
216
        // Keep on walking if possible
217
        const auto dir = world->FindHumanPath(pos, dest, 20);
8✔
218
        if(dir)
8✔
219
            StartWalking(*dir);
8✔
220
        else
221
        {
222
            // Point now unreachable -> abort and go gome
223
            WorkAborted();
×
224
            StartWalkingHome();
×
225
        }
226
    }
227
}
13✔
228

229
void nofFarmhand::StartWalkingHome()
5✔
230
{
231
    state = State::WalkingHome;
5✔
232
    // Fahne vor dem Gebäude anpeilen
233
    dest = world->GetNeighbour(workplace->GetPos(), Direction::SouthEast);
5✔
234

235
    // Zu Laufen anfangen
236
    WalkHome();
5✔
237
}
5✔
238

239
void nofFarmhand::WalkHome()
13✔
240
{
241
    // Sind wir zu Hause angekommen? (genauer an der Flagge !!)
242
    if(pos == dest)
13✔
243
    {
244
        // Weiteres übernimmt nofBuildingWorker
245
        WorkingReady();
5✔
246
        return;
5✔
247
    }
248

249
    const auto dir = world->FindHumanPath(pos, dest, 40);
8✔
250
    // Weg suchen und ob wir überhaupt noch nach Hause kommen
251
    if(!dir)
8✔
252
    {
253
        // Kein Weg führt mehr nach Hause--> Rumirren
254
        AbrogateWorkplace();
×
255
        StartWandering();
×
256
        Wander();
×
257
    } else
258
    {
259
        // All good, let's start walking there
260
        StartWalking(*dir);
8✔
261
    }
262
}
263

264
void nofFarmhand::WorkAborted()
×
265
{
266
    // Platz freigeben, falls man gerade arbeitet
267
    if(state == State::Work || state == State::WalkToWorkpoint)
×
268
        world->SetReserved(dest, false);
×
269
}
×
270

271
/// Zeichnen der Figur in sonstigen Arbeitslagen
272
void nofFarmhand::DrawOtherStates(DrawPoint drawPt)
×
273
{
274
    switch(state)
×
275
    {
276
        case State::WalkToWorkpoint:
×
277
        {
278
            // Normales Laufen zeichnen
279
            DrawWalking(drawPt);
×
280
        }
281
        break;
×
282
        default: return;
×
283
    }
284
}
285

286
/// Inform derived class about the start of the whole working process (at the beginning when walking out of the house)
287
void nofFarmhand::WalkingStarted() {}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc