# Gold Miner code review 1) `Utilities` - круто и аккуратно, возьму себе идею 2) `class LockItLinkBonus` слишком громоздкий и непростой. Возможно ли выделить подклассы, которым делегировать логические части (Single responsibility)? Например, `LockItLinkWheelsManager`, `LockItLinkAnimationsManager`, `LockItLinkCfgManager`. Не совсем понятна роль `LockItLinkBonusUtils` : например `StartRollingWheels()`, `OnWheelsStop()` находятся в Bonus, а `ResetBasicAndBonusWheels()`, `InitLockedSymbolsOnWheel()` - в Utils. Почему `LockItLinkBonusUtils` находится за пределами `utilities`? Возможно ли сгрупировать десятки мемберов в логически консистентные структуры? ```cpp= int m_current_block_index = 0; int m_cfg_show_border_delay = 0; int m_cfg_delay_xw_model = 0; int m_cfg_wait_before_delay = 0; int m_cfg_delay_lock_animation = 0; int m_cfg_delta_final_animation = 0; gwc::Time m_cfg_show_light_final = 0; gwc::Time m_cfg_show_scale_delay = 0; double m_cfg_fly_animation_scale_up = 0; double m_cfg_multiplier_move_speed = 0; int m_cfg_multiplier_spawn_delay = 0; int m_cfg_delay_run_finish_animation = 0; gwc::Coord m_cfg_offset_shine_multiplier_x = 0; gwc::Coord m_cfg_offset_shine_multiplier_y = 0; ``` комментарии выглядят лишними ```cpp= { 1, { 2 }}, //case 1 { 2, { 1, 2, 3 }},//case 2 { 3, { 2 }}, //case 3 { 4, { 2 }}, //case 4 { 5, { 1 }}, //case 5 { 6, { 3 }}, //case 6 { 7, { 1, 3}}, //case 7 { 8, { 3 }}, //case 8 { 9, { 1 }}, //case 9 { 10,{ 2 }} //case 10 ``` выигрывают ли эти методы что-то? ```cpp= int GetLockedSymbolsBlocksSize() { return vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetLockedSymbolsBlocks().size(); } int GetRowIndexForOffset() { return vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetIndexRowOffset(); } bool LockItLinkBonus::IsRecoveryGame() const { return vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->IsRecoveryGame(); } ``` особенно учитывая что ниже всё равно вызываются длинные цепочки ```cpp= if (m_show_text_on_crown || m_first_call_bonus) return vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetLockedBlock(); if (m_offset_animation_call_bonus) return vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetLockedSymbols(); const auto& locked_blocks = vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetLockedSymbolsBlocks(); ``` Тернарки: я бы заменил 1 на 2: ```cpp 1) return (extra_games) ? false : true; 2) return extra_games == 0; 1) (m_first_call_bonus) ? GetCurrentSymbolsVector(false) : GetCurrentSymbolsVector(true); 2) GetCurrentSymbolsVector(!m_first_call_bonus) ``` магические константы посреди большого файла, которые могут много геморроя принести после правок спайна - лучше выносить куда-то, хотя бы в анонимный namespace ```cpp= if (track_name == "holdenlink_show") //388 строчка else if (track_name == "holdenlink_hide") //431 строчка ``` `dynamic_cast`ы лучше проверять на успех (`assert ptr != nullptr`) ```cpp= cpp::count_ptr<gui::Wait> wait_before_move = dynamic_cast<gui::Wait*>(vlib::ObjectFac::Instance()->LoadAction(buffer)); wait_before_move->SetDuration(delay_before_move); ``` 3) `count_*` = посчитать что-то (глагол) `*_count` = количество чего-то (существительное) `RepeatAnimation` = повторить (глагол, но используется как сущ) `RepeatedAnimation`, `LoopedAnimation` = зацикленная (существительное) в коде много первого, хотелось бы второе 4) `throw "Gold Miner | BonusEnteringModel::MousePressed. Unexpected button id: " + button->GetIdentity();` - в чём преимущество перед `ENGINE_LOG_ERROR`? Насколько я понял исключение нигде не перехватывается для обработки. Если всё же есть смысл в исключении - наверное лучше бросать объект, а не строку. (как чуть позже `throw std::logic_error("GoldMiner.BasicGame.GetRepeatAnimationName. Unexpected symbol_id: " + std::to_string(symbol_id));`) 5) ` const auto& bang_positions = []() -> BonusBangAnimation::BangPositions` - мгновенный вызов лямбды - неоправданно запутанно как по мне. Обычного блока кода достаточно. 6) `m_counter_view{ nullptr }` `m_bonus_game_selected = false` `m_mult_animation_activated{ true }` - хотелось бы последовательности и единого стиля дефолтов в hpp. Например, `m_bonus_game_selected{ false }` 7) я бы заменил 1 на 2 ```cpp 1) bla = series_animation_duration < 1000 ? 1001 : series_animation_duration; 2) const gwc::Time MIN_DURATION = 1000; bla = std::max(MIN_DURATION, series_animation_duration); ``` 8) не совсем логично выглядят вертикальные отступы (пустые строки) ```cpp= const auto element = series->GetElement(i); auto movie_name = element->GetMovieName(); auto movie = vgui::ResourceManager::Instance()->GetMovie(movie_name); auto duration = movie->GetTotalTime(); tmp = cpp::max(tmp, duration); ``` ```cpp= Notify(GOLD_MINER_HOLD_N_LINK_BONUS.SET_SPIN_CONTINUE); Notify(GOLD_MINER_HOLD_N_LINK_BONUS.START_CARDS_SHINE_ANIMATION); if (m_offset_animation_call_bonus) { m_offset_animation_call_bonus = false; ShowLockedSymbolsOnWheels(); LockItLinkBonusUtils::InitMultTextScattersPosition(true, m_offset_animation_call_bonus, GetRowIndexForOffset()); SetScatterMultiplierFromLockedSymbols(); LockItLinkBonusUtils::InitLockedSymbolsOnPositions(); LockItLinkBonusUtils::ResetLockedSymbols(); LockItLinkBonusUtils::ShowLockedBlockMultipliers(); ... LockItLinkBonusUtils::HideOriginSymbols(locked_symbol.pos.col, locked_symbol.pos.row); const auto& wheel = GetBonusWheelNum(locked_symbol.pos.col, locked_symbol.pos.row); m_wheels->GetWheel(wheel)->HideSymbol(0); ``` 9) хотелось бы поразбивать большие методы типа ```cpp= void SeriesProcessor::UpdateColormodeForNoActiveCells(bool use_normal_mode) { if (!m_abstract_wheels || m_noactive_color_mode.IsEqualRGBA(m_normal_color_mode)) return; const auto wheel_count = m_abstract_wheels->GetWheelCount(); for (int wheel = 0; wheel < wheel_count; ++wheel) { const auto visible_count = m_abstract_wheels->GetWheel(wheel)->GetVisibleCount(); for (int row = -1; row < visible_count; ++row) { if (GameUtilities::IsHoldLinkBonus()) { const auto& fig_id = m_abstract_wheels->GetWheel(wheel)->GetContents(row); if (fig_id >= GameUtilities::GetHoldSymbolIdWithMult()) { const auto& row_index_for_offset = vcore::VCORE_Game::Instance()->Traits()->LoteriaLaCorona()->GetIndexRowOffset(); if (row_index_for_offset != -1) { if (row_index_for_offset == row) continue; } else continue; } } if (use_normal_mode) m_abstract_wheels->GetWheel(wheel)->GetWheelView(row)->SetRGBAMode(m_normal_color_mode); else if (row < 0 || row >= visible_count || !m_overlay_movie_panels[wheel][row]->GetImage()) m_abstract_wheels->GetWheel(wheel)->GetWheelView(row)->SetRGBAMode(m_noactive_color_mode); if (auto* label_with_multiplier = vlib::ObjectFac::Instance()->LoadLabel(fmt::format("scatter.text.W{}#R{}", wheel, row))) { if (use_normal_mode || !m_preprocess_finished) label_with_multiplier->SetRGBAMode(m_normal_color_mode); else label_with_multiplier->SetRGBAMode(m_noactive_color_mode); label_with_multiplier->SetAlpha(255); } } } } ``` что-то в духе ```cpp= void ::UpdateColorMode(use_normal_mode) { if (...) return; for (wheel in wheels) for (row in rows) UpdateColorModeForCell(wheel, row, use_normal) } void ::UpdateColorModeForCell(wheel, row, use_normal) { if (GameUtilities::IsHoldLinkBonus()) UpdateColorModeForCellInHoldLink(wheel, row) UpdateColorModeForView(wheel, row, use_normal) UpdateColorModeForLabel(wheel, row) } void ::UpdateColorModeForView(wheel, row, use_normal) {...} void ::UpdateColorModeForLabel(wheel, row) {...} ``` можно даже подумать чтобы вынести это в отдельный подкласс (Single Responsibility) 10) долго думал над результатом `Vec2-Vec2+Coord*double` - потом понял, что `size` и `SIZE`, а так же `anchor_px` и `ANCHOR` - разные вещи. Не самый очевидный код ```cpp= auto pos = overlay_panel->ConvertAbsPosToParentSpace(overlay_panel->GetAnchorAbsPos()); const auto& anchor_px = overlay_panel->GetAnchorInPixels(); const auto& span = overlay_panel->GetSpan(); const gwc::Coord& SPAN = 480; constexpr auto ANCHOR = 0.5; pos = pos - anchor_px + span * ANCHOR; ``` 11) в разметке непривычно видеть смешанные Panel и Action+Drivers, но тут дело вкуса