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

APN-Pucky / tyrant_optimize / 20666461190

02 Jan 2026 08:51PM UTC coverage: 70.411% (+0.05%) from 70.364%
20666461190

Pull #103

github

APN-Pucky
typo2
Pull Request #103: [WIP] add `_DEBUG_STRAP`

35 of 48 new or added lines in 3 files covered. (72.92%)

1 existing line in 1 file now uncovered.

4814 of 6837 relevant lines covered (70.41%)

8310861.48 hits per line

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

84.1
/sim.cpp
1
#include "sim.h"
2

3
#include <boost/range/adaptors.hpp>
4
#include <boost/range/join.hpp>
5
#include <iostream>
6
#include <random>
7
#include <string>
8
#include <sstream>
9
#include <vector>
10
#include <cmath>
11

12
#include "card.h"
13
#include "cards.h"
14
#include "deck.h"
15

16
template<enum CardType::CardType def_cardtype>
17
void perform_bge_devour(Field* fd, CardStatus* att_status, CardStatus* def_status, unsigned att_dmg);
18
template<enum CardType::CardType def_cardtype>
19
void perform_bge_heroism(Field* fd, CardStatus* att_status, CardStatus* def_status, unsigned att_dmg);
20
void perform_poison(Field* fd, CardStatus* att_status, CardStatus* def_status);
21
void perform_corrosive(Field* fd, CardStatus* att_status, CardStatus* def_status);
22
void perform_mark(Field* fd, CardStatus* att_status, CardStatus* def_status);
23
void perform_berserk(Field* fd, CardStatus* att_status, CardStatus* def_status);
24
template<enum CardType::CardType def_cardtype>
25
void perform_counter(Field* fd, CardStatus* att_status, CardStatus* def_status);
26
void perform_hunt(Field* fd, CardStatus* att_status, CardStatus* def_status);
27
void perform_subdue(Field* fd, CardStatus* att_status, CardStatus* def_status);
28
bool check_and_perform_valor(Field* fd, CardStatus* src);
29
bool check_and_perform_bravery(Field* fd, CardStatus* src);
30
bool check_and_perform_early_enhance(Field* fd, CardStatus* src);
31
bool check_and_perform_later_enhance(Field* fd, CardStatus* src);
32
CardStatus* check_and_perform_summon(Field* fd, CardStatus* src);
33
//------------------------------------------------------------------------------
34
inline unsigned remove_absorption(Field* fd, CardStatus* status, unsigned dmg);
35
inline unsigned remove_absorption(CardStatus* status, unsigned dmg);
36
inline unsigned remove_disease(CardStatus* status, unsigned heal);
37
//------------------------------------------------------------------------------
38
inline bool has_attacked(CardStatus* c) { return (c->m_step == CardStep::attacked); }
12,686,827✔
39
inline bool is_alive(CardStatus* c) { return (c->m_hp > 0); }
13,329,367✔
40
inline bool can_act(CardStatus* c) { return is_alive(c) && !c->m_jammed; }
356,024,102✔
41
inline bool is_active(CardStatus* c) { return can_act(c) && (c->m_delay == 0); }
297,059,705✔
42
inline bool is_active_next_turn(CardStatus* c) { return can_act(c) && (c->m_delay <= 1); }
42,068,777✔
43
inline bool will_activate_this_turn(CardStatus* c) { return is_active(c) && ((c->m_step == CardStep::none) || (c->m_step == CardStep::attacking && c->has_skill(Skill::flurry) && c->m_action_index < c->skill_base_value(Skill::flurry)));}
260,349✔
44
// Can be healed / repaired
45
inline bool can_be_healed(CardStatus* c) { return is_alive(c) && (c->m_hp < c->max_hp()); }
35,706✔
46
// Strange Transmission [Gilians] features
47
#ifdef TUO_GILIAN
48
inline bool is_gilian(CardStatus* c) { return(
49
        (c->m_card->m_id >= 25054 && c->m_card->m_id <= 25063) // Gilian Commander
50
        ||  (c->m_card->m_id >= 38348 && c->m_card->m_id <= 38388) // Gilian assaults plus the Gil's Shard
51
        ); }
52
inline bool is_alive_gilian(CardStatus* c) { return(is_alive(c) && is_gilian(c)); }
53
#else
54
# define is_gilian(c) (false)
55
# define is_alive_gilian(c) (false)
56
#endif
57

58
//------------------------------------------------------------------------------
59
inline std::string status_description(const CardStatus* status)
55,358,899✔
60
{
61
    return status->description();
22,042,090✔
62
}
63
//------------------------------------------------------------------------------
NEW
64
inline std::string strap_string(const CardStatus* status)
×
65
{
66
    // Replace ' ' with '_' for strap key
NEW
67
    std::string result = boost::replace_all_copy(status->m_card->m_name, " ", "_");
×
NEW
68
    boost::replace_all(result, "'", "_");
×
NEW
69
    return result;
×
NEW
70
}
×
71
//------------------------------------------------------------------------------
72
template <typename CardsIter, typename Functor>
73
inline unsigned Field::make_selection_array(CardsIter first, CardsIter last, Functor f)
107,180,743✔
74
{
75
    this->selection_array.clear();
107,180,743✔
76
    for(auto c = first; c != last; ++c)
435,318,660✔
77
    {
78
        if (f(*c))
661,999,145✔
79
        {
80
            this->selection_array.push_back(*c);
171,333,955✔
81
        }
82
    }
83
    return(this->selection_array.size());
107,180,743✔
84
}
85
inline CardStatus * Field::left_assault(const CardStatus * status)
987,413✔
86
{
87
    return left_assault(status, 1);
3,704,769✔
88
}
89
inline CardStatus * Field::left_assault(const CardStatus * status, const unsigned n)
987,413✔
90
{
91
    auto & assaults = this->players[status->m_player]->assaults;
987,413✔
92
    if (status->m_index >= n)
987,413✔
93
    {
94
        auto left_status = &assaults[status->m_index - n];
773,216✔
95
        if (is_alive(left_status))
773,216✔
96
        {
97
            return left_status;
98
        }
99
    }
100
    return nullptr;
101
}
102
inline CardStatus * Field::right_assault(const CardStatus * status)
987,413✔
103
{
104
    return right_assault(status, 1);
3,706,788✔
105
}
106
inline CardStatus * Field::right_assault(const CardStatus * status, const unsigned n)
987,413✔
107
{
108
    auto & assaults = this->players[status->m_player]->assaults;
987,413✔
109
    if ((status->m_index + n) < assaults.size())
987,413✔
110
    {
111
        auto right_status = &assaults[status->m_index + n];
745,407✔
112
        if (is_alive(right_status))
745,407✔
113
        {
114
            return right_status;
115
        }
116
    }
117
    return nullptr;
118
}
119
inline void Field::print_selection_array()
×
120
{
121
#ifndef NDEBUG
122
    for(auto c: this->selection_array)
×
123
    {
124
        _DEBUG_MSG(2, "+ %s\n", status_description(c).c_str());
×
125
    }
126
#endif
127
}
×
128

129
//------------------------------------------------------------------------------
130
inline void Field::prepare_action()
86,295,392✔
131
{
132
    damaged_units_to_times.clear();
172,590,784✔
133
}
134

135
//------------------------------------------------------------------------------
136
inline void Field::finalize_action()
85,196,587✔
137
{
138
    for (auto unit_it = damaged_units_to_times.begin(); unit_it != damaged_units_to_times.end(); ++ unit_it)
85,450,771✔
139
    {
140
        if (__builtin_expect(!unit_it->second, false))
254,184✔
141
        { continue; }
107,592✔
142
        CardStatus * dmg_status = unit_it->first;
254,184✔
143
        if (__builtin_expect(!is_alive(dmg_status), false))
254,184✔
144
        { continue; }
107,592✔
145
        unsigned barrier_base = dmg_status->skill(Skill::barrier);
146,592✔
146
        if (barrier_base)
146,592✔
147
        {
148
            unsigned protect_value = barrier_base * unit_it->second;
146,592✔
149
            _DEBUG_STRAP(
146,592✔
150
                    "turn", turn,
151
                    "active_player", tapi,
152
                    "barrier_"+strap_string(dmg_status), 1.0,
153
                    "barrier_protect", protect_value,
154
                    "barrier_base", barrier_base,
155
                    "barrier_times", unit_it->second
156
            );
146,592✔
157
            _DEBUG_MSG(1, "%s protects itself for %u (barrier %u x %u damage taken)\n",
146,592✔
158
                    status_description(dmg_status).c_str(), protect_value, barrier_base, unit_it->second);
146,592✔
159
            dmg_status->m_protected += protect_value;
146,592✔
160
        }
161
    }
162
}
85,196,587✔
163

164
//------------------------------------------------------------------------------
165
inline unsigned CardStatus::skill_base_value(Skill::Skill skill_id) const
1,056,487,683✔
166
{
167
    return m_card->m_skill_value[skill_id + m_primary_skill_offset[skill_id]]
1,056,487,683✔
168
        + (skill_id == Skill::berserk ? m_enraged : 0)
×
169
        + (skill_id == Skill::counter ? m_entrapped : 0)
29,482,249✔
170
        ;
171
}
172
//------------------------------------------------------------------------------
173
inline unsigned CardStatus::skill(Skill::Skill skill_id) const
669,236,760✔
174
{
175
    return (is_activation_skill_with_x(skill_id)
669,236,760✔
176
            ? safe_minus(skill_base_value(skill_id), m_sabotaged)
177
            : skill_base_value(skill_id))
671,317,104✔
178
        + enhanced(skill_id);
671,317,104✔
179
}
180
//------------------------------------------------------------------------------
181
inline bool CardStatus::has_skill(Skill::Skill skill_id) const
385,308,099✔
182
{
183
    return skill_base_value(skill_id);
53,624✔
184
}
185
//------------------------------------------------------------------------------
186
inline unsigned CardStatus::enhanced(Skill::Skill skill_id) const
764,608,831✔
187
{
188
    return m_enhanced_value[skill_id + m_primary_skill_offset[skill_id]];
146,592✔
189
}
190
//------------------------------------------------------------------------------
191
inline unsigned CardStatus::protected_value() const
47,442,516✔
192
{
193
    return m_protected + m_protected_stasis;
47,442,516✔
194
}
195
//------------------------------------------------------------------------------
196
/**
197
 * @brief Maximum health.
198
 * This takes subduing into account by reducing the permanent health buffs by subdue.
199
 * 
200
 * @return unsigned maximum health.
201
 */
202
inline unsigned CardStatus::max_hp() const
86,455,123✔
203
{
204
    return (m_card->m_health + safe_minus(m_perm_health_buff, m_subdued));
84,020,290✔
205
}
206
/** @brief Increase of current health.
207
 *  This takes disease into account and removes as needed.
208
 *
209
 *  @param [in] value increase of health.
210
 *  @return applied increase of health.
211
 */
212
inline unsigned CardStatus::add_hp(unsigned value)
19,852,365✔
213
{
214
    value = remove_disease(this,value);
19,852,365✔
215
    return (m_hp = std::min(m_hp + value, max_hp()));
35,537,500✔
216
}
217
/** @brief Permanent increase of maximum health.
218
 *  This takes disease into account and removes as needed.
219
 *  The increase of the maximum health entails an increase of current health by the same amount.
220
 *
221
 *  @param [in] value increase of maximum health.
222
 *  @return applied increase of maximum health.
223
 */ 
224
inline unsigned CardStatus::ext_hp(unsigned value)
12,039,254✔
225
{
226
    value = remove_disease(this,value);
12,039,254✔
227
    m_perm_health_buff += value;
12,039,254✔
228
    // we can safely call add_hp without worring about the second call to remove_disease because
229
    // the first call will have already removed the disease or the value will be 0
230
    return add_hp(value);
12,039,254✔
231
}
232
//------------------------------------------------------------------------------
233
inline void CardStatus::set(const Card* card)
20,333,083✔
234
{
235
    this->set(*card);
20,333,083✔
236
}
237
//------------------------------------------------------------------------------
238
inline void CardStatus::set(const Card& card)
20,333,083✔
239
{
240
    m_card = &card;
20,333,083✔
241
    m_index = 0;
20,333,083✔
242
    m_action_index=0;
20,333,083✔
243
    m_player = 0;
20,333,083✔
244
    m_delay = card.m_delay;
20,333,083✔
245
    m_hp = card.m_health;
20,333,083✔
246
    m_absorption = 0;
20,333,083✔
247
    m_step = CardStep::none;
20,333,083✔
248
    m_perm_health_buff = 0;
20,333,083✔
249
    m_perm_attack_buff = 0;
20,333,083✔
250
    m_temp_attack_buff = 0;
20,333,083✔
251
    m_corroded_rate = 0;
20,333,083✔
252
    m_corroded_weakened = 0;
20,333,083✔
253
    m_subdued = 0;
20,333,083✔
254
    m_enfeebled = 0;
20,333,083✔
255
    m_evaded = 0;
20,333,083✔
256
    m_inhibited = 0;
20,333,083✔
257
    m_sabotaged = 0;
20,333,083✔
258
    m_jammed = false;
20,333,083✔
259
    m_overloaded = false;
20,333,083✔
260
    m_paybacked = 0;
20,333,083✔
261
    m_tributed = 0;
20,333,083✔
262
    m_poisoned = 0;
20,333,083✔
263
    m_protected = 0;
20,333,083✔
264
    m_protected_stasis = 0;
20,333,083✔
265
    m_enraged = 0;
20,333,083✔
266
    m_entrapped = 0;
20,333,083✔
267
    m_marked = 0;
20,333,083✔
268
    m_diseased = 0;
20,333,083✔
269
    m_rush_attempted = false;
20,333,083✔
270
    m_sundered = false;
20,333,083✔
271
    //APN
272
    m_summoned = false;
20,333,083✔
273

274
    std::memset(m_primary_skill_offset, 0, sizeof m_primary_skill_offset);
20,333,083✔
275
    std::memset(m_evolved_skill_offset, 0, sizeof m_evolved_skill_offset);
20,333,083✔
276
    std::memset(m_enhanced_value, 0, sizeof m_enhanced_value);
20,333,083✔
277
    std::memset(m_skill_cd, 0, sizeof m_skill_cd);
20,333,083✔
278
}
20,333,083✔
279
//------------------------------------------------------------------------------
280
/**
281
 * @brief Calculate the attack power of the CardStatus.
282
 * 
283
 * @return unsigned 
284
 */
285
inline unsigned CardStatus::attack_power() const
165,498,327✔
286
{
287
    signed attack = calc_attack_power();
165,498,327✔
288
    if(__builtin_expect(attack <0,false))
165,498,327✔
289
    {
290
        std::cout << m_card->m_name << " " << m_card->m_attack  << " " << attack << " " << m_temp_attack_buff << " " << m_corroded_weakened << std::endl;
×
291
    }
292
    _DEBUG_ASSERT(attack >= 0);
165,498,327✔
293
    return (unsigned)attack;
165,498,327✔
294
}
295

296
/**
297
 * @brief Calculate the attack power of the CardStatus.
298
 * 
299
 * The attack power is the base attack plus. 
300
 * The subdued value gets subtracted from the permanent attack buff, if this is above zero it is added to the attack power.
301
 * The corroded value gets subtracted from the attack power, but not below zero.
302
 * Finally the temporary attack buff is added.
303
 * 
304
 * @return signed attack power.
305
 */
306
inline signed CardStatus::calc_attack_power() const
166,102,344✔
307
{
308
    if(__builtin_expect(this->m_field->fixes[Fix::corrosive_protect_armor],true)) // MK - 05/01/2023 6:58 AM: "Corrosive now counts as temporary attack reduction instead of permanent, so it is calculated after Subdue and can now counter Rally even if the permanent attack is zeroed."
166,102,344✔
309
    {
310
        return
166,102,344✔
311
        (signed)safe_minus(
332,204,688✔
312
                m_card->m_attack + safe_minus(m_perm_attack_buff, m_subdued)+ m_temp_attack_buff,
166,102,344✔
313
                m_corroded_weakened
166,102,344✔
314
                );
166,102,344✔
315
    }
316
    else{
317
        return
×
318
        (signed)safe_minus(
×
319
                m_card->m_attack + safe_minus(m_perm_attack_buff, m_subdued),
×
320
                m_corroded_weakened
×
321
                )
322
        + m_temp_attack_buff;
×
323
    }
324

325
}
326
//------------------------------------------------------------------------------
327
const Card* card_by_id_safe(const Cards& cards, const unsigned card_id)
×
328
{
329
    const auto cardIter = cards.cards_by_id.find(card_id);
×
330
    if (cardIter == cards.cards_by_id.end()) assert(false);//"UnknownCard.id[" + to_string(card_id) + "]"); }
×
331
    return cardIter->second;
×
332
}
333
std::string card_name_by_id_safe(const Cards& cards, const unsigned card_id)
108,799✔
334
{
335
    const auto cardIter = cards.cards_by_id.find(card_id);
108,799✔
336
    if (cardIter == cards.cards_by_id.end()) { return "UnknownCard.id[" + tuo::to_string(card_id) + "]"; }
108,799✔
337
    return cardIter->second->m_name;
217,598✔
338
}
339
//------------------------------------------------------------------------------
340
std::string card_description(const Cards& cards, const Card* c)
2,517,576✔
341
{
342
    std::string desc;
2,517,576✔
343
    desc = c->m_name;
2,517,576✔
344
    switch(c->m_type)
2,517,576✔
345
    {
346
        case CardType::assault:
2,080,048✔
347
            desc += ": " + tuo::to_string(c->m_attack) + "/" + tuo::to_string(c->m_health) + "/" + tuo::to_string(c->m_delay);
10,400,240✔
348
            break;
2,080,048✔
349
        case CardType::structure:
436,964✔
350
            desc += ": " + tuo::to_string(c->m_health) + "/" + tuo::to_string(c->m_delay);
1,747,856✔
351
            break;
436,964✔
352
        case CardType::commander:
564✔
353
            desc += ": hp:" + tuo::to_string(c->m_health);
1,692✔
354
            break;
564✔
355
        case CardType::num_cardtypes:
×
356
            assert(false);
×
357
            break;
358
    }
359
    if (c->m_rarity >= 4) { desc += " " + rarity_names[c->m_rarity]; }
4,225,355✔
360
    if (c->m_faction != allfactions) { desc += " " + faction_names[c->m_faction]; }
5,035,152✔
361
    for (auto& skill: c->m_skills_on_play) { desc += ", " + skill_description(cards, skill, Skill::Trigger::play); }
2,651,332✔
362
    for (auto& skill: c->m_skills) { desc += ", " + skill_description(cards, skill, Skill::Trigger::activate); }
16,650,622✔
363
    //APN
364
    for (auto& skill: c->m_skills_on_attacked) { desc += ", " + skill_description(cards, skill, Skill::Trigger::attacked); }
3,083,106✔
365
    for (auto& skill: c->m_skills_on_death) { desc += ", " + skill_description(cards, skill, Skill::Trigger::death); }
2,693,376✔
366
    return desc;
2,517,576✔
367
}
×
368
//------------------------------------------------------------------------------
369
std::string CardStatus::description() const
55,358,899✔
370
{
371
    std::string desc = "P" + tuo::to_string(m_player) + " ";
166,076,697✔
372
    switch(m_card->m_type)
55,358,899✔
373
    {
374
        case CardType::commander: desc += "Commander "; break;
17,055,338✔
375
        case CardType::assault: desc += "Assault " + tuo::to_string(m_index) + " "; break;
127,581,200✔
376
        case CardType::structure: desc += "Structure " + tuo::to_string(m_index) + " "; break;
25,633,044✔
377
        case CardType::num_cardtypes: assert(false); break;
×
378
    }
379
    desc += "[" + m_card->m_name;
110,717,798✔
380
    switch (m_card->m_type)
55,358,899✔
381
    {
382
        case CardType::assault:
31,895,300✔
383
            desc += " att:[[" + tuo::to_string(m_card->m_attack) + "(base)";
127,581,200✔
384
            if (m_perm_attack_buff)
31,895,300✔
385
            {
386
                desc += "+[" + tuo::to_string(m_perm_attack_buff) + "(perm)";
29,029,576✔
387
                if (m_subdued) { desc += "-" + tuo::to_string(m_subdued) + "(subd)"; }
8,532,112✔
388
                desc += "]";
7,257,394✔
389
            }
390
            if (m_corroded_weakened) { desc += "-" + tuo::to_string(m_corroded_weakened) + "(corr)"; }
33,301,646✔
391
            desc += "]";
31,895,300✔
392
            if (m_temp_attack_buff) { desc += (m_temp_attack_buff > 0 ? "+" : "") + tuo::to_string(m_temp_attack_buff) + "(temp)"; }
48,997,268✔
393
            desc += "]=" + tuo::to_string(attack_power());
95,685,900✔
394
        case CardType::structure:
55,358,899✔
395
        case CardType::commander:
55,358,899✔
396
            desc += " hp:" + tuo::to_string(m_hp);
166,076,697✔
397
            if(m_absorption)desc += " absorb:" + tuo::to_string(m_absorption);
61,102,931✔
398
            break;
399
        case CardType::num_cardtypes:
×
400
            assert(false);
×
401
            break;
402
    }
403
    if (m_delay) { desc += " cd:" + tuo::to_string(m_delay); }
80,070,597✔
404
    // Status w/o value
405
    if (m_jammed) { desc += ", jammed"; }
55,358,899✔
406
    if (m_overloaded) { desc += ", overloaded"; }
55,358,899✔
407
    if (m_sundered) { desc += ", sundered"; }
55,358,899✔
408
    if (m_summoned) { desc+= ", summoned"; }
55,358,899✔
409
    // Status w/ value
410
    if (m_corroded_weakened || m_corroded_rate) { desc += ", corroded " + tuo::to_string(m_corroded_weakened) + " (rate: " + tuo::to_string(m_corroded_rate) + ")"; }
57,369,367✔
411
    if (m_subdued) { desc += ", subdued " + tuo::to_string(m_subdued); }
57,518,971✔
412
    if (m_enfeebled) { desc += ", enfeebled " + tuo::to_string(m_enfeebled); }
60,647,583✔
413
    if (m_inhibited) { desc += ", inhibited " + tuo::to_string(m_inhibited); }
58,375,041✔
414
    if (m_sabotaged) { desc += ", sabotaged " + tuo::to_string(m_sabotaged); }
58,579,967✔
415
    if (m_poisoned) { desc += ", poisoned " + tuo::to_string(m_poisoned); }
57,172,993✔
416
    if (m_protected) { desc += ", protected " + tuo::to_string(m_protected); }
76,468,447✔
417
    if (m_protected_stasis) { desc += ", stasis " + tuo::to_string(m_protected_stasis); }
60,193,329✔
418
    if (m_enraged) { desc += ", enraged " + tuo::to_string(m_enraged); }
61,518,323✔
419
    if (m_entrapped) { desc += ", entrapped " + tuo::to_string(m_entrapped); }
74,996,879✔
420
    if (m_marked) { desc += ", marked " + tuo::to_string(m_marked); }
55,672,189✔
421
    if (m_diseased) { desc += ", diseased " + tuo::to_string(m_diseased); }
56,457,615✔
422
    //    if(m_step != CardStep::none) { desc += ", Step " + to_string(static_cast<int>(m_step)); }
423
    //APN
424
    const Skill::Trigger s_triggers[] = { Skill::Trigger::play, Skill::Trigger::activate, Skill::Trigger::death , Skill::Trigger::attacked};
55,358,899✔
425
    for (const Skill::Trigger& trig: s_triggers)
276,794,495✔
426
    {
427
        std::vector<SkillSpec> card_skills(
221,435,596✔
428
                (trig == Skill::Trigger::play) ? m_card->m_skills_on_play :
221,435,596✔
429
                (trig == Skill::Trigger::activate) ? m_card->m_skills :
166,076,697✔
430
                //APN
431
                (trig == Skill::Trigger::attacked) ? m_card->m_skills_on_attacked :
110,717,798✔
432
                (trig == Skill::Trigger::death) ? m_card->m_skills_on_death :
55,358,899✔
433
                std::vector<SkillSpec>());
221,435,596✔
434

435
        // emulate Berserk/Counter by status Enraged/Entrapped unless such skills exist (only for normal skill triggering)
436
        if (trig == Skill::Trigger::activate)
221,435,596✔
437
        {
438
            if (m_enraged && !std::count_if(card_skills.begin(), card_skills.end(), [](const SkillSpec ss) { return (ss.id == Skill::berserk); }))
58,438,611✔
439
            {
440
                SkillSpec ss{Skill::berserk, m_enraged, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, false, 0,};
3,039,138✔
441
                card_skills.emplace_back(ss);
3,039,138✔
442
            }
443
            if (m_entrapped && !std::count_if(card_skills.begin(), card_skills.end(), [](const SkillSpec ss) { return (ss.id == Skill::counter); }))
65,177,889✔
444
            {
445
                SkillSpec ss{Skill::counter, m_entrapped, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, false, 0,};
9,612,894✔
446
                card_skills.emplace_back(ss);
9,612,894✔
447
            }
448
        }
449
        for (const auto& ss : card_skills)
399,425,913✔
450
        {
451
            std::string skill_desc;
177,990,317✔
452
            if (m_evolved_skill_offset[ss.id]) { skill_desc += "->" + skill_names[ss.id + m_evolved_skill_offset[ss.id]]; }
178,389,582✔
453
            if (m_enhanced_value[ss.id]) { skill_desc += " +" + tuo::to_string(m_enhanced_value[ss.id]); }
180,890,113✔
454
            if (!skill_desc.empty())
177,990,317✔
455
            {
456
                desc += ", " + (
3,698,326✔
457
                        (trig == Skill::Trigger::play) ? "(On Play)" :
3,698,326✔
458
                        (trig == Skill::Trigger::attacked) ? "(On Attacked)" :
1,849,163✔
459
                        (trig == Skill::Trigger::death) ? "(On Death)" :
1,849,163✔
460
                        std::string("")) + skill_names[ss.id] + skill_desc;
9,245,815✔
461
            }
462
        }
177,990,317✔
463
    }
221,435,596✔
464
    return desc + "]";
55,358,899✔
465
}
55,358,899✔
466
//------------------------------------------------------------------------------
467
void Hand::reset(std::mt19937& re)
2,239,000✔
468
{
469
    assaults.reset();
2,239,000✔
470
    structures.reset();
2,239,000✔
471
    deck->shuffle(re);
2,239,000✔
472
    commander.set(deck->shuffled_commander);
2,239,000✔
473
    total_cards_destroyed = 0;
2,239,000✔
474
    total_nonsummon_cards_destroyed = 0;
2,239,000✔
475
    if (commander.skill(Skill::stasis))
2,239,000✔
476
    {
477
        stasis_faction_bitmap |= (1u << commander.m_card->m_faction);
×
478
    }
479
}
2,239,000✔
480

481
//---------------------- $40 Game rules implementation -------------------------
482
// Everything about how a battle plays out, except the following:
483
// the implementation of the attack by an assault card is in the next section;
484
// the implementation of the active skills is in the section after that.
485
unsigned turn_limit{50};
486

487
//------------------------------------------------------------------------------
488
inline unsigned opponent(unsigned player)
46,668,680✔
489
{
490
    return((player + 1) % 2);
46,668,680✔
491
}
492

493
//------------------------------------------------------------------------------
494
SkillSpec apply_evolve(const SkillSpec& s, signed offset)
182,858✔
495
{
496
    SkillSpec evolved_s = s;
182,858✔
497
    evolved_s.id = static_cast<Skill::Skill>(evolved_s.id + offset);
182,858✔
498
    return(evolved_s);
182,858✔
499
}
500

501
//------------------------------------------------------------------------------
502
SkillSpec apply_enhance(const SkillSpec& s, unsigned enhanced_value)
×
503
{
504
    SkillSpec enahnced_s = s;
×
505
    enahnced_s.x += enhanced_value;
×
506
    return(enahnced_s);
×
507
}
508

509
//------------------------------------------------------------------------------
510
SkillSpec apply_sabotage(const SkillSpec& s, unsigned sabotaged_value)
143,618✔
511
{
512
    SkillSpec sabotaged_s = s;
143,618✔
513
    sabotaged_s.x -= std::min(sabotaged_s.x, sabotaged_value);
×
514
    return(sabotaged_s);
143,618✔
515
}
516

517
//------------------------------------------------------------------------------
518
inline void resolve_scavenge(Storage<CardStatus>& store)
33,349,976✔
519
{
520
    for(auto status : store.m_indirect)
129,159,901✔
521
    {
522
        if(!is_alive(status))continue;
95,809,925✔
523
        unsigned scavenge_value = status->skill(Skill::scavenge);
83,819,790✔
524
        if(!scavenge_value)continue;
83,819,790✔
525

526
        _DEBUG_MSG(1, "%s activates Scavenge %u\n",
5,637,974✔
527
                status_description(status).c_str(), scavenge_value);
5,637,974✔
528
        status->ext_hp(scavenge_value);
5,637,974✔
529
    }
530
}
33,349,976✔
531
//------------------------------------------------------------------------------
532
/**
533
 * @brief Handle death of a card
534
 * 
535
 * @param fd Field pointer 
536
 * @param paybacked Is the death caused by payback?
537
 */
538
void prepend_on_death(Field* fd, bool paybacked=false)
109,722,622✔
539
{
540
    if (fd->killed_units.empty())
109,722,622✔
541
        return;
542
    bool skip_all_except_summon = fd->fixes[Fix::death_from_bge] && (fd->current_phase == Field::bge_phase);
8,236,306✔
543
    if (skip_all_except_summon)
×
544
    {
545
        _DEBUG_MSG(2, "Death from BGE Fix (skip all death depended triggers except summon)\n");
×
546
    }
547
    auto& assaults = fd->players[fd->killed_units[0]->m_player]->assaults;
8,236,306✔
548
    unsigned stacked_poison_value = 0;
8,236,306✔
549
    unsigned last_index = 99999;
8,236,306✔
550
    CardStatus* left_virulence_victim = nullptr;
8,236,306✔
551
    for (auto status: fd->killed_units)
16,573,800✔
552
    {
553
        // Skill: Scavenge
554
        // Any unit dies => perm-hp-buff
555
        if (__builtin_expect(!skip_all_except_summon, true))
8,337,494✔
556
        {
557
            resolve_scavenge(fd->players[0]->assaults);
8,337,494✔
558
            resolve_scavenge(fd->players[1]->assaults);
8,337,494✔
559
            resolve_scavenge(fd->players[0]->structures);
8,337,494✔
560
            resolve_scavenge(fd->players[1]->structures);
8,337,494✔
561
        }
562

563
        if ((status->m_card->m_type == CardType::assault) && (!skip_all_except_summon))
8,337,494✔
564
        {
565
            // Skill: Avenge
566
            const unsigned host_idx = status->m_index;
7,345,191✔
567
            unsigned from_idx, till_idx;
7,345,191✔
568
            //scan all assaults for Avenge
569
            from_idx = 0;
7,345,191✔
570
            till_idx = assaults.size() - 1;
7,345,191✔
571
            for (; from_idx <= till_idx; ++ from_idx)
29,259,461✔
572
            {
573
                if (from_idx == host_idx) { continue; }
33,353,658✔
574
                CardStatus* adj_status = &assaults[from_idx];
14,569,079✔
575
                if (!is_alive(adj_status)) { continue; }
14,569,079✔
576
                unsigned avenge_value = adj_status->skill(Skill::avenge);
12,816,951✔
577
                if (!avenge_value) { continue; }
12,816,951✔
578

579
                // Use half value rounded up
580
                // (for distance > 1, i. e. non-standard Avenge triggering)
581
                if (std::abs((signed)from_idx - (signed)host_idx) > 1)
3,129,691✔
582
                {
583
                    avenge_value = (avenge_value + 1) / 2;
1,746,820✔
584
                }
585
                _DEBUG_STRAP(
3,129,691✔
586
                        "turn", fd->turn,
587
                        "active_player", fd->tapi,
588
                        "avenge_"+strap_string(adj_status), 1.0,
589
                        "avenge_value", avenge_value,
590
                        "avenge_half", (std::abs((signed)from_idx - (signed)host_idx) > 1 ? 1 : 0)
591
                );
3,129,691✔
592
                _DEBUG_MSG(1, "%s activates %sAvenge %u\n",
3,287,150✔
593
                        status_description(adj_status).c_str(),
594
                        (std::abs((signed)from_idx - (signed)host_idx) > 1 ? "Half-" : ""),
595
                         avenge_value);
3,129,691✔
596
                if (!adj_status->m_sundered)
3,129,691✔
597
                { adj_status->m_perm_attack_buff += avenge_value; }
2,923,675✔
598
                adj_status->ext_hp(avenge_value);
3,129,691✔
599
            }
600

601
            // Passive BGE: Virulence
602
            if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::virulence], false))
7,345,191✔
603
            {
604
                if (status->m_index != last_index + 1)
34,335✔
605
                {
606
                    stacked_poison_value = 0;
33,957✔
607
                    left_virulence_victim = nullptr;
33,957✔
608
                    if (status->m_index > 0)
33,957✔
609
                    {
610
                        auto left_status = &assaults[status->m_index - 1];
7,147✔
611
                        if (is_alive(left_status))
7,147✔
612
                        {
613
                            left_virulence_victim = left_status;
34,335✔
614
                        }
615
                    }
616
                }
617
                if (status->m_poisoned > 0)
34,335✔
618
                {
619
                    if (left_virulence_victim != nullptr)
1,571✔
620
                    {
621
                        _DEBUG_MSG(1, "Virulence: %s spreads left poison +%u to %s\n",
141✔
622
                                status_description(status).c_str(), status->m_poisoned,
623
                                status_description(left_virulence_victim).c_str());
141✔
624
                        left_virulence_victim->m_poisoned += status->m_poisoned;
141✔
625
                    }
626
                    stacked_poison_value += status->m_poisoned;
1,571✔
627
                    _DEBUG_MSG(1, "Virulence: %s spreads right poison +%u = %u\n",
1,571✔
628
                            status_description(status).c_str(), status->m_poisoned, stacked_poison_value);
629
                }
630
                if (status->m_index + 1 < assaults.size())
34,335✔
631
                {
632
                    auto right_status = &assaults[status->m_index + 1];
11,107✔
633
                    if (is_alive(right_status))
11,107✔
634
                    {
635
                        _DEBUG_MSG(1, "Virulence: spreads stacked poison +%u to %s\n",
10,277✔
636
                                stacked_poison_value, status_description(right_status).c_str());
10,277✔
637
                        right_status->m_poisoned += stacked_poison_value;
10,277✔
638
                    }
639
                }
640
                last_index = status->m_index;
34,335✔
641
            }
642
        }
643

644
        // Passive BGE: Revenge
645
        // Fix::death_from_bge: should not affect passive BGE, keep it as was before
646
        if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::revenge], false))
8,337,494✔
647
        {
648
            if (fd->bg_effects[fd->tapi][PassiveBGE::revenge] < 0)
73,397✔
649
                throw std::runtime_error("BGE Revenge: value must be defined & positive");
×
650
            SkillSpec ss_heal{Skill::heal, (unsigned)fd->bg_effects[fd->tapi][PassiveBGE::revenge], allfactions, 0, 0, Skill::no_skill, Skill::no_skill, true, 0,};
73,397✔
651
            SkillSpec ss_rally{Skill::rally, (unsigned)fd->bg_effects[fd->tapi][PassiveBGE::revenge], allfactions, 0, 0, Skill::no_skill, Skill::no_skill, true, 0,};
73,397✔
652
            CardStatus* commander = &fd->players[status->m_player]->commander;
73,397✔
653
            _DEBUG_MSG(2, "Revenge: Preparing (head) skills  %s and %s\n",
73,397✔
654
                    skill_description(fd->cards, ss_heal).c_str(),
655
                    skill_description(fd->cards, ss_rally).c_str());
73,397✔
656
            fd->skill_queue.emplace(fd->skill_queue.begin()+0, commander, ss_heal);
73,397✔
657
            fd->skill_queue.emplace(fd->skill_queue.begin()+1, commander, ss_rally); // +1: keep ss_heal at first place
73,397✔
658
        }
659

660
        // resolve On-Death skills
661
        for (auto& ss: status->m_card->m_skills_on_death)
8,724,018✔
662
        {
663
            if (__builtin_expect(skip_all_except_summon && (ss.id != Skill::summon), false))
386,524✔
664
            { continue; }
×
665
                SkillSpec tss = ss;
386,524✔
666
            _DEBUG_MSG(2, "On Death %s: Preparing (tail) skill %s\n",
386,524✔
667
                    status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
386,524✔
668
            if(fd->fixes[Fix::revenge_on_death] && is_activation_harmful_skill(ss.id) && paybacked)
386,524✔
669
            {
670
                    _DEBUG_MSG(2, "On Death Revenge Fix\n");
311✔
671
                    tss.s2 = Skill::revenge;
311✔
672
            }
673
            fd->skill_queue.emplace_back(status, tss);
386,524✔
674
        }
675
    }
676
    fd->killed_units.clear();
117,958,928✔
677
}
678

679
//------------------------------------------------------------------------------
680
void(*skill_table[Skill::num_skills])(Field*, CardStatus* src, const SkillSpec&);
681
void resolve_skill(Field* fd)
154,001,893✔
682
{
683
    while (!fd->skill_queue.empty())
251,719,354✔
684
    {
685
        auto skill_instance(fd->skill_queue.front());
97,717,461✔
686
        auto& status(std::get<0>(skill_instance));
97,717,461✔
687
        const auto& ss(std::get<1>(skill_instance));
97,717,461✔
688
        fd->skill_queue.pop_front();
97,717,461✔
689
        if (__builtin_expect(status->m_card->m_skill_trigger[ss.id] == Skill::Trigger::activate, true))
97,717,461✔
690
        {
691
            if (!is_alive(status))
93,759,990✔
692
            {
693
                _DEBUG_MSG(2, "%s failed to %s because it is dead.\n",
4,015✔
694
                        status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
4,015✔
695
                continue;
2,759,633✔
696
            }
4,015✔
697
            if (status->m_jammed)
93,755,975✔
698
            {
699
                _DEBUG_MSG(2, "%s failed to %s because it is Jammed.\n",
1,425✔
700
                        status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
1,425✔
701
                continue;
1,425✔
702
            }
1,425✔
703
        }
704

705
        // is summon? (non-activation skill)
706
        if (ss.id == Skill::summon)
97,712,021✔
707
        {
708
            check_and_perform_summon(fd, status);
2,680,076✔
709
            continue;
2,680,076✔
710
        }
711
        _DEBUG_ASSERT(is_activation_skill(ss.id) || ss.id == Skill::enhance); // enhance is no trigger, but  queues the skill
95,124,987✔
712

713
        SkillSpec modified_s = ss;
95,031,945✔
714

715
        // apply evolve
716
        signed evolved_offset = status->m_evolved_skill_offset[modified_s.id];
95,031,945✔
717
        if (evolved_offset != 0)
95,031,945✔
718
        { modified_s = apply_evolve(modified_s, evolved_offset); }
182,858✔
719

720
        // apply sabotage (only for X-based activation skills)
721
        unsigned sabotaged_value = status->m_sabotaged;
95,031,945✔
722
        if ((sabotaged_value > 0) && is_activation_skill_with_x(modified_s.id))
95,031,945✔
723
        { modified_s = apply_sabotage(modified_s, sabotaged_value); }
213,119✔
724

725
        // apply enhance
726
        unsigned enhanced_value = status->enhanced(modified_s.id);
95,031,945✔
727
        if (enhanced_value > 0)
95,031,945✔
728
        { modified_s = apply_enhance(modified_s, enhanced_value); }
×
729

730
        // perform skill (if it is still applicable)
731
        if (is_activation_skill_with_x(modified_s.id) && !modified_s.x)
95,031,945✔
732
        {
733
            _DEBUG_MSG(2, "%s failed to %s because its X value is zeroed (sabotaged).\n",
74,117✔
734
                    status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
74,117✔
735
            continue;
74,117✔
736
        }
74,117✔
737
        else { skill_table[modified_s.id](fd, status, modified_s); }
94,957,828✔
738
    }
739
}
154,001,893✔
740

741
void apply_corrosion(CardStatus * status)
17,370,145✔
742
{
743
      if (status->m_corroded_rate)
17,370,145✔
744
      {
745
        unsigned v = std::min(status->m_corroded_rate, status->attack_power());
103,238✔
746
        unsigned corrosion = std::min(v, status->m_card->m_attack
206,476✔
747
                + status->m_perm_attack_buff - status->m_corroded_weakened);
103,238✔
748
        _DEBUG_MSG(1, "%s loses Attack by %u (+corrosion %u).\n", status_description(status).c_str(), v, corrosion);
103,238✔
749
        status->m_corroded_weakened += corrosion;
103,238✔
750
      }
751
}
17,370,145✔
752
void remove_corrosion(CardStatus * status)
2,648,180✔
753
{
754
       if (status->m_corroded_rate)
2,648,180✔
755
       {
756
           _DEBUG_MSG(1, "%s loses Status corroded.\n", status_description(status).c_str());
4,224✔
757
           status->m_corroded_rate = 0;
4,224✔
758
           status->m_corroded_weakened = 0;
4,224✔
759
       }
760
}
2,648,180✔
761
//------------------------------------------------------------------------------
762
bool attack_phase(Field* fd);
763

764
template<Skill::Skill skill_id>
765
inline bool check_and_perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s, bool is_evadable
766
#ifndef NQUEST
767
        , bool & has_counted_quest
768
#endif
769
        );
770

771
    template <enum CardType::CardType type>
772
void evaluate_skills(Field* fd, CardStatus* status, const std::vector<SkillSpec>& skills, bool* attacked=nullptr)
51,009,615✔
773
{
774
    _DEBUG_ASSERT(status);
51,009,615✔
775
    unsigned num_actions(1);
776
    for (unsigned action_index(0); action_index < num_actions; ++ action_index)
106,531,587✔
777
    {
778
        status->m_action_index = action_index;
56,620,777✔
779
        fd->prepare_action();
56,620,777✔
780
        _DEBUG_ASSERT(fd->skill_queue.size() == 0);
56,620,777✔
781
        for (auto & ss: skills)
218,012,422✔
782
        {
783
            if (!is_activation_skill(ss.id)) { continue; }
228,528,488✔
784
            if (status->m_skill_cd[ss.id] > 0) { continue; }
94,254,802✔
785
            _DEBUG_MSG(2, "Evaluating %s skill %s\n",
93,520,154✔
786
                    status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
787
            fd->skill_queue.emplace_back(status, ss);
93,520,154✔
788
            resolve_skill(fd);
93,520,154✔
789
        }
790
        if (type == CardType::assault)
791
        {
792
            // Attack
793
            if (can_act(status))
21,117,130✔
794
            {
795
                if (attack_phase(fd))
20,960,825✔
796
                {
797
                    *attacked = true;
18,468,950✔
798
                    if (__builtin_expect(fd->end, false)) { break; }
18,468,950✔
799
                    //Apply corrosion
800
                    apply_corrosion(status);
17,370,145✔
801
                }
802
                else
803
                {
804
                  // Remove Corrosion
805
                  remove_corrosion(status);
2,491,875✔
806
                }
807
            }
808
            else
809
            {
810
                _DEBUG_MSG(2, "%s cannot take attack.\n", status_description(status).c_str());
156,305✔
811
                // Remove Corrosion
812
                remove_corrosion(status);
156,305✔
813
            }
814
        }
815
        fd->finalize_action();
55,521,972✔
816
        // Flurry
817
        if (can_act(status) && status->has_skill(Skill::flurry) && (status->m_skill_cd[Skill::flurry] == 0))
112,525,852✔
818
        {
819
#ifndef NQUEST
820
            if (status->m_player == 0)
821
            {
822
                fd->inc_counter(QuestType::skill_use, Skill::flurry);
823
            }
824
#endif
825
            _DEBUG_STRAP(
2,217,924✔
826
                    "turn", fd->turn,
827
                    "active_player", fd->tapi,
828
                    "flurry_"+strap_string(status), 1.0,
829
                    "flurry_count", status->skill_base_value(Skill::flurry)
830
            );
831
            _DEBUG_MSG(1, "%s activates Flurry x %d\n",
2,348,636✔
832
                    status_description(status).c_str(), status->skill_base_value(Skill::flurry));
833
            num_actions += status->skill_base_value(Skill::flurry);
2,217,924✔
834
            for (const auto & ss : skills)
8,868,579✔
835
            {
836
                Skill::Skill evolved_skill_id = static_cast<Skill::Skill>(ss.id + status->m_evolved_skill_offset[ss.id]);
6,650,655✔
837
                if (evolved_skill_id == Skill::flurry)
6,650,655✔
838
                {
839
                    status->m_skill_cd[ss.id] = ss.c;
2,217,924✔
840
                }
841
            }
842
        }
843
    }
844
}
51,009,615✔
845

846
struct PlayCard
847
{
848
    const Card* card;
849
    Field* fd;
850
    CardStatus* status;
851
    Storage<CardStatus>* storage;
852
    const unsigned actor_index;
853
    const CardStatus* actor_status;
854

855
    PlayCard(const Card* card_, Field* fd_, unsigned ai_, CardStatus* as_) :
18,094,083✔
856
        card{card_},
18,094,083✔
857
        fd{fd_},
18,094,083✔
858
        status{nullptr},
18,094,083✔
859
        storage{nullptr},
18,094,083✔
860
        actor_index{ai_},
18,094,083✔
861
        actor_status{as_}
18,094,083✔
862
    {}
863

864
    template <enum CardType::CardType type>
865
        CardStatus* op()
15,116,181✔
866
        {
867
            return op<type>(false);
×
868
        }
869

870
    template <enum CardType::CardType type>
871
        CardStatus* op(bool summoned)
18,094,083✔
872
        {
873
            setStorage<type>();
18,094,083✔
874
            placeCard<type>(summoned);
18,094,083✔
875

876
            unsigned played_faction_mask(0);
18,094,083✔
877
            unsigned same_faction_cards_count(0);
18,094,083✔
878
            bool bge_megamorphosis = fd->bg_effects[status->m_player][PassiveBGE::megamorphosis];
18,094,083✔
879
            //played_status = status;
880
            //played_card = card;
881
            if(__builtin_expect(fd->fixes[Fix::barrier_each_turn],true) && status->has_skill(Skill::barrier)){
18,094,083✔
882
                _DEBUG_MSG(1,"%s gets barrier protection %u per turn\n",status_description(status).c_str(),status->skill(Skill::barrier));
513,384✔
883
                status->m_protected += status->skill(Skill::barrier);
400,167✔
884
            }
885
            if (status->m_delay == 0)
18,094,083✔
886
            {
887
                    check_and_perform_bravery(fd,status);
1,383,950✔
888
                check_and_perform_valor(fd, status);
1,383,950✔
889
            }
890
            
891

892
            //refresh/init absorb
893
            if(status->has_skill(Skill::absorb))
18,094,083✔
894
            {
895
                status->m_absorption = status->skill_base_value(Skill::absorb);
961,124✔
896
            }
897

898

899
            // 1. Evaluate skill Allegiance & count assaults with same faction (structures will be counted later)
900
            // 2. Passive BGE Cold Sleep
901
            for (CardStatus* status_i : fd->players[status->m_player]->assaults.m_indirect)
70,038,910✔
902
            {
903
                if (status_i == status || !is_alive(status_i)) { continue; } // except itself
51,944,827✔
904
                //std::cout << status_description(status_i).c_str();
905
                _DEBUG_ASSERT(is_alive(status_i));
37,598,933✔
906
                if (bge_megamorphosis || (status_i->m_card->m_faction == card->m_faction))
37,598,933✔
907
                {
908
                    ++ same_faction_cards_count;
14,761,905✔
909
                    unsigned allegiance_value = status_i->skill(Skill::allegiance);
14,761,905✔
910
                    if (__builtin_expect(allegiance_value, false) && !status->m_summoned)
14,761,905✔
911
                    {
912
                        _DEBUG_MSG(1, "%s activates Allegiance %u\n", status_description(status_i).c_str(), allegiance_value);
913,534✔
913
                        if (! status_i->m_sundered)
825,432✔
914
                        { status_i->m_perm_attack_buff += allegiance_value; }
798,318✔
915
                        status_i->ext_hp(allegiance_value);
825,432✔
916
                    }
917
                }
918
                if (__builtin_expect(fd->bg_effects[status->m_player][PassiveBGE::coldsleep], false)
37,598,933✔
919
                        && status_i->m_protected_stasis && can_be_healed(status_i))
89,543,760✔
920
                {
921
                    unsigned bge_value = (status_i->m_protected_stasis + 1) / 2;
2,103✔
922
                    _DEBUG_MSG(1, "Cold Sleep: %s heals itself for %u\n", status_description(status_i).c_str(), bge_value);
6,309✔
923
                    status_i->add_hp(bge_value);
2,103✔
924
                }
925
            }
926

927
            // Setup faction marks (bitmap) for stasis (skill Stasis / Passive BGE TemporalBacklash)
928
            // unless Passive BGE Megamorphosis is enabled
929
            if (__builtin_expect(!bge_megamorphosis, true))
18,094,083✔
930
            {
931
                played_faction_mask = (1u << card->m_faction);
18,029,041✔
932
                // do played card have stasis? mark this faction for stasis check
933
                if (__builtin_expect(status->skill(Skill::stasis), false)
18,029,041✔
934
                        || __builtin_expect(fd->bg_effects[status->m_player][PassiveBGE::temporalbacklash] && status->skill(Skill::counter), false))
18,029,041✔
935
                {
936
                    fd->players[status->m_player]->stasis_faction_bitmap |= played_faction_mask;
3,021,853✔
937
                }
938
            }
939

940
            // Evaluate Passive BGE Oath-of-Loyalty
941
            unsigned allegiance_value;
942
            if (__builtin_expect(fd->bg_effects[status->m_player][PassiveBGE::oath_of_loyalty], false)
18,094,083✔
943
                    && ((allegiance_value = status->skill(Skill::allegiance)) > 0))
18,094,083✔
944
            {
945
                // count structures with same faction (except fortresses, dominions and other non-normal structures)
946
                for (CardStatus * status_i : fd->players[status->m_player]->structures.m_indirect)
2,083✔
947
                {
948
                    if ((status_i->m_card->m_category == CardCategory::normal)
873✔
949
                            && (bge_megamorphosis || (status_i->m_card->m_faction == card->m_faction)))
873✔
950
                    {
951
                        ++ same_faction_cards_count;
×
952
                    }
953
                }
954

955
                // apply Passive BGE Oath-of-Loyalty when multiplier isn't zero
956
                if (same_faction_cards_count)
1,210✔
957
                {
958
                    unsigned bge_value = allegiance_value * same_faction_cards_count;
436✔
959
                    _DEBUG_MSG(1, "Oath of Loyalty: %s activates Allegiance %u x %u = %u\n",
872✔
960
                            status_description(status).c_str(), allegiance_value, same_faction_cards_count, bge_value);
961
                    status->m_perm_attack_buff += bge_value;
436✔
962
                    status->ext_hp(bge_value);
436✔
963
                }
964
            }
965

966
            // summarize stasis when:
967
            //  1. Passive BGE Megamorphosis is enabled
968
            //  2. current faction is marked for it
969
            if ((card->m_delay > 0) && (card->m_type == CardType::assault)
16,710,133✔
970
                    && __builtin_expect(bge_megamorphosis || (fd->players[status->m_player]->stasis_faction_bitmap & played_faction_mask), false))
31,135,406✔
971
            {
972
                unsigned stacked_stasis = (bge_megamorphosis || (fd->players[status->m_player]->commander.m_card->m_faction == card->m_faction))
5,806,484✔
973
                    ? fd->players[status->m_player]->commander.skill(Skill::stasis)
10,448,387✔
974
                    : 0u;
975
#ifndef NDEBUG
976
                if (stacked_stasis > 0)
4,641,903✔
977
                {
978
                    _DEBUG_MSG(2, "+ Stasis [%s]: stacks +%u stasis protection from %s (total stacked: %u)\n",
×
979
                            faction_names[card->m_faction].c_str(), stacked_stasis,
980
                            status_description(&fd->players[status->m_player]->commander).c_str(), stacked_stasis);
981
                }
982
#endif
983
                for (CardStatus * status_i : fd->players[status->m_player]->structures.m_indirect)
11,026,449✔
984
                {
985
                    if ((bge_megamorphosis || (status_i->m_card->m_faction == card->m_faction)) && is_alive(status_i))
5,176,751✔
986
                    {
987
                        stacked_stasis += status_i->skill(Skill::stasis);
1,531,606✔
988
#ifndef NDEBUG
989
                        if (status_i->skill(Skill::stasis) > 0)
1,531,606✔
990
                        {
991
                            _DEBUG_MSG(2, "+ Stasis [%s]: stacks +%u stasis protection from %s (total stacked: %u)\n",
5,176,751✔
992
                                    faction_names[card->m_faction].c_str(), status_i->skill(Skill::stasis),
993
                                    status_description(status_i).c_str(), stacked_stasis);
994
                        }
995
#endif
996
                    }
997
                }
998
                for (CardStatus * status_i : fd->players[status->m_player]->assaults.m_indirect)
22,709,548✔
999
                {
1000
                    if ((bge_megamorphosis || (status_i->m_card->m_faction == card->m_faction)) && is_alive(status_i))
16,859,850✔
1001
                    {
1002
                        stacked_stasis += status_i->skill(Skill::stasis);
12,825,545✔
1003
#ifndef NDEBUG
1004
                        if (status_i->skill(Skill::stasis) > 0)
12,825,545✔
1005
                        {
1006
                            _DEBUG_MSG(2, "+ Stasis [%s]: stacks +%u stasis protection from %s (total stacked: %u)\n",
5,780,784✔
1007
                                    faction_names[card->m_faction].c_str(), status_i->skill(Skill::stasis),
1008
                                    status_description(status_i).c_str(), stacked_stasis);
1009
                        }
1010
#endif
1011
                        if (__builtin_expect(fd->bg_effects[status->m_player][PassiveBGE::temporalbacklash] && status_i->skill(Skill::counter), false))
12,825,545✔
1012
                        {
1013
                            stacked_stasis += (status_i->skill(Skill::counter) + 1) / 2;
3,014✔
1014
#ifndef NDEBUG
1015
                            _DEBUG_MSG(2, "Temporal Backlash: + Stasis [%s]: stacks +%u stasis protection from %s (total stacked: %u)\n",
16,862,864✔
1016
                                    faction_names[card->m_faction].c_str(), (status_i->skill(Skill::counter) + 1) / 2,
1017
                                    status_description(status_i).c_str(), stacked_stasis);
1018
#endif
1019
                        }
1020
                    }
1021
                }
1022
                status->m_protected_stasis = stacked_stasis;
5,849,698✔
1023
#ifndef NDEBUG
1024
                if (stacked_stasis > 0)
5,849,698✔
1025
                {
1026
                    _DEBUG_MSG(1, "%s gains %u stasis protection\n",
4,884,549✔
1027
                            status_description(status).c_str(), stacked_stasis);
1028
                }
1029
#endif
1030
                // no more stasis for current faction: do unmark (if no Passive BGE Megamorphosis)
1031
                if (__builtin_expect(!bge_megamorphosis, true) && __builtin_expect(!stacked_stasis, false))
5,849,698✔
1032
                {
1033
                    fd->players[status->m_player]->stasis_faction_bitmap &= ~played_faction_mask;
1,199,480✔
1034
                    _DEBUG_MSG(1, "- Stasis [%s]: no more units with stasis from %s\n",
1,208,999✔
1035
                            faction_names[card->m_faction].c_str(),status_description(&fd->players[status->m_player]->commander).c_str());
1036
                }
1037

1038
            }
1039
            //Devotion BGE
1040
            if (__builtin_expect(fd->bg_effects[status->m_player][PassiveBGE::devotion], false)
18,094,083✔
1041
                    && !summoned && card->m_category == CardCategory::normal
64,796✔
1042
                    && fd->players[status->m_player]->commander.m_card->m_faction == card->m_faction)
18,154,060✔
1043
            {
1044
                unsigned devotion_percent = fd->bg_effects[status->m_player][PassiveBGE::devotion];
9,412✔
1045
                unsigned bge_buff = (card->m_health*devotion_percent+99)/100;
9,412✔
1046
                _DEBUG_MSG(1, "Devotion %s: Gains %u HP\n",
18,824✔
1047
                        status_description(status).c_str(), bge_buff);
1048
                status->ext_hp(bge_buff); // <bge_value>% bonus health (rounded up)
9,412✔
1049
            }
1050

1051

1052
            // resolve On-Play skills
1053
            // Fix Death on BGE: [On Play] skills during BGE phase can be invoked only by means of [On Death] trigger
1054
            if (__builtin_expect(fd->fixes[Fix::death_from_bge] && (fd->current_phase != Field::bge_phase), true))
18,094,083✔
1055
            {
1056
                for (const auto& ss: card->m_skills_on_play)
21,465,040✔
1057
                {
1058
                    _DEBUG_MSG(2, "On Play %s: Preparing (tail) skill %s\n",
3,370,957✔
1059
                            status_description(status).c_str(), skill_description(fd->cards, ss).c_str());
1060
                    fd->skill_queue.emplace_back(status, ss);
3,370,957✔
1061
                }
1062
            }
1063
            else
1064
            {
1065
                _DEBUG_MSG(2, "Death from BGE Fix: suppress [On Play] skills invoked by [On Death] summon\n");
×
1066
            }
1067

1068
            return status;
18,094,083✔
1069
        }
1070

1071
    template <enum CardType::CardType>
1072
        void setStorage()
1073
        {
1074
        }
1075

1076
    template <enum CardType::CardType type>
1077
        void placeCard(bool summoned)
18,094,083✔
1078
        {
1079
            status = &storage->add_back();
18,094,083✔
1080
            status->set(card);
18,094,083✔
1081
            status->m_index = storage->size() - 1;
18,094,083✔
1082
            status->m_player = actor_index;
18,094,083✔
1083
            status->m_field = fd;
18,094,083✔
1084
            status->m_summoned = summoned;
18,094,083✔
1085

1086
            // reduce delay for summoned card by tower (structure) for normal (non-triggered) summon skill
1087
            if (summoned && __builtin_expect(fd->fixes[Fix::reduce_delay_summoned_by_tower], true)
18,094,083✔
1088
                && actor_status->m_card->m_skill_trigger[Skill::summon] == Skill::Trigger::activate
2,964,008✔
1089
                && actor_status->m_card->m_type == CardType::structure
287,291✔
1090
                && status->m_delay > 0)
8,411✔
1091
            {
1092
                --status->m_delay;
8,411✔
1093

1094
                _DEBUG_MSG(1, "%s reduces its timer (as summoned by tower)\n", status_description(status).c_str());
16,822✔
1095
            }
1096

1097
#ifndef NQUEST
1098
            if (actor_index == 0)
1099
            {
1100
                if (card->m_type == CardType::assault)
1101
                {
1102
                    fd->inc_counter(QuestType::faction_assault_card_use, card->m_faction);
1103
                }
1104
                fd->inc_counter(QuestType::type_card_use, type);
1105
            }
1106
#endif
1107
            _DEBUG_MSG(1, "%s plays %s %u [%s]\n",
20,607,302✔
1108
                    status_description(actor_status).c_str(), cardtype_names[type].c_str(),
1109
                    static_cast<unsigned>(storage->size() - 1), card_description(fd->cards, card).c_str());
1110
        }
18,094,083✔
1111
};
1112
// assault
1113
    template <>
1114
void PlayCard::setStorage<CardType::assault>()
14,288,710✔
1115
{
1116
    storage = &fd->players[actor_index]->assaults;
14,288,710✔
1117
}
×
1118
// structure
1119
    template <>
1120
void PlayCard::setStorage<CardType::structure>()
3,805,373✔
1121
{
1122
    storage = &fd->players[actor_index]->structures;
3,805,373✔
1123
}
×
1124

1125
// Check if a skill actually proc'ed.
1126
    template<Skill::Skill skill_id>
1127
inline bool skill_check(Field* fd, CardStatus* c, CardStatus* ref)
216,381,621✔
1128
{
1129
    return is_defensive_skill(skill_id) || is_alive(c);
2,178,641✔
1130
}
1131

1132
    template<>
1133
inline bool skill_check<Skill::heal>(Field* fd, CardStatus* c, CardStatus* ref)
63,730,023✔
1134
{
1135
    return can_be_healed(c);
38,162✔
1136
}
1137

1138
    template<>
1139
inline bool skill_check<Skill::mend>(Field* fd, CardStatus* c, CardStatus* ref)
159,907✔
1140
{
1141
    return can_be_healed(c);
×
1142
}
1143

1144
    template<>
1145
inline bool skill_check<Skill::rally>(Field* fd, CardStatus* c, CardStatus* ref)
30,563,334✔
1146
{
1147
    return !c->m_sundered;
30,563,334✔
1148
}
1149

1150
    template<>
1151
inline bool skill_check<Skill::overload>(Field* fd, CardStatus* c, CardStatus* ref)
776,462✔
1152
{
1153
    return is_active(c) && !c->m_overloaded && !has_attacked(c);
617,050✔
1154
}
1155

1156
    template<>
1157
inline bool skill_check<Skill::jam>(Field* fd, CardStatus* c, CardStatus* ref)
9,022,476✔
1158
{
1159
    if(__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::ironwill], false) && c->has_skill(Skill::armor))return false;
9,022,476✔
1160
    // active player performs Jam
1161
    if (fd->tapi == ref->m_player)
9,020,318✔
1162
    { return is_active_next_turn(c); }
8,991,452✔
1163

1164

1165
    // inactive player performs Jam
1166
    return will_activate_this_turn(c);
28,866✔
1167
}
1168

1169
    template<>
1170
inline bool skill_check<Skill::leech>(Field* fd, CardStatus* c, CardStatus* ref)
25,596✔
1171
{
1172
    return can_be_healed(c) || (fd->fixes[Fix::leech_increase_max_hp] && is_alive(c));
21,082✔
1173
}
1174

1175
    template<>
1176
inline bool skill_check<Skill::coalition>(Field* fd, CardStatus* c, CardStatus* ref)
1177
{
1178
    return is_active(c);
1179
}
1180

1181
    template<>
1182
inline bool skill_check<Skill::payback>(Field* fd, CardStatus* c, CardStatus* ref)
2,567,767✔
1183
{
1184
    return (ref->m_card->m_type == CardType::assault);
2,567,767✔
1185
}
1186

1187
    template<>
1188
inline bool skill_check<Skill::revenge>(Field* fd, CardStatus* c, CardStatus* ref)
1189
{
1190
    return skill_check<Skill::payback>(fd, c, ref);
1191
}
1192

1193
    template<>
1194
inline bool skill_check<Skill::tribute>(Field* fd, CardStatus* c, CardStatus* ref)
101,091,855✔
1195
{
1196
    return (ref->m_card->m_type == CardType::assault) && (c != ref);
101,091,855✔
1197
}
1198

1199
    template<>
1200
inline bool skill_check<Skill::refresh>(Field* fd, CardStatus* c, CardStatus* ref)
1,593,843✔
1201
{
1202
    return can_be_healed(c);
1,593,843✔
1203
}
1204

1205
    template<>
1206
inline bool skill_check<Skill::drain>(Field* fd, CardStatus* c, CardStatus* ref)
450,877✔
1207
{
1208
    return can_be_healed(c);
450,877✔
1209
}
1210

1211
    template<>
1212
inline bool skill_check<Skill::mark>(Field* fd, CardStatus* c, CardStatus* ref)
58,876✔
1213
{
1214
    return (ref->m_card->m_type == CardType::assault);
58,876✔
1215
}
1216

1217
    template<>
1218
inline bool skill_check<Skill::disease>(Field* fd, CardStatus* c, CardStatus* ref)
193,966✔
1219
{
1220
    return is_alive(c) && (ref->m_card->m_type == CardType::assault);
193,966✔
1221
}
1222
    template<>
1223
inline bool skill_check<Skill::inhibit>(Field* fd, CardStatus* c, CardStatus* ref)
2,266,096✔
1224
{
1225
    return is_alive(c) && (ref->m_card->m_type == CardType::assault);
2,266,096✔
1226
}
1227
    template<>
1228
inline bool skill_check<Skill::sabotage>(Field* fd, CardStatus* c, CardStatus* ref)
510,944✔
1229
{
1230
    return is_alive(c) && (ref->m_card->m_type == CardType::assault);
510,944✔
1231
}
1232
inline unsigned remove_disease(CardStatus* status, unsigned heal)
31,891,619✔
1233
{
1234
    unsigned remaining_heal(heal);
31,891,619✔
1235
    if(__builtin_expect(status->m_diseased == 0,true))
31,891,619✔
1236
    {
1237
        //skip
1238
    }
1239
    else if (heal > status->m_diseased)
74,075✔
1240
    {
1241
        _DEBUG_MSG(1, "%s disease-blocked %u heal\n", status_description(status).c_str(), status->m_diseased);
18,568✔
1242
        remaining_heal = heal - status->m_diseased;
18,568✔
1243
        status->m_diseased = 0;
18,568✔
1244
    }
1245
    else
1246
    {
1247
        _DEBUG_MSG(1, "%s disease-blocked %u heal\n", status_description(status).c_str(), heal);
55,507✔
1248
        status->m_diseased -= heal;
55,507✔
1249
        remaining_heal = 0;
55,507✔
1250
    }
1251
    return remaining_heal;
31,891,619✔
1252
}
1253

1254
// Field is currently not needed for remove_absorption, but is here for similiar structure as remove_hp
1255
inline unsigned remove_absorption(Field* fd, CardStatus* status, unsigned dmg)
24,616,942✔
1256
{
1257
    return remove_absorption(status,dmg);
24,616,942✔
1258
}
1259
/**
1260
 * @brief Remove absorption from a CardStatus
1261
 * 
1262
 * @param status CardStatus to remove absorption from
1263
 * @param dmg damage to remove absorption from
1264
 * @return unsigned remaining damage after absorption is removed
1265
 */
1266
inline unsigned remove_absorption(CardStatus* status, unsigned dmg)
24,616,942✔
1267
{
1268
    unsigned remaining_dmg(dmg);
24,616,942✔
1269
    if(__builtin_expect(status->m_absorption == 0,true))
24,616,942✔
1270
    {
1271
        //skip
1272
    }
1273
    else if(dmg > status->m_absorption)
2,494,302✔
1274
    {
1275
        _DEBUG_MSG(1, "%s absorbs %u damage\n", status_description(status).c_str(), status->m_absorption);
360,021✔
1276
        remaining_dmg = dmg - status->m_absorption;
360,021✔
1277
        status->m_absorption = 0;
360,021✔
1278
    }
1279
    else
1280
    {
1281
        _DEBUG_MSG(1, "%s absorbs %u damage\n", status_description(status).c_str(), dmg);
2,134,281✔
1282
        status->m_absorption -= dmg;
2,134,281✔
1283
        remaining_dmg = 0;
2,134,281✔
1284
    }
1285
    return remaining_dmg;
24,616,942✔
1286
}
1287

1288
void remove_hp(Field* fd, CardStatus* status, unsigned dmg)
34,478,973✔
1289
{
1290
    if (__builtin_expect(!dmg, false)) { return; }
34,478,973✔
1291
    _DEBUG_ASSERT(is_alive(status));
23,720,539✔
1292
    _DEBUG_MSG(2, "%s takes %u damage\n", status_description(status).c_str(), dmg);
23,720,539✔
1293
    status->m_hp = safe_minus(status->m_hp, dmg);
23,720,539✔
1294
    if (fd->current_phase < Field::end_phase && status->has_skill(Skill::barrier))
23,720,539✔
1295
    {
1296
        ++fd->damaged_units_to_times[status];
258,237✔
1297
        _DEBUG_MSG(2, "%s damaged %u times\n",
258,237✔
1298
                status_description(status).c_str(), fd->damaged_units_to_times[status]);
1299
    }
1300
    if (status->m_hp == 0)
23,720,539✔
1301
    {
1302
#ifndef NQUEST
1303
        if (status->m_player == 1)
1304
        {
1305
            if (status->m_card->m_type == CardType::assault)
1306
            {
1307
                fd->inc_counter(QuestType::faction_assault_card_kill, status->m_card->m_faction);
1308
            }
1309
            fd->inc_counter(QuestType::type_card_kill, status->m_card->m_type);
1310
        }
1311
#endif
1312
        _DEBUG_MSG(1, "%s dies\n", status_description(status).c_str());
8,337,494✔
1313
        _DEBUG_ASSERT(status->m_card->m_type != CardType::commander);
8,337,494✔
1314
        fd->killed_units.push_back(status);
8,337,494✔
1315
        ++fd->players[status->m_player]->total_cards_destroyed;
8,337,494✔
1316
        if(!status->m_summoned)++fd->players[status->m_player]->total_nonsummon_cards_destroyed;
8,337,494✔
1317
        if (__builtin_expect((status->m_player == 0) && (fd->players[0]->deck->vip_cards.count(status->m_card->m_id)), false))
11,145,347✔
1318
        {
1319
            fd->players[0]->commander.m_hp = 0;
×
1320
            fd->end = true;
×
1321
        }
1322
    }
1323
}
1324

1325
inline bool is_it_dead(CardStatus& c)
124,294,075✔
1326
{
1327
    if (c.m_hp == 0) // yes it is
124,294,075✔
1328
    {
1329
        _DEBUG_MSG(1, "Dead and removed: %s\n", status_description(&c).c_str());
8,337,494✔
1330
        return true;
8,337,494✔
1331
    }
1332
    return false; // nope still kickin'
1333
}
1334

1335
inline bool is_it_dominion(CardStatus* c)
×
1336
{
1337
    return (c->m_card->m_category == CardCategory::dominion_alpha);
×
1338
}
1339

1340
inline void remove_dead(Storage<CardStatus>& storage)
62,807,740✔
1341
{
1342
    storage.remove(is_it_dead);
62,807,740✔
1343
}
1344

1345
void cooldown_skills(CardStatus * status)
41,872,841✔
1346
{
1347
    for (const auto & ss : status->m_card->m_skills)
161,406,890✔
1348
    {
1349
        if (status->m_skill_cd[ss.id] > 0)
119,534,049✔
1350
        {
1351
            _DEBUG_MSG(2, "%s reduces timer (%u) of skill %s\n",
2,982,871✔
1352
                    status_description(status).c_str(), status->m_skill_cd[ss.id], skill_names[ss.id].c_str());
2,982,871✔
1353
            -- status->m_skill_cd[ss.id];
2,982,871✔
1354
        }
1355
    }
1356
}
41,872,841✔
1357
/**
1358
 * Handle:
1359
 * Absorb, (Activation)Summon, Bravery, (Initial)Valor, Inhibit, Sabotage, Disease, Enhance, (Cooldown/Every X) Reductions
1360
 * 
1361
 * Does not handle these skills for newly summoned units ( i.e. valor, barrier)
1362
 **/
1363
void turn_start_phase_update(Field*fd, CardStatus * status)
49,741,709✔
1364
{
1365
            //apply Absorb + Triggered\{Valor} Enhances
1366
            check_and_perform_early_enhance(fd,status);
99,483,418✔
1367
            if(fd->fixes[Fix::enhance_early])check_and_perform_later_enhance(fd,status);
49,741,709✔
1368
            //refresh absorb
1369
            if(status->has_skill(Skill::absorb))
49,741,709✔
1370
            {
1371
                status->m_absorption = status->skill_base_value(Skill::absorb);
3,168,104✔
1372
            }
1373
            if(__builtin_expect(fd->fixes[Fix::barrier_each_turn],true) && status->has_skill(Skill::barrier)){
49,741,709✔
1374
                _DEBUG_MSG(1,"%s gets barrier protection %u per turn\n",status_description(status).c_str(),status->skill(Skill::barrier));
981,877✔
1375
                status->m_protected += status->skill(Skill::barrier);
981,877✔
1376
            }
1377
            check_and_perform_bravery(fd,status);
49,741,709✔
1378
            if (status->m_delay > 0)
49,741,709✔
1379
            {
1380
                _DEBUG_MSG(1, "%s reduces its timer\n", status_description(status).c_str());
23,570,803✔
1381
                --status->m_delay;
23,570,803✔
1382
                if (status->m_delay == 0)
23,570,803✔
1383
                {
1384
                    check_and_perform_valor(fd, status);
10,505,084✔
1385
                    if (status->m_card->m_skill_trigger[Skill::summon] == Skill::Trigger::activate)
10,505,084✔
1386
                    {
1387
                        check_and_perform_summon(fd, status);
8,489,527✔
1388
                    }
1389
                }
1390
            }
1391
            else
1392
            {
1393
                cooldown_skills(status);
26,170,906✔
1394
            }
1395
}
49,741,709✔
1396

1397
void turn_start_phase(Field* fd)
15,701,935✔
1398
{
1399
    // Active player's commander card:
1400
    cooldown_skills(&fd->tap->commander);
15,701,935✔
1401
    //grab assaults before new ones get summoned
1402
    auto& assaults(fd->tap->assaults);
15,701,935✔
1403
    unsigned end(assaults.size());
15,701,935✔
1404

1405
    //Perform early enhance for commander
1406
    check_and_perform_early_enhance(fd,&fd->tap->commander);
31,403,870✔
1407
    if(fd->fixes[Fix::enhance_early])check_and_perform_later_enhance(fd,&fd->tap->commander);
15,701,935✔
1408

1409
    // Active player's structure cards:
1410
    // update index
1411
    // reduce delay; reduce skill cooldown
1412
    {
15,701,935✔
1413
        auto& structures(fd->tap->structures);
15,701,935✔
1414
        for(unsigned index(0); index < structures.size(); ++index)
35,065,827✔
1415
        {
1416
            CardStatus * status = &structures[index];
19,363,892✔
1417
            status->m_index = index;
19,363,892✔
1418
            turn_start_phase_update(fd,status);
19,363,892✔
1419
        }
1420
    }
1421
    // Active player's assault cards:
1422
    // update index
1423
    // reduce delay; reduce skill cooldown
1424
    {
1425
        for(unsigned index(0); index < end; ++index)
46,079,752✔
1426
        {
1427
            CardStatus * status = &assaults[index];
30,377,817✔
1428
            status->m_index = index;
30,377,817✔
1429
            turn_start_phase_update(fd,status);
30,377,817✔
1430
        }
1431
    }
1432
    // Defending player's structure cards:
1433
    // update index
1434
    {
15,701,935✔
1435
        auto& structures(fd->tip->structures);
15,701,935✔
1436
        for(unsigned index(0), end(structures.size()); index < end; ++index)
35,948,208✔
1437
        {
1438
            CardStatus& status(structures[index]);
20,246,273✔
1439
            status.m_index = index;
20,246,273✔
1440
        }
1441
    }
1442
    // Defending player's assault cards:
1443
    // update index
1444
    {
15,701,935✔
1445
        auto& assaults(fd->tip->assaults);
15,701,935✔
1446
        for(unsigned index(0), end(assaults.size()); index < end; ++index)
53,150,488✔
1447
        {
1448
            CardStatus& status(assaults[index]);
37,448,553✔
1449
            status.m_index = index;
37,448,553✔
1450
        }
1451
    }
1452
}
15,701,935✔
1453

1454
void turn_end_phase(Field* fd)
15,701,935✔
1455
{
1456
    // Inactive player's assault cards:
1457
    {
15,701,935✔
1458
        auto& assaults(fd->tip->assaults);
15,701,935✔
1459
        for(unsigned index(0), end(assaults.size()); index < end; ++ index)
53,214,541✔
1460
        {
1461
            CardStatus& status(assaults[index]);
37,512,606✔
1462
            if (status.m_hp <= 0)
37,512,606✔
1463
            {
1464
                continue;
6,503,604✔
1465
            }
1466
            status.m_enfeebled = 0;
31,009,002✔
1467
            status.m_protected = 0;
31,009,002✔
1468
            std::memset(status.m_primary_skill_offset, 0, sizeof status.m_primary_skill_offset);
31,009,002✔
1469
            std::memset(status.m_evolved_skill_offset, 0, sizeof status.m_evolved_skill_offset);
31,009,002✔
1470
            std::memset(status.m_enhanced_value, 0, sizeof status.m_enhanced_value);
31,009,002✔
1471
            status.m_evaded = 0;  // so far only useful in Inactive turn
31,009,002✔
1472
            status.m_paybacked = 0;  // ditto
31,009,002✔
1473
            status.m_entrapped = 0;
31,009,002✔
1474
        }
1475
    }
1476
    // Inactive player's structure cards:
1477
    {
15,701,935✔
1478
        auto& structures(fd->tip->structures);
15,701,935✔
1479
        for(unsigned index(0), end(structures.size()); index < end; ++ index)
35,951,770✔
1480
        {
1481
            CardStatus& status(structures[index]);
20,249,835✔
1482
            if (status.m_hp <= 0)
20,249,835✔
1483
            {
1484
                continue;
991,892✔
1485
            }
1486
            // reset the structure's (barrier) protect
1487
            status.m_protected = 0;
19,257,943✔
1488
            status.m_evaded = 0;  // so far only useful in Inactive turn
19,257,943✔
1489
        }
1490
    }
1491

1492
    // Active player's assault cards:
1493
    {
15,701,935✔
1494
        auto& assaults(fd->tap->assaults);
15,701,935✔
1495
        for(unsigned index(0), end(assaults.size()); index < end; ++ index)
60,303,962✔
1496
        {
1497
            CardStatus& status(assaults[index]);
44,602,027✔
1498
            if (status.m_hp <= 0)
44,602,027✔
1499
            {
1500
                continue;
820,556✔
1501
            }
1502
            unsigned refresh_value = status.skill(Skill::refresh) + (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::crackdown],false)?(status.skill(Skill::subdue)+1)/2:0); //BGE: crackdown refresh+=subdue/2
43,781,471✔
1503

1504
            if (refresh_value && skill_check<Skill::refresh>(fd, &status, nullptr))
43,781,471✔
1505
            {
1506
                _DEBUG_MSG(1, "%s refreshes %u health\n", status_description(&status).c_str(), refresh_value);
99,269✔
1507
                status.add_hp(refresh_value);
99,269✔
1508
            }
1509
            if (status.m_poisoned > 0)
43,781,471✔
1510
            {
1511
                if(! __builtin_expect(fd->fixes[Fix::poison_after_attacked],true))
133,062✔
1512
                {
1513
                    unsigned poison_dmg = remove_absorption(fd,&status,status.m_poisoned + status.m_enfeebled);
×
1514
                    poison_dmg = safe_minus(poison_dmg, status.protected_value());
×
1515
                    if (poison_dmg > 0)
×
1516
                    {
1517
#ifndef NQUEST
1518
                    if (status.m_player == 1)
1519
                    {
1520
                        fd->inc_counter(QuestType::skill_damage, Skill::poison, 0, poison_dmg);
1521
                    }
1522
#endif
NEW
1523
                        _DEBUG_STRAP(
×
1524
                            "turn", fd->turn,
1525
                            "active_player", fd->tapi,
1526
                            "poison_damage_"+strap_string(&status), 1.0,
1527
                            "poison_damage", poison_dmg
NEW
1528
                        );
×
1529
                        _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg);
×
1530
                        remove_hp(fd, &status, poison_dmg);  // simultaneous
×
1531
                    }
1532
                }
1533
                else {
1534
                    unsigned poison_dmg = status.m_poisoned;
133,062✔
1535
                    _DEBUG_STRAP(
133,062✔
1536
                        "turn", fd->turn,
1537
                        "active_player", fd->tapi,
1538
                        "poison_damage_"+strap_string(&status), 1.0,
1539
                        "poison_damage", poison_dmg
1540
                    );
133,062✔
1541
                    _DEBUG_MSG(1, "%s takes poison damage %u\n", status_description(&status).c_str(), poison_dmg);
133,062✔
1542
                    remove_hp(fd, &status, poison_dmg);  // simultaneous
133,062✔
1543
                    status.m_poisoned = status.m_poisoned - (poison_dmg+1)/2;
133,062✔
1544
                }
1545
            }
1546
            // end of the opponent's next turn for enemy units
1547
            status.m_temp_attack_buff = 0;
43,781,471✔
1548
            status.m_jammed = false;
43,781,471✔
1549
            status.m_enraged = 0;
43,781,471✔
1550
            status.m_sundered = false;
43,781,471✔
1551
            status.m_inhibited = 0;
43,781,471✔
1552
            status.m_sabotaged = 0;
43,781,471✔
1553
            status.m_tributed = 0;
43,781,471✔
1554
            status.m_overloaded = false;
43,781,471✔
1555
            status.m_step = CardStep::none;
43,781,471✔
1556
        }
1557
    }
1558
    // Active player's structure cards:
1559
    // nothing so far
1560

1561
    prepend_on_death(fd);  // poison
15,701,935✔
1562
    resolve_skill(fd);
15,701,935✔
1563
    remove_dead(fd->tap->assaults);
15,701,935✔
1564
    remove_dead(fd->tap->structures);
15,701,935✔
1565
    remove_dead(fd->tip->assaults);
15,701,935✔
1566
    remove_dead(fd->tip->structures);
15,701,935✔
1567
}
15,701,935✔
1568

1569
//---------------------- $50 attack by assault card implementation -------------
1570
/**
1571
 * @brief Return the damage dealt to the attacker (att) by defender (def) through counter skill
1572
 * The counter is increased by the attacker's enfeebled value, while the attacker's protected value is subtracted from the damage.
1573
 * The absorption is removed from the damage before the protected value is subtracted.
1574
 * 
1575
 * @param fd Field pointer 
1576
 * @param att attacker CardStatus
1577
 * @param def defender CardStatus
1578
 * @return unsigned 
1579
 */
1580
inline unsigned counter_damage(Field* fd, CardStatus* att, CardStatus* def)
2,077,330✔
1581
{
1582
    _DEBUG_ASSERT(att->m_card->m_type == CardType::assault);
2,077,330✔
1583
    _DEBUG_ASSERT(def->skill(Skill::counter) > 0); // counter skill must be present otherwise enfeeble is wrongly applied
2,077,330✔
1584

1585
    return safe_minus(remove_absorption(fd,att,def->skill(Skill::counter) + att->m_enfeebled), att->protected_value());
2,077,330✔
1586
}
1587

1588
inline CardStatus* select_first_enemy_wall(Field* fd)
6,474,033✔
1589
{
1590
    for(unsigned i(0); i < fd->tip->structures.size(); ++i)
12,643,201✔
1591
    {
1592
        CardStatus* c(&fd->tip->structures[i]);
6,960,994✔
1593
        if (c->has_skill(Skill::wall) && is_alive(c)) { return c; }
6,960,994✔
1594
    }
1595
    return nullptr;
1596
}
1597

1598
inline CardStatus* select_first_enemy_assault(Field* fd)
1599
{
1600
    for(unsigned i(0); i < fd->tip->assaults.size(); ++i)
48,814✔
1601
    {
1602
        CardStatus* c(&fd->tip->assaults[i]);
38,635✔
1603
        if (is_alive(c)) { return c; }
38,635✔
1604
    }
1605
    return nullptr;
1606
}
1607

1608

1609
inline bool alive_assault(Storage<CardStatus>& assaults, unsigned index)
18,468,950✔
1610
{
1611
    return(assaults.size() > index && is_alive(&assaults[index]));
13,329,367✔
1612
}
1613

1614
void remove_commander_hp(Field* fd, CardStatus& status, unsigned dmg)
5,680,973✔
1615
{
1616
    _DEBUG_ASSERT(is_alive(&status));
5,680,973✔
1617
    _DEBUG_ASSERT(status.m_card->m_type == CardType::commander);
5,680,973✔
1618
    _DEBUG_MSG(2, "%s takes %u damage\n", status_description(&status).c_str(), dmg);
5,680,973✔
1619
    status.m_hp = safe_minus(status.m_hp, dmg);
5,680,973✔
1620
    if (status.m_hp == 0)
5,680,973✔
1621
    {
1622
        _DEBUG_MSG(1, "%s dies\n", status_description(&status).c_str());
1,098,805✔
1623
        fd->end = true;
1,098,805✔
1624
    }
1625
}
5,680,973✔
1626

1627
//------------------------------------------------------------------------------
1628
// implementation of one attack by an assault card, against either an enemy
1629
// assault card, the first enemy wall, or the enemy commander.
1630
struct PerformAttack
1631
{
1632
    Field* fd;
1633
    CardStatus* att_status;
1634
    CardStatus* def_status;
1635
    unsigned att_dmg;
1636

1637
    PerformAttack(Field* fd_, CardStatus* att_status_, CardStatus* def_status_) :
18,468,950✔
1638
        fd(fd_), att_status(att_status_), def_status(def_status_), att_dmg(0)
18,468,950✔
1639
    {}
1640
    /**
1641
     * @brief Perform the attack
1642
     * 
1643
     * New Evaluation order:
1644
     *  Flying
1645
     *  Subdue
1646
     *  Attack Damage
1647
     *  On-Attacked Skills
1648
     *  Counter
1649
     *  Leech
1650
     *  Drain
1651
     *  Berserk
1652
     *  Mark
1653
     * 
1654
     * Old Evaluation order:
1655
     *  check flying
1656
     *  modify damage
1657
     *  deal damage
1658
     *  assaults only: (poison,inihibit, sabotage)
1659
     *  on-attacked skills
1660
     *  counter, berserk
1661
     *  assaults only: (leech if still alive)
1662
     *  bge
1663
     *  subdue
1664
     * 
1665
     * @tparam def_cardtype 
1666
     * @return unsigned 
1667
     */
1668
    template<enum CardType::CardType def_cardtype>
1669
        unsigned op()
18,468,950✔
1670
        {
1671
            // Bug fix? 2023-04-03 a card with zero attack power can not attack and won't trigger subdue
1672
            // Confirmed subdue behaviour by MK 2023-07-12
1673
            if(att_status->attack_power()== 0) { return 0; }
18,468,950✔
1674

1675

1676
            if(__builtin_expect(def_status->has_skill(Skill::flying),false) && fd->flip()) {
18,468,950✔
1677
                _DEBUG_MSG(1, "%s flies away from %s\n",
101,910✔
1678
                        status_description(def_status).c_str(),
1679
                        status_description(att_status).c_str());
1680
                return 0;
50,955✔
1681
            }
1682

1683
            if ( __builtin_expect(fd->fixes[Fix::subdue_before_attack],true)) {
18,417,995✔
1684
                perform_subdue(fd, att_status, def_status);
18,417,995✔
1685
                if(att_status->attack_power() == 0) // MK - 05/01/2023 6:58 AM: "Previously, when an attack was made, it checked if it had 0 attack and canceled. Now, after that check, it applies Subdue and checks again if it has 0 attack, and then cancels the rest of the attack.Previously, when an attack was made, it checked if it had 0 attack and canceled. Now, after that check, it applies Subdue and checks again if it has 0 attack, and then cancels the rest of the attack."
18,417,995✔
1686
                { 
1687
                    return 0; 
1688
                }
1689
            }
1690
            
1691
            // APN Bug fix for subdue not reducing attack damage
1692
            // https://github.com/APN-Pucky/tyrant_optimize/issues/79
1693
            unsigned pre_modifier_dmg = att_status->attack_power();
18,414,558✔
1694

1695
            modify_attack_damage<def_cardtype>(pre_modifier_dmg);
18,414,558✔
1696

1697
            if(att_dmg) {
18,414,558✔
1698
                // Deal the attack damage
1699
                attack_damage<def_cardtype>();
15,409,942✔
1700

1701
                // Game Over -> return
1702
                if (__builtin_expect(fd->end, false)) { return att_dmg; }
15,409,942✔
1703
                damage_dependant_pre_oa<def_cardtype>();
8,937,143✔
1704
            }
1705
            // on attacked does also trigger if the attack damage is blocked to zero
1706
            on_attacked<def_cardtype>();
17,315,753✔
1707

1708
            // only if alive attacker
1709
            if (is_alive(att_status) && (__builtin_expect(fd->fixes[Fix::counter_without_damage],true) || att_dmg) )
17,315,753✔
1710
            {
1711
                perform_counter<def_cardtype>(fd, att_status, def_status);    
17,303,763✔
1712
            }
1713
            if (is_alive(att_status) && (__builtin_expect(fd->fixes[Fix::corrosive_protect_armor],true) ||att_dmg )) // MK - 05/01/2023 6:58 AM: "Corrosive will apply to an attacker even if the attack did 0 damage, just like new Counter"
17,315,753✔
1714
            {
1715
                perform_corrosive(fd, att_status, def_status);
16,591,372✔
1716
            }
1717
            if (is_alive(att_status) && att_dmg)
17,315,753✔
1718
            {
1719
                // Bug fix? 2023-04-03 leech after counter but before drain/berserk
1720
                // Skill: Leech
1721
                do_leech<def_cardtype>();
8,778,339✔
1722
            }
1723

1724
            // Bug fix? 2023-04-03 drain now part of the PerformAttack.op() instead of attack_phase, like hunt.
1725
            // perform swipe/drain
1726
            perform_swipe_drain<def_cardtype>(fd, att_status, def_status, att_dmg);
17,315,753✔
1727

1728
            if (is_alive(att_status) && att_dmg) {
17,315,753✔
1729
                perform_berserk(fd, att_status, def_status);
13,647,445✔
1730
            }
1731
            if (is_alive(att_status) )  {
17,315,753✔
1732
                perform_poison(fd, att_status, def_status);
16,591,372✔
1733
            }
1734

1735
            if (is_alive(att_status) && att_dmg) {
17,315,753✔
1736
                perform_mark(fd, att_status, def_status);
13,647,445✔
1737

1738
                perform_bge_heroism<def_cardtype>(fd, att_status, def_status);
13,647,445✔
1739
                perform_bge_devour<def_cardtype>(fd, att_status, def_status);
13,647,445✔
1740

1741
                if ( __builtin_expect(!fd->fixes[Fix::subdue_before_attack],false)) {
13,647,445✔
1742
                    perform_subdue(fd, att_status, def_status);
×
1743
                }
1744
            }
1745
            // perform hunt
1746
            perform_hunt(fd, att_status, def_status);
17,315,753✔
1747

1748
            return att_dmg;
17,315,753✔
1749
        }
1750

1751
        /**
1752
         * @brief Modify attack damage
1753
         * This setts att_dmg in PerformAttack.
1754
         * 
1755
         * @tparam CardType::CardType 
1756
         * @param pre_modifier_dmg base damage
1757
         */
1758
    template<enum CardType::CardType>
1759
        void modify_attack_damage(unsigned pre_modifier_dmg)
18,414,558✔
1760
        {
1761
            _DEBUG_ASSERT(att_status->m_card->m_type == CardType::assault);
18,414,558✔
1762
            att_dmg = pre_modifier_dmg;
18,414,558✔
1763
            if (att_dmg == 0)
18,414,558✔
1764
            { return; }
×
1765
#ifndef NDEBUG
1766
            std::string desc;
18,414,558✔
1767
#endif
1768
            auto& att_assaults = fd->tap->assaults; // (active) attacker assaults
18,414,558✔
1769
            auto& def_assaults = fd->tip->assaults; // (inactive) defender assaults
18,414,558✔
1770
            unsigned legion_value = 0;
18,414,558✔
1771
            unsigned coalition_value = 0;
18,414,558✔
1772

1773
            // Enhance damage (if additional damage isn't prevented)
1774
            if (! att_status->m_sundered)
18,414,558✔
1775
            {
1776
                // Skill: Mark
1777
                unsigned marked_value = def_status->m_marked;
17,382,040✔
1778
                if(marked_value)
17,382,040✔
1779
                {
1780
#ifndef NDEBUG
1781
                    if (debug_print > 0) { desc += "+" + tuo::to_string(marked_value) + "(mark)"; }
48,327✔
1782
#endif
1783
                    att_dmg += marked_value;
12,249✔
1784
                }
1785
                // Skill: Legion
1786
                unsigned legion_base = att_status->skill(Skill::legion);
17,382,040✔
1787
                if (__builtin_expect(legion_base, false))
17,382,040✔
1788
                {
1789
                    unsigned itr_idx, till_idx;
1790
                    bool bge_megamorphosis = fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis] && !fd->fixes[Fix::legion_under_mega];
2,056,705✔
1791
                    //scan all assaults for Global Legion
1792
                    itr_idx = 0;
2,056,705✔
1793
                    till_idx = att_assaults.size() - 1;
2,056,705✔
1794
                    for (; itr_idx <= till_idx; ++ itr_idx)
13,216,415✔
1795
                    {
1796
                        if(itr_idx == att_status->m_index)continue; //legion doesn't count itself, unlike coalition
11,159,710✔
1797
                        legion_value += is_alive(&att_assaults[itr_idx])
18,183,755✔
1798
                          && (bge_megamorphosis || (att_assaults[itr_idx].m_card->m_faction == att_status->m_card->m_faction));
9,103,005✔
1799
                    }
1800
                    if (legion_value)
2,056,705✔
1801
                    {
1802
                        legion_value *= legion_base;
1,897,502✔
1803
#ifndef NDEBUG
1804
                        if (debug_print > 0) { desc += "+" + tuo::to_string(legion_value) + "(legion)"; }
2,164,295✔
1805
#endif
1806
                        att_dmg += legion_value;
1,897,502✔
1807
                    }
1808
                }
1809

1810
                // Skill: Coalition
1811
                unsigned coalition_base = att_status->skill(Skill::coalition);
17,382,040✔
1812
                if (__builtin_expect(coalition_base, false))
17,382,040✔
1813
                {
1814
                    uint8_t factions_bitmap = 0;
1,306,633✔
1815
                    for (CardStatus * status : att_assaults.m_indirect)
6,681,692✔
1816
                    {
1817
                        if (! is_alive(status)) { continue; }
5,375,059✔
1818
                        factions_bitmap |= (1 << (status->m_card->m_faction));
5,369,662✔
1819
                    }
1820
                    _DEBUG_ASSERT(factions_bitmap);
1,306,633✔
1821
                    unsigned uniq_factions = byte_bits_count(factions_bitmap);
1,306,633✔
1822
                    coalition_value = coalition_base * uniq_factions;
1,306,633✔
1823
#ifndef NDEBUG
1824
                    if (debug_print > 0) { desc += "+" + tuo::to_string(coalition_value) + "(coalition/x" + tuo::to_string(uniq_factions) + ")"; }
1,841,969✔
1825
#endif
1826
                    att_dmg += coalition_value;
1,306,633✔
1827
                }
1828

1829
                // Skill: Rupture
1830
                unsigned rupture_value = att_status->skill(Skill::rupture);
17,382,040✔
1831
                if (rupture_value > 0)
17,382,040✔
1832
                {
1833
#ifndef NDEBUG
1834
                    if (debug_print > 0) { desc += "+" + tuo::to_string(rupture_value) + "(rupture)"; }
×
1835
#endif
1836
                    att_dmg += rupture_value;
×
1837
                }
1838

1839
                // Skill: Venom
1840
                unsigned venom_value = att_status->skill(Skill::venom);
17,382,040✔
1841
                if (venom_value > 0 && def_status->m_poisoned > 0)
17,382,040✔
1842
                {
1843
#ifndef NDEBUG
1844
                    if (debug_print > 0) { desc += "+" + tuo::to_string(venom_value) + "(venom)"; }
94,169✔
1845
#endif
1846
                    att_dmg += venom_value;
41,966✔
1847
                }
1848

1849
                // Passive BGE: Bloodlust
1850
                if (fd->bloodlust_value > 0)
17,382,040✔
1851
                {
1852
#ifndef NDEBUG
1853
                    if (debug_print > 0) { desc += "+" + tuo::to_string(fd->bloodlust_value) + "(bloodlust)"; }
206,324✔
1854
#endif
1855
                    att_dmg += fd->bloodlust_value;
51,581✔
1856
                }
1857

1858
                // State: Enfeebled
1859
                if (def_status->m_enfeebled > 0)
17,382,040✔
1860
                {
1861
#ifndef NDEBUG
1862
                    if (debug_print > 0) { desc += "+" + tuo::to_string(def_status->m_enfeebled) + "(enfeebled)"; }
2,479,656✔
1863
#endif
1864
                    att_dmg += def_status->m_enfeebled;
1,554,993✔
1865
                }
1866
            }
1867
            // prevent damage
1868
#ifndef NDEBUG
1869
            std::string reduced_desc;
18,414,558✔
1870
#endif
1871
            unsigned reduced_dmg(0); // damage reduced by armor, protect and in turn again canceled by pierce, etc.
18,414,558✔
1872
            unsigned armor_value = 0;
18,414,558✔
1873
            // Armor
1874
            if (def_status->m_card->m_type == CardType::assault) {
18,414,558✔
1875
                // Passive BGE: Fortification (adj step -> 1 (1 left, host, 1 right)
1876
                unsigned adj_size = (unsigned)(fd->bg_effects[fd->tapi][PassiveBGE::fortification]);
11,941,759✔
1877
                unsigned host_idx = def_status->m_index;
11,941,759✔
1878
                unsigned from_idx = safe_minus(host_idx, adj_size);
11,941,759✔
1879
                unsigned till_idx = std::min(host_idx + adj_size, safe_minus(def_assaults.size(), 1));
22,329,046✔
1880
                for (; from_idx <= till_idx; ++ from_idx)
23,910,625✔
1881
                {
1882
                    CardStatus* adj_status = &def_assaults[from_idx];
11,968,866✔
1883
                    if (!is_alive(adj_status)) { continue; }
11,968,866✔
1884
                    armor_value = std::max(armor_value, adj_status->skill(Skill::armor));
15,177,894✔
1885
                }
1886
            }
1887
            if (armor_value > 0)
18,414,558✔
1888
            {
1889
#ifndef NDEBUG
1890
                if(debug_print > 0) { reduced_desc += tuo::to_string(armor_value) + "(armor)"; }
3,549,250✔
1891
#endif
1892
                reduced_dmg += armor_value;
1893
            }
1894
            if (def_status->protected_value() > 0)
18,414,558✔
1895
            {
1896
#ifndef NDEBUG
1897
                if(debug_print > 0) { reduced_desc += (reduced_desc.empty() ? "" : "+") + tuo::to_string(def_status->protected_value()) + "(protected)"; }
9,712,505✔
1898
#endif
1899
                reduced_dmg += def_status->protected_value();
7,164,599✔
1900
            }
1901
            unsigned pierce_value = att_status->skill(Skill::pierce);
18,414,558✔
1902
            if (reduced_dmg > 0 && pierce_value > 0)
18,414,558✔
1903
            {
1904
#ifndef NDEBUG
1905
                if (debug_print > 0) { reduced_desc += "-" + tuo::to_string(pierce_value) + "(pierce)"; }
479,575✔
1906
#endif
1907
                reduced_dmg = safe_minus(reduced_dmg, pierce_value);
18,414,558✔
1908
            }
1909
            unsigned rupture_value = att_status->skill(Skill::rupture);
18,414,558✔
1910
            if (reduced_dmg > 0 && rupture_value > 0)
18,414,558✔
1911
            {
1912
#ifndef NDEBUG
1913
                if (debug_print > 0) { reduced_desc += "-" + tuo::to_string(rupture_value) + "(rupture)"; }
×
1914
#endif
1915
                reduced_dmg = safe_minus(reduced_dmg, rupture_value);
18,414,558✔
1916
            }
1917
            if(__builtin_expect(fd->fixes[Fix::corrosive_protect_armor],true)) // MK - 05/01/2023 6:58 AM: "Corrosive status reduces protection from attacks (equivalent to giving the attacker Pierce X). Note that the value is equal to initial Corrosive application, not attack removed."
18,414,558✔
1918
            {
1919
                unsigned corrosive_value = def_status->m_corroded_rate;
18,414,558✔
1920
                if (reduced_dmg > 0 && corrosive_value > 0)
18,414,558✔
1921
                {
1922
#ifndef NDEBUG
1923
                    if (debug_print > 0) { reduced_desc += "-" + tuo::to_string(corrosive_value) + "(corrosive)"; }
32,163✔
1924
#endif
1925
                    reduced_dmg = safe_minus(reduced_dmg, corrosive_value);
18,414,558✔
1926
                }
1927
            }
1928
            att_dmg = safe_minus(att_dmg, reduced_dmg);
18,414,558✔
1929
#ifndef NDEBUG
1930
            if (debug_print > 0)
18,414,558✔
1931
            {
1932
                if(!reduced_desc.empty()) { desc += "-[" + reduced_desc + "]"; }
4,736,888✔
1933
                if(!desc.empty()) { desc += "=" + tuo::to_string(att_dmg); }
5,323,734✔
1934
                else { assert(att_dmg == pre_modifier_dmg); }
1,663,221✔
1935
                _DEBUG_STRAP(
2,883,392✔
1936
                    "turn", fd->turn,
1937
                    "active_player", fd->tapi,
1938
                    "attack_attacker_"+strap_string(att_status), 1.0,
1939
                    "attack_defender_"+strap_string(def_status), 1.0,
1940
                    "attack_damage", pre_modifier_dmg
1941
                );
1942
                _DEBUG_MSG(1, "%s attacks %s for %u%s damage\n",
5,766,784✔
1943
                        status_description(att_status).c_str(),
1944
                        status_description(def_status).c_str(), pre_modifier_dmg, desc.c_str());
1945
            }
1946
#endif
1947
            // Passive BGE: Brigade
1948
            if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::brigade] && legion_value, false)
18,414,558✔
1949
                    && can_be_healed(att_status))
×
1950
            {
1951
                _DEBUG_MSG(1, "Brigade: %s heals itself for %u\n",
×
1952
                        status_description(att_status).c_str(), legion_value);
1953
                att_status->add_hp(legion_value);
×
1954
            }
1955

1956
            // Passive BGE: Unity
1957
            if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::unity] && coalition_value, false)
18,414,558✔
1958
                    && can_be_healed(att_status))
18,418,050✔
1959
            {
1960
                _DEBUG_MSG(1, "Unity: %s heals itself for %u\n",
844✔
1961
                        status_description(att_status).c_str(), (coalition_value + 1)/2);
1962
                att_status->add_hp((coalition_value + 1)/2);
422✔
1963
            }
1964
        }
18,414,558✔
1965

1966
    template<enum CardType::CardType>
1967
        void attack_damage()
9,728,969✔
1968
        {
1969

1970
            remove_hp(fd, def_status, att_dmg);
9,728,969✔
1971
            prepend_on_death(fd);
9,728,969✔
1972
            resolve_skill(fd);
9,728,969✔
1973
        }
9,728,969✔
1974

1975
    template<enum CardType::CardType>
1976
        void on_attacked() {
17,315,753✔
1977
            //APN
1978
            // resolve On-Attacked skills
1979
            for (const auto& ss: def_status->m_card->m_skills_on_attacked)
17,515,743✔
1980
            {
1981
                _DEBUG_MSG(1, "On Attacked %s: Preparing (tail) skill %s\n",
399,980✔
1982
                        status_description(def_status).c_str(), skill_description(fd->cards, ss).c_str());
1983
                fd->skill_queue.emplace_back(def_status, ss);
199,990✔
1984
                resolve_skill(fd);
199,990✔
1985
            }
1986
        }
17,315,753✔
1987

1988
    template<enum CardType::CardType>
1989
        void damage_dependant_pre_oa() {}
1990

1991
    template<enum CardType::CardType>
1992
        void do_leech() {}
1993

1994
    template<enum CardType::CardType>
1995
        void perform_swipe_drain(Field* fd, CardStatus* att_status, CardStatus* def_status,unsigned att_dmg) {}
1996
    };
1997

1998

1999
    template<>
2000
void PerformAttack::attack_damage<CardType::commander>()
5,680,973✔
2001
{
2002
    remove_commander_hp(fd, *def_status, att_dmg);
5,680,973✔
2003
}
×
2004

2005
    template<>
2006
void PerformAttack::damage_dependant_pre_oa<CardType::assault>()
8,937,143✔
2007
{
2008
    if (!__builtin_expect(fd->fixes[Fix::poison_after_attacked],true))
8,937,143✔
2009
    {
2010
    unsigned poison_value = std::max(att_status->skill(Skill::poison), att_status->skill(Skill::venom));
×
2011
    
2012
    if (poison_value > def_status->m_poisoned && skill_check<Skill::poison>(fd, att_status, def_status))
×
2013
    {
2014
        // perform_skill_poison
2015
#ifndef NQUEST
2016
        if (att_status->m_player == 0)
2017
        {
2018
            fd->inc_counter(QuestType::skill_use, Skill::poison);
2019
        }
2020
#endif
2021
        _DEBUG_MSG(1, "%s poisons %s by %u\n",
×
2022
                status_description(att_status).c_str(),
2023
                status_description(def_status).c_str(), poison_value);
×
2024
        def_status->m_poisoned = poison_value;
×
2025
    }
2026
    }
2027
    else {            
2028
        unsigned venom_value = att_status->skill(Skill::venom);
8,937,143✔
2029
        if (venom_value > 0 && skill_check<Skill::venom>(fd, att_status, def_status)) {
8,937,143✔
2030
#ifndef NQUEST
2031
              if (att_status->m_player == 0)
2032
              {
2033
                 fd->inc_counter(QuestType::skill_use, Skill::venom);
2034
              }
2035
#endif
2036

2037
              _DEBUG_MSG(1, "%s venoms %s by %u\n",
309,473✔
2038
                  status_description(att_status).c_str(),
2039
                  status_description(def_status).c_str(), venom_value);
309,473✔
2040
              // new venom keeps adding stacks
2041
              def_status->m_poisoned += venom_value;
309,473✔
2042
        }
2043
    }
2044
}
8,937,143✔
2045

2046

2047
    template<>
2048
void PerformAttack::do_leech<CardType::assault>()
8,778,339✔
2049
{
2050
    unsigned leech_value = std::min(att_dmg, att_status->skill(Skill::leech));
8,778,339✔
2051
    if(leech_value > 0 && skill_check<Skill::leech>(fd, att_status, nullptr))
8,778,339✔
2052
    {
2053
#ifndef NQUEST
2054
        if (att_status->m_player == 0)
2055
        {
2056
            fd->inc_counter(QuestType::skill_use, Skill::leech);
2057
        }
2058
#endif
2059
        _DEBUG_MSG(1, "%s leeches %u health\n", status_description(att_status).c_str(), leech_value);
25,596✔
2060
        if (__builtin_expect(fd->fixes[Fix::leech_increase_max_hp],true)) {
25,596✔
2061
            att_status->ext_hp(leech_value);
25,596✔
2062
        }
2063
        else {
2064
            att_status->add_hp(leech_value);
×
2065
        }
2066
    }
2067
}
8,778,339✔
2068

2069
// General attack phase by the currently evaluated assault, taking into accounts exotic stuff such as flurry, etc.
2070
unsigned attack_commander(Field* fd, CardStatus* att_status)
6,474,033✔
2071
{
2072
    CardStatus* def_status{select_first_enemy_wall(fd)}; // defending wall
6,474,033✔
2073
    if (def_status != nullptr)
6,474,033✔
2074
    {
2075
        return PerformAttack{fd, att_status, def_status}.op<CardType::structure>();
791,826✔
2076
    }
2077
    else
2078
    {
2079
        return PerformAttack{fd, att_status, &fd->tip->commander}.op<CardType::commander>();
5,682,207✔
2080
    }
2081
}
2082
// Return true if actually attacks
2083
bool attack_phase(Field* fd)
20,960,825✔
2084
{
2085
    CardStatus* att_status(&fd->tap->assaults[fd->current_ci]); // attacking card
20,960,825✔
2086
    Storage<CardStatus>& def_assaults(fd->tip->assaults);
20,960,825✔
2087

2088
    if (!att_status->attack_power())
20,960,825✔
2089
    {
2090
        _DEBUG_MSG(1, "%s cannot take attack (zeroed)\n", status_description(att_status).c_str());
2,491,875✔
2091
        return false;
2,491,875✔
2092
    }
2093

2094
    unsigned att_dmg = 0;
18,468,950✔
2095
    if (alive_assault(def_assaults, fd->current_ci))
18,468,950✔
2096
    {
2097
        CardStatus* def_status = &def_assaults[fd->current_ci];
11,994,917✔
2098
        att_dmg = PerformAttack{fd, att_status, def_status}.op<CardType::assault>();
11,994,917✔
2099
        
2100
    }
2101
    else
2102
    {
2103
        // might be blocked by walls
2104
        att_dmg = attack_commander(fd, att_status);
6,474,033✔
2105
    }
2106

2107
    // Passive BGE: Bloodlust
2108
    if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::bloodlust], false)
18,468,950✔
2109
            && !fd->assault_bloodlusted && (att_dmg > 0))
18,468,950✔
2110
    {
2111
        fd->bloodlust_value += fd->bg_effects[fd->tapi][PassiveBGE::bloodlust];
113,537✔
2112
        fd->assault_bloodlusted = true;
113,537✔
2113
    }
2114

2115
    return true;
2116
}
2117

2118
//---------------------- $65 active skills implementation ----------------------
2119
template<
2120
bool C
2121
, typename T1
2122
, typename T2
2123
>
2124
struct if_
2125
{
2126
    typedef T1 type;
2127
};
2128

2129
template<
2130
typename T1
2131
, typename T2
2132
>
2133
struct if_<false,T1,T2>
2134
{
2135
    typedef T2 type;
2136
};
2137

2138
    template<unsigned skill_id>
2139
inline bool skill_predicate(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
279✔
2140
{ return skill_check<static_cast<Skill::Skill>(skill_id)>(fd, dst, src); }
329,512,626✔
2141

2142
    template<>
2143
inline bool skill_predicate<Skill::enhance>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
26,615,005✔
2144
{
2145
    if (!is_alive(dst)) return false;
26,615,005✔
2146
    if (!dst->has_skill(s.s)) return false;
53,230,010✔
2147
    if (is_active(dst)) return true;
6,404,484✔
2148
    if (is_defensive_skill(s.s)) return true;
3,430,032✔
2149
    if (is_instant_debuff_skill(s.s)) return true; // Enhance Sabotage, Inhibit, Disease also without dst being active
560,949✔
2150
    if (is_triggered_skill(s.s) && s.s != Skill::valor) return true;// Enhance Allegiance, Stasis, Bravery ( + not in TU: Flurry, Summon; No enhance on inactive dst: Valor)
560,849✔
2151

2152
    /* Strange Transmission [Gilians]: strange gillian's behavior implementation:
2153
     * The Gillian commander and assaults can enhance any skills on any assaults
2154
     * regardless of jammed/delayed states. But what kind of behavior is in the case
2155
     * when gilians are played among standard assaults, I don't know. :)
2156
     */
2157
    return is_alive_gilian(src);
2158
}
2159

2160
    template<>
2161
inline bool skill_predicate<Skill::evolve>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
2,197,714✔
2162
{
2163
    if (!is_alive(dst)) return false;
2,197,714✔
2164
    if (!dst->has_skill(s.s)) return false;
4,389,932✔
2165
    if (dst->has_skill(s.s2)) return false;
1,344,556✔
2166
    if (is_active(dst)) return true;
672,278✔
2167
    if (is_defensive_skill(s.s2)) return true;
361,655✔
2168

2169
    /* Strange Transmission [Gilians]: strange gillian's behavior implementation:
2170
     * The Gillian commander and assaults can enhance any skills on any assaults
2171
     * regardless of jammed/delayed states. But what kind of behavior is in the case
2172
     * when gilians are played among standard assaults, I don't know. :)
2173
     */
2174
    return is_alive_gilian(src);
2175
}
2176

2177
    template<>
2178
inline bool skill_predicate<Skill::mimic>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
778,812✔
2179
{
2180
    // skip dead units
2181
    if (!is_alive(dst)) return false;
778,812✔
2182

2183
    //include on activate/attacked/death
2184
    //for(const auto & a  : {dst->m_card->m_skills,dst->m_card->m_skills_on_play,dst->m_card->m_skills_on_death,dst->m_card->m_skills_on_attacked})
2185
    //
2186
    std::vector<std::vector<SkillSpec>> all;
732,502✔
2187
    all.emplace_back(dst->m_card->m_skills);
732,502✔
2188
    all.emplace_back(dst->m_card->m_skills_on_attacked);
732,502✔
2189

2190
    for(std::vector<SkillSpec> & a  : all)
1,171,678✔
2191
    {
2192
    // scan all enemy skills until first activation
2193
    for (const SkillSpec & ss: a)
2,252,038✔
2194
    {
2195
        // get skill
2196
        Skill::Skill skill_id = static_cast<Skill::Skill>(ss.id);
1,812,862✔
2197

2198
        // skip non-activation skills and Mimic (Mimic can't be mimicked)
2199
        if (!is_activation_skill(skill_id) || (skill_id == Skill::mimic))
2,335,565✔
2200
        { continue; }
1,290,159✔
2201

2202
        // skip mend for non-assault mimickers
2203
        if ((skill_id == Skill::mend || skill_id == Skill::fortify) && (src->m_card->m_type != CardType::assault))
522,703✔
2204
        { continue; }
2,800✔
2205

2206
        // enemy has at least one activation skill that can be mimicked, so enemy is eligible target for Mimic
2207
        return true;
732,502✔
2208
    }
2209
    }
2210

2211
    // found nothing (enemy has no skills to be mimicked, so enemy isn't eligible target for Mimic)
2212
    return false;
2213
}
732,502✔
2214

2215
    template<>
2216
inline bool skill_predicate<Skill::overload>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
574,323✔
2217
{
2218
    // basic skill check
2219
    if (!skill_check<Skill::overload>(fd, dst, src))
918,504✔
2220
    { return false; }
2221

2222
    // check skills
2223
    bool inhibited_searched = false;
299,415✔
2224
    for (const auto& ss: dst->m_card->m_skills)
833,154✔
2225
    {
2226
        // skip cooldown skills
2227
        if (dst->m_skill_cd[ss.id] > 0)
762,022✔
2228
        { continue; }
9,624✔
2229

2230
        // get evolved skill
2231
        Skill::Skill evolved_skill_id = static_cast<Skill::Skill>(ss.id + dst->m_evolved_skill_offset[ss.id]);
752,398✔
2232

2233
        // unit with an activation hostile skill is always valid target for OL
2234
        if (is_activation_hostile_skill(evolved_skill_id))
979,909✔
2235
        { return true; }
2236

2237
        // unit with an activation helpful skill is valid target only when there are inhibited units
2238
        // TODO check mend/fortify valid overload target?!?
2239
        if ((evolved_skill_id != Skill::mend && evolved_skill_id != Skill::fortify)
527,540✔
2240
                && is_activation_helpful_skill(evolved_skill_id)
533,739✔
2241
                && __builtin_expect(!inhibited_searched, true))
683,196✔
2242
        {
2243
            for (const auto & c: fd->players[dst->m_player]->assaults.m_indirect)
830,730✔
2244
            {
2245
                if (is_alive(c) && c->m_inhibited)
681,163✔
2246
                { return true; }
228,283✔
2247
            }
2248
            inhibited_searched = true;
2249
        }
2250
    }
2251
    return false;
2252
}
2253

2254
    template<>
2255
inline bool skill_predicate<Skill::rally>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
21,123,555✔
2256
{
2257
    return skill_check<Skill::rally>(fd, dst, src) // basic skill check
21,123,555✔
2258
        && (__builtin_expect((fd->tapi == dst->m_player), true) // is target on the active side?
21,123,555✔
2259
                ? is_active(dst) && !has_attacked(dst) // normal case
12,070,047✔
2260
                : is_active_next_turn(dst) // diverted case / on-death activation
21,123,555✔
2261
           )
21,123,555✔
2262
        ;
2263
}
2264

2265
    template<>
2266
inline bool skill_predicate<Skill::enrage>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
30,023,200✔
2267
{
2268
    return (__builtin_expect((fd->tapi == dst->m_player), true) // is target on the active side?
30,023,200✔
2269
            ? is_active(dst) && (dst->m_step == CardStep::none) // normal case
14,438,718✔
2270
            : is_active_next_turn(dst) // on-death activation
43,920,157✔
2271
           )
2272
        && (dst->attack_power()) // card can perform direct attack
43,920,157✔
2273
        ;
2274
}
2275

2276
    template<>
2277
inline bool skill_predicate<Skill::rush>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
×
2278
{
2279
    return (!src->m_rush_attempted)
×
2280
        && (dst->m_delay >= ((src->m_card->m_type == CardType::assault) && (dst->m_index < src->m_index) ? 2u : 1u));
×
2281
}
2282

2283
    template<>
2284
inline bool skill_predicate<Skill::weaken>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
32,893,095✔
2285
{
2286
    if(__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::ironwill], false) && dst->has_skill(Skill::armor))return false;
32,893,095✔
2287
    if (!dst->attack_power()) { return false; }
32,889,477✔
2288

2289
    // active player performs Weaken (normal case)
2290
    if (__builtin_expect((fd->tapi == src->m_player), true))
28,767,428✔
2291
    { return is_active_next_turn(dst); }
28,650,530✔
2292

2293
    // APN - On-Attacked/Death don't target the attacking card
2294

2295
    // inactive player performs Weaken (inverted case (on-death activation))
2296
    return will_activate_this_turn(dst);
116,898✔
2297
}
2298

2299
    template<>
2300
inline bool skill_predicate<Skill::sunder>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
16,959,582✔
2301
{
2302
    return skill_predicate<Skill::weaken>(fd, src, dst, s);
100✔
2303
}
2304

2305
    template<unsigned skill_id>
2306
inline void perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
2307
{ assert(false); }
2308

2309
    template<>
2310
inline void perform_skill<Skill::enfeeble>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
6,947,592✔
2311
{
2312
    dst->m_enfeebled += s.x;
6,947,592✔
2313
}
×
2314

2315
    template<>
2316
inline void perform_skill<Skill::enhance>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
5,442,347✔
2317
{
2318
    dst->m_enhanced_value[s.s + dst->m_primary_skill_offset[s.s]] += s.x;
5,442,347✔
2319
}
22✔
2320

2321
    template<>
2322
inline void perform_skill<Skill::evolve>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
298,504✔
2323
{
2324
    auto primary_s1 = dst->m_primary_skill_offset[s.s] + s.s;
298,504✔
2325
    auto primary_s2 = dst->m_primary_skill_offset[s.s2] + s.s2;
298,504✔
2326
    dst->m_primary_skill_offset[s.s] = primary_s2 - s.s;
298,504✔
2327
    dst->m_primary_skill_offset[s.s2] = primary_s1 - s.s2;
298,504✔
2328
    dst->m_evolved_skill_offset[primary_s1] = s.s2 - primary_s1;
298,504✔
2329
    dst->m_evolved_skill_offset[primary_s2] = s.s - primary_s2;
298,504✔
2330
}
×
2331

2332
    template<>
2333
inline void perform_skill<Skill::heal>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
7,633,650✔
2334
{
2335
    dst->add_hp(s.x);
7,633,650✔
2336

2337
    // Passive BGE: ZealotsPreservation
2338
    if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::zealotspreservation], false)
7,633,650✔
2339
            && (src->m_card->m_type == CardType::assault))
7,633,650✔
2340
    {
2341
        unsigned bge_value = (s.x + 1) / 2;
411✔
2342
        _DEBUG_MSG(1, "Zealot's Preservation: %s Protect %u on %s\n",
411✔
2343
                status_description(src).c_str(), bge_value,
2344
                status_description(dst).c_str());
411✔
2345
        dst->m_protected += bge_value;
411✔
2346
    }
2347
}
7,633,650✔
2348

2349
    template<>
2350
inline void perform_skill<Skill::jam>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
1,595,707✔
2351
{
2352
    dst->m_jammed = true;
1,595,707✔
2353
}
×
2354

2355
    template<>
2356
inline void perform_skill<Skill::mend>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
4,274✔
2357
{
2358
    dst->add_hp(s.x);
4,271✔
2359
}
3✔
2360

2361
    template<>
2362
inline void perform_skill<Skill::fortify>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
2,409,826✔
2363
{
2364
    dst->ext_hp(s.x);
1,756,350✔
2365
}
653,476✔
2366

2367
    template<>
2368
inline void perform_skill<Skill::siege>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
2,524,619✔
2369
{ 
2370
    //only structures can be sieged
2371
    _DEBUG_ASSERT(dst->m_card->m_type != CardType::assault);
2,524,619✔
2372
    _DEBUG_ASSERT(dst->m_card->m_type != CardType::commander);
2,524,619✔
2373
    unsigned siege_dmg = remove_absorption(fd,dst,s.x);
2,524,619✔
2374
    // structure should not have protect normally..., but let's allow it for barrier support
2375
    siege_dmg = safe_minus(siege_dmg, src->m_overloaded ? 0 : dst->m_protected);
2,524,619✔
2376
    remove_hp(fd, dst, siege_dmg);
2,524,619✔
2377
}
2,524,619✔
2378

2379
    template<>
2380
inline void perform_skill<Skill::mortar>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
3,600,670✔
2381
{
2382
    if (dst->m_card->m_type == CardType::structure)
3,600,670✔
2383
    {
2384
        perform_skill<Skill::siege>(fd, src, dst, s);
2,345,264✔
2385
    }
2386
    else
2387
    {
2388
        unsigned strike_dmg = remove_absorption(fd,dst,(s.x + 1) / 2 + dst->m_enfeebled);
1,255,406✔
2389
        strike_dmg = safe_minus(strike_dmg, src->m_overloaded ? 0 : dst->protected_value());
1,255,406✔
2390
        remove_hp(fd, dst, strike_dmg);
1,255,406✔
2391
    }
2392
}
3,600,670✔
2393

2394
    template<>
2395
inline void perform_skill<Skill::overload>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
202,146✔
2396
{
2397
    dst->m_overloaded = true;
202,146✔
2398
}
7✔
2399

2400
    template<>
2401
inline void perform_skill<Skill::protect>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
24,312,894✔
2402
{
2403
    dst->m_protected += s.x;
24,312,894✔
2404
}
427✔
2405

2406
    template<>
2407
inline void perform_skill<Skill::rally>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
9,367,711✔
2408
{
2409
    dst->m_temp_attack_buff += s.x;
9,367,711✔
2410
}
226,638✔
2411

2412
    template<>
2413
inline void perform_skill<Skill::enrage>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
9,478,458✔
2414
{
2415
    dst->m_enraged += s.x;
9,478,458✔
2416
    // Passive BGE: Furiosity
2417
    if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::furiosity], false)
9,478,458✔
2418
            && can_be_healed(dst))
9,478,458✔
2419
    {
2420
        unsigned bge_value = s.x;
6,826✔
2421
        _DEBUG_MSG(1, "Furiosity: %s Heals %s for %u\n",
6,826✔
2422
                status_description(src).c_str(),
2423
                status_description(dst).c_str(), bge_value);
6,826✔
2424
        dst->add_hp(bge_value);
6,826✔
2425
    }
2426
}
9,478,458✔
2427

2428
    template<>
2429
inline void perform_skill<Skill::entrap>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
5,545,408✔
2430
{
2431
    dst->m_entrapped += s.x;
5,545,408✔
2432
}
86,678✔
2433

2434
    template<>
2435
inline void perform_skill<Skill::rush>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
×
2436
{
2437
    dst->m_delay -= std::min(std::max(s.x, 1u), dst->m_delay);
×
2438
    if (dst->m_delay == 0)
×
2439
    {
2440
        check_and_perform_valor(fd, dst);
×
2441
        if(dst->m_card->m_skill_trigger[Skill::summon] == Skill::Trigger::activate)check_and_perform_summon(fd, dst);
×
2442
    }
2443
}
×
2444

2445
    template<>
2446
inline void perform_skill<Skill::strike>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
18,207,170✔
2447
{
2448
    unsigned strike_dmg = remove_absorption(fd,dst,s.x+ dst->m_enfeebled);
18,207,170✔
2449
    strike_dmg = safe_minus(strike_dmg , src->m_overloaded ? 0 : dst->protected_value());
18,207,170✔
2450
    remove_hp(fd, dst, strike_dmg);
18,207,170✔
2451
}
18,207,170✔
2452

2453
    template<>
2454
inline void perform_skill<Skill::weaken>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
8,814,816✔
2455
{
2456
    dst->m_temp_attack_buff -= (unsigned)std::min(s.x, dst->attack_power());
8,814,816✔
2457
}
×
2458

2459
    template<>
2460
inline void perform_skill<Skill::sunder>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
3,033,301✔
2461
{
2462
    dst->m_sundered = true;
3,033,301✔
2463
    perform_skill<Skill::weaken>(fd, src, dst, s);
2,985,962✔
2464
}
×
2465

2466
    template<>
2467
inline void perform_skill<Skill::mimic>(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s)
340,126✔
2468
{
2469
    // collect all mimickable enemy skills
2470
    std::vector<const SkillSpec *> mimickable_skills;
340,126✔
2471
    mimickable_skills.reserve(dst->m_card->m_skills.size()+dst->m_card->m_skills_on_play.size()+dst->m_card->m_skills_on_death.size()+dst->m_card->m_skills_on_attacked.size());
340,126✔
2472
    _DEBUG_MSG(2, " * Mimickable skills of %s\n", status_description(dst).c_str());
340,126✔
2473
    //include on activate/attacked
2474
    std::vector<std::vector<SkillSpec>> all;
340,126✔
2475
    all.emplace_back(dst->m_card->m_skills);
340,126✔
2476
    all.emplace_back(dst->m_card->m_skills_on_attacked);
340,126✔
2477
    for(std::vector<SkillSpec> & a : all)
1,020,378✔
2478
    {
2479
    for (const SkillSpec & ss: a)
1,699,467✔
2480
    {
2481
        // get skill
2482
        Skill::Skill skill_id = static_cast<Skill::Skill>(ss.id);
1,019,215✔
2483

2484
        // skip non-activation skills and Mimic (Mimic can't be mimicked)
2485
        if (!is_activation_skill(skill_id) || (skill_id == Skill::mimic))
1,466,187✔
2486
        { continue; }
572,243✔
2487

2488
        // skip mend for non-assault mimickers
2489
        if ((skill_id == Skill::mend || skill_id == Skill::fortify) && (src->m_card->m_type != CardType::assault))
446,972✔
2490
        { continue; }
1,200✔
2491

2492
        mimickable_skills.emplace_back(&ss);
445,772✔
2493
        _DEBUG_MSG(2, "  + %s\n", skill_description(fd->cards, ss).c_str());
1,019,215✔
2494
    }
2495
    }
2496

2497
    // select skill
2498
    unsigned mim_idx = 0;
340,126✔
2499
    switch (mimickable_skills.size())
340,126✔
2500
    {
2501
        case 0: assert(false); break;
×
2502
        case 1: break;
2503
        default: mim_idx = (fd->re() % mimickable_skills.size()); break;
103,878✔
2504
    }
2505
    // prepare & perform selected skill
2506
    const SkillSpec & mim_ss = *mimickable_skills[mim_idx];
340,126✔
2507
    Skill::Skill mim_skill_id = static_cast<Skill::Skill>(mim_ss.id);
340,126✔
2508
    auto skill_value = s.x + src->enhanced(mim_skill_id); //enhanced skill from mimic ?!?
340,126✔
2509
    SkillSpec mimicked_ss{mim_skill_id, skill_value, allfactions, mim_ss.n, 0, mim_ss.s, mim_ss.s2, mim_ss.all, mim_ss.card_id,};
340,126✔
2510
    _DEBUG_MSG(1, " * Mimicked skill: %s\n", skill_description(fd->cards, mimicked_ss).c_str());
340,126✔
2511
    skill_table[mim_skill_id](fd, src, mimicked_ss);
340,126✔
2512
}
340,126✔
2513

2514
    template<unsigned skill_id>
2515
inline unsigned select_fast(Field* fd, CardStatus* src, const std::vector<CardStatus*>& cards, const SkillSpec& s)
107,180,743✔
2516
{
2517
    if ((s.y == allfactions)
107,180,743✔
2518
            || fd->bg_effects[fd->tapi][PassiveBGE::metamorphosis]
18,593,648✔
2519
            || fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis])
125,619,176✔
2520
    {
2521
        auto pred = [fd, src, s](CardStatus* c) {
343,070,417✔
2522
            return(skill_predicate<skill_id>(fd, src, c, s));
296,856,173✔
2523
        };
2524
        return fd->make_selection_array(cards.begin(), cards.end(), pred);
88,897,200✔
2525
    }
2526
    else
2527
    {
2528
        auto pred = [fd, src, s](CardStatus* c) {
92,248,243✔
2529
            return ((c->m_card->m_faction == s.y || c->m_card->m_faction == progenitor) && skill_predicate<skill_id>(fd, src, c, s));
87,240,607✔
2530
        };
2531
        return fd->make_selection_array(cards.begin(), cards.end(), pred);
18,283,543✔
2532
    }
2533
}
2534

2535
    template<>
2536
inline unsigned select_fast<Skill::mend>(Field* fd, CardStatus* src, const std::vector<CardStatus*>& cards, const SkillSpec& s)
109,325✔
2537
{
2538
    fd->selection_array.clear();
109,325✔
2539
    bool critical_reach = fd->bg_effects[fd->tapi][PassiveBGE::criticalreach];
109,325✔
2540
    auto& assaults = fd->players[src->m_player]->assaults;
109,325✔
2541
    unsigned adj_size = 1 + (unsigned)(critical_reach);
109,325✔
2542
    unsigned host_idx = src->m_index;
109,325✔
2543
    unsigned from_idx = safe_minus(host_idx, adj_size);
109,325✔
2544
    unsigned till_idx = std::min(host_idx + adj_size, safe_minus(assaults.size(), 1));
213,340✔
2545
    for (; from_idx <= till_idx; ++ from_idx)
375,861✔
2546
    {
2547
        if (from_idx == host_idx) { continue; }
268,111✔
2548
        CardStatus* adj_status = &assaults[from_idx];
157,211✔
2549
        if (!is_alive(adj_status)) { continue; }
157,211✔
2550
        if (skill_predicate<Skill::mend>(fd, src, adj_status, s))
311,272✔
2551
        {
2552
            fd->selection_array.push_back(adj_status);
4,545✔
2553
        }
2554
    }
2555
    return fd->selection_array.size();
109,325✔
2556
}
2557

2558
    template<>
2559
inline unsigned select_fast<Skill::fortify>(Field* fd, CardStatus* src, const std::vector<CardStatus*>& cards, const SkillSpec& s)
1,193,821✔
2560
{
2561
    fd->selection_array.clear();
1,193,821✔
2562
    bool critical_reach = fd->bg_effects[fd->tapi][PassiveBGE::criticalreach];
1,193,821✔
2563
    auto& assaults = fd->players[src->m_player]->assaults;
1,193,821✔
2564
    unsigned adj_size = 1 + (unsigned)(critical_reach);
1,193,821✔
2565
    unsigned host_idx = src->m_index;
1,193,821✔
2566
    unsigned from_idx = safe_minus(host_idx, adj_size);
1,193,821✔
2567
    unsigned till_idx = std::min(host_idx + adj_size, safe_minus(assaults.size(), 1));
2,346,526✔
2568
    for (; from_idx <= till_idx; ++ from_idx)
4,228,963✔
2569
    {
2570
        if (from_idx == host_idx) { continue; }
3,049,853✔
2571
        CardStatus* adj_status = &assaults[from_idx];
1,841,321✔
2572
        if (!is_alive(adj_status)) { continue; }
1,841,321✔
2573
        if (skill_predicate<Skill::fortify>(fd, src, adj_status, s))
1,826,610✔
2574
        {
2575
            fd->selection_array.push_back(adj_status);
1,826,610✔
2576
        }
2577
    }
2578
    return fd->selection_array.size();
1,193,821✔
2579
}
2580
inline std::vector<CardStatus*>& skill_targets_hostile_assault(Field* fd, CardStatus* src)
38,160,107✔
2581
{
2582
    return(fd->players[opponent(src->m_player)]->assaults.m_indirect);
38,160,107✔
2583
}
2584

2585
inline std::vector<CardStatus*>& skill_targets_allied_assault(Field* fd, CardStatus* src)
66,614,070✔
2586
{
2587
    return(fd->players[src->m_player]->assaults.m_indirect);
66,614,070✔
2588
}
2589

2590
inline std::vector<CardStatus*>& skill_targets_hostile_structure(Field* fd, CardStatus* src)
3,709,712✔
2591
{
2592
    return(fd->players[opponent(src->m_player)]->structures.m_indirect);
3,709,712✔
2593
}
2594

2595
inline std::vector<CardStatus*>& skill_targets_allied_structure(Field* fd, CardStatus* src)
2596
{
2597
    return(fd->players[src->m_player]->structures.m_indirect);
2598
}
2599

2600
    template<unsigned skill>
2601
std::vector<CardStatus*>& skill_targets(Field* fd, CardStatus* src)
2602
{
2603
    std::cerr << "skill_targets: Error: no specialization for " << skill_names[skill] << "\n";
2604
    throw;
2605
}
2606

2607
template<> std::vector<CardStatus*>& skill_targets<Skill::enfeeble>(Field* fd, CardStatus* src)
9,644,090✔
2608
{ return(skill_targets_hostile_assault(fd, src)); }
9,644,090✔
2609

2610
template<> std::vector<CardStatus*>& skill_targets<Skill::enhance>(Field* fd, CardStatus* src)
12,071,182✔
2611
{ return(skill_targets_allied_assault(fd, src)); }
12,071,182✔
2612

2613
template<> std::vector<CardStatus*>& skill_targets<Skill::evolve>(Field* fd, CardStatus* src)
959,049✔
2614
{ return(skill_targets_allied_assault(fd, src)); }
959,049✔
2615

2616
template<> std::vector<CardStatus*>& skill_targets<Skill::heal>(Field* fd, CardStatus* src)
20,129,200✔
2617
{ return(skill_targets_allied_assault(fd, src)); }
20,129,200✔
2618

2619
template<> std::vector<CardStatus*>& skill_targets<Skill::jam>(Field* fd, CardStatus* src)
2,854,015✔
2620
{ return(skill_targets_hostile_assault(fd, src)); }
2,854,015✔
2621

2622
template<> std::vector<CardStatus*>& skill_targets<Skill::mend>(Field* fd, CardStatus* src)
109,325✔
2623
{ return(skill_targets_allied_assault(fd, src)); }
109,325✔
2624

2625
template<> std::vector<CardStatus*>& skill_targets<Skill::fortify>(Field* fd, CardStatus* src)
1,193,821✔
2626
{ return(skill_targets_allied_assault(fd, src)); }
1,193,821✔
2627

2628
template<> std::vector<CardStatus*>& skill_targets<Skill::overload>(Field* fd, CardStatus* src)
137,009✔
2629
{ return(skill_targets_allied_assault(fd, src)); }
137,009✔
2630

2631
template<> std::vector<CardStatus*>& skill_targets<Skill::protect>(Field* fd, CardStatus* src)
8,336,542✔
2632
{ return(skill_targets_allied_assault(fd, src)); }
8,336,542✔
2633

2634
template<> std::vector<CardStatus*>& skill_targets<Skill::rally>(Field* fd, CardStatus* src)
6,816,261✔
2635
{ return(skill_targets_allied_assault(fd, src)); }
6,816,261✔
2636

2637
template<> std::vector<CardStatus*>& skill_targets<Skill::enrage>(Field* fd, CardStatus* src)
12,073,334✔
2638
{ return(skill_targets_allied_assault(fd, src)); }
12,073,334✔
2639

2640
template<> std::vector<CardStatus*>& skill_targets<Skill::entrap>(Field* fd, CardStatus* src)
4,788,347✔
2641
{ return(skill_targets_allied_assault(fd, src)); }
4,788,347✔
2642

2643
template<> std::vector<CardStatus*>& skill_targets<Skill::rush>(Field* fd, CardStatus* src)
×
2644
{ return(skill_targets_allied_assault(fd, src)); }
×
2645

2646
template<> std::vector<CardStatus*>& skill_targets<Skill::siege>(Field* fd, CardStatus* src)
3,709,712✔
2647
{ return(skill_targets_hostile_structure(fd, src)); }
3,709,712✔
2648

2649
template<> std::vector<CardStatus*>& skill_targets<Skill::strike>(Field* fd, CardStatus* src)
14,683,321✔
2650
{ return(skill_targets_hostile_assault(fd, src)); }
1,192,575✔
2651

2652
template<> std::vector<CardStatus*>& skill_targets<Skill::sunder>(Field* fd, CardStatus* src)
5,016,272✔
2653
{ return(skill_targets_hostile_assault(fd, src)); }
5,016,272✔
2654

2655
template<> std::vector<CardStatus*>& skill_targets<Skill::weaken>(Field* fd, CardStatus* src)
5,442,808✔
2656
{ return(skill_targets_hostile_assault(fd, src)); }
5,442,808✔
2657

2658
template<> std::vector<CardStatus*>& skill_targets<Skill::mimic>(Field* fd, CardStatus* src)
519,601✔
2659
{ return(skill_targets_hostile_assault(fd, src)); }
519,601✔
2660

2661
    template<Skill::Skill skill_id>
2662
inline bool check_and_perform_skill(Field* fd, CardStatus* src, CardStatus* dst, const SkillSpec& s, bool is_evadable
115,743,576✔
2663
#ifndef NQUEST
2664
        , bool & has_counted_quest
2665
#endif
2666
        )
2667
{
2668
    if (__builtin_expect(skill_check<skill_id>(fd, dst, src), true))
115,743,576✔
2669
    {
2670
#ifndef NQUEST
2671
        if (src->m_player == 0 && ! has_counted_quest)
2672
        {
2673
            fd->inc_counter(QuestType::skill_use, skill_id, dst->m_card->m_id);
2674
            has_counted_quest = true;
2675
        }
2676
#endif
2677
        if (is_evadable && (dst->m_evaded < dst->skill(Skill::evade)))
115,743,576✔
2678
        {
2679
            ++ dst->m_evaded;
14,651,721✔
2680
            _DEBUG_STRAP(
14,651,721✔
2681
                "turn", fd->turn,
2682
                "active_player", fd->tapi,
2683
                "evade_source_"+strap_string(src),1.0,
2684
                "evade_destination_"+strap_string(dst),1.0,
2685
                "evade_skill_"+skill_names[s.id],1.0,
2686
                "evade_skill_x",s.x
2687
            );
2688
            _DEBUG_MSG(1, "%s %s on %s but it evades\n",
16,616,849✔
2689
                    status_description(src).c_str(), skill_short_description(fd->cards, s).c_str(),
2690
                    status_description(dst).c_str());
2691
            return(false);
14,651,721✔
2692
        }
2693
        _DEBUG_STRAP(
101,091,855✔
2694
            "turn", fd->turn,
2695
            "active_player", fd->tapi,
2696
            "skill_source_"+strap_string(src),1.0,
2697
            "skill_target_"+strap_string(dst),1.0,
2698
            "skill_"+skill_names[s.id],1.0,
2699
            "skill_x", s.x
2700
        );
2701
        _DEBUG_MSG(1, "%s %s on %s\n",
127,046,103✔
2702
                status_description(src).c_str(), skill_short_description(fd->cards, s).c_str(),
2703
                status_description(dst).c_str());
2704
        perform_skill<skill_id>(fd, src, dst, s);
101,091,855✔
2705
        if (s.c > 0)
101,091,855✔
2706
        {
2707
            src->m_skill_cd[skill_id] = s.c;
1,266,463✔
2708
        }
2709
        // Skill: Tribute
2710
        if (skill_check<Skill::tribute>(fd, dst, src)
119,896,437✔
2711
                // only activation helpful skills can be tributed (* except Evolve, Enhance, and Rush)
2712
                && is_activation_helpful_skill(s.id) && (s.id != Skill::evolve) && (s.id != Skill::enhance) && (s.id != Skill::rush)
18,804,582✔
2713
                && (dst->m_tributed < dst->skill(Skill::tribute))
7,374,353✔
2714
                && skill_check<skill_id>(fd, src, src))
91,712,530✔
2715
        {
2716
            ++ dst->m_tributed;
964,195✔
2717
            _DEBUG_STRAP(
964,195✔
2718
                "turn", fd->turn,
2719
                "active_player", fd->tapi,
2720
                "tribute_source_"+strap_string(dst),1.0,
2721
                "tribute_destination_"+strap_string(src),1.0,
2722
                "tribute_skill_"+skill_names[s.id],1.0,
2723
                "tribute_skill_x",s.x
2724
            );
2725
            _DEBUG_MSG(1, "%s tributes %s back to %s\n",
964,195✔
2726
                    status_description(dst).c_str(), skill_short_description(fd->cards, s).c_str(),
2727
                    status_description(src).c_str());
2728
            perform_skill<skill_id>(fd, src, src, s);
964,195✔
2729
        }
2730
        return(true);
101,091,855✔
2731
    }
2732
    _DEBUG_MSG(1, "(CANCELLED) %s %s on %s\n",
×
2733
            status_description(src).c_str(), skill_short_description(fd->cards, s).c_str(),
2734
            status_description(dst).c_str());
2735
    return(false);
2736
}
2737
template<enum CardType::CardType def_cardtype>
2738
void perform_bge_devour(Field* fd, CardStatus* att_status, CardStatus* def_status)
13,647,445✔
2739
{
2740
// Passive BGE: Devour
2741
                unsigned leech_value;
2742
                if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::devour], false)
8,778,339✔
2743
                        && ((leech_value = att_status->skill(Skill::leech) + att_status->skill(Skill::refresh)) > 0)
47,636✔
2744
                        && (def_cardtype == CardType::assault))
8,778,339✔
2745
                {
2746
                    unsigned bge_denominator = (fd->bg_effects[fd->tapi][PassiveBGE::devour] > 0)
1,774✔
2747
                        ? (unsigned)fd->bg_effects[fd->tapi][PassiveBGE::devour]
887✔
2748
                        : 4;
2749
                    unsigned bge_value = (leech_value - 1) / bge_denominator + 1;
887✔
2750
                    if (! att_status->m_sundered)
887✔
2751
                    {
2752
                        _DEBUG_MSG(1, "Devour: %s gains %u attack\n",
2,625✔
2753
                                status_description(att_status).c_str(), bge_value);
2754
                        att_status->m_perm_attack_buff += bge_value;
875✔
2755
                    }
2756
                    _DEBUG_MSG(1, "Devour: %s extends max hp / heals itself for %u\n",
2,661✔
2757
                            status_description(att_status).c_str(), bge_value);
2758
                    att_status->ext_hp(bge_value);
887✔
2759
                }
2760
}
8,778,339✔
2761
template<enum CardType::CardType def_cardtype>
2762
void perform_bge_heroism(Field* fd, CardStatus* att_status, CardStatus* def_status)
13,647,445✔
2763
{
2764
// Passive BGE: Heroism
2765
                unsigned valor_value;
2766
                if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::heroism], false)
13,647,445✔
2767
                        && ((valor_value = att_status->skill(Skill::valor) + att_status->skill(Skill::bravery)) > 0)
23,766✔
2768
                        && !att_status->m_sundered
2,451✔
2769
                        && (def_cardtype == CardType::assault) && (def_status->m_hp <= 0))
8,780,775✔
2770
                {
2771
                    _DEBUG_MSG(1, "Heroism: %s gain %u attack\n",
1,998✔
2772
                            status_description(att_status).c_str(), valor_value);
2773
                    att_status->m_perm_attack_buff += valor_value;
666✔
2774
                }
2775
                }
8,778,339✔
2776
/**
2777
 * @brief Perform Mark skill, increases Mark-counter.
2778
 * 
2779
 * @param fd     Field
2780
 * @param att_status Attacker
2781
 * @param def_status Defender
2782
 */
2783
void perform_mark(Field* fd, CardStatus* att_status, CardStatus* def_status)
13,647,445✔
2784
{
2785
    // Bug fix? 2023-04-03 mark should come after berserk
2786
    // Increase Mark-counter
2787
    unsigned mark_base = att_status->skill(Skill::mark);
13,647,445✔
2788
    if(mark_base && skill_check<Skill::mark>(fd,att_status,def_status)) {
13,647,445✔
2789
        _DEBUG_STRAP(
35,891✔
2790
            "turn", fd->turn,
2791
            "active_player", fd->tapi,
2792
            "mark_attacker_"+strap_string(att_status), 1.0,
2793
            "mark_defender_"+strap_string(def_status), 1.0,
2794
            "mark_value", mark_base
2795
        );
35,891✔
2796
        _DEBUG_MSG(1, "%s marks %s for %u\n",
35,891✔
2797
                status_description(att_status).c_str(), status_description(def_status).c_str(), mark_base);
35,891✔
2798
        def_status->m_marked += mark_base;
35,891✔
2799
    }
2800
}
13,647,445✔
2801
/**
2802
 * @brief Perform Berserk skill and Passive BGE: EnduringRage
2803
 * 
2804
 * Increases attack by berserk value if not sundered.
2805
 * 
2806
 * @param fd     Field
2807
 * @param att_status Attacker
2808
 * @param def_status Defender
2809
 */
2810
void perform_berserk(Field* fd, CardStatus* att_status, CardStatus* def_status)
13,647,445✔
2811
{
2812
            // Skill: Berserk
2813
            unsigned berserk_value = att_status->skill(Skill::berserk);
13,647,445✔
2814
            if ( !att_status->m_sundered && (berserk_value > 0))
13,647,445✔
2815
            {
2816
                // perform_skill_berserk
2817
                att_status->m_perm_attack_buff += berserk_value;
6,474,830✔
2818
#ifndef NQUEST
2819
                if (att_status->m_player == 0)
2820
                {
2821
                    fd->inc_counter(QuestType::skill_use, Skill::berserk);
2822
                }
2823
#endif
2824

2825
                // Passive BGE: EnduringRage
2826
                if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::enduringrage], false))
6,474,830✔
2827
                {
2828
                    unsigned bge_denominator = (fd->bg_effects[fd->tapi][PassiveBGE::enduringrage] > 0)
21,709✔
2829
                        ? (unsigned)fd->bg_effects[fd->tapi][PassiveBGE::enduringrage]
21,709✔
2830
                        : 2;
21,709✔
2831
                    unsigned bge_value = (berserk_value - 1) / bge_denominator + 1;
21,709✔
2832
                    _DEBUG_MSG(1, "EnduringRage: %s heals and protects itself for %u\n",
21,709✔
2833
                            status_description(att_status).c_str(), bge_value);
21,709✔
2834
                    att_status->add_hp(bge_value);
21,709✔
2835
                    att_status->m_protected += bge_value;
21,709✔
2836
                }
2837
            }
2838

2839

2840
}
13,647,445✔
2841
/**
2842
 * @brief Poisoning of a attacking card
2843
 * 
2844
 * @param fd  Field
2845
 * @param att_status Attacking card
2846
 * @param def_status Defending card
2847
 */
2848
void perform_poison(Field* fd, CardStatus* att_status, CardStatus* def_status)
16,591,372✔
2849
{
2850
    if(__builtin_expect(fd->fixes[Fix::poison_after_attacked],true))
16,591,372✔
2851
    {
2852
        if (is_alive(att_status) && def_status->has_skill(Skill::poison))
16,591,372✔
2853
        {
2854
            unsigned poison_value = def_status->skill(Skill::poison);
10,222✔
2855
            _DEBUG_STRAP(
10,222✔
2856
                "turn", fd->turn,
2857
                "active_player", fd->tapi,
2858
                "poison_attacker_"+strap_string(att_status),1.0,
2859
                "poison_defender_"+strap_string(def_status),1.0,
2860
                "poison_value", poison_value
2861
            );
10,222✔
2862
            _DEBUG_MSG(1, "%s gets poisoned by %u from %s\n",
10,222✔
2863
                            status_description(att_status).c_str(), poison_value,
2864
                            status_description(def_status).c_str());
10,222✔
2865
            att_status->m_poisoned += poison_value; 
10,222✔
2866
        }
2867
    }
2868
}
16,591,372✔
2869

2870
void perform_corrosive(Field* fd, CardStatus* att_status, CardStatus* def_status)
16,591,372✔
2871
{
2872
    // Skill: Corrosive
2873
    unsigned corrosive_value = def_status->skill(Skill::corrosive);
16,591,372✔
2874
    if (corrosive_value > att_status->m_corroded_rate)
16,591,372✔
2875
    {
2876
        // perform_skill_corrosive
2877
        _DEBUG_STRAP(
45,054✔
2878
            "turn", fd->turn,
2879
            "active_player", fd->tapi,
2880
            "corrosive_attacker_"+strap_string(att_status), 1.0,
2881
            "corrosive_defender_"+strap_string(def_status), 1.0,
2882
            "corrosive_value", corrosive_value
2883
        );
45,054✔
2884
        _DEBUG_MSG(1, "%s corrodes %s by %u\n",
45,054✔
2885
                status_description(def_status).c_str(),
2886
                status_description(att_status).c_str(), corrosive_value);
45,054✔
2887
        att_status->m_corroded_rate = corrosive_value;
45,054✔
2888
    }
2889
}
16,591,372✔
2890

2891
template<enum CardType::CardType def_cardtype>
2892
void perform_counter(Field* fd, CardStatus* att_status, CardStatus* def_status)
17,303,763✔
2893
{
2894
            // Enemy Skill: Counter
2895
            if (def_status->has_skill(Skill::counter))
17,303,763✔
2896
            {
2897
                // perform_skill_counter
2898
                unsigned counter_dmg(counter_damage(fd, att_status, def_status));
2,077,330✔
2899
#ifndef NQUEST
2900
                if (def_status->m_player == 0)
2901
                {
2902
                    fd->inc_counter(QuestType::skill_use, Skill::counter);
2903
                    fd->inc_counter(QuestType::skill_damage, Skill::counter, 0, counter_dmg);
2904
                }
2905
#endif
2906
                _DEBUG_STRAP(
2,077,330✔
2907
                    "turn", fd->turn,
2908
                    "active_player", fd->tapi,
2909
                    "counter_attacker_"+strap_string(att_status), 1.0,
2910
                    "counter_defender_"+strap_string(def_status), 1.0,
2911
                    "counter_damage", counter_dmg
2912
                );
2913
                _DEBUG_MSG(1, "%s takes %u counter damage from %s\n",
3,135,132✔
2914
                        status_description(att_status).c_str(), counter_dmg,
2915
                        status_description(def_status).c_str());
2916
                remove_hp(fd, att_status, counter_dmg);
2,077,330✔
2917
                prepend_on_death(fd);
2,077,330✔
2918
                resolve_skill(fd);
2,077,330✔
2919

2920
                // Passive BGE: Counterflux
2921
                if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::counterflux], false)
2,077,330✔
2922
                        && (def_cardtype == CardType::assault) && is_alive(def_status))
1,228,292✔
2923
                {
2924
                    unsigned flux_denominator = (fd->bg_effects[fd->tapi][PassiveBGE::counterflux] > 0)
8,262✔
2925
                        ? (unsigned)fd->bg_effects[fd->tapi][PassiveBGE::counterflux]
4,131✔
2926
                        : 4;
2927
                    unsigned flux_value = (def_status->skill(Skill::counter) - 1) / flux_denominator + 1;
4,131✔
2928
                    _DEBUG_MSG(1, "Counterflux: %s heals itself and berserks for %u\n",
12,393✔
2929
                            status_description(def_status).c_str(), flux_value);
2930
                    def_status->add_hp(flux_value);
4,131✔
2931
                    if (! def_status->m_sundered)
4,131✔
2932
                    { def_status->m_perm_attack_buff += flux_value; }
3,718✔
2933
                }
2934
            }
2935
}
17,303,763✔
2936
bool check_and_perform_enhance(Field* fd, CardStatus* src, bool early)
130,887,288✔
2937
{
2938
      if(!is_active(src))return false; // active
130,887,288✔
2939
      if(!src->has_skill(Skill::enhance))return false; // enhance Skill
82,487,584✔
2940
      for(auto ss : src->m_card->m_skills)
95,823,024✔
2941
      {
2942
          if(ss.id != Skill::enhance)continue;
71,867,118✔
2943
          if(early ^ (ss.s == Skill::allegiance || ss.s == Skill::absorb ||ss.s == Skill::stasis || ss.s == Skill::bravery))continue; //only specified skills are 'early'
24,481,538✔
2944
          skill_table[ss.id](fd,src,ss);
11,977,953✔
2945
      }
2946
      return true;
23,955,906✔
2947
}
2948
bool check_and_perform_early_enhance(Field* fd, CardStatus* src)
65,443,644✔
2949
{
2950
      return check_and_perform_enhance(fd,src,true);
65,443,644✔
2951
}
2952
bool check_and_perform_later_enhance(Field* fd, CardStatus* src)
65,443,644✔
2953
{
2954
      return check_and_perform_enhance(fd,src,false);
65,443,644✔
2955
}
2956
/**
2957
 * @brief Perform drain skill
2958
 * 
2959
 * @param fd Field
2960
 * @param att_status Attacker status
2961
 * @param def_status Defender status
2962
 * @param att_dmg damage dealt by attacker
2963
 */
2964
template<>
2965
void PerformAttack::perform_swipe_drain<CardType::assault>(Field* fd, CardStatus* att_status, CardStatus* def_status,unsigned att_dmg) {
11,941,759✔
2966
        unsigned swipe_value = att_status->skill(Skill::swipe);
11,941,759✔
2967
        unsigned drain_value = att_status->skill(Skill::drain);
11,941,759✔
2968
        if (swipe_value || drain_value)
11,941,759✔
2969
        {
2970
            Storage<CardStatus>& def_assaults(fd->tip->assaults);
837,021✔
2971
            bool critical_reach = fd->bg_effects[fd->tapi][PassiveBGE::criticalreach];
837,021✔
2972
            auto drain_total_dmg = att_dmg;
837,021✔
2973
            unsigned adj_size = 1 + (unsigned)(critical_reach);
837,021✔
2974
            unsigned host_idx = def_status->m_index;
837,021✔
2975
            unsigned from_idx = safe_minus(host_idx, adj_size);
837,021✔
2976
            unsigned till_idx = std::min(host_idx + adj_size, safe_minus(def_assaults.size(), 1));
1,418,159✔
2977
            for (; from_idx <= till_idx; ++ from_idx)
2,333,658✔
2978
            {
2979
                if (from_idx == host_idx) { continue; }
1,626,715✔
2980
                CardStatus* adj_status = &def_assaults[from_idx];
659,616✔
2981
                if (!is_alive(adj_status)) { continue; }
659,616✔
2982
                _DEBUG_ASSERT(adj_status->m_card->m_type == CardType::assault); //only assaults
529,538✔
2983
                //unsigned swipe_dmg = safe_minus(
2984
                //    swipe_value + drain_value + def_status->m_enfeebled,
2985
                //    def_status->protected_value());
2986
                unsigned remaining_dmg = remove_absorption(fd,adj_status,swipe_value + drain_value + adj_status->m_enfeebled);
529,538✔
2987
                remaining_dmg = safe_minus(remaining_dmg,adj_status->protected_value());
529,538✔
2988
                _DEBUG_STRAP(
529,538✔
2989
                    "turn", fd->turn,
2990
                    "active_player", fd->tapi,
2991
                    "swipe_attacker_"+strap_string(att_status),1.0,
2992
                    "swipe_defender_"+strap_string(adj_status),1.0,
2993
                    "swipe_damage", remaining_dmg
2994
                );
529,538✔
2995
                _DEBUG_MSG(1, "%s swipes %s for %u damage\n",
529,538✔
2996
                        status_description(att_status).c_str(),
2997
                        status_description(adj_status).c_str(), remaining_dmg);
529,538✔
2998

2999
                remove_hp(fd, adj_status, remaining_dmg);
529,538✔
3000
                drain_total_dmg += remaining_dmg;
529,538✔
3001
            }
3002
            if (drain_value && skill_check<Skill::drain>(fd, att_status, nullptr))
837,021✔
3003
            {
3004
                _DEBUG_STRAP(
40,727✔
3005
                    "turn", fd->turn,
3006
                    "active_player", fd->tapi,
3007
                    "drain_attacker_"+strap_string(att_status),1.0,
3008
                    "drain_amount", drain_total_dmg
3009
                );
40,727✔
3010
                _DEBUG_MSG(1, "%s drains %u hp\n",
40,727✔
3011
                        status_description(att_status).c_str(), drain_total_dmg);
40,727✔
3012
                att_status->add_hp(drain_total_dmg);
40,727✔
3013
            }
3014
            prepend_on_death(fd);
837,021✔
3015
            resolve_skill(fd);
837,021✔
3016
        }
3017
}
11,941,759✔
3018
/**
3019
 * @brief Perform Hunt skill
3020
 * 
3021
 * @param fd Field 
3022
 * @param att_status Attacker status
3023
 * @param def_status Defender status
3024
 */
3025
void perform_hunt(Field* fd, CardStatus* att_status, CardStatus* def_status) {
17,315,753✔
3026
    unsigned hunt_value = att_status->skill(Skill::hunt);
17,315,753✔
3027
        if(hunt_value)
17,315,753✔
3028
        {
3029
            CardStatus* hunted_status{select_first_enemy_assault(fd)};
33,058✔
3030
            if (hunted_status != nullptr)
33,058✔
3031
            {
3032
                unsigned remaining_dmg = remove_absorption(fd,hunted_status,hunt_value + hunted_status->m_enfeebled);
22,879✔
3033
                remaining_dmg = safe_minus(remaining_dmg,hunted_status->protected_value());
22,879✔
3034
                _DEBUG_STRAP(
22,879✔
3035
                    "turn", fd->turn,
3036
                    "active_player", fd->tapi,
3037
                    "hunt_attacker_"+strap_string(att_status),1.0,
3038
                    "hunt_defender_"+strap_string(hunted_status),1.0,
3039
                    "hunt_damage", remaining_dmg
3040
                );
22,879✔
3041
                _DEBUG_MSG(1, "%s hunts %s for %u damage\n",
22,879✔
3042
                        status_description(att_status).c_str(),
3043
                        status_description(hunted_status).c_str(), remaining_dmg);
22,879✔
3044

3045
                remove_hp(fd, hunted_status, remaining_dmg);
22,879✔
3046

3047
                prepend_on_death(fd);
22,879✔
3048
                resolve_skill(fd);
22,879✔
3049
            }
3050
        }
3051
}
17,315,753✔
3052
/**
3053
 * @brief Perform Subdue skill
3054
 * 
3055
 * @param fd Field
3056
 * @param att_status Attacker status
3057
 * @param def_status Defender status
3058
 */
3059
void perform_subdue(Field* fd, CardStatus* att_status, CardStatus* def_status)
18,417,995✔
3060
{
3061
                // Skill: Subdue
3062
                unsigned subdue_value = def_status->skill(Skill::subdue);
18,417,995✔
3063
                if (__builtin_expect(subdue_value, false))
18,417,995✔
3064
                {
3065

3066
                    _DEBUG_MSG(1, "%s subdues %s by %u\n",
604,017✔
3067
                            status_description(def_status).c_str(),
3068
                            status_description(att_status).c_str(), subdue_value);
604,017✔
3069
                    att_status->m_subdued += subdue_value;
604,017✔
3070
                    //fix negative attack
3071
                    if(att_status->calc_attack_power()<0)
604,017✔
3072
                    {
3073
                        att_status->m_temp_attack_buff -= att_status->calc_attack_power();
2,270✔
3074
                    }
3075
                    _DEBUG_STRAP(
604,017✔
3076
                        "turn", fd->turn,
3077
                        "active_player", fd->tapi,
3078
                        "subdue_attacker_"+strap_string(att_status),1.0,
3079
                        "subdue_defender_"+strap_string(def_status),1.0,
3080
                        "subdue_value", subdue_value,
3081
                        "subdue_hp_loss", safe_minus(att_status->m_hp , att_status->max_hp())
3082
                    );
604,017✔
3083
                    if (att_status->m_hp > att_status->max_hp())
677,771✔
3084
                    {
3085
                        _DEBUG_MSG(1, "%s loses %u HP due to subdue (max hp: %u)\n",
99,340✔
3086
                                status_description(att_status).c_str(),
3087
                                (att_status->m_hp - att_status->max_hp()),
3088
                                att_status->max_hp());
99,340✔
3089
                        att_status->m_hp = att_status->max_hp();
170,376✔
3090
                    }
3091
                }
3092
}
18,417,995✔
3093
bool check_and_perform_valor(Field* fd, CardStatus* src)
11,889,034✔
3094
{
3095
    unsigned valor_value = src->skill(Skill::valor);
11,889,034✔
3096
    if (valor_value && !src->m_sundered && skill_check<Skill::valor>(fd, src, nullptr))
23,778,068✔
3097
    {
3098
        _DEBUG_ASSERT(src->m_card->m_type == CardType::assault); //only assaults
45,776✔
3099
        unsigned opponent_player = opponent(src->m_player);
45,776✔
3100
        const CardStatus * dst = fd->players[opponent_player]->assaults.size() > src->m_index ?
45,776✔
3101
            &fd->players[opponent_player]->assaults[src->m_index] :
20,639✔
3102
            nullptr;
45,776✔
3103
        if (dst == nullptr || dst->m_hp <= 0)
20,639✔
3104
        {
3105
            _DEBUG_MSG(1, "%s loses Valor (no blocker)\n", status_description(src).c_str());
25,137✔
3106
            return false;
25,137✔
3107
        }
3108
        else if (dst->attack_power() <= src->attack_power())
20,639✔
3109
        {
3110
            _DEBUG_MSG(1, "%s loses Valor (weak blocker %s)\n", status_description(src).c_str(), status_description(dst).c_str());
2,278✔
3111
            return false;
2,278✔
3112
        }
3113
#ifndef NQUEST
3114
        if (src->m_player == 0)
3115
        {
3116
            fd->inc_counter(QuestType::skill_use, Skill::valor);
3117
        }
3118
#endif
3119
        _DEBUG_MSG(1, "%s activates Valor %u\n", status_description(src).c_str(), valor_value);
18,361✔
3120
        src->m_perm_attack_buff += valor_value;
18,361✔
3121
        return true;
18,361✔
3122
    }
3123
    return false;
3124
}
3125

3126
bool check_and_perform_bravery(Field* fd, CardStatus* src)
51,125,659✔
3127
{
3128
    unsigned bravery_value = src->skill(Skill::bravery);
51,125,659✔
3129
    if (bravery_value && !src->m_sundered && skill_check<Skill::bravery>(fd, src, nullptr))
102,251,318✔
3130
    {
3131
        _DEBUG_ASSERT(src->m_card->m_type == CardType::assault); //only assaults
1,394,585✔
3132
        unsigned opponent_player = opponent(src->m_player);
1,394,585✔
3133
        const CardStatus * dst = fd->players[opponent_player]->assaults.size() > src->m_index ?
1,394,585✔
3134
            &fd->players[opponent_player]->assaults[src->m_index] :
786,734✔
3135
            nullptr;
1,394,585✔
3136
        if (dst == nullptr || dst->m_hp <= 0)
786,734✔
3137
        {
3138
            _DEBUG_MSG(1, "%s loses Bravery (no blocker)\n", status_description(src).c_str());
607,851✔
3139
            return false;
607,851✔
3140
        }
3141
        else if (dst->attack_power() <= src->attack_power())
786,734✔
3142
        {
3143
            _DEBUG_MSG(1, "%s loses Bravery (weak blocker %s)\n", status_description(src).c_str(), status_description(dst).c_str());
574,727✔
3144
            return false;
574,727✔
3145
        }
3146
#ifndef NQUEST
3147
        if (src->m_player == 0)
3148
        {
3149
            fd->inc_counter(QuestType::skill_use, Skill::bravery);
3150
        }
3151
#endif
3152
        _DEBUG_MSG(1, "%s activates Bravery %u\n", status_description(src).c_str(), bravery_value);
212,007✔
3153
        src->m_perm_attack_buff += bravery_value;
212,007✔
3154

3155
        //BGE: superheroism
3156
        if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::superheroism], false))
212,007✔
3157
        {
3158
            unsigned bge_value = bravery_value * fd->bg_effects[fd->tapi][PassiveBGE::superheroism];
960✔
3159
            const SkillSpec ss_heal{Skill::heal, bge_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, true, 0,};
960✔
3160
            _DEBUG_MSG(1, "%s activates SuperHeroism: %s\n", status_description(src).c_str(),
960✔
3161
                    skill_description(fd->cards, ss_heal).c_str());
960✔
3162
            //fd->skill_queue.emplace(fd->skill_queue.begin()+0, src, ss_heal);
3163
            skill_table[Skill::heal](fd,src,ss_heal); //Probably better to perform the skill direct instead of queuing it
960✔
3164
        }
3165

3166
        return true;
212,007✔
3167
    }
3168
    return false;
3169
}
3170

3171
bool check_and_perform_inhibit(Field* fd, CardStatus* att_status,CardStatus* def_status)
27,542,640✔
3172
{
3173
      unsigned inhibit_value = att_status->skill(Skill::inhibit);
27,542,640✔
3174
      if (inhibit_value > def_status->m_inhibited && skill_check<Skill::inhibit>(fd, att_status, def_status))
29,808,736✔
3175
      {
3176
        _DEBUG_STRAP(
2,266,095✔
3177
            "turn", fd->turn,
3178
            "active_player", fd->tapi,
3179
            "inhibit_attacker_"+strap_string(att_status),1.0,
3180
            "inhibit_defender_"+strap_string(def_status),1.0,
3181
            "inhibit_value", inhibit_value
3182
        );
2,266,095✔
3183
        _DEBUG_MSG(1, "%s inhibits %s by %u\n",
2,266,095✔
3184
                status_description(att_status).c_str(),
3185
                status_description(def_status).c_str(), inhibit_value);
2,266,095✔
3186
        def_status->m_inhibited = inhibit_value;
2,266,095✔
3187
        return true;
2,266,095✔
3188
      }
3189
      return false;
3190
}
3191
bool check_and_perform_sabotage(Field* fd, CardStatus* att_status, CardStatus* def_status)
27,542,640✔
3192
{
3193
    unsigned sabotage_value = att_status->skill(Skill::sabotage);
27,542,640✔
3194
    if (sabotage_value > def_status->m_sabotaged && skill_check<Skill::sabotage>(fd, att_status, def_status))
28,053,584✔
3195
    {
3196
        _DEBUG_STRAP(
510,944✔
3197
            "turn", fd->turn,
3198
            "active_player", fd->tapi,
3199
            "sabotage_attacker_"+strap_string(att_status),1.0,
3200
            "sabotage_defender_"+strap_string(def_status),1.0,
3201
            "sabotage_value", sabotage_value
3202
        );
510,944✔
3203
        _DEBUG_MSG(1, "%s sabotages %s by %u\n",
510,944✔
3204
                status_description(att_status).c_str(),
3205
                status_description(def_status).c_str(), sabotage_value);
510,944✔
3206
        def_status->m_sabotaged = sabotage_value;
510,944✔
3207
        return true;
510,944✔
3208
    }
3209
    return false;
3210
}
3211
bool check_and_perform_disease(Field* fd, CardStatus* att_status,CardStatus* def_status)
27,542,640✔
3212
{
3213
    unsigned disease_base = att_status->skill(Skill::disease);
27,542,640✔
3214
    if(disease_base && skill_check<Skill::disease>(fd, att_status, def_status)) {
27,736,606✔
3215
        _DEBUG_STRAP(
193,966✔
3216
            "turn", fd->turn,
3217
            "active_player", fd->tapi,
3218
            "disease_attacker_"+strap_string(att_status),1.0,
3219
            "disease_defender_"+strap_string(def_status),1.0,
3220
            "disease_value", disease_base
3221
        );
193,966✔
3222
        _DEBUG_MSG(1, "%s diseases %s for %u\n",
193,966✔
3223
        status_description(att_status).c_str(), status_description(def_status).c_str(), disease_base);
193,966✔
3224
        def_status->m_diseased += disease_base;
193,966✔
3225
        return true;
193,966✔
3226
    }
3227
    return false;
3228
}
3229

3230
CardStatus* check_and_perform_summon(Field* fd, CardStatus* src)
11,169,603✔
3231
{
3232
    unsigned summon_card_id = src->m_card->m_skill_value[Skill::summon];
11,169,603✔
3233
    if (summon_card_id)
11,169,603✔
3234
    {
3235
        const Card* summoned_card(fd->cards.by_id(summon_card_id));
2,977,902✔
3236
        _DEBUG_MSG(1, "%s summons %s\n", status_description(src).c_str(), summoned_card->m_name.c_str());
2,977,902✔
3237
        CardStatus* summoned_status = nullptr;
2,977,902✔
3238
        switch (summoned_card->m_type)
2,977,902✔
3239
        {
3240
            case CardType::assault:
1,074,952✔
3241
                summoned_status = PlayCard(summoned_card, fd, src->m_player, src).op<CardType::assault>(true);
1,074,952✔
3242
                return summoned_status;
1,074,952✔
3243
            case CardType::structure:
1,902,950✔
3244
                summoned_status = PlayCard(summoned_card, fd, src->m_player, src).op<CardType::structure>(true);
1,902,950✔
3245
                return summoned_status;
1,902,950✔
3246
            default:
×
3247
                _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n",
×
3248
                        summoned_card->m_id, card_description(fd->cards, summoned_card).c_str(),
3249
                        summoned_card->m_type);
×
3250
                _DEBUG_ASSERT(false);
×
3251
                __builtin_unreachable();
3252
        }
3253
    }
3254
    return nullptr;
3255
}
3256

3257

3258
    template<Skill::Skill skill_id>
3259
size_t select_targets(Field* fd, CardStatus* tsrc, const SkillSpec& s)
107,291,314✔
3260
{
3261
    size_t n_candidates;
3262
    CardStatus* src;
3263
    if(fd->fixes[Fix::revenge_on_death] && s.s2 == Skill::revenge)
107,291,314✔
3264
    {
3265
            _DEBUG_MSG(2,"FIX ON DEATH REVENGE SELECTION")
34,582✔
3266
            src = &fd->players[(tsrc->m_player+1)%2]->commander; // selection like enemy commander
34,582✔
3267
    }
34,582✔
3268
    else
3269
    {
3270
            src = tsrc;
3271
    }
3272
    switch (skill_id)
3273
    {
3274
        case Skill::mortar:
3,484,912✔
3275
            n_candidates = select_fast<Skill::siege>(fd, src, skill_targets<Skill::siege>(fd, src), s);
3,484,912✔
3276
            if (n_candidates == 0)
3,484,912✔
3277
            {
3278
                n_candidates = select_fast<Skill::strike>(fd, src, skill_targets<Skill::strike>(fd, src), s);
1,192,575✔
3279
            }
3280
            break;
3281

3282
        default:
103,806,402✔
3283
            n_candidates = select_fast<skill_id>(fd, src, skill_targets<skill_id>(fd, src), s);
103,806,402✔
3284
            break;
3285
    }
3286

3287
    // (false-loop)
3288
    unsigned n_selected = n_candidates;
108,230,913✔
3289
    do
107,291,314✔
3290
    {
3291
        // no candidates
3292
        if (n_candidates == 0)
104,998,977✔
3293
        { break; }
3294

3295
        // show candidates (debug)
3296
        _DEBUG_SELECTION("%s", skill_names[skill_id].c_str());
66,493,906✔
3297

3298
        // analyze targets count / skill
3299
        unsigned n_targets = s.n > 0 ? s.n : 1;
65,339,122✔
3300
        if (s.all || n_targets >= n_candidates || skill_id == Skill::mend || skill_id == Skill::fortify)  // target all or mend
65,339,122✔
3301
        { break; }
3302

3303
        // shuffle & trim
3304
        for (unsigned i = 0; i < n_targets; ++i)
40,052,350✔
3305
        {
3306
            std::swap(fd->selection_array[i], fd->selection_array[fd->rand(i, n_candidates - 1)]);
20,215,960✔
3307
        }
3308
        fd->selection_array.resize(n_targets);
19,836,390✔
3309
        if (n_targets > 1)
19,836,390✔
3310
        {
3311
            std::sort(fd->selection_array.begin(), fd->selection_array.end(),
254,387✔
3312
                    [](const CardStatus * a, const CardStatus * b) { return a->m_index < b->m_index; });
632,037✔
3313
        }
3314
        n_selected = n_targets;
3315

3316
    } while (false); // (end)
3317

3318
    return n_selected;
107,291,314✔
3319
}
3320

3321
    template<Skill::Skill skill_id>
3322
void perform_targetted_allied_fast(Field* fd, CardStatus* src, const SkillSpec& s)
66,608,427✔
3323
{
3324
    select_targets<skill_id>(fd, src, s);
66,608,427✔
3325
    unsigned num_inhibited = 0;
66,608,427✔
3326
#ifndef NQUEST
3327
    bool has_counted_quest = false;
3328
#endif
3329
    bool src_overloaded = src->m_overloaded;
66,608,427✔
3330
    std::vector<CardStatus*> selection_array = fd->selection_array;
66,608,427✔
3331
    for (CardStatus * dst: selection_array)
134,053,487✔
3332
    {
3333
        if (dst->m_inhibited > 0 && !src_overloaded)
67,445,060✔
3334
        {
3335
            _DEBUG_MSG(1, "%s %s on %s but it is inhibited\n",
4,198,917✔
3336
                    status_description(src).c_str(), skill_short_description(fd->cards, s).c_str(),
3337
                    status_description(dst).c_str());
3338
            -- dst->m_inhibited;
3,735,159✔
3339
            ++ num_inhibited;
3,735,159✔
3340
            continue;
3,735,159✔
3341
        }
3,735,159✔
3342
        check_and_perform_skill<skill_id>(fd, src, dst, s, false
63,709,901✔
3343
#ifndef NQUEST
3344
                , has_counted_quest
3345
#endif
3346
                );
3347
    }
3348

3349
    // Passive BGE: Divert
3350
    if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::divert], false)
66,608,427✔
3351
            && (num_inhibited > 0))
66,608,427✔
3352
    {
3353
        SkillSpec diverted_ss = s;
5,593✔
3354
        diverted_ss.y = allfactions;
5,593✔
3355
        diverted_ss.n = 1;
5,593✔
3356
        diverted_ss.all = false;
5,593✔
3357
        for (unsigned i = 0; i < num_inhibited; ++ i)
11,236✔
3358
        {
3359
            select_targets<skill_id>(fd, &fd->tip->commander, diverted_ss);
5,643✔
3360
            std::vector<CardStatus*> selection_array = fd->selection_array;
5,643✔
3361
            for (CardStatus * dst: selection_array)
9,543✔
3362
            {
3363
                if (dst->m_inhibited > 0)
3,900✔
3364
                {
3365
                    _DEBUG_MSG(1, "%s %s (Diverted) on %s but it is inhibited\n",
681✔
3366
                            status_description(src).c_str(), skill_short_description(fd->cards, diverted_ss).c_str(),
3367
                            status_description(dst).c_str());
3368
                    -- dst->m_inhibited;
227✔
3369
                    continue;
227✔
3370
                }
227✔
3371
                _DEBUG_MSG(1, "%s %s (Diverted) on %s\n",
11,019✔
3372
                        status_description(src).c_str(), skill_short_description(fd->cards, diverted_ss).c_str(),
3373
                        status_description(dst).c_str());
3374
                perform_skill<skill_id>(fd, src, dst, diverted_ss);
3,693✔
3375
            }
3376
        }
3377
    }
3378
}
66,608,427✔
3379

3380
void perform_targetted_allied_fast_rush(Field* fd, CardStatus* src, const SkillSpec& s)
×
3381
{
3382
    if (src->m_card->m_type == CardType::commander)
×
3383
    {  // Passive BGE skills are casted as by commander
3384
        perform_targetted_allied_fast<Skill::rush>(fd, src, s);
×
3385
        return;
×
3386
    }
3387
    if (src->m_rush_attempted)
×
3388
    {
3389
        _DEBUG_MSG(2, "%s does not check Rush again.\n", status_description(src).c_str());
×
3390
        return;
×
3391
    }
3392
    _DEBUG_MSG(1, "%s attempts to activate Rush.\n", status_description(src).c_str());
×
3393
    perform_targetted_allied_fast<Skill::rush>(fd, src, s);
×
3394
    src->m_rush_attempted = true;
×
3395
}
3396

3397
    template<Skill::Skill skill_id>
3398
void perform_targetted_hostile_fast(Field* fd, CardStatus* src, const SkillSpec& s)
40,677,244✔
3399
{
3400
    select_targets<skill_id>(fd, src, s);
40,677,244✔
3401
    std::vector<CardStatus *> paybackers;
40,677,244✔
3402
#ifndef NQUEST
3403
    bool has_counted_quest = false;
3404
#endif
3405
    const bool has_turningtides = (fd->bg_effects[fd->tapi][PassiveBGE::turningtides] && (skill_id == Skill::weaken || skill_id == Skill::sunder));
40,677,244✔
3406
    unsigned turningtides_value(0), old_attack(0);
40,677,244✔
3407

3408
    // apply skill to each target(dst)
3409
    unsigned selection_array_len = fd->selection_array.size();
40,677,244✔
3410
    std::vector<CardStatus*> selection_array = fd->selection_array;
40,677,244✔
3411
    for (CardStatus * dst: selection_array)
92,693,470✔
3412
    {
3413
        // TurningTides
3414
        if (__builtin_expect(has_turningtides, false))
12,825,279✔
3415
        {
3416
            old_attack = dst->attack_power();
11,191✔
3417
        }
3418

3419
        // check & apply skill to target(dst)
3420
        if (check_and_perform_skill<skill_id>(fd, src, dst, s, ! (src->m_overloaded || (__builtin_expect(fd->fixes[Fix::dont_evade_mimic_selection],true) &&  skill_id == Skill::mimic))
52,356,352✔
3421
#ifndef NQUEST
3422
                    , has_counted_quest
3423
#endif
3424
                    ))
3425
        {
3426
            // TurningTides: get max attack decreasing
3427
            if (__builtin_expect(has_turningtides, false))
8,736,621✔
3428
            {
3429
                turningtides_value = std::max(turningtides_value, safe_minus(old_attack, dst->attack_power()));
25,666✔
3430
            }
3431

3432
            // Payback/Revenge: collect paybackers/revengers
3433
            unsigned payback_value = dst->skill(Skill::payback) + dst->skill(Skill::revenge);
37,364,505✔
3434
            if ((s.id != Skill::mimic) && (dst->m_paybacked < payback_value) && skill_check<Skill::payback>(fd, dst, src))
37,364,505✔
3435
            {
3436
                paybackers.reserve(selection_array_len);
987,928✔
3437
                paybackers.push_back(dst);
987,928✔
3438
            }
3439
        }
3440
    }
3441

3442
    // apply TurningTides
3443
    if (__builtin_expect(has_turningtides, false) && (turningtides_value > 0))
10,459,080✔
3444
    {
3445
        SkillSpec ss_rally{Skill::rally, turningtides_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, s.all, 0,};
8,378✔
3446
        _DEBUG_MSG(1, "TurningTides %u!\n", turningtides_value);
16,756✔
3447
        perform_targetted_allied_fast<Skill::rally>(fd, &fd->players[src->m_player]->commander, ss_rally);
8,378✔
3448
    }
3449

3450
    prepend_on_death(fd);  // skills
40,677,244✔
3451

3452
    // Payback/Revenge
3453
    for (CardStatus * pb_status: paybackers)
41,665,172✔
3454
    {
3455
        turningtides_value = 0;
987,928✔
3456

3457
        // apply Revenge
3458
        if (pb_status->skill(Skill::revenge))
987,928✔
3459
        {
3460
            unsigned revenged_count(0);
3461
            for (unsigned case_index(0); case_index < 3; ++ case_index)
3,949,652✔
3462
            {
3463
                CardStatus * target_status;
3464
#ifndef NDEBUG
3465
                const char * target_desc;
3466
#endif
3467
                switch (case_index)
2,962,239✔
3468
                {
3469
                    // revenge to left
3470
                    case 0:
987,413✔
3471
                        if (!(target_status = fd->left_assault(src))) { continue; }
242,864✔
3472
#ifndef NDEBUG
3473
                        target_desc = "left";
3474
#endif
3475
                        break;
3476

3477
                        // revenge to core
3478
                    case 1:
3479
                        target_status = src;
3480
#ifndef NDEBUG
3481
                        target_desc = "core";
3482
#endif
3483
                        break;
3484

3485
                        // revenge to right
3486
                    case 2:
987,413✔
3487
                        if (!(target_status = fd->right_assault(src))) { continue; }
2,332,980✔
3488
#ifndef NDEBUG
3489
                        target_desc = "right";
3490
#endif
3491
                        break;
3492

3493
                        // wtf?
3494
                    default:
3495
                        __builtin_unreachable();
3496
                }
3497

3498
                // skip illegal target
3499
                if (!skill_predicate<skill_id>(fd, target_status, target_status, s))
2,474,492✔
3500
                {
3501
                    continue;
153,905✔
3502
                }
3503

3504
                // skip dead target
3505
                if (!is_alive(target_status))
2,320,587✔
3506
                {
3507
#ifndef NDEBUG
3508
                    _DEBUG_MSG(1, "(CANCELLED: target unit dead) %s Revenge (to %s) %s on %s\n",
×
3509
                            status_description(pb_status).c_str(), target_desc,
3510
                            skill_short_description(fd->cards, s).c_str(), status_description(target_status).c_str());
3511
#endif
3512
                    continue;
×
3513
                }
×
3514

3515
                // TurningTides
3516
                if (__builtin_expect(has_turningtides, false))
78,096✔
3517
                {
3518
                    old_attack = target_status->attack_power();
815✔
3519
                }
3520

3521
                // apply revenged skill
3522
                _DEBUG_STRAP(
2,320,587✔
3523
                    "turn", fd->turn,
3524
                    "active_player", fd->tapi,
3525
                    "revenger_"+strap_string(pb_status), 1.0,
3526
                    "revenged_target_"+strap_string(target_status), 1.0,
3527
                    "revenged_skill_"+skill_names[skill_id], 1.0,
3528
                    "revenged_skill_value", s.x
3529
                );
3530
                _DEBUG_MSG(1, "%s Revenge (to %s) %s on %s\n",
2,505,763✔
3531
                        status_description(pb_status).c_str(), target_desc,
3532
                        skill_short_description(fd->cards, s).c_str(), status_description(target_status).c_str());
3533
                perform_skill<skill_id>(fd, pb_status, target_status, s);
2,320,587✔
3534
                ++ revenged_count;
2,320,587✔
3535

3536
                // revenged TurningTides: get max attack decreasing
3537
                if (__builtin_expect(has_turningtides, false))
78,096✔
3538
                {
3539
                    turningtides_value = std::max(turningtides_value, safe_minus(old_attack, target_status->attack_power()));
2,123✔
3540
                }
3541
            }
3542
            if (revenged_count)
987,413✔
3543
            {
3544
                // consume remaining payback/revenge
3545
                ++ pb_status->m_paybacked;
966,179✔
3546

3547
                // apply TurningTides
3548
                if (__builtin_expect(has_turningtides, false) && (turningtides_value > 0))
41,373✔
3549
                {
3550
                    SkillSpec ss_rally{Skill::rally, turningtides_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, false, 0,};
426✔
3551
                    _DEBUG_MSG(1, "Paybacked TurningTides %u!\n", turningtides_value);
852✔
3552
                    perform_targetted_allied_fast<Skill::rally>(fd, &fd->players[pb_status->m_player]->commander, ss_rally);
426✔
3553
                }
3554
            }
3555
        }
3556
        // apply Payback
3557
        else
3558
        {
3559
            // skip illegal target(src)
3560
            if (!skill_predicate<skill_id>(fd, src, src, s))
515✔
3561
            {
3562
                continue;
171✔
3563
            }
3564

3565
            // skip dead target(src)
3566
            if (!is_alive(src))
344✔
3567
            {
3568
                _DEBUG_MSG(1, "(CANCELLED: src unit dead) %s Payback %s on %s\n",
×
3569
                        status_description(pb_status).c_str(), skill_short_description(fd->cards, s).c_str(),
3570
                        status_description(src).c_str());
3571
                continue;
×
3572
            }
×
3573

3574
            // TurningTides
3575
            if (__builtin_expect(has_turningtides, false))
99✔
3576
            {
3577
                old_attack = src->attack_power();
×
3578
            }
3579

3580
            // apply paybacked skill
3581
            _DEBUG_STRAP(
344✔
3582
                "turn", fd->turn,
3583
                "active_player", fd->tapi,
3584
                "paybacker_"+strap_string(pb_status), 1.0,
3585
                "paybacked_target_"+strap_string(src), 1.0,
3586
                "paybacked_skill_"+skill_names[skill_id], 1.0,
3587
                "paybacked_skill_value", s.x
3588
            );
3589
            _DEBUG_MSG(1, "%s Payback %s on %s\n",
1,032✔
3590
                    status_description(pb_status).c_str(), skill_short_description(fd->cards, s).c_str(), status_description(src).c_str());
3591
            perform_skill<skill_id>(fd, pb_status, src, s);
344✔
3592
            ++ pb_status->m_paybacked;
344✔
3593

3594
            // handle paybacked TurningTides
3595
            if (__builtin_expect(has_turningtides, false))
99✔
3596
            {
3597
                turningtides_value = std::max(turningtides_value, safe_minus(old_attack, src->attack_power()));
×
3598
                if (turningtides_value > 0)
×
3599
                {
3600
                    SkillSpec ss_rally{Skill::rally, turningtides_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, false, 0,};
×
3601
                    _DEBUG_MSG(1, "Paybacked TurningTides %u!\n", turningtides_value);
×
3602
                    perform_targetted_allied_fast<Skill::rally>(fd, &fd->players[pb_status->m_player]->commander, ss_rally);
×
3603
                }
3604
            }
3605
        }
3606
    }
3607

3608
    prepend_on_death(fd,true);  // paybacked skills
40,677,244✔
3609
}
40,677,244✔
3610

3611
//------------------------------------------------------------------------------
3612
inline unsigned evaluate_brawl_score(Field* fd, unsigned player)
×
3613
{
3614
    const auto & p = fd->players;
×
3615
    return 55
×
3616
        // - (10 - p[player]->deck->cards.size())
3617
        // + (10 - p[opponent(player)]->deck->cards.size())
3618
        + p[opponent(player)]->total_cards_destroyed
×
3619
        + p[player]->deck->shuffled_cards.size()
×
3620
        - (unsigned)((fd->turn+7)/8);
×
3621
}
3622

3623
inline unsigned evaluate_war_score(Field* fd, unsigned player)
×
3624
{
3625
    return 208 - ((unsigned)(fd->turn)/2)*4;
×
3626
}
3627
int evaluate_card(Field* fd,const Card* cs);
3628
int evaluate_skill(Field* fd,const Card* c , SkillSpec* ss)
×
3629
{
3630
        // TODO optimize this
3631
        int tvalue = ss->x;
×
3632

3633
        if(ss->card_id != 0)tvalue += 1*evaluate_card(fd,card_by_id_safe(fd->cards,ss->card_id));
×
3634
        tvalue += 10*(ss->id==Skill::flurry);
×
3635
        tvalue += 10*(ss->id==Skill::jam);
×
3636
        tvalue += 5*(ss->id==Skill::overload);
×
3637
        tvalue += 2*(ss->id==Skill::flying);
×
3638
        tvalue += 2*(ss->id==Skill::evolve);
×
3639
        tvalue += 2*(ss->id==Skill::wall);
×
3640
        tvalue += 5*(ss->id==Skill::tribute);
×
3641

3642
        tvalue *= 1.+1.5*(ss->id==Skill::flurry);
×
3643
        tvalue *= 1.+1.5*(ss->id==Skill::drain);
×
3644
        tvalue *= 1.+1.5*(ss->id==Skill::mortar);
×
3645
        tvalue *= 1.+1.5*(ss->id==Skill::scavenge);
×
3646
        tvalue *= 1.+1.5*(ss->id==Skill::disease);
×
3647

3648
        tvalue *= 1.+1.3*(ss->id==Skill::rally);
×
3649
        tvalue *= 1.+1.3*(ss->id==Skill::strike);
×
3650

3651
        tvalue *= 1.+1.2*(ss->id==Skill::avenge);
×
3652
        tvalue *= 1.+1.1*(ss->id==Skill::sunder);
×
3653
        tvalue *= 1.+1.1*(ss->id==Skill::venom);
×
3654

3655
        tvalue *= 1.+1.0*(ss->id==Skill::evade);
×
3656
        tvalue *= 1.+1.0*(ss->id==Skill::enfeeble);
×
3657

3658
        tvalue *= 1.+0.2*(ss->id==Skill::protect);
×
3659

3660
        tvalue *= 1.+0.2*(ss->id==Skill::fortify);
×
3661
        tvalue *= 1.+0.5*(ss->id==Skill::mend);
×
3662

3663
        tvalue *= 1.+0.4*(ss->id==Skill::jam);
×
3664
        tvalue *= 1.+0.4*(ss->id==Skill::overload);
×
3665
        //tvalue *= 1.+0.4*(ss->id==Skill::rupture);
3666
        tvalue *= 1.+0.4*(ss->id==Skill::bravery);
×
3667
        tvalue *= 1.+0.4*(ss->id==Skill::entrap);
×
3668
        tvalue *= 1.+0.4*(ss->id==Skill::heal);
×
3669

3670

3671
        tvalue *= 1.+0.3*(ss->id==Skill::revenge);
×
3672
        tvalue *= 1.+0.3*(ss->id==Skill::enrage);
×
3673

3674

3675
        //tvalue *= 1.+2.1*(ss->id==Skill::hunt);
3676
        tvalue *= 1.+0.1*(ss->id==Skill::mark);
×
3677
        tvalue *= 1.+0.1*(ss->id==Skill::coalition);
×
3678
        tvalue *= 1.+0.1*(ss->id==Skill::legion);
×
3679
        //tvalue *= 1.+1.1*(ss->id==Skill::barrier);
3680
        tvalue *= 1.+0.1*(ss->id==Skill::pierce);
×
3681
        tvalue *= 1.+0.1*(ss->id==Skill::armor);
×
3682
        //tvalue *= 1.+0.1*(ss->id==Skill::swipe);
3683
        //tvalue *= 1.+0.1*(ss->id==Skill::berserk);
3684
        tvalue *= 1.-0.1*(ss->id==Skill::weaken);
×
3685

3686

3687

3688
        tvalue *= 1.-0.5 *(ss->id==Skill::sabotage); //sucks
×
3689
        tvalue *= 1.-0.5 *(ss->id==Skill::inhibit); //sucks
×
3690
        tvalue *= 1.-0.5 *(ss->id==Skill::corrosive); //sucks
×
3691
        tvalue *= 1.-0.5 *(ss->id==Skill::payback); //sucks
×
3692
        tvalue *= 1.-0.5 *(ss->id==Skill::leech); //sucks
×
3693

3694

3695
        tvalue *= 1.+1*ss->all;
×
3696
        tvalue *= 1.-1./5.*ss->all*(ss->y!=0);
×
3697
        tvalue *= 1.+1*std::min<int>(3,ss->n);
×
3698
        tvalue *= 1.-1./3.* ((c->m_skill_trigger[ss->id] == Skill::Trigger::death) + (c->m_skill_trigger[ss->id] == Skill::Trigger::play));
×
3699
        tvalue *= 1./(2.+ss->c);
×
3700
        //if(tvalue == 0) std::cout << ss->id << " "<<tvalue << std::endl;
3701
        //if(tvalue > 10000) std::cout << ss->id <<" "<< tvalue << std::endl;
3702
        return 0.9*tvalue; // 0.85
×
3703
}
3704
int evaluate_card(Field* fd,const Card* cs)
×
3705
{
3706
        int value = 0;
×
3707
        value += cs->m_health;
×
3708
        value += 2*cs->m_attack;
×
3709
        for( auto ss : cs->m_skills) {
×
3710
                value += evaluate_skill(fd,cs,&ss);
×
3711
        }
3712
        int denom_scale = 1+cs->m_delay*0;
×
3713
        //if(value > 10000) std::cout << cs->m_name << value << std::endl;
3714
        return value /denom_scale;
×
3715
}
3716
int evaluate_cardstatus(Field* fd,CardStatus* cs)
×
3717
{
3718
        int value = 0;
×
3719
        value += cs->m_hp;
×
3720
        value += 2*cs->attack_power();
×
3721
        value += cs->protected_value();
×
3722
        for( auto ss : cs->m_card->m_skills) {
×
3723
                value += evaluate_skill(fd,cs->m_card,&ss);
×
3724
        }
3725
        value -= (cs->m_enfeebled);
×
3726
        int denom_scale = 1+cs->m_delay*0;
×
3727
#ifdef DEBUG
3728
        if(value > 10000) std::cout << cs->m_card->m_name << value <<std::endl;
3729
#endif
3730
        return value /denom_scale;
×
3731
}
3732
// calculate a value for current field, high values are better for fd->tap
3733
// dead commander -> the player gets zero value
3734
int evaluate_field(Field* fd)
×
3735
{
3736
        int value = 0;
×
3737

3738
        int scale = is_alive(&fd->tap->commander);
×
3739
        auto& assaults(fd->tap->assaults);
×
3740
        auto& structures(fd->tap->structures);
×
3741

3742

3743
        value += 0.5*scale * evaluate_cardstatus(fd,&fd->tap->commander);
×
3744
        for(unsigned index(0); index < assaults.size();++index)
×
3745
        {
3746
                value += scale * evaluate_cardstatus(fd,&assaults[index]);
×
3747
        }
3748
        for(unsigned index(0); index < structures.size();++index)
×
3749
        {
3750
                value += scale * evaluate_cardstatus(fd,&structures[index]);
×
3751
        }
3752

3753
        scale = is_alive(&fd->tip->commander);
×
3754
        auto& eassaults(fd->tip->assaults);
×
3755
        auto& estructures(fd->tip->structures);
×
3756
        value -= 0.5*scale * evaluate_cardstatus(fd,&fd->tip->commander);
×
3757
        for(unsigned index(0); index < eassaults.size();++index)
×
3758
        {
3759
                value -= (scale * evaluate_cardstatus(fd,&eassaults[index]));
×
3760
        }
3761
        for(unsigned index(0); index < estructures.size();++index)
×
3762
        {
3763
                value -= (scale * evaluate_cardstatus(fd,&estructures[index]));
×
3764
        }
3765
        return value;
×
3766
}
3767

3768

3769
Results<uint64_t> evaluate_sim_result(Field* fd, bool single_turn_both)
1,119,500✔
3770
{
3771
    typedef unsigned points_score_type;
1,119,500✔
3772
    const auto & p = fd->players;
1,119,500✔
3773
    unsigned raid_damage = 0;
1,119,500✔
3774
#ifndef NQUEST
3775
    unsigned quest_score = 0;
3776
#endif
3777

3778
    if(single_turn_both)
1,119,500✔
3779
    {
3780
        bool sign = evaluate_field(fd)<0;
×
3781
        unsigned val = evaluate_field(fd) *(1-2*sign);
×
3782
        return {!is_alive(&fd->players[1]->commander),sign,!is_alive(&fd->players[0]->commander),val,1};
×
3783
    }
3784
    switch (fd->optimization_mode)
1,119,500✔
3785
    {
3786
        case OptimizationMode::raid:
×
3787
            raid_damage = 15
×
3788
                + (p[1]->total_nonsummon_cards_destroyed)
×
3789
                - (10 * p[1]->commander.m_hp / p[1]->commander.max_hp());
×
3790
            break;
×
3791
#ifndef NQUEST
3792
        case OptimizationMode::quest:
3793
            if (fd->quest.quest_type == QuestType::card_survival)
3794
            {
3795
                for (const auto & status: p[0]->assaults.m_indirect)
3796
                { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); }
3797
                for (const auto & status: p[0]->structures.m_indirect)
3798
                { fd->quest_counter += (fd->quest.quest_key == status->m_card->m_id); }
3799
                for (const auto & card: p[0]->deck->shuffled_cards)
3800
                { fd->quest_counter += (fd->quest.quest_key == card->m_id); }
3801
            }
3802
            quest_score = fd->quest.must_fulfill ? (fd->quest_counter >= fd->quest.quest_value ? fd->quest.quest_score : 0) : std::min<unsigned>(fd->quest.quest_score, fd->quest.quest_score * fd->quest_counter / fd->quest.quest_value);
3803
            _DEBUG_MSG(1, "Quest: %u / %u = %u%%.\n", fd->quest_counter, fd->quest.quest_value, quest_score);
3804
            break;
3805
#endif
3806
        default:
3807
            break;
3808
    }
3809
    // you lose
3810
    if(!is_alive(&fd->players[0]->commander))
1,119,500✔
3811
    {
3812
        _DEBUG_MSG(1, "You lose.\n");
163,194✔
3813
        switch (fd->optimization_mode)
163,194✔
3814
        {
3815
            case OptimizationMode::raid: return {0, 0, 1, (points_score_type)raid_damage,1};
×
3816
            case OptimizationMode::brawl: return {0, 0, 1, (points_score_type) 5,1};
×
3817
            case OptimizationMode::brawl_defense:
×
3818
                                          {
×
3819
                                              unsigned enemy_brawl_score = evaluate_brawl_score(fd, 1);
×
3820
                                              unsigned max_score = max_possible_score[(size_t)OptimizationMode::brawl_defense];
×
3821
                                              if(enemy_brawl_score> max_score)
×
3822
                                                std::cerr << "WARNING: enemy_brawl_score > max_possible_brawl_score" << std::endl;
×
3823
                                              return {0, 0, 1, (points_score_type)safe_minus(max_score , enemy_brawl_score),1};
×
3824
                                          }
3825
            case OptimizationMode::war: return {0,0,1, (points_score_type) 20,1};
×
3826
            case OptimizationMode::war_defense:
×
3827
                                        {
×
3828
                                            unsigned enemy_war_score = evaluate_war_score(fd, 1);
×
3829
                                            unsigned max_score = max_possible_score[(size_t)OptimizationMode::war_defense];
×
3830
                                            if(enemy_war_score> max_score)
×
3831
                                                std::cerr << "WARNING: enemy_war_score > max_possible_war_score" << std::endl;
×
3832
                                            return {0, 0, 1, (points_score_type)safe_minus(max_score , enemy_war_score),1};
×
3833
                                        }
3834
#ifndef NQUEST
3835
            case OptimizationMode::quest: return {0, 0, 1, (points_score_type)(fd->quest.must_win ? 0 : quest_score),1};
3836
#endif
3837
            default: return {0, 0, 1, 0,1};
163,194✔
3838
        }
3839
    }
3840
    // you win
3841
    if(!is_alive(&fd->players[1]->commander))
956,306✔
3842
    {
3843
        _DEBUG_MSG(1, "You win.\n");
935,611✔
3844
        switch (fd->optimization_mode)
935,611✔
3845
        {
3846
            case OptimizationMode::brawl:
×
3847
                {
×
3848
                    unsigned brawl_score = evaluate_brawl_score(fd, 0);
×
3849
                    return {1, 0, 0, (points_score_type)brawl_score,1};
×
3850
                }
3851
            case OptimizationMode::brawl_defense:
×
3852
                {
×
3853
                    unsigned max_score = max_possible_score[(size_t)OptimizationMode::brawl_defense];
×
3854
                    unsigned min_score = min_possible_score[(size_t)OptimizationMode::brawl_defense];
×
3855
                    return {1, 0, 0, (points_score_type)(max_score - min_score),1};
×
3856
                }
3857
            case OptimizationMode::campaign:
×
3858
                {
×
3859
                    unsigned total_dominions_destroyed = (p[0]->deck->alpha_dominion != nullptr) - p[0]->structures.count(is_it_dominion);
×
3860
                    unsigned campaign_score = 100 - 10 * (p[0]->total_nonsummon_cards_destroyed - total_dominions_destroyed);
×
3861
                    return {1, 0, 0, (points_score_type)campaign_score,1};
×
3862
                }
3863
            case OptimizationMode::war:
×
3864
                {
×
3865
                    unsigned war_score = evaluate_war_score(fd, 0);
×
3866
                    return {1,0,0, (points_score_type) war_score,1};
×
3867
                }
3868
            case OptimizationMode::war_defense:
×
3869
                {
×
3870
                    unsigned max_score = max_possible_score[(size_t)OptimizationMode::war_defense];
×
3871
                    unsigned min_score = min_possible_score[(size_t)OptimizationMode::war_defense];
×
3872
                    return {1, 0, 0, (points_score_type)(max_score - min_score),1};
×
3873
                }
3874
#ifndef NQUEST
3875
            case OptimizationMode::quest: return {1, 0, 0, (points_score_type)(fd->quest.win_score + quest_score),1};
3876
#endif
3877
            default:
935,611✔
3878
                                          return {1, 0, 0, 100,1};
935,611✔
3879
        }
3880
    }
3881
    if (fd->turn > turn_limit)
20,695✔
3882
    {
3883
        _DEBUG_MSG(1, "Stall after %u turns.\n", turn_limit);
20,695✔
3884
        switch (fd->optimization_mode)
20,695✔
3885
        {
3886
            case OptimizationMode::defense: return {0, 1, 0, 100,1};
×
3887
            case OptimizationMode::raid: return {0, 1, 0, (points_score_type)raid_damage,1};
×
3888
            case OptimizationMode::brawl: return {0, 1, 0, 5,1};
×
3889
            case OptimizationMode::brawl_defense:
×
3890
                                          {
×
3891
                                              unsigned max_score = max_possible_score[(size_t)OptimizationMode::brawl_defense];
×
3892
                                              unsigned min_score = min_possible_score[(size_t)OptimizationMode::brawl_defense];
×
3893
                                              return {1, 0, 0, (points_score_type)(max_score - min_score),1};
×
3894
                                          }
3895
            case OptimizationMode::war: return {0,1,0, (points_score_type) 20,1};
×
3896
            case OptimizationMode::war_defense:
×
3897
                                        {
×
3898
                                            unsigned max_score = max_possible_score[(size_t)OptimizationMode::war_defense];
×
3899
                                            unsigned min_score = min_possible_score[(size_t)OptimizationMode::war_defense];
×
3900
                                            return {1, 0, 0, (points_score_type)(max_score - min_score),1};
×
3901
                                        }
3902
#ifndef NQUEST
3903
            case OptimizationMode::quest: return {0, 1, 0, (points_score_type)(fd->quest.must_win ? 0 : quest_score),1};
3904
#endif
3905
            default: return {0, 1, 0, 0,1};
20,695✔
3906
        }
3907
    }
3908

3909
    // Huh? How did we get here?
3910
    assert(false);
×
3911
    return {0, 0, 0, 0,1};
3912
}
3913

3914
//------------------------------------------------------------------------------
3915
//turns_both sets the number of turns to sim before exiting before winner exists.
3916
Results<uint64_t> play(Field* fd,bool skip_init, bool skip_preplay,unsigned turns_both)
1,119,500✔
3917
{
3918
    if(!skip_init){ //>>> start skip init
1,119,500✔
3919
        fd->players[0]->commander.m_player = 0;
1,119,500✔
3920
        fd->players[1]->commander.m_player = 1;
1,119,500✔
3921
        fd->tapi = fd->gamemode == surge ? 1 : 0;
1,119,500✔
3922
        fd->tipi = opponent(fd->tapi);
1,119,500✔
3923
        fd->tap = fd->players[fd->tapi];
1,119,500✔
3924
        fd->tip = fd->players[fd->tipi];
1,119,500✔
3925
        fd->end = false;
1,119,500✔
3926

3927
        // Play dominion & fortresses
3928
        for (unsigned _(0), ai(fd->tapi); _ < 2; ++_)
3,358,500✔
3929
        {
3930
            if (fd->players[ai]->deck->alpha_dominion)
2,239,000✔
3931
            { PlayCard(fd->players[ai]->deck->alpha_dominion, fd, ai, &fd->players[ai]->commander).op<CardType::structure>(); }
1,236,543✔
3932
            for (const Card* played_card: fd->players[ai]->deck->shuffled_forts)
4,478,000✔
3933
            {
3934

3935
                switch (played_card->m_type)
×
3936
                {
3937
                    case CardType::assault:
×
3938
                        PlayCard(played_card, fd, ai, &fd->players[ai]->commander).op<CardType::assault>();
×
3939
                        break;
×
3940
                    case CardType::structure:
×
3941
                        PlayCard(played_card, fd, ai, &fd->players[ai]->commander).op<CardType::structure>();
×
3942
                        break;
×
3943
                    case CardType::commander:
×
3944
                    case CardType::num_cardtypes:
×
3945
                        _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n",
×
3946
                                played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type);
×
3947
                        assert(false);
×
3948
                        break;
3949
                }
3950
            }
3951
            resolve_skill(fd);
2,239,000✔
3952
            std::swap(fd->tapi, fd->tipi);
2,239,000✔
3953
            std::swap(fd->tap, fd->tip);
2,239,000✔
3954
            ai = opponent(ai);
2,239,000✔
3955
        }
3956
    }//>>> end skip init
3957
    unsigned both_turn_limit = fd->turn+2*turns_both;
1,119,500✔
3958
    while(__builtin_expect(fd->turn <= turn_limit && !fd->end && (turns_both==0 || fd->turn < both_turn_limit), true))
15,722,630✔
3959
    {
3960
        if(!skip_preplay){ //>>> start skip init
15,701,935✔
3961

3962
            fd->current_phase = Field::playcard_phase;
15,701,935✔
3963
            // Initialize stuff, remove dead cards
3964
            _DEBUG_MSG(1, "------------------------------------------------------------------------\n"
15,701,935✔
3965
                    "TURN %u begins for %s\n", fd->turn, status_description(&fd->tap->commander).c_str());
15,701,935✔
3966

3967
            // reduce timers & perform triggered skills (like Summon)
3968
            fd->prepare_action();
15,701,935✔
3969
            turn_start_phase(fd); // summon may postpone skills to be resolved
15,701,935✔
3970
            resolve_skill(fd); // resolve postponed skills recursively
15,701,935✔
3971
            fd->finalize_action();
15,701,935✔
3972

3973
            //bool bge_megamorphosis = fd->bg_effects[fd->tapi][PassiveBGE::megamorphosis];
3974

3975
        }//>>> end skip init
3976
        else { skip_preplay = false;}
3977
        // Play a card
3978
        const Card* played_card(fd->tap->deck->next(fd));
15,701,935✔
3979
        if (played_card)
15,701,935✔
3980
        {
3981

3982
            // Begin 'Play Card' phase action
3983
            fd->prepare_action();
13,879,638✔
3984

3985
            // Play selected card
3986
            //CardStatus* played_status = nullptr;
3987
            switch (played_card->m_type)
13,879,638✔
3988
            {
3989
                case CardType::assault:
13,213,758✔
3990
                    PlayCard(played_card, fd, fd->tapi, &fd->tap->commander).op<CardType::assault>();
13,213,758✔
3991
                    break;
13,213,758✔
3992
                case CardType::structure:
665,880✔
3993
                    PlayCard(played_card, fd, fd->tapi, &fd->tap->commander).op<CardType::structure>();
665,880✔
3994
                    break;
665,880✔
3995
                case CardType::commander:
×
3996
                case CardType::num_cardtypes:
×
3997
                    _DEBUG_MSG(0, "Unknown card type: #%u %s: %u\n",
×
3998
                            played_card->m_id, card_description(fd->cards, played_card).c_str(), played_card->m_type);
×
3999
                    assert(false);
×
4000
                    break;
4001
            }
4002
            resolve_skill(fd); // resolve postponed skills recursively
13,879,638✔
4003
            //status_description(played_status)
4004
            //_DEBUG_MSG(3,"Card played: %s", status_description(played_status).c_str());
4005
            // End 'Play Card' phase action
4006
            fd->finalize_action();
13,879,638✔
4007

4008

4009

4010
        }
4011
        if (__builtin_expect(fd->end, false)) { break; }
15,701,935✔
4012

4013
        //-------------------------------------------------
4014
        // Phase: (Later-) Enhance, Inhibit, Sabotage, Disease
4015
        //-------------------------------------------------
4016
        //Skill: Enhance
4017
        //Perform later enhance for commander
4018
        if(!fd->fixes[Fix::enhance_early]) {
15,701,935✔
4019
        check_and_perform_later_enhance(fd,&fd->tap->commander);
×
4020
        auto& structures(fd->tap->structures);
×
4021
        for(unsigned index(0); index < structures.size(); ++index)
×
4022
        {
4023
            CardStatus * status = &structures[index];
×
4024
            //enhance everything else after card was played
4025
            check_and_perform_later_enhance(fd,status);
×
4026
        }
4027
        }
4028
        //Perform Inhibit, Sabotage, Disease
4029
        auto& assaults(fd->tap->assaults);
15,701,935✔
4030
        for(unsigned index(0); index < assaults.size(); ++index)
60,301,535✔
4031
        {
4032
            CardStatus * att_status = &assaults[index];
44,599,600✔
4033
            if(att_status->m_index >= fd->tip->assaults.size())continue; //skip no enemy
44,599,600✔
4034
            auto def_status = &fd->tip->assaults[att_status->m_index];
27,542,833✔
4035
            if(!is_alive(def_status))continue; //skip dead
27,542,833✔
4036

4037
            check_and_perform_inhibit(fd,att_status,def_status);
27,542,640✔
4038
            check_and_perform_sabotage(fd,att_status,def_status);
27,542,640✔
4039
            check_and_perform_disease(fd,att_status,def_status);
27,542,640✔
4040
        }
4041
        //-------------------------------------------------
4042

4043
        // Evaluate Passive BGE Heroism skills
4044
        if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::heroism], false))
15,701,935✔
4045
        {
4046
            for (CardStatus * dst: fd->tap->assaults.m_indirect)
242,960✔
4047
            {
4048
                unsigned bge_value = (dst->skill(Skill::valor) + dst->skill(Skill::bravery)+ 1) / 2;
150,360✔
4049
                if (bge_value <= 0)
150,360✔
4050
                { continue; }
132,911✔
4051
                SkillSpec ss_protect{Skill::protect, bge_value, allfactions, 0, 0, Skill::no_skill, Skill::no_skill, false, 0,};
17,547✔
4052
                if (dst->m_inhibited > 0)
17,547✔
4053
                {
4054
                    _DEBUG_MSG(1, "Heroism: %s on %s but it is inhibited\n",
98✔
4055
                            skill_short_description(fd->cards, ss_protect).c_str(), status_description(dst).c_str());
98✔
4056
                    -- dst->m_inhibited;
98✔
4057

4058
                    // Passive BGE: Divert
4059
                    if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::divert], false))
98✔
4060
                    {
4061
                        SkillSpec diverted_ss = ss_protect;
×
4062
                        diverted_ss.y = allfactions;
×
4063
                        diverted_ss.n = 1;
×
4064
                        diverted_ss.all = false;
×
4065
                        // for (unsigned i = 0; i < num_inhibited; ++ i)
4066
                        {
×
4067
                            select_targets<Skill::protect>(fd, &fd->tip->commander, diverted_ss);
×
4068
                            std::vector<CardStatus*> selection_array = fd->selection_array;
×
4069
                            for (CardStatus * dst: selection_array)
×
4070
                            {
4071
                                if (dst->m_inhibited > 0)
×
4072
                                {
4073
                                    _DEBUG_MSG(1, "Heroism: %s (Diverted) on %s but it is inhibited\n",
×
4074
                                            skill_short_description(fd->cards, diverted_ss).c_str(), status_description(dst).c_str());
×
4075
                                    -- dst->m_inhibited;
×
4076
                                    continue;
×
4077
                                }
×
4078
                                _DEBUG_MSG(1, "Heroism: %s (Diverted) on %s\n",
×
4079
                                        skill_short_description(fd->cards, diverted_ss).c_str(), status_description(dst).c_str());
×
4080
                                perform_skill<Skill::protect>(fd, &fd->tap->commander, dst, diverted_ss);  // XXX: the caster
×
4081
                            }
4082
                        }
×
4083
                    }
4084
                    continue;
98✔
4085
                }
98✔
4086
#ifndef NQUEST
4087
                bool has_counted_quest = false;
4088
#endif
4089
                check_and_perform_skill<Skill::protect>(fd, &fd->tap->commander, dst, ss_protect, false
17,449✔
4090
#ifndef NQUEST
4091
                        , has_counted_quest
4092
#endif
4093
                        );
4094
            }
4095
        }
4096

4097
        // Evaluate activation BGE skills
4098
        fd->current_phase = Field::bge_phase;
15,701,935✔
4099
        for (const auto & bg_skill: fd->bg_skills[fd->tapi])
15,794,977✔
4100
        {
4101
            fd->prepare_action();
93,042✔
4102
            _DEBUG_MSG(2, "Evaluating BG skill %s\n", skill_description(fd->cards, bg_skill).c_str());
93,042✔
4103
            fd->skill_queue.emplace_back(&fd->tap->commander, bg_skill);
93,042✔
4104
            resolve_skill(fd);
93,042✔
4105
            fd->finalize_action();
93,042✔
4106
        }
4107
        if (__builtin_expect(fd->end, false)) { break; }
15,701,935✔
4108

4109
        // Evaluate commander
4110
        fd->current_phase = Field::commander_phase;
15,701,935✔
4111
        evaluate_skills<CardType::commander>(fd, &fd->tap->commander, fd->tap->commander.m_card->m_skills);
15,701,935✔
4112
        if (__builtin_expect(fd->end, false)) { break; }
15,701,935✔
4113

4114
        // Evaluate structures
4115
        fd->current_phase = Field::structures_phase;
15,701,935✔
4116
        for (fd->current_ci = 0; !fd->end && (fd->current_ci < fd->tap->structures.size()); ++fd->current_ci)
37,631,014✔
4117
        {
4118
            CardStatus* current_status(&fd->tap->structures[fd->current_ci]);
21,929,079✔
4119
            if (!is_active(current_status))
21,929,079✔
4120
            {
4121
                _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str());
6,994,515✔
4122
            }
4123
            else
4124
            {
4125
                evaluate_skills<CardType::structure>(fd, current_status, current_status->m_card->m_skills);
14,934,564✔
4126
            }
4127
        }
4128

4129
        // Evaluate assaults
4130
        fd->current_phase = Field::assaults_phase;
15,701,935✔
4131
        fd->bloodlust_value = 0;
15,701,935✔
4132
        for (fd->current_ci = 0; !fd->end && (fd->current_ci < fd->tap->assaults.size()); ++fd->current_ci)
56,025,646✔
4133
        {
4134
            CardStatus* current_status(&fd->tap->assaults[fd->current_ci]);
41,422,516✔
4135
            bool attacked = false;
41,422,516✔
4136
            if (!is_active(current_status))
41,422,516✔
4137
            {
4138
                _DEBUG_MSG(2, "%s cannot take action.\n", status_description(current_status).c_str());
21,049,400✔
4139
                // Passive BGE: HaltedOrders
4140
                /*
4141
                unsigned inhibit_value;
4142
                if (__builtin_expect(fd->bg_effects[fd->tapi][PassiveBGE::haltedorders], false)
4143
                        && (current_status->m_delay > 0) // still frozen
4144
                        && (fd->current_ci < fd->tip->assaults.size()) // across slot isn't empty
4145
                        && is_alive(&fd->tip->assaults[fd->current_ci]) // across assault is alive
4146
                        && ((inhibit_value = current_status->skill(Skill::inhibit))
4147
                            > fd->tip->assaults[fd->current_ci].m_inhibited)) // inhibit/re-inhibit(if higher)
4148
                        {
4149
                            CardStatus* across_status(&fd->tip->assaults[fd->current_ci]);
4150
                            _DEBUG_MSG(1, "Halted Orders: %s inhibits %s by %u\n",
4151
                                    status_description(current_status).c_str(),
4152
                                    status_description(across_status).c_str(), inhibit_value);
4153
                            across_status->m_inhibited = inhibit_value;
4154
                        }
4155
                        */
4156
            }
4157
            else
4158
            {
4159
                if (current_status->m_protected_stasis)
20,373,116✔
4160
                {
4161
                    _DEBUG_MSG(1, "%s loses Stasis protection (activated)\n",
1,740,085✔
4162
                            status_description(current_status).c_str());
4163
                }
4164
                current_status->m_protected_stasis = 0;
20,373,116✔
4165
                fd->assault_bloodlusted = false;
20,373,116✔
4166
                current_status->m_step = CardStep::attacking;
20,373,116✔
4167
                evaluate_skills<CardType::assault>(fd, current_status, current_status->m_card->m_skills, &attacked);
20,373,116✔
4168
                if (__builtin_expect(fd->end, false)) { break; }
20,373,116✔
4169
                if (__builtin_expect(!is_alive(current_status), false)) { continue; }
19,274,311✔
4170
            }
4171

4172
            current_status->m_step = CardStep::attacked;
39,587,705✔
4173
        }
4174
        fd->current_phase = Field::end_phase;
15,701,935✔
4175
        turn_end_phase(fd);
15,701,935✔
4176
        if (__builtin_expect(fd->end, false)) { break; }
15,701,935✔
4177
        _DEBUG_MSG(1, "TURN %u ends for %s\n", fd->turn, status_description(&fd->tap->commander).c_str());
14,603,130✔
4178
        std::swap(fd->tapi, fd->tipi);
14,603,130✔
4179
        std::swap(fd->tap, fd->tip);
14,603,130✔
4180
        ++fd->turn;
14,603,130✔
4181
    }
4182

4183
    return evaluate_sim_result(fd,turns_both!= 0);
1,119,500✔
4184
}
4185

4186
//------------------------------------------------------------------------------
4187
void fill_skill_table()
81✔
4188
{
4189
    memset(skill_table, 0, sizeof skill_table);
81✔
4190
    skill_table[Skill::mortar] = perform_targetted_hostile_fast<Skill::mortar>;
81✔
4191
    skill_table[Skill::enfeeble] = perform_targetted_hostile_fast<Skill::enfeeble>;
81✔
4192
    skill_table[Skill::enhance] = perform_targetted_allied_fast<Skill::enhance>;
81✔
4193
    skill_table[Skill::evolve] = perform_targetted_allied_fast<Skill::evolve>;
81✔
4194
    skill_table[Skill::heal] = perform_targetted_allied_fast<Skill::heal>;
81✔
4195
    skill_table[Skill::jam] = perform_targetted_hostile_fast<Skill::jam>;
81✔
4196
    skill_table[Skill::mend] = perform_targetted_allied_fast<Skill::mend>;
81✔
4197
    skill_table[Skill::fortify] = perform_targetted_allied_fast<Skill::fortify>;
81✔
4198
    skill_table[Skill::overload] = perform_targetted_allied_fast<Skill::overload>;
81✔
4199
    skill_table[Skill::protect] = perform_targetted_allied_fast<Skill::protect>;
81✔
4200
    skill_table[Skill::rally] = perform_targetted_allied_fast<Skill::rally>;
81✔
4201
    skill_table[Skill::enrage] = perform_targetted_allied_fast<Skill::enrage>;
81✔
4202
    skill_table[Skill::entrap] = perform_targetted_allied_fast<Skill::entrap>;
81✔
4203
    skill_table[Skill::rush] = perform_targetted_allied_fast_rush;
81✔
4204
    skill_table[Skill::siege] = perform_targetted_hostile_fast<Skill::siege>;
81✔
4205
    skill_table[Skill::strike] = perform_targetted_hostile_fast<Skill::strike>;
81✔
4206
    skill_table[Skill::sunder] = perform_targetted_hostile_fast<Skill::sunder>;
81✔
4207
    skill_table[Skill::weaken] = perform_targetted_hostile_fast<Skill::weaken>;
81✔
4208
    skill_table[Skill::mimic] = perform_targetted_hostile_fast<Skill::mimic>;
81✔
4209
}
81✔
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