From aae7fc9602f15d24d2c8184bbbad307339f99a48 Mon Sep 17 00:00:00 2001 From: iBerserker89 Date: Tue, 3 Feb 2026 13:38:36 -0300 Subject: [PATCH] ajustar o spawn de cartas [issue: #114] --- scripts/game/select_upgrades_scene.gd | 143 ++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 9 deletions(-) diff --git a/scripts/game/select_upgrades_scene.gd b/scripts/game/select_upgrades_scene.gd index 8bf8874..89b9db0 100644 --- a/scripts/game/select_upgrades_scene.gd +++ b/scripts/game/select_upgrades_scene.gd @@ -34,11 +34,54 @@ func _on_card_chosen(track: int) -> void: closed.emit(track) -## Popula o container de cartas com uma quantidade específica de upgrades aleatórios. -## Embaralha as opções de upgrades disponíveis e atribui um tipo de upgrade -## a cada carta visível no container, respeitando o limite definido por "count". -func _populate_random(count : int = 3) -> void: - var all_weapons := [ +## Converte o ID da arma (WeaponId) para o "track" correspondente de upgrade (UpgradeTrack). +## Retorna -1 se o weapon_id não for reconhecido (sem track válido / arma inexistente). +func _weapon_id_to_track(weapon_id: int) -> int: + match weapon_id: + PlayerUpgrades.WeaponId.BULLET_1: return PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_1 + PlayerUpgrades.WeaponId.BULLET_2: return PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_2 + PlayerUpgrades.WeaponId.BULLET_3: return PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_3 + PlayerUpgrades.WeaponId.BULLET_4: return PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_4 + _: return -1 + + +## Retorna o nível atual de um track de upgrade consultando os valores armazenados em PlayerUpgrades. +## Para tracks desconhecidos, retorna 0 (trata como "ainda não evoluído"). +func _get_level_for_track(track: int) -> int: + match track: + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_1: return PlayerUpgrades.active_weapon_1_level + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_2: return PlayerUpgrades.active_weapon_2_level + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_3: return PlayerUpgrades.active_weapon_3_level + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_4: return PlayerUpgrades.active_weapon_4_level + PlayerUpgrades.UpgradeTrack.PASSIVE_SHIELD: return PlayerUpgrades.passive_shield_level + PlayerUpgrades.UpgradeTrack.PASSIVE_SPEED: return PlayerUpgrades.passive_speed_level + _: return 0 + + +## Preenche as cartas de upgrade exibidas na UI com picks aleatórios gerados por _build_random_upgrade_picks(). +## Mostra as primeiras N cartas (até "count") e oculta as cartas extras, evitando acessar índices inválidos. +func _populate_random(count: int = 3) -> void: + var cards := upgrade_card_container.get_children() + if cards.is_empty(): + return + var upgrade_picks := _build_random_upgrade_picks(count) + for card in range(cards.size()): + if card < upgrade_picks.size(): + cards[card].visible = true + cards[card].upgrade = upgrade_picks[card] + else: + cards[card].visible = false + + +## Monta uma lista de "tracks" de upgrade para oferecer ao jogador: +## 1) Prioriza incluir a arma atualmente selecionada se ela existir e ainda não estiver no nível MAX. +## 2) Tenta garantir uma "arma nova" (track com nível 0); se não houver, usa fallback para arma não-MAX +## e, se todas as armas estiverem MAX, libera qualquer arma. +## 3) Completa o restante com upgrades aleatórios não-MAX (inclui passivas); se não houver, libera MAX, +## sempre evitando repetir tracks dentro dos picks. +func _build_random_upgrade_picks(count: int = 3) -> Array[int]: + # Lista completa de "tracks" que podem aparecer (armas + passivas). + var all_tracks := [ PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_1, PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_2, PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_3, @@ -46,7 +89,89 @@ func _populate_random(count : int = 3) -> void: PlayerUpgrades.UpgradeTrack.PASSIVE_SHIELD, PlayerUpgrades.UpgradeTrack.PASSIVE_SPEED, ] - all_weapons.shuffle() - var cards := upgrade_card_container.get_children() - for i in range(min(count, cards.size())): - cards[i].upgrade = all_weapons[i] + # Lista apenas das armas ativas (tracks 1..4). + var weapon_tracks := [ + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_1, + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_2, + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_3, + PlayerUpgrades.UpgradeTrack.ACTIVE_WEAPON_4, + ] + # Picks finais (tracks escolhidos). + var picks: Array[int] = [] + # Helper: um track está "MAX" se seu nível atingiu o máximo global. + var is_track_maxed := func(track: int) -> bool: + return _get_level_for_track(track) >= PlayerUpgrades.MAX_LEVEL + # Descobre o track da arma atualmente selecionada. + # Se o player ainda não existir (ou não for válido), fica -1. + var current_weapon_track: int = -1 + if is_instance_valid(Singleton.player): + current_weapon_track = _weapon_id_to_track(Singleton.player.selected_weapon_id) + # Helper: retorna true se TODAS as armas ativas estão MAX. + # Isso define se o código pode "repetir armas maxadas" quando não há alternativa. + var are_all_weapons_maxed := func() -> bool: + for weapon_track in weapon_tracks: + if not is_track_maxed.call(weapon_track): + return false + return true + var can_repeat_maxed_weapons = are_all_weapons_maxed.call() + + # Upgrade da arma atual, se existir e não estiver MAX + if picks.size() < count and current_weapon_track != -1 and not is_track_maxed.call(current_weapon_track): + picks.append(current_weapon_track) + + # Tentar oferecer arma nova (nível == 0) + # - Tenta arma nova (nível 0), exceto a arma atual. + # - Se não houver nova e ainda existir arma não-MAX, pega qualquer arma não-MAX (exceto a atual). + # - Se todas as armas estiverem MAX, libera qualquer arma (exceto a atual). + if picks.size() < count: + var weapon_candidates: Array[int] = [] + # Arma nova (level == 0), sem repetir a atual. + for weapon_track in weapon_tracks: + if weapon_track == current_weapon_track: + continue + if _get_level_for_track(weapon_track) == 0: + if can_repeat_maxed_weapons or not is_track_maxed.call(weapon_track): + weapon_candidates.append(weapon_track) + # Fallback: qualquer arma não-MAX (sem repetir a atual), + # - Apenas se ainda nao estiver no estado "todas as armas MAX". + if weapon_candidates.is_empty() and not can_repeat_maxed_weapons: + for weapon_track in weapon_tracks: + if weapon_track == current_weapon_track: + continue + if not is_track_maxed.call(weapon_track): + weapon_candidates.append(weapon_track) + # Se todas as armas estiverem MAX, libera qualquer arma (exceto a atual). + if weapon_candidates.is_empty() and can_repeat_maxed_weapons: + for weapon_track in weapon_tracks: + if weapon_track == current_weapon_track: + continue + weapon_candidates.append(weapon_track) + # Escolhe aleatoriamente 1 candidato e adiciona (sem duplicar). + if not weapon_candidates.is_empty(): + weapon_candidates.shuffle() + var chosen_weapon_track := weapon_candidates[0] + if not picks.has(chosen_weapon_track): + picks.append(chosen_weapon_track) + + # Completa o restante aleatoriamente + # - Primeiro tenta apenas tracks não-MAX (armas + passivas), sem repetir picks. + # - Se não existir mais nenhum não-MAX, libera qualquer track restante (mesmo MAX), sem repetir. + if picks.size() < count: + var pool: Array[int] = [] + # Pool com tracks não-MAX. + for track in all_tracks: + if picks.has(track): + continue + if not is_track_maxed.call(track): + pool.append(track) + # Se acabou, libera qualquer track restante. + if pool.is_empty(): + for track in all_tracks: + if not picks.has(track): + pool.append(track) + # Embaralha e vai puxando os cards até completar. + pool.shuffle() + while picks.size() < count and not pool.is_empty(): + picks.append(pool.pop_front()) + + return picks