Achievements added. Updates to frontend and backend. Basic testing completed, but may be some bugs, further testing required. Basic achievement added for linking discord account. Reward for achievements added too - Discord linking gives you a Discord Mod Class.

This commit is contained in:
Jamon committed 2024-08-09 19:19:47 +12:00
1 parent 6cc8afe4e9
commit b4ffcbed2a
28 files changed
+526 -4892

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

+268 -64
View File
@@ -89,15 +89,20 @@ class NotificationSystem {
document.body.appendChild(this.notificationContainer);
}
showNotification(message) {
showNotification(message, image = null) {
const notificationElement = document.createElement('div');
notificationElement.className = 'custom-notification draggable';
notificationElement.innerHTML = `
<div class="notification-content">
<p>${message}</p>
<button class="close-notification">Close</button>
</div>
`;
let content = `<div class="notification-content">`;
if (image) {
content += `<img src="${image}" alt="Achievement" class="notification-image">`;
}
content += `
<p>${message}</p>
<button class="close-notification">Close</button>
</div>`;
notificationElement.innerHTML = content;
const closeButton = notificationElement.querySelector('.close-notification');
closeButton.addEventListener('click', () => this.closeNotification(notificationElement));
@@ -107,6 +112,7 @@ class NotificationSystem {
makeDraggable(notificationElement);
}
closeNotification(notificationElement) {
@@ -115,9 +121,170 @@ class NotificationSystem {
}
}
// Create a global instance of the NotificationSystem
const notificationSystem = new NotificationSystem();
function handleAchievementUnlock(data) {
const message = `Achievement Unlocked: ${data.name}\n${data.description}`;
notificationSystem.showNotification(message, data.image);
}
function fetchUserAchievements() {
const achievementsGrid = document.getElementById('achievementsGrid');
if (!achievementsGrid) {
console.error('Achievements grid not found');
return;
}
if (!currentUser) {
displaySignInMessage(achievementsGrid, 'achievements');
return;
}
fetch('/get-user-achievements', {
headers: {
'Authorization': getSessionToken()
}
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(achievements => {
if (Array.isArray(achievements) && achievements.length > 0) {
displayUserAchievements(achievements);
} else {
displayNoAchievementsMessage(achievementsGrid);
}
})
.catch(error => {
console.error('Error fetching user achievements:', error);
displayErrorMessage(achievementsGrid, 'Failed to load achievements. Please try again later.');
});
}
function displaySignInMessage(container, feature) {
container.innerHTML = `
<div class="sign-in-message">
<p>Please sign in to view your ${feature}.</p>
<button id="signInButton" class="glowing-button">Sign In</button>
</div>
`;
document.getElementById('signInButton').addEventListener('click', showLoginForm);
}
function displayNoAchievementsMessage(container) {
container.innerHTML = `
<div class="no-achievements-message">
<p>You haven't unlocked any achievements yet.</p>
<p>Keep playing to earn achievements!</p>
</div>
`;
}
function displayErrorMessage(container, message) {
container.innerHTML = `
<div class="error-message">
<p>${message}</p>
</div>
`;
}
function displayUserAchievements(achievements) {
const achievementsGrid = document.getElementById('achievementsGrid');
if (!achievementsGrid) {
console.error('Achievements grid not found');
return;
}
achievementsGrid.innerHTML = '';
achievements.forEach(achievement => {
const achievementElement = document.createElement('div');
achievementElement.classList.add('achievement');
achievementElement.innerHTML = `<img src="${achievement.image}" alt="${achievement.name}">`;
achievementElement.addEventListener('click', () => showAchievementInfo(achievement));
achievementElement.addEventListener('mouseover', () => showAchievementInfo(achievement));
achievementsGrid.appendChild(achievementElement);
});
// Show info for the first achievement by default
if (achievements.length > 0) {
showAchievementInfo(achievements[0]);
}
}
function showAchievementInfo(achievement) {
const image = document.getElementById('achievementImage');
const name = document.getElementById('achievementName');
const description = document.getElementById('achievementDescription');
const unlockedClasses = document.getElementById('unlockedClasses');
const unlockedSkins = document.getElementById('unlockedSkins');
image.src = achievement.image;
name.textContent = achievement.name;
description.textContent = achievement.description;
unlockedClasses.innerHTML = '<h5>Classes:</h5>';
if (achievement.unlocked_classes && achievement.unlocked_classes.length > 0) {
achievement.unlocked_classes.forEach(c => {
unlockedClasses.innerHTML += `<div class="unlock-item"><img src="${c.image}" alt="${c.name}"><span>${c.name}</span></div>`;
});
} else {
unlockedClasses.innerHTML += '<p>No classes unlocked</p>';
}
unlockedSkins.innerHTML = '<h5>Skins:</h5>';
if (achievement.unlocked_skins && achievement.unlocked_skins.length > 0) {
achievement.unlocked_skins.forEach(s => {
unlockedSkins.innerHTML += `<div class="unlock-item"><img src="${s.image}" alt="${s.name}"><span>${s.name}</span></div>`;
});
} else {
unlockedSkins.innerHTML += '<p>No skins unlocked</p>';
}
// For debugging
debugLog('Achievement:', achievement);
}
function hideAchievementInfo() {
const infoBox = document.getElementById('achievementInfoBox');
infoBox.classList.add('hidden');
}
document.addEventListener('click', (event) => {
const achievementsTab = document.getElementById('achievements');
if (achievementsTab && !achievementsTab.contains(event.target)) {
// Optionally hide or reset achievement info here if needed
}
});
function createAchievementsTab() {
const achievementsTab = document.createElement('div');
achievementsTab.id = 'achievements';
achievementsTab.classList.add('tab-content');
achievementsTab.innerHTML = `
<div class="achievements-container">
<div id="achievementsGrid" class="achievements-grid"></div>
<div id="achievementInfoBox" class="achievement-info-box">
<img id="achievementImage" src="" alt="Achievement">
<h3 id="achievementName"></h3>
<p id="achievementDescription"></p>
<div id="achievementUnlocks">
<h4>Unlocks:</h4>
<div id="unlockedClasses"></div>
<div id="unlockedSkins"></div>
</div>
</div>
</div>
`;
return achievementsTab;
}
function initShop() {
let shopButton = document.getElementById('shopButton');
if (!shopButton) {
@@ -1160,51 +1327,56 @@ function hideClassSelector() {
}
function fetchAndDisplayClasses() {
fetch('/get-available-classes')
.then(response => response.json())
.then(classes => {
// Sort classes: unlocked first, then locked
classes.sort((a, b) => {
if (a.unlocked_by_default === b.unlocked_by_default) return 0;
return a.unlocked_by_default ? -1 : 1;
});
fetch('/get-available-classes', {
headers: {
'Authorization': getSessionToken()
}
})
.then(response => response.json())
.then(classes => {
// Sort classes: unlocked first, then locked
classes.sort((a, b) => {
if (a.unlocked === b.unlocked) return 0;
return a.unlocked ? -1 : 1;
});
const classSelector = document.getElementById('classSelector');
classSelector.innerHTML = '';
classes.forEach(playerClass => {
const classOption = document.createElement('div');
classOption.classList.add('class-option');
if (playerClass._id === selectedClassId) {
classOption.classList.add('selected');
}
classOption.innerHTML = `<img src="${playerClass.image_source}" alt="${playerClass.name}">`;
if (!playerClass.unlocked_by_default) {
classOption.classList.add('locked');
const lockOverlay = document.createElement('div');
lockOverlay.classList.add('lock-overlay');
lockOverlay.innerHTML = '<i class="fas fa-lock"></i>';
classOption.appendChild(lockOverlay);
}
classOption.addEventListener('click', () => selectClass(playerClass));
classOption.addEventListener('mouseover', () => displayClassInfo(playerClass));
classSelector.appendChild(classOption);
});
// Display info for the first unlocked class by default
const firstUnlockedClass = classes.find(c => c.unlocked_by_default);
if (firstUnlockedClass) {
displayClassInfo(firstUnlockedClass);
} else if (classes.length > 0) {
displayClassInfo(classes[0]);
const classSelector = document.getElementById('classSelector');
classSelector.innerHTML = '';
classes.forEach(playerClass => {
const classOption = document.createElement('div');
classOption.classList.add('class-option');
if (playerClass._id === selectedClassId) {
classOption.classList.add('selected');
}
})
.catch(error => console.error('Error loading classes:', error));
classOption.innerHTML = `<img src="${playerClass.image_source}" alt="${playerClass.name}">`;
if (!playerClass.unlocked) {
classOption.classList.add('locked');
const lockOverlay = document.createElement('div');
lockOverlay.classList.add('lock-overlay');
lockOverlay.innerHTML = '<i class="fas fa-lock"></i>';
classOption.appendChild(lockOverlay);
}
classOption.addEventListener('click', () => selectClass(playerClass));
classOption.addEventListener('mouseover', () => displayClassInfo(playerClass));
classSelector.appendChild(classOption);
});
// Display info for the first unlocked class by default
const firstUnlockedClass = classes.find(c => c.unlocked);
if (firstUnlockedClass) {
displayClassInfo(firstUnlockedClass);
} else if (classes.length > 0) {
displayClassInfo(classes[0]);
}
})
.catch(error => console.error('Error loading classes:', error));
}
function displayClassInfo(playerClass) {
const classImage = document.getElementById('classImage');
const className = document.getElementById('className');
@@ -1726,10 +1898,35 @@ document.querySelectorAll('.close-button').forEach(button => {
});
});
// Modify the showWidget function
function showWidget() {
uiWidget.classList.add('visible');
openInterfaces.menu = true;
// Add Achievements tab if it doesn't exist
if (!document.getElementById('achievements')) {
const achievementsTab = createAchievementsTab();
const widgetContent = uiWidget.querySelector('.widget-content');
if (widgetContent) {
widgetContent.appendChild(achievementsTab);
} else {
console.error('Widget content container not found');
}
// Add Achievements button to tab buttons
const tabButtons = uiWidget.querySelector('.widget-tabs');
if (tabButtons) {
const achievementsButton = document.createElement('button');
achievementsButton.classList.add('tab-button');
achievementsButton.setAttribute('data-tab', 'achievements');
achievementsButton.textContent = 'Achievements';
tabButtons.appendChild(achievementsButton);
achievementsButton.addEventListener('click', () => showTab('achievements'));
} else {
console.error('Widget tabs container not found');
}
}
if (gameStarted && player) {
pausePlayer();
showTab('settings'); // Open the Settings tab by default when in-game
@@ -1739,6 +1936,7 @@ function showWidget() {
updateAccountTab();
}
function updateAccountTab() {
const accountContent = document.getElementById('accountContent');
if (currentUser) {
@@ -1935,7 +2133,9 @@ function showTab(tabName) {
if (tabName === 'customization') {
onCustomizationTabOpen();
} else if (tabName === 'account') {
updateAccountTab(); // Update account tab content when selected
updateAccountTab();
} else if (tabName === 'achievements') {
fetchUserAchievements();
}
}
@@ -1966,6 +2166,7 @@ function startGame() {
});
});
socket.on('achievement_unlocked', handleAchievementUnlock);
socket.on('reconnect', (attemptNumber) => {
@@ -3104,13 +3305,8 @@ function drawGameObjects() {
function selectClass(playerClass) {
if (!playerClass.unlocked_by_default && currentUser === null) {
notificationSystem.showNotification('You need to be logged in to select this class.');
return;
}
if (!playerClass.unlocked_by_default) {
showUnlockRequirements(playerClass);
if (!playerClass.unlocked) {
notificationSystem.showNotification('This class is locked. Unlock it to select.');
return;
}
@@ -3118,7 +3314,7 @@ function selectClass(playerClass) {
const classSelector = document.getElementById('classSelector');
const classOptions = classSelector.querySelectorAll('.class-option');
classOptions.forEach(option => option.classList.remove('selected'));
const selectedOption = classSelector.querySelector(`.class-option:nth-child(${Array.from(classOptions).indexOf(event.currentTarget) + 1})`);
const selectedOption = Array.from(classOptions).find(option => option.querySelector('img').alt === playerClass.name);
if (selectedOption) {
selectedOption.classList.add('selected');
}
@@ -3129,21 +3325,29 @@ function selectClass(playerClass) {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': getSessionToken()
},
body: JSON.stringify({ username: currentUser, class_id: selectedClassId }),
body: JSON.stringify({ class_id: selectedClassId }),
})
.then(response => {
if (!response.ok) {
return response.json().then(data => {
throw new Error(data.error || 'Failed to select class');
});
}
return response.json();
})
.then(response => response.json())
.then(data => {
if (data.success) {
debugLog('Class selected successfully');
notificationSystem.showNotification(`${playerClass.name} class selected successfully!`);
} else {
console.error('Failed to select class:', data.error);
notificationSystem.showNotification('Failed to select class. Please try again.');
throw new Error(data.error || 'Failed to select class');
}
})
.catch(error => {
console.error('Error selecting class:', error);
notificationSystem.showNotification('An error occurred while selecting the class. Please try again.');
console.error('Error selecting class:', error.message);
notificationSystem.showNotification(`Failed to select class: ${error.message}`);
});
}
}
+140
View File
@@ -1296,6 +1296,146 @@ body {
z-index: 2000;
}
.achievements-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
gap: 10px;
padding: 10px;
height: 100%;
overflow-y: auto;
flex: 1;
}
#achievementsContainer {
height: 100%;
overflow-y: auto;
}
.achievements-container {
display: flex;
height: 100%;
}
.achievement {
width: 80px;
height: 80px;
border: 2px solid #00ffff;
border-radius: 10px;
overflow: hidden;
cursor: pointer;
transition: transform 0.2s;
}
.achievement:hover {
transform: scale(1.1);
}
.achievement img {
width: 100%;
height: 100%;
object-fit: cover;
}
.achievement-info-box {
width: 300px;
padding: 20px;
background-color: rgba(18, 4, 88, 0.8);
border-left: 2px solid #00ffff;
overflow-y: auto;
}
.achievement-info-box.hidden {
display: none;
}
.achievement-info-box img {
width: 100px;
height: 100px;
object-fit: cover;
border-radius: 10px;
margin-bottom: 10px;
}
.achievement-info-box h3 {
color: #00ffff;
margin-bottom: 10px;
}
.achievement-info-box p {
color: #ffffff;
margin-bottom: 20px;
}
#achievementUnlocks h4 {
color: #00ffff;
margin-bottom: 10px;
}
#achievementUnlocks h5 {
color: #00ffff;
margin-top: 10px;
margin-bottom: 5px;
}
.sign-in-message,
.no-achievements-message,
.error-message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
text-align: center;
color: #ffffff;
font-family: 'Orbitron', sans-serif;
}
.sign-in-message p,
.no-achievements-message p,
.error-message p {
margin-bottom: 20px;
}
.glowing-button {
background-color: transparent;
border: 2px solid #00ffff;
color: #00ffff;
padding: 10px 20px;
font-size: 16px;
font-family: 'Orbitron', sans-serif;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 0 10px #00ffff;
}
.glowing-button:hover {
background-color: #00ffff;
color: #000000;
box-shadow: 0 0 20px #00ffff;
}
.unlock-item {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.unlock-item img {
width: 30px;
height: 30px;
object-fit: cover;
margin-right: 10px;
}
.unlock-item span {
color: #ffffff;
}
.custom-notification {
background-color: rgba(18, 4, 88, 0.9);
color: #00ffff;