/battle/report-stats
Метод принимает отчет о завершенном бое и двигает счетчики objectives, которые известны только из боя.
Адрес метода
/battle/report-stats
Требует сессию пользователя
Обработка идемпотентна по battleId: клиент повторяет отправку отчета при сбое сети, поэтому повторный
отчет с тем же battleId не двигает счетчики, но возвращает актуальное состояние.
battleId должен совпадать с серверной боевой сессией — отчёт с неизвестным или просроченным (дольше 6 часов)
battleId отклоняется с ошибкой по параметру battleId. Режим боя (gameMode) определяется сервером по найденной
сессии, а не берётся из отчёта — значение gameMode в теле запроса игнорируется.
Подходят два вида сессий:
- уровень —
battleId, который вернул /level/start или /level/continue (32 hex-символа). Режим —campaignилиdungeon, по самому уровню; - арена — числовой
battle_id, который вернул /arena/fight/start (или/arena/fight/rebattle). Режим —arena(gameMode = 2). Учитывается только строка атаки самого игрока: зеркальная строка защиты боем не является и отчёта не порождает.
Оба вида сессий выдаются сервером и оплачены игроком (энергией — уровень, попыткой или билетом — арена), поэтому
исключений «режим из отчёта» здесь нет и быть не может: battleReported() двигает все общие счётчики (враги, гемы,
комбо, урон), так что любая лазейка по gameMode открыла бы фарм всех счётчиков разом.
Счетчики, которые сервер знает сам (battles_played, battles_lost, levels_completed,
campaign_levels_completed, dungeon_levels_completed, campaign_max_level_reached,
dungeon_max_level_reached), из отчета не берутся — они приходят из
/level/complete, где режим и результат серверные. Иначе бой посчитался бы дважды.
Аренные счётчики (arena_battles_played, arena_wins, arena_losses, arena_max_ap,
arena_league_reached) из отчёта тоже не берутся: отчёт существует только у боя, доигранного до конца, а
сдача и брошенный бой на арене — это тоже поражения, которые нужно посчитать. Они приходят из
ArenaManager::applyResult() — единственной точки, через которую проходит любая завершённая атака.
Параметры
Тело запроса повторяет клиентский DTO BattleStatsReport. Поля, которых нет в таблице ниже, игнорируются.
| Параметр | Обязательный | Тип | Описание |
|---|---|---|---|
| battleId | ДА | string | Идентификатор боя, по нему обеспечивается идемпотентность |
| gameMode | НЕТ | int | Не используется — режим боя всегда определяется сервером по найденной сессии battleId. Поле оставлено для совместимости с клиентским DTO и полностью игнорируется |
| outcome | НЕТ | string | win или loss. Другие значения отклоняются с ошибкой по параметру outcome |
| minHeroHpDuringBattle | НЕТ | int | Минимальный запас здоровья героя за бой |
| enemiesDefeated | НЕТ | int | Количество побежденных врагов за бой |
| combosActivated | НЕТ | int | Количество активированных комбо |
| longestComboLength | НЕТ | int | Самое длинное комбо за бой |
| damageDealtByChips | НЕТ | int | Урон, нанесенный чипами |
| damageDealtBySkills | НЕТ | int | Урон, нанесенный скиллами |
| maxDamageInSingleHit | НЕТ | int | Максимальный урон одним ударом |
| damageTaken | НЕТ | int | Полученный урон |
| healingDone | НЕТ | int | Восстановленное здоровье |
| chipsMatchedByColor | НЕТ | object | { red, blue, green, yellow, purple } |
| bonusActivations | НЕТ | object | { cat, dog, pepe } |
| damageDealtByElement | НЕТ | object | { poison, fire, bleed } |
| skillsUsage | НЕТ | object | { distinctIdsByColor, distinctIdsByFaction, totalActivations } |
| hero | НЕТ | object | { buffs, debuffs }, у каждого { peakSimultaneous, removedEarly } |
| enemies | НЕТ | object | { buffs, debuffs }, у каждого { peakSimultaneous, removedEarly } |
Счетчики
Накопительные: enemies_defeated, gems_matched_<color>,
combos_activated, <cat|dog|pepe>_bonuses_activated, gem_damage_dealt, skill_damage_dealt,
damage_received, health_restored, <poison|fire|bleed>_damage_dealt, debuffs_removed_from_self,
buffs_removed_from_enemy. При победе — battle_won_no_damage_taken (damageTaken = 0),
battle_won_no_skills (skillsUsage.totalActivations = 0), battle_won_1_hp (minHeroHpDuringBattle = 1),
battle_won_low_hp (minHeroHpDuringBattle < 10).
Счетчики-максимумы: combo_max_length, max_damage_in_single_hit, max_simultaneous_buffs_on_self,
max_simultaneous_debuffs_on_enemy, max_unique_color_skills_in_battle, max_unique_faction_skills_in_battle.
Отсутствующее поле это не ноль
Счетчики условий победы двигаются только если соответствующее поле реально пришло в отчете:
battle_won_no_damage_taken требует damageTaken, battle_won_no_skills —
skillsUsage.totalActivations, battle_won_1_hp и battle_won_low_hp — minHeroHpDuringBattle.
Без этих данных условие не считается выполненным, иначе отчет без них засчитывал бы достижение сам собой.
Числовые значения отчета ограничены сверху разумным пределом: заведомо невозможное значение урезается до предела с записью в лог, а не отклоняет отчет целиком — иначе одно поле стоило бы игроку всех счетчиков за бой.
Результат
{
"response": {
"questsDirty": true,
"achievementsDirty": false,
"unclaimedQuestsCount": 1,
"unclaimedAchievementsCount": 0,
"dailyChestProgress": {
"points": 20,
"claimableChestsCount": 0
},
"pendingCompletions": [
{
"id": "quest_6a71ba248ee7b2.78729389",
"type": "quest",
"templateId": "dq_combos_10",
"tierIndex": 0,
"occurredAt": 1786010916
}
]
},
"update": {
"member": {
"wallet": [],
"options": {}
}
}
}
| Поле | Тип | Описание |
|---|---|---|
| questsDirty | bool | Состояние ежедневных заданий изменилось — клиенту нужно перечитать quests state |
| achievementsDirty | bool | Состояние достижений изменилось — клиенту нужно перечитать achievements state |
| unclaimedQuestsCount | int | Количество ежедневных заданий с доступной наградой |
| unclaimedAchievementsCount | int | Количество достижений с доступной наградой |
| dailyChestProgress | object | Короткое состояние daily chest progress |
| pendingCompletions | array | Очередь pending completions |
При повторном отчете с тем же battleId оба флага dirty будут false.