GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/windows/killstats.cpp Lines: 69 240 28.8 %
Date: 2021-03-17 Branches: 86 363 23.7 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2009  The Mana World Development Team
4
 *  Copyright (C) 2011-2019  The ManaPlus Developers
5
 *  Copyright (C) 2009-2021  Andrei Karas
6
 *
7
 *  This file is part of The ManaPlus Client.
8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
#include "gui/windows/killstats.h"
24
25
#include "gui/widgets/button.h"
26
#include "gui/widgets/label.h"
27
#include "gui/widgets/layoutcell.h"
28
29
#include "gui/windows/setupwindow.h"
30
31
#include "client.h"
32
#include "game.h"
33
34
#include "being/localplayer.h"
35
#include "being/playerinfo.h"
36
37
#include "utils/gettext.h"
38
39
#ifdef WIN32
40
#include <sys/time.h>
41
#endif  // WIN32
42
43
#include "debug.h"
44
45
KillStats *killStats = nullptr;
46
47
1
KillStats::KillStats() :
48
    // TRANSLATORS: kill stats window name
49
1
    Window(_("Kill stats"), Modal_false, nullptr, "killstats.xml"),
50
    ActionListener(),
51
    AttributeListener(),
52
    mKillTimer(0),
53
    // TRANSLATORS: kill stats window button
54
1
    mResetButton(new Button(this, _("Reset stats"), "reset",
55

1
        BUTTON_SKIN, this)),
56
    // TRANSLATORS: kill stats window button
57
1
    mTimerButton(new Button(this, _("Reset timer"), "timer",
58

1
        BUTTON_SKIN, this)),
59
    mLine1(nullptr),
60
    mLine2(nullptr),
61
    mLine3(nullptr),
62
    // TRANSLATORS: kill stats window label
63
2
    mLine4(new Label(this, strprintf(_("Kills: %s, total exp: %s"),
64

1
        "?", "?"))),
65
    // TRANSLATORS: kill stats window label
66

2
    mLine5(new Label(this, strprintf(_("Avg Exp: %s"), "?"))),
67
    // TRANSLATORS: kill stats window label
68
2
    mLine6(new Label(this, strprintf(_("No. of avg mob to next level: %s"),
69

1
        "?"))),
70
    // TRANSLATORS: kill stats window label
71
2
    mLine7(new Label(this, strprintf(_("Kills/Min: %s, Exp/Min: %s"),
72

1
        "?", "?"))),
73
2
    mExpSpeed1Label(new Label(this, strprintf(ngettext(
74
        // TRANSLATORS: kill stats window label
75

1
        "Exp speed per %d min: %s", "Exp speed per %d min: %s", 1), 1, "?"))),
76
2
    mExpTime1Label(new Label(this, strprintf(ngettext(
77
        "Time for next level per %d min: %s",
78

1
        "Time for next level per %d min: %s", 1), 1, "?"))),
79
2
    mExpSpeed5Label(new Label(this, strprintf(ngettext(
80

1
        "Exp speed per %d min: %s", "Exp speed per %d min: %s", 5), 5, "?"))),
81
2
    mExpTime5Label(new Label(this, strprintf(ngettext(
82
        "Time for next level per %d min: %s",
83

1
        "Time for next level per %d min: %s", 5), 5, "?"))),
84
2
    mExpSpeed15Label(new Label(this, strprintf(ngettext(
85
        "Exp speed per %d min: %s", "Exp speed per %d min: %s", 15),
86

1
        15, "?"))),
87
2
    mExpTime15Label(new Label(this, strprintf(ngettext(
88
        "Time for next level per %d min: %s",
89

1
        "Time for next level per %d min: %s", 15), 15, "?"))),
90
    // TRANSLATORS: kill stats window label
91

2
    mLastKillExpLabel(new Label(this, strprintf("%s ?", _("Last kill exp:")))),
92
    mKillCounter(0),
93
    mExpCounter(0),
94
    mKillTCounter(0),
95
    mExpTCounter(0),
96
    m1minExpTime(0),
97
    m1minExpNum(0),
98
    m1minSpeed(0),
99
    m5minExpTime(0),
100
    m5minExpNum(0),
101
    m5minSpeed(0),
102
    m15minExpTime(0),
103
    m15minExpNum(0),
104



34
    m15minSpeed(0)
105
{
106
4
    setWindowName("Kill stats");
107
1
    setCloseButton(true);
108
1
    setResizable(true);
109
2
    setSaveVisible(true);
110
1
    setStickyButtonLock(true);
111
1
    setDefaultSize(250, 250, 350, 300);
112
113
1
    if (setupWindow != nullptr)
114
        setupWindow->registerWindowForReset(this);
115
116
1
    const int64_t xp(PlayerInfo::getAttribute64(Attributes::PLAYER_EXP));
117
    int64_t xpNextLevel(PlayerInfo::getAttribute64(
118
1
        Attributes::PLAYER_EXP_NEEDED));
119
120
1
    if (xpNextLevel == 0)
121
1
        xpNextLevel = 1;
122
123
    // TRANSLATORS: kill stats window label
124

3
    mLine1 = new Label(this, strprintf(_("Level: %d at %f%%"),
125
1
        localPlayer->getLevel(), static_cast<double>(xp)
126

2
        / static_cast<double>(xpNextLevel) * 100.0));
127
128
2
    const std::string strXp = toString(CAST_U64(xp));
129
2
    const std::string strXpNextLevel = toString(CAST_U64(xpNextLevel));
130
2
    const std::string strXpLeft = toString(CAST_U64(xpNextLevel - xp));
131
2
    const std::string strXpPercent = toString(CAST_U64(xpNextLevel / 100));
132
    // TRANSLATORS: kill stats window label
133
5
    mLine2 = new Label(this, strprintf(_("Exp: %s/%s Left: %s"),
134
        strXp.c_str(),
135
        strXpNextLevel.c_str(),
136

1
        strXpLeft.c_str()));
137
138
    // TRANSLATORS: kill stats window label
139
3
    mLine3 = new Label(this, strprintf(_("1%% = %s exp, avg mob for 1%%: %s"),
140
        strXpPercent.c_str(),
141

1
        "?"));
142
143
2
    place(0, 0, mLine1, 6, 1).setPadding(0);
144
2
    place(0, 1, mLine2, 6, 1).setPadding(0);
145
2
    place(0, 2, mLine3, 6, 1).setPadding(0);
146
2
    place(0, 3, mLine4, 6, 1).setPadding(0);
147
2
    place(0, 4, mLine5, 6, 1).setPadding(0);
148
2
    place(0, 5, mLine6, 6, 1).setPadding(0);
149
2
    place(0, 6, mLine7, 6, 1).setPadding(0);
150
151
2
    place(0, 7, mLastKillExpLabel, 6, 1).setPadding(0);
152
2
    place(0, 8, mExpSpeed1Label, 6, 1).setPadding(0);
153
2
    place(0, 9, mExpTime1Label, 6, 1).setPadding(0);
154
2
    place(0, 10, mExpSpeed5Label, 6, 1).setPadding(0);
155
2
    place(0, 11, mExpTime5Label, 6, 1).setPadding(0);
156
2
    place(0, 12, mExpSpeed15Label, 6, 1).setPadding(0);
157
2
    place(0, 13, mExpTime15Label, 6, 1).setPadding(0);
158
159
2
    place(5, 12, mTimerButton, 1, 1).setPadding(0);
160
2
    place(5, 13, mResetButton, 1, 1).setPadding(0);
161
162
1
    loadWindowState();
163
2
    enableVisibleSound(true);
164
1
}
165
166
void KillStats::action(const ActionEvent &event)
167
{
168
    const std::string &eventId = event.getId();
169
    if (eventId == "reset")
170
    {
171
        mKillCounter = 0;
172
        mExpCounter = 0;
173
        const std::string strXpPercent = toString(CAST_U64(
174
            PlayerInfo::getAttribute64(Attributes::PLAYER_EXP_NEEDED) / 100));
175
        mLine3->setCaption(strprintf("1%% = %s exp, avg mob for 1%%: %s",
176
            strXpPercent.c_str(),
177
            "?"));
178
        // TRANSLATORS: kill stats window label
179
        mLine4->setCaption(strprintf(_("Kills: %s, total exp: %s"), "?", "?"));
180
        // TRANSLATORS: kill stats window label
181
        mLine5->setCaption(strprintf(_("Avg Exp: %s"), "?"));
182
        mLine6->setCaption(strprintf(
183
            // TRANSLATORS: kill stats window label
184
            _("No. of avg mob to next level: %s"), "?"));
185
186
        resetTimes();
187
    }
188
    else if (eventId == "timer")
189
    {
190
        mKillTimer = 0;
191
        mKillTCounter = 0;
192
        mExpTCounter = 0;
193
        mLine7->setCaption(strprintf(
194
            // TRANSLATORS: kill stats window label
195
            _("Kills/Min: %s, Exp/Min: %s"), "?", "?"));
196
197
        resetTimes();
198
    }
199
}
200
201
void KillStats::resetTimes()
202
{
203
    m1minExpTime = 0;
204
    m1minExpNum = 0;
205
    m1minSpeed = 0;
206
    m5minExpTime = 0;
207
    m5minExpNum = 0;
208
    m5minSpeed = 0;
209
    m15minExpTime = 0;
210
    m15minExpNum = 0;
211
    m15minSpeed = 0;
212
}
213
214
void KillStats::gainXp(int64_t xp)
215
{
216
    const int64_t expNeed = PlayerInfo::getAttribute64(
217
        Attributes::PLAYER_EXP_NEEDED);
218
    if (xp == expNeed)
219
        xp = 0;
220
    else if (xp == 0)
221
        return;
222
223
    mKillCounter++;
224
    mKillTCounter++;
225
226
    mExpCounter = mExpCounter + xp;
227
    mExpTCounter = mExpTCounter + xp;
228
    if (mKillCounter == 0)
229
        mKillCounter = 1;
230
231
    const float AvgExp = static_cast<float>(mExpCounter)
232
        / static_cast<float>(mKillCounter);
233
    int64_t xpNextLevel(expNeed);
234
235
    if (mKillTimer == 0)
236
        mKillTimer = cur_time;
237
238
    if (xpNextLevel == 0)
239
        xpNextLevel = 1;
240
241
    double timeDiff = difftime(cur_time, mKillTimer) / 60;
242
243
    if (timeDiff <= 0.01)
244
        timeDiff = 1;
245
246
    const int64_t exp = PlayerInfo::getAttribute64(Attributes::PLAYER_EXP);
247
    // TRANSLATORS: kill stats window label
248
    mLine1->setCaption(strprintf(_("Level: %d at %f%%"),
249
        localPlayer->getLevel(), static_cast<double>(exp)
250
        / static_cast<double>(xpNextLevel) * 100.0));
251
252
    const std::string strXp = toString(CAST_U64(exp));
253
    const std::string strXpNextLevel = toString(CAST_U64(xpNextLevel));
254
    const std::string strXpLeft = toString(CAST_U64(xpNextLevel - exp));
255
    const std::string strXpPercent = toString(CAST_U64(xpNextLevel / 100));
256
    // TRANSLATORS: kill stats window label
257
    mLine2->setCaption(strprintf(_("Exp: %s/%s Left: %s"),
258
        strXp.c_str(),
259
        strXpNextLevel.c_str(),
260
        strXpLeft.c_str()));
261
262
    if (AvgExp >= -0.001F && AvgExp <= 0.001F)
263
    {
264
        // TRANSLATORS: kill stats window label
265
        mLine3->setCaption(strprintf(_("1%% = %s exp, avg mob for 1%%: %s"),
266
            strXpPercent.c_str(),
267
            "?"));
268
269
        // TRANSLATORS: kill stats window label
270
        mLine5->setCaption(strprintf(_("Avg Exp: %s"),
271
            toString(AvgExp).c_str()));
272
273
        mLine6->setCaption(strprintf(
274
            // TRANSLATORS: kill stats window label
275
            _("No. of avg mob to next level: %s"), "?"));
276
    }
277
    else
278
    {
279
        // TRANSLATORS: kill stats window label
280
        mLine3->setCaption(strprintf(_("1%% = %s exp, avg mob for 1%%: %s"),
281
            strXpPercent.c_str(), toString((static_cast<float>(
282
            xpNextLevel) / 100) / AvgExp).c_str()));
283
284
        // TRANSLATORS: kill stats window label
285
        mLine5->setCaption(strprintf(_("Avg Exp: %s"),
286
            toString(AvgExp).c_str()));
287
288
        // TRANSLATORS: kill stats window label
289
        mLine6->setCaption(strprintf(_("No. of avg mob to next level: %s"),
290
            toString(static_cast<float>(xpNextLevel - exp) / AvgExp).c_str()));
291
    }
292
    // TRANSLATORS: kill stats window label
293
    mLine4->setCaption(strprintf(_("Kills: %s, total exp: %s"),
294
        toString(mKillCounter).c_str(),
295
        toString(CAST_U64(mExpCounter)).c_str()));
296
297
    // TRANSLATORS: kill stats window label
298
    mLine7->setCaption(strprintf(_("Kills/Min: %s, Exp/Min: %s"),
299
        toString(mKillTCounter / timeDiff).c_str(),
300
        toString(CAST_U64(mExpTCounter / timeDiff)).c_str()));
301
302
    mLastKillExpLabel->setCaption(strprintf("%s %s",
303
        // TRANSLATORS: kill stats window label
304
        _("Last kill exp:"),
305
        strXp.c_str()));
306
307
    recalcStats();
308
    update();
309
}
310
311
void KillStats::recalcStats()
312
{
313
    BLOCK_START("KillStats::recalcStats")
314
    const time_t curTime = cur_time;
315
316
    // Need Update Exp Counter
317
    if (curTime - m1minExpTime > 60)
318
    {
319
        const int64_t newExp = PlayerInfo::getAttribute(
320
            Attributes::PLAYER_EXP);
321
        if (m1minExpTime != 0)
322
            m1minSpeed = CAST_S32(newExp - m1minExpNum);
323
        else
324
            m1minSpeed = 0;
325
        m1minExpTime = curTime;
326
        m1minExpNum = newExp;
327
    }
328
329
    if (curTime != 0 && mLastHost == 0xFF6B66 && cur_time > 1)
330
    {
331
        const int newExp = PlayerInfo::getAttribute(
332
            Attributes::PLAYER_EXP_NEEDED);
333
        if (m1minExpTime != 0)
334
            m1minSpeed = CAST_S32(newExp - m1minExpNum);
335
        mStatsReUpdated = true;
336
        m1minExpNum = newExp;
337
    }
338
339
    if (curTime - m5minExpTime > 60*5)
340
    {
341
        const int64_t newExp = PlayerInfo::getAttribute(
342
            Attributes::PLAYER_EXP);
343
        if (m5minExpTime != 0)
344
            m5minSpeed = CAST_S32(newExp - m5minExpNum);
345
        else
346
            m5minSpeed = 0;
347
        m5minExpTime = curTime;
348
        m5minExpNum = newExp;
349
    }
350
351
    if (curTime - m15minExpTime > 60*15)
352
    {
353
        const int64_t newExp = PlayerInfo::getAttribute(
354
            Attributes::PLAYER_EXP);
355
        if (m15minExpTime != 0)
356
            m15minSpeed = CAST_S32(newExp - m15minExpNum);
357
        else
358
            m15minSpeed = 0;
359
        m15minExpTime = curTime;
360
        m15minExpNum = newExp;
361
    }
362
    BLOCK_END("KillStats::recalcStats")
363
}
364
365
void KillStats::update()
366
{
367
    BLOCK_START("KillStats::update")
368
369
    mExpSpeed1Label->setCaption(strprintf(ngettext("Exp speed per %d min: %s",
370
        "Exp speed per %d min: %s", 1),
371
        1,
372
        toString(m1minSpeed).c_str()));
373
374
    if (m1minSpeed != 0)
375
    {
376
        // TRANSLATORS: kill stats window label
377
        mExpTime1Label->setCaption(strprintf(_("  Time for next level: %s"),
378
            toString(static_cast<float>((PlayerInfo::getAttribute(
379
            Attributes::PLAYER_EXP_NEEDED) - PlayerInfo::getAttribute(
380
            Attributes::PLAYER_EXP)) /
381
            static_cast<float>(m1minSpeed))).c_str()));
382
    }
383
    else
384
    {
385
        mExpTime1Label->setCaption(strprintf(
386
            // TRANSLATORS: kill stats window label
387
            _("  Time for next level: %s"), "?"));
388
    }
389
    mExpTime1Label->adjustSize();
390
391
    mExpSpeed5Label->setCaption(strprintf(ngettext("Exp speed per %d min: %s",
392
        "Exp speed per %d min: %s", 5),
393
        5,
394
        toString(m5minSpeed / 5).c_str()));
395
    mExpSpeed5Label->adjustSize();
396
397
    if (m5minSpeed != 0)
398
    {
399
        // TRANSLATORS: kill stats window label
400
        mExpTime5Label->setCaption(strprintf(_("  Time for next level: %s"),
401
            toString(static_cast<float>((PlayerInfo::getAttribute(
402
            Attributes::PLAYER_EXP_NEEDED) - PlayerInfo::getAttribute(
403
            Attributes::PLAYER_EXP)) / m5minSpeed * 5)).c_str()));
404
    }
405
    else
406
    {
407
        mExpTime5Label->setCaption(strprintf(
408
            // TRANSLATORS: kill stats window label
409
            _("  Time for next level: %s"), "?"));
410
    }
411
    mExpTime5Label->adjustSize();
412
413
414
    mExpSpeed15Label->setCaption(strprintf(ngettext("Exp speed per %d min: %s",
415
        "Exp speed per %d min: %s", 15), 15, toString(
416
        m15minSpeed / 15).c_str()));
417
    mExpSpeed15Label->adjustSize();
418
419
    if (m15minSpeed != 0)
420
    {
421
        // TRANSLATORS: kill stats window label
422
        mExpTime15Label->setCaption(strprintf(_("  Time for next level: %s"),
423
            toString(static_cast<float>((PlayerInfo::getAttribute(
424
            Attributes::PLAYER_EXP_NEEDED) - PlayerInfo::getAttribute(
425
            Attributes::PLAYER_EXP)) / m15minSpeed * 15)).c_str()));
426
    }
427
    else
428
    {
429
        mExpTime15Label->setCaption(strprintf(
430
            // TRANSLATORS: kill stats window label
431
            _("  Time for next level: %s"), "?"));
432
    }
433
434
    BLOCK_END("KillStats::update")
435
}
436
437
void KillStats::attributeChanged(const AttributesT id,
438
                                 const int64_t oldVal,
439
                                 const int64_t newVal)
440
{
441
    PRAGMA45(GCC diagnostic push)
442
    PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
443
    switch (id)
444
    {
445
        case Attributes::PLAYER_EXP:
446
        case Attributes::PLAYER_EXP_NEEDED:
447
            gainXp(newVal - oldVal);
448
            break;
449
        case Attributes::PLAYER_BASE_LEVEL:
450
        {
451
            const std::string strXpPercent = toString(CAST_U64(
452
                PlayerInfo::getAttribute(
453
                Attributes::PLAYER_EXP_NEEDED) / 100));
454
            mKillCounter = 0;
455
            mKillTCounter = 0;
456
            mExpCounter = 0;
457
            mExpTCounter = 0;
458
            mLine3->setCaption(strprintf("1%% = %s exp, avg mob for 1%%: %s",
459
                strXpPercent.c_str(),
460
                "?"));
461
            mLine4->setCaption(strprintf(
462
                // TRANSLATORS: kill stats window label
463
                _("Kills: %s, total exp: %s"), "?", "?"));
464
                // TRANSLATORS: kill stats window label
465
            mLine5->setCaption(strprintf(_("Avg Exp: %s"), "?"));
466
            mLine6->setCaption(strprintf(
467
                // TRANSLATORS: kill stats window label
468
                _("No. of avg mob to next level: %s"), "?"));
469
            mLine7->setCaption(strprintf(
470
                // TRANSLATORS: kill stats window label
471
                _("Kills/Min: %s, Exp/Min: %s"), "?", "?"));
472
473
            resetTimes();
474
            update();
475
            break;
476
        }
477
        default:
478
            break;
479
    }
480
    PRAGMA45(GCC diagnostic pop)
481

3
}