Cangaceiros — Matchmaker Completo📝 Editar Jogador
Edite as informações do jogador. As alterações refletem automaticamente no balanceamento e na tabela de elos.
Observação: Os elos são calculados automaticamente com base nas vitórias/derrotas.
Gerenciar Duos
Registre duos para que fiquem no mesmo time durante o balanceamento (máx 1 duo por time)
Adicionar Novo Duo
⚠️ Cada jogador só pode estar em um duo. O sistema manterá balanceamento de elos.
Balanceamento & Partidas
Registrar Partida
Votação de Bagre (Time Perdedor)
Votação feita pelo time que perdeu. Bagre perde 1 ponto. 3x seguidos = -3 pontos.
🏆 Votação em Tempo Real
Veja quem está liderando as votações:
Atualiza automaticamente a cada 10 segundos
Logs
Pronto...
Como Usar
1. Preencha os nicks dos 10 jogadores
2. As lanes e elos serão carregados automaticamente
3. Clique em "Balancear Times"
4. Os times serão formados automaticamente
* Apenas administradores podem iniciar e salvar partidas
* Os elos são calculados automaticamente com base nas vitórias/derrotas
Carregando tabela de elos...
Alterar Avatar
`);
win.document.close();
}/* ====== ESTATÍSTICAS ====== */async function updateStats() {
try {
const playersSnap = await getDocs(collection(db, "players"));
const matchesSnap = await getDocs(collection(db, "matches"));
const players = [];
playersSnap.forEach(d => players.push(d.data()));
const matches = [];
matchesSnap.forEach(d => matches.push(d.data()));
window.allMatches = matches;
if (typeof calculatePlayerLaneStats === 'function') {
calculatePlayerLaneStats(matches, players);
} else {
console.error("calculatePlayerLaneStats não está definida");
}
const totalPlayers = players.length;
const totalMatches = matches.length;
const totalPoints = players.reduce((sum, p) => sum + (p.pontos || 0), 0);
const avgPoints = totalPlayers > 0 ? (totalPoints / totalPlayers).toFixed(1) : 0;
const topPlayer = players.length > 0 ?
players.reduce((max, p) => (p.pontos || 0) > (max.pontos || 0) ? p : max) :
null;
const azulWins = matches.filter(m => m.vencedor === 'azul').length;
const vermelhoWins = matches.filter(m => m.vencedor === 'vermelho').length;
if (typeof calculatePlayerLaneStats === 'function') {
calculatePlayerLaneStats(matches, players);
} else {
console.error("calculatePlayerLaneStats não está definida");
}
const statsContent = document.getElementById("statsContent");
if (statsContent) {
statsContent.innerHTML = `
Total de Jogadores
${totalPlayers}
Total de Partidas
${totalMatches}
Média de Pontos
${avgPoints}
Vitórias Vermelho
${vermelhoWins}
${topPlayer ? `
Top Player
${topPlayer.nickname}
${topPlayer.pontos || 0} pontos
` : ''}
`;
}
} catch (error) {
console.error("Erro ao carregar estatísticas:", error);
}
}/* ====== RANKING INICIAL ====== */async function loadInitialRanking() {
try {
// Verificar se db está definido
if (typeof db === 'undefined' || !db) {
console.error("Firebase não está inicializado!");
return;
}
console.log("Carregando ranking inicial...");
const snap = await getDocs(collection(db, "players"));
const arr = [];
snap.forEach(d => arr.push(d.data()));
arr.forEach(p => {
p.pontos = p.pontos || 0;
p.vitorias = p.vitorias || 0;
p.derrotas = p.derrotas || 0;
p.mvp = p.mvp || 0;
p.ace = p.ace || 0;
p.bagre = p.bagre || 0;
});
window.rankingData = arr;
const matchesSnap = await getDocs(collection(db, "matches"));
const matches = [];
matchesSnap.forEach(d => matches.push(d.data()));
window.allMatches = matches;
if (typeof calculatePlayerLaneStats === 'function') {
calculatePlayerLaneStats(matches, arr);
} else {
console.error("calculatePlayerLaneStats não está definida");
}
if (typeof renderRanking === 'function') {
renderRanking();
}
// Carregar tabela de elos inicial
if (typeof calcularEloCompleto === 'function') {
const elosCalculados = arr.map(player => calcularEloCompleto(player));
elosCalculados.sort((a, b) => {
const nivelA = ELOS[a.elo]?.nivel || 0;
const nivelB = ELOS[b.elo]?.nivel || 0;
if (nivelB !== nivelA) return nivelB - nivelA;
if (a.divisao !== b.divisao) return a.divisao - b.divisao;
return (b.pdls || 0) - (a.pdls || 0);
});
if (typeof loadEloTable === 'function') {
loadEloTable(elosCalculados, 'all');
} else {
console.error("loadEloTable não está definida");
}
} else {
console.error("calcularEloCompleto não está definida");
}
} catch (error) {
console.error("Erro ao carregar ranking:", error);
}
}/* ====== PROCESSAR PARÂMETROS DE URL ====== */function processUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
const voteType = urlParams.get('vote');
const matchId = urlParams.get('match');
if (voteType && matchId) {
createVotePage(matchId, voteType);
window.history.replaceState({}, document.title, window.location.pathname);
}
}/* ====== INICIALIZAÇÃO FINAL ====== */// Aguardar DOM estar completamente pronto
function startApp() {
// Carregar imagens dos elos PRIMEIRO (do localStorage)
if (typeof loadEloImages === 'function') {
loadEloImages();
}
// Aguardar um pouco mais para garantir que todos os elementos estejam no DOM
setTimeout(() => {
// Inicializar UI
if (typeof updateUIForUserType === 'function') {
updateUIForUserType();
}// Carregar dados iniciais
if (typeof loadInitialRanking === 'function') {
loadInitialRanking();
}
}, 100);
}// Inicializar quando DOM estiver pronto
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', startApp);
} else {
// DOM já está pronto
startApp();
}// Listener em tempo real para players (atualiza ranking e tabela de elos automaticamente)
// Verificar se db está definido antes de criar o listener
function setupPlayersListener() {
if (typeof db !== 'undefined' && db) {
onSnapshot(collection(db, "players"), async (snap) => {
try {
const arr = [];
snap.forEach(d => arr.push(d.data()));
arr.forEach(p => {
p.pontos = p.pontos || 0;
p.vitorias = p.vitorias || 0;
p.derrotas = p.derrotas || 0;
p.mvp = p.mvp || 0;
p.ace = p.ace || 0;
p.bagre = p.bagre || 0;
});
window.rankingData = arr;
// Carregar matches para calcular estatísticas de lanes
const matchesSnap = await getDocs(collection(db, "matches"));
const matches = [];
matchesSnap.forEach(d => matches.push(d.data()));
window.allMatches = matches;
if (typeof calculatePlayerLaneStats === 'function') {
calculatePlayerLaneStats(matches, arr);
} else {
console.error("calculatePlayerLaneStats não está definida");
}
if (typeof renderRanking === 'function') {
renderRanking();
}
// Atualizar tabela de elos
// Primeiro calcular elos sem filtro para determinar lane principal
if (typeof calcularEloCompleto === 'function') {
const elosCalculados = arr.map(player => calcularEloCompleto(player));
elosCalculados.sort((a, b) => {
const nivelA = ELOS[a.elo]?.nivel || 0;
const nivelB = ELOS[b.elo]?.nivel || 0;
if (nivelB !== nivelA) return nivelB - nivelA;
if (a.divisao !== b.divisao) return a.divisao - b.divisao;
return (b.pdls || 0) - (a.pdls || 0);
});
// Armazenar dados dos players para uso no filtro
window.rankingData = arr;
const eloFilter = document.getElementById('eloFilter');
if (typeof loadEloTable === 'function') {
loadEloTable(elosCalculados, eloFilter?.value || 'all');
} else {
console.error("loadEloTable não está definida");
}
} else {
console.error("calcularEloCompleto não está definida");
// Armazenar dados dos players mesmo se não conseguir calcular elos
window.rankingData = arr;
}
} catch (error) {
console.error("Erro no listener de players:", error);
}
}, (error) => {
console.error("Erro ao configurar listener de players:", error);
});
} else {
// Firebase não está pronto ainda, tentar novamente
setTimeout(setupPlayersListener, 500);
}
}// Configurar listener quando Firebase estiver pronto
setupPlayersListener();// Verificar parâmetros de URL
window.addEventListener('load', processUrlParams);// Atualizar estatísticas periodicamente
setInterval(updateStats, 30000);// Fechar modais ao clicar fora
const voteModal = document.getElementById('voteModal');
if (voteModal) {
voteModal.addEventListener('click', (e) => {
if (e.target.id === 'voteModal') {
e.target.classList.remove('show');
}
});
}/* ====== SISTEMA DE UPLOAD DE AVATAR DO RANKING ====== */// Event listener para clicar no avatar
document.addEventListener('click', (e) => {
const avatarEl = e.target.closest('.rank-avatar[data-nick]');
if (avatarEl && isAdmin) {
const nickname = avatarEl.dataset.nick;
if (nickname) {
openAvatarUploadModal(nickname);
}
}
});function openAvatarUploadModal(nickname) {
const modal = document.getElementById('avatarUploadModal');
const playerNameEl = document.getElementById('avatarPlayerName');
const previewEl = document.getElementById('avatarPreview');
const letterEl = document.getElementById('avatarLetter');
const fileInput = document.getElementById('avatarFileInput');
const saveBtn = document.getElementById('saveAvatarBtn');
if (!modal) return;
// Buscar dados do jogador
const player = window.rankingData?.find(p => p.nickname === nickname);
playerNameEl.textContent = nickname;
letterEl.textContent = nickname.charAt(0).toUpperCase();
// Mostrar avatar atual se existir
if (player?.avatarUrl) {
previewEl.innerHTML = `

`;
} else {
previewEl.innerHTML = `
${nickname.charAt(0).toUpperCase()}`;
}
// Resetar file input
fileInput.value = '';
saveBtn.disabled = true;
// Preview da nova imagem
fileInput.onchange = (e) => {
const file = e.target.files[0];
if (file) {
if (!file.type.startsWith('image/')) {
showToast('Por favor, selecione um arquivo de imagem!', 'error');
return;
}
const reader = new FileReader();
reader.onload = (e) => {
previewEl.innerHTML = `

`;
saveBtn.disabled = false;
};
reader.readAsDataURL(file);
}
};
// Mostrar modal
modal.style.display = 'flex';
// Configurar botões
document.getElementById('cancelAvatarBtn').onclick = () => {
modal.style.display = 'none';
};
document.getElementById('removeAvatarBtn').onclick = async () => {
if (!confirm(`Remover avatar de ${nickname}?`)) return;
try {
const playerRef = doc(db, "players", nickname);
await updateDoc(playerRef, {
avatarUrl: null
});
showToast('Avatar removido com sucesso!', 'success');
modal.style.display = 'none';
// Recarregar ranking
if (typeof loadInitialRanking === 'function') {
loadInitialRanking();
}
} catch (error) {
console.error('Erro ao remover avatar:', error);
showToast('Erro ao remover avatar!', 'error');
}
};
document.getElementById('saveAvatarBtn').onclick = async () => {
const file = fileInput.files[0];
if (!file) {
showToast('Selecione uma imagem!', 'error');
return;
}
saveBtn.disabled = true;
saveBtn.textContent = 'Salvando...';
try {
// Converter para base64
const reader = new FileReader();
reader.onload = async (e) => {
const base64Image = e.target.result;
// Salvar no Firebase
const playerRef = doc(db, "players", nickname);
await updateDoc(playerRef, {
avatarUrl: base64Image
});
showToast('Avatar atualizado com sucesso!', 'success');
modal.style.display = 'none';
// Recarregar ranking
if (typeof loadInitialRanking === 'function') {
loadInitialRanking();
}
};
reader.readAsDataURL(file);
} catch (error) {
console.error('Erro ao salvar avatar:', error);
showToast('Erro ao salvar avatar!', 'error');
saveBtn.disabled = false;
saveBtn.textContent = 'Salvar Avatar';
}
};
}// Fechar modal ao clicar fora
document.getElementById('avatarUploadModal')?.addEventListener('click', (e) => {
if (e.target.id === 'avatarUploadModal') {
e.target.style.display = 'none';
}
});// Limpar monitoramento ao fechar
window.addEventListener('beforeunload', () => {
stopLiveVotingUpdates();
});// Atualizar variáveis globais (já inicializadas acima)
window.duos = duos;
window.isAdmin = isAdmin;// Log function
function log(message) {
const logElement = document.getElementById('log');
if (logElement) {
logElement.textContent = message + '\n' + logElement.textContent;
}
}