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

Return-To-The-Roots / s25client / 24783147581

22 Apr 2026 02:11PM UTC coverage: 50.384% (+0.02%) from 50.362%
24783147581

push

github

web-flow
Merge pull request #1913 from Flamefire/soldier-cancel

79 of 104 new or added lines in 13 files covered. (75.96%)

1 existing line in 1 file now uncovered.

23066 of 45780 relevant lines covered (50.38%)

42797.98 hits per line

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

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

5
#include "nofDefender.h"
6

7
#include "GlobalGameSettings.h"
8
#include "SerializedGameData.h"
9
#include "addons/const_addons.h"
10
#include "buildings/nobMilitary.h"
11
#include "nofAttacker.h"
12
#include "nofPassiveSoldier.h"
13
#include "random/Random.h"
14
#include "world/GameWorld.h"
15
#include "nodeObjs/noFighting.h"
16
#include "gameData/BuildingProperties.h"
17

18
nofDefender::nofDefender(const MapPoint pos, const unsigned char player, nobBaseMilitary& home,
4✔
19
                         const unsigned char rank, nofAttacker& attacker)
4✔
20
    : nofActiveSoldier(pos, player, home, rank, SoldierState::DefendingWalkingTo), attacker(&attacker)
4✔
21
{}
4✔
22

23
nofDefender::nofDefender(const nofPassiveSoldier& other, nofAttacker& attacker)
17✔
24
    : nofActiveSoldier(other, SoldierState::DefendingWalkingTo), attacker(&attacker)
17✔
25
{}
17✔
26

27
void nofDefender::Serialize(SerializedGameData& sgd) const
×
28
{
29
    nofActiveSoldier::Serialize(sgd);
×
30

31
    if(state != SoldierState::FigureWork)
×
32
        sgd.PushObject(attacker, true);
×
33
}
×
34

35
nofDefender::nofDefender(SerializedGameData& sgd, const unsigned obj_id) : nofActiveSoldier(sgd, obj_id)
×
36
{
37
    if(state != SoldierState::FigureWork)
×
38
        attacker = sgd.PopObject<nofAttacker>(GO_Type::NofAttacker);
×
39
    else
40
        attacker = nullptr;
×
41
}
×
42

43
void nofDefender::Walked()
16✔
44
{
45
    switch(state)
16✔
46
    {
47
        case SoldierState::DefendingWalkingTo:
16✔
48
            // Start fight with attacker
49
            world->AddFigure(pos, std::make_unique<noFighting>(*attacker, *this));
16✔
50
            state = SoldierState::Fighting;
16✔
51
            attacker->FightVsDefenderStarted();
16✔
52
            break;
16✔
53
        case SoldierState::DefendingWalkingFrom:
×
NEW
54
            if(!homeBld)
×
55
            {
56
                // Home destroyed -> Start wandering around
57
                attacker = nullptr;
×
58
                state = SoldierState::FigureWork;
×
59
                StartWandering();
×
60
                Wander();
×
61
            } else
62
            {
63
                // Arrived in building
64
                if(attacker)
×
65
                {
66
                    // An attacker arrived in the meantime so go back out
67
                    state = SoldierState::DefendingWalkingTo;
×
68
                    StartWalking(Direction::SouthEast);
×
69
                } else
70
                {
NEW
71
                    nobBaseMilitary* bld = homeBld;
×
72
                    RTTR_Assert(bld->GetDefender() == this); // I should be the defender
×
73
                    bld->AddActiveSoldier(world->RemoveFigure(pos, *this));
×
74
                    RTTR_Assert(!bld->GetDefender()); // No defender anymore
×
75
                }
76
            }
77
            break;
×
78
        default: break;
×
79
    }
80
}
16✔
81

82
void nofDefender::HomeDestroyed()
×
83
{
NEW
84
    homeBld = nullptr;
×
85

86
    switch(state)
×
87
    {
88
        case SoldierState::DefendingWaiting:
×
89
            // Handle now as we are not moving
90
            attacker = nullptr;
×
91
            state = SoldierState::FigureWork;
×
92
            StartWandering();
×
93
            Wander();
×
94
            break;
×
95
        case SoldierState::DefendingWalkingTo:
×
96
        case SoldierState::DefendingWalkingFrom:
97
            attacker = nullptr;
×
98
            StartWandering();
×
99
            state = SoldierState::FigureWork;
×
100
            break;
×
101
        case SoldierState::Fighting:
×
102
            // Just continue fighting if we have already started started
103
        default: break;
×
104
    }
105
}
×
106

107
void nofDefender::HomeDestroyedAtBegin()
×
108
{
NEW
109
    homeBld = nullptr;
×
110

111
    state = SoldierState::FigureWork;
×
112

113
    StartWandering();
×
114
    StartWalking(RANDOM_ENUM(Direction));
×
115
}
×
116

117
void nofDefender::WonFighting()
1✔
118
{
119
    // addon BattlefieldPromotion active? -> increase rank!
120
    if(world->GetGGS().isEnabled(AddonId::BATTLEFIELD_PROMOTION))
1✔
121
        IncreaseRank();
×
122
    // Attacker is dead
123
    attacker = nullptr;
1✔
124

125
    if(!homeBld)
1✔
126
    {
127
        // Home destroyed so abort and wander around
128
        state = SoldierState::FigureWork;
×
129
        StartWandering();
×
130
        Wander();
×
131

132
        return;
×
133
    }
134

135
    attacker = homeBld->FindAttackerNearBuilding();
1✔
136
    if(attacker)
1✔
137
    {
138
        // New attacker found so wait for him
139
        state = SoldierState::DefendingWaiting;
1✔
140
    } else
141
    {
142
        // Otherwise go back into the building
143
        state = SoldierState::DefendingWalkingFrom;
×
144
        StartWalking(Direction::NorthWest);
×
145
    }
146
}
147

148
void nofDefender::LostFighting()
13✔
149
{
150
    attacker = nullptr;
13✔
151

152
    // Notify building if it still exists
153
    if(homeBld)
13✔
154
    {
155
        homeBld->ResetDefender();
13✔
156
        // A military building potentially needs to get new soldiers if this wasn't the last one
157
        if(BuildingProperties::IsMilitary(homeBld->GetBuildingType()))
13✔
158
        {
159
            RTTR_Assert(dynamic_cast<nobBaseMilitary*>(homeBld));
12✔
160
            if(static_cast<nobMilitary*>(homeBld)->GetNumTroops())
12✔
161
                static_cast<nobMilitary*>(homeBld)->RegulateTroops();
1✔
162
        }
163
        homeBld = nullptr;
13✔
164
    }
165
}
13✔
166

167
void nofDefender::AttackerArrested()
×
168
{
NEW
169
    attacker = homeBld->FindAttackerNearBuilding();
×
170
    if(!attacker)
×
171
    {
172
        // Go back into the building
173
        state = SoldierState::DefendingWalkingFrom;
×
174
        StartWalking(Direction::NorthWest);
×
175
    }
176
}
×
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