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

Return-To-The-Roots / s25client / 22239234288

20 Feb 2026 08:04PM UTC coverage: 50.316% (-0.5%) from 50.79%
22239234288

Pull #1720

github

web-flow
Merge 2f74b463f into ed1e143dc
Pull Request #1720: Add leather addon

282 of 1072 new or added lines in 65 files covered. (26.31%)

47 existing lines in 28 files now uncovered.

23027 of 45765 relevant lines covered (50.32%)

42926.09 hits per line

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

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

5
#include "nofMetalworker.h"
6
#include "EventManager.h"
7
#include "GamePlayer.h"
8
#include "GlobalGameSettings.h"
9
#include "Loader.h"
10
#include "SerializedGameData.h"
11
#include "SoundManager.h"
12
#include "addons/const_addons.h"
13
#include "buildings/nobUsual.h"
14
#include "helpers/containerUtils.h"
15
#include "network/GameClient.h"
16
#include "notifications/NotificationManager.h"
17
#include "notifications/ToolNote.h"
18
#include "ogl/glArchivItem_Bitmap_Player.h"
19
#include "postSystem/PostMsg.h"
20
#include "random/Random.h"
21
#include "world/GameWorld.h"
22
#include "gameData/ToolConsts.h"
23
#include "s25util/Log.h"
24

25
nofMetalworker::nofMetalworker(const MapPoint pos, const unsigned char player, nobUsual* workplace)
3✔
26
    : nofWorkman(Job::Metalworker, pos, player, workplace)
3✔
27
{
28
    toolOrderSub = world->GetNotifications().subscribe<ToolNote>([this](const ToolNote& note) {
6✔
29
        if((note.type == ToolNote::OrderPlaced || note.type == ToolNote::SettingsChanged)
16✔
30
           && note.player == this->player)
9✔
31
            CheckForOrders();
9✔
32
    });
3✔
33
}
3✔
34

35
nofMetalworker::nofMetalworker(SerializedGameData& sgd, const unsigned obj_id) : nofWorkman(sgd, obj_id)
×
36
{
37
    if(sgd.GetGameDataVersion() < 5)
×
38
    {
39
        const auto iWare = sgd.PopUnsignedChar();
×
40
        // GoodType::Nothing is moved because of adding new wares due addons
41
        // The old GoodType::Nothing is now GoodType::Grapes
NEW
42
        if(iWare == rttr::enum_cast(GoodType::Grapes))
×
UNCOV
43
            nextProducedTool = boost::none;
×
44
        else
45
            nextProducedTool = GoodType(iWare);
×
46
    } else
47
        nextProducedTool = sgd.PopOptionalEnum<GoodType>();
×
48
    if(state == State::EnterBuilding && current_ev == nullptr && !ware && !nextProducedTool)
×
49
    {
50
        LOG.write("Found invalid metalworker. Assuming corrupted savegame -> Trying to fix this. If you encounter this "
×
51
                  "with a new game, "
52
                  "report this!");
53
        RTTR_Assert(false);
×
54
        state = State::WaitingForWaresOrProductionStopped;
55
        current_ev = GetEvMgr().AddEvent(this, 1000, 2);
56
    }
57
    toolOrderSub = world->GetNotifications().subscribe<ToolNote>([this](const ToolNote& note) {
×
58
        if((note.type == ToolNote::OrderPlaced || note.type == ToolNote::SettingsChanged)
×
59
           && note.player == this->player)
×
60
            CheckForOrders();
×
61
    });
×
62
}
×
63

64
void nofMetalworker::Serialize(SerializedGameData& sgd) const
×
65
{
66
    nofWorkman::Serialize(sgd);
×
67
    sgd.PushOptionalEnum<uint8_t>(nextProducedTool);
×
68
}
×
69

70
void nofMetalworker::DrawWorking(DrawPoint drawPt)
×
71
{
72
    constexpr helpers::EnumArray<DrawPoint, Nation> offsets = {{{-11, -13}, {31, 5}, {32, 6}, {30, 10}, {28, 5}}};
×
73

74
    const unsigned now_id = GAMECLIENT.Interpolate(230, current_ev);
×
75

76
    LOADER.GetPlayerImage("rom_bobs", 190 + (now_id % 23))
×
77
      ->DrawFull(drawPt + offsets[workplace->GetNation()], COLOR_WHITE, world->GetPlayer(workplace->GetPlayer()).color);
×
78

79
    // Hämmer-Sound
80
    if(now_id % 23 == 3 || now_id % 23 == 7)
×
81
    {
82
        world->GetSoundMgr().playNOSound(72, *this, now_id, 100);
×
83
        was_sounding = true;
×
84
    }
85
    // Säge-Sound 1
86
    else if(now_id % 23 == 9)
×
87
    {
88
        world->GetSoundMgr().playNOSound(54, *this, now_id);
×
89
        was_sounding = true;
×
90
    } else if(now_id % 23 == 17)
×
91
    {
92
        world->GetSoundMgr().playNOSound(55, *this, now_id);
×
93
        was_sounding = true;
×
94
    }
95

96
    last_id = now_id;
×
97
}
×
98

99
// Mapping of indices in TOOLS to IDs in JOBS.BOB
100
constexpr helpers::EnumArray<uint8_t, Tool> CARRYTOOLS_IDS = {78, 79, 80, 91, 81, 82, 83, 84, 85, 87, 88, 86};
101

102
unsigned short nofMetalworker::GetCarryID() const
×
103
{
104
    const auto toolIdx = helpers::indexOf(TOOL_TO_GOOD, ware);
×
105
    return (toolIdx >= 0) ? CARRYTOOLS_IDS[static_cast<Tool>(toolIdx)] : 0;
×
106
}
107

108
bool nofMetalworker::HasToolOrder() const
28✔
109
{
110
    const GamePlayer& owner = world->GetPlayer(player);
28✔
111
    for(const auto tool : helpers::enumRange<Tool>())
716✔
112
    {
113
        if(owner.GetToolsOrdered(tool) > 0u)
309✔
114
            return true;
7✔
115
    }
116
    return false;
21✔
117
}
118

119
bool nofMetalworker::AreWaresAvailable() const
30✔
120
{
121
    if(!nofWorkman::AreWaresAvailable())
30✔
122
        return false;
6✔
123
    // If produce nothing on zero is disabled we will always produce something ->OK
124
    if(world->GetGGS().getSelection(AddonId::METALWORKSBEHAVIORONZERO) == 0)
24✔
125
        return true;
×
126
    // Any tool order?
127
    if(HasToolOrder())
24✔
128
        return true;
5✔
129
    // Any non-zero priority?
130
    const GamePlayer& owner = world->GetPlayer(player);
19✔
131
    for(const auto tool : helpers::enumRange<Tool>())
532✔
132
    {
133
        if(owner.GetToolPriority(tool) > 0u)
228✔
134
            return true;
×
135
    }
136
    return false;
19✔
137
}
138

139
bool nofMetalworker::StartWorking()
5✔
140
{
141
    nextProducedTool = GetOrderedTool();
5✔
142
    if(!nextProducedTool)
5✔
143
        nextProducedTool = GetRandomTool();
1✔
144

145
    return nextProducedTool && nofWorkman::StartWorking();
5✔
146
}
147

148
void nofMetalworker::CheckForOrders()
9✔
149
{
150
    // If we are waiting and an order or setting was changed -> See if we can work
151
    if(state == State::WaitingForWaresOrProductionStopped)
9✔
152
        TryToWork();
3✔
153
}
9✔
154

155
helpers::OptionalEnum<GoodType> nofMetalworker::GetOrderedTool()
5✔
156
{
157
    GamePlayer& owner = world->GetPlayer(player);
5✔
158
    std::vector<Tool> random_array;
10✔
159
    for(const auto tool : helpers::enumRange<Tool>())
140✔
160
    {
161
        if(owner.GetToolsOrdered(tool) == 0)
60✔
162
            continue;
53✔
163
        unsigned toolPriority = std::max(owner.GetToolPriority(tool), 1u);
7✔
164
        random_array.insert(random_array.end(), toolPriority, tool);
7✔
165
    }
166
    if(random_array.empty())
5✔
167
        return boost::none;
1✔
168

169
    const Tool tool = RANDOM_ELEMENT(random_array);
4✔
170

171
    owner.ToolOrderProcessed(tool);
4✔
172

173
    if(!HasToolOrder())
4✔
174
    {
175
        SendPostMessage(player,
2✔
176
                        std::make_unique<PostMsg>(GetEvMgr().GetCurrentGF(),
4✔
177
                                                  _("Completed the ordered amount of tools."), PostCategory::Economy));
4✔
178
    }
179

180
    return TOOL_TO_GOOD[tool];
4✔
181
}
182

183
helpers::OptionalEnum<GoodType> nofMetalworker::GetRandomTool()
1✔
184
{
185
    GamePlayer& owner = world->GetPlayer(player);
1✔
186

187
    // Fill array where the # of occurrences of a tool is its priority
188
    // Drawing a random entry will make higher priority items more likely
189
    std::vector<Tool> random_array;
2✔
190
    random_array.reserve(helpers::NumEnumValues_v<Tool>);
1✔
191
    for(const auto tool : helpers::enumRange<Tool>())
28✔
192
    {
193
        random_array.insert(random_array.end(), owner.GetToolPriority(tool), tool);
12✔
194
    }
195

196
    // if they're all zero
197
    if(random_array.empty())
1✔
198
    {
199
        // do nothing if addon is enabled, otherwise produce random ware (orig S2 behavior)
200
        if(world->GetGGS().getSelection(AddonId::METALWORKSBEHAVIORONZERO) == 1)
1✔
201
            return boost::none;
1✔
202
        else
203
            return RANDOM_ELEMENT(TOOL_TO_GOOD);
×
204
    }
205

206
    return TOOL_TO_GOOD[RANDOM_ELEMENT(random_array)];
×
207
}
208

209
helpers::OptionalEnum<GoodType> nofMetalworker::ProduceWare()
3✔
210
{
211
    return nextProducedTool;
3✔
212
}
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