Initial commit
This commit is contained in:
commit
d8c301215b
81 files changed
+133278
No files matched your search
@@ -0,0 +1,38 @@
|
||||
// Add this to your game.js file or create a new cursor.js file
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const cursor = document.createElement('div');
|
||||
cursor.classList.add('custom-cursor');
|
||||
cursor.innerHTML = '<div class="cursor-dot"></div><div class="cursor-outline"></div>';
|
||||
document.body.appendChild(cursor);
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
cursor.style.left = e.clientX + 'px';
|
||||
cursor.style.top = e.clientY + 'px';
|
||||
});
|
||||
|
||||
document.addEventListener('mousedown', () => {
|
||||
cursor.classList.add('clicking');
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
cursor.classList.remove('clicking');
|
||||
});
|
||||
|
||||
// Optional: Add a subtle trailing effect
|
||||
let trailDot = null;
|
||||
setInterval(() => {
|
||||
if (trailDot) {
|
||||
trailDot.remove();
|
||||
}
|
||||
trailDot = cursor.querySelector('.cursor-dot').cloneNode(true);
|
||||
trailDot.style.transition = 'all 0.15s linear';
|
||||
trailDot.style.opacity = '0.5';
|
||||
cursor.appendChild(trailDot);
|
||||
setTimeout(() => {
|
||||
if (trailDot) {
|
||||
trailDot.style.opacity = '0';
|
||||
}
|
||||
}, 50);
|
||||
}, 100);
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
function makeDraggable(element) {
|
||||
let isDragging = false;
|
||||
let startX, startY, initialLeft, initialTop;
|
||||
|
||||
// Check if the device is mobile
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
if (!isMobile) {
|
||||
element.addEventListener("mousedown", dragStart, { passive: false });
|
||||
document.addEventListener("mousemove", drag, { passive: false });
|
||||
document.addEventListener("mouseup", dragEnd);
|
||||
}
|
||||
|
||||
// For mobile devices, we'll only add touch events if we want to allow dragging
|
||||
// If you want to completely disable dragging on mobile, you can remove these lines
|
||||
element.addEventListener("touchstart", dragStart, { passive: true });
|
||||
document.addEventListener("touchmove", drag, { passive: true });
|
||||
document.addEventListener("touchend", dragEnd);
|
||||
|
||||
function dragStart(e) {
|
||||
if (e.target.closest('.close-notification')) return;
|
||||
|
||||
if (isMobile) {
|
||||
// On mobile, only start dragging if it's not a scrollable element
|
||||
const isScrollable = e.target.closest('.widget-content') !== null;
|
||||
if (isScrollable) return;
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
|
||||
if (e.type === "touchstart") {
|
||||
startX = e.touches[0].clientX;
|
||||
startY = e.touches[0].clientY;
|
||||
} else {
|
||||
startX = e.clientX;
|
||||
startY = e.clientY;
|
||||
}
|
||||
|
||||
initialLeft = element.offsetLeft;
|
||||
initialTop = element.offsetTop;
|
||||
|
||||
element.style.position = 'absolute';
|
||||
element.style.zIndex = 1000;
|
||||
}
|
||||
|
||||
function drag(e) {
|
||||
if (!isDragging) return;
|
||||
|
||||
if (isMobile) {
|
||||
// Prevent default only if we're actually dragging
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
let clientX, clientY;
|
||||
if (e.type === "touchmove") {
|
||||
clientX = e.touches[0].clientX;
|
||||
clientY = e.touches[0].clientY;
|
||||
} else {
|
||||
clientX = e.clientX;
|
||||
clientY = e.clientY;
|
||||
}
|
||||
|
||||
const deltaX = clientX - startX;
|
||||
const deltaY = clientY - startY;
|
||||
|
||||
element.style.left = initialLeft + deltaX + 'px';
|
||||
element.style.top = initialTop + deltaY + 'px';
|
||||
}
|
||||
|
||||
function dragEnd(e) {
|
||||
isDragging = false;
|
||||
element.style.zIndex = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to apply draggable functionality to an element
|
||||
function applyDraggable(elementId) {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.classList.add('draggable');
|
||||
makeDraggable(element);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply draggable functionality to widgets and notification popups
|
||||
function initDraggableElements() {
|
||||
applyDraggable('uiWidget');
|
||||
applyDraggable('infoWidget');
|
||||
applyDraggable('classWidget');
|
||||
applyDraggable('shopWidget');
|
||||
|
||||
// For notifications, we need to apply it dynamically when they're created
|
||||
const originalShowNotification = notificationSystem.showNotification;
|
||||
notificationSystem.showNotification = function(message) {
|
||||
originalShowNotification.call(this, message);
|
||||
const notifications = document.querySelectorAll('.custom-notification');
|
||||
notifications.forEach(notification => {
|
||||
notification.classList.add('draggable');
|
||||
makeDraggable(notification);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Call this function after your DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', initDraggableElements);
|
||||
|
||||
|
||||
+3576
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,30 @@
|
||||
// Add this to your game.js file or create a new security.js file
|
||||
|
||||
// Disable context menu (right-click menu)
|
||||
document.addEventListener('contextmenu', function(e) {
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
// Disable image dragging
|
||||
document.addEventListener('dragstart', function(e) {
|
||||
if (e.target.tagName.toLowerCase() === 'img') {
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Prevent text selection
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.style.webkitUserSelect = 'none';
|
||||
document.body.style.msUserSelect = 'none';
|
||||
document.body.style.mozUserSelect = 'none';
|
||||
|
||||
// Disable touch callout on mobile devices
|
||||
document.body.style.webkitTouchCallout = 'none';
|
||||
|
||||
// Optional: Disable saving images on mobile
|
||||
document.body.style.webkitTapHighlightColor = 'rgba(0,0,0,0)';
|
||||
|
||||
// Prevent copying text
|
||||
document.body.oncopy = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
@@ -0,0 +1,267 @@
|
||||
const SKIN_CACHE_KEY = 'cachedSkins';
|
||||
const SELECTED_SKINS_CACHE_KEY = 'cachedSelectedSkins';
|
||||
const SKIN_CACHE_TIMESTAMP_KEY = 'skinCacheTimestamp';
|
||||
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
|
||||
let loadSkinsDebounceTimer;
|
||||
|
||||
|
||||
|
||||
function loadSkinOptions() {
|
||||
if (!currentUser) {
|
||||
displayNoSkinsMessage("Please sign in to view and select skins.");
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(loadSkinsDebounceTimer);
|
||||
|
||||
loadSkinsDebounceTimer = setTimeout(() => {
|
||||
const cachedSkins = localStorage.getItem(SKIN_CACHE_KEY);
|
||||
const cachedSelectedSkins = localStorage.getItem(SELECTED_SKINS_CACHE_KEY);
|
||||
const cacheTimestamp = localStorage.getItem(SKIN_CACHE_TIMESTAMP_KEY);
|
||||
const currentTime = Date.now();
|
||||
|
||||
if (cachedSkins && cachedSelectedSkins && cacheTimestamp && (currentTime - parseInt(cacheTimestamp) < CACHE_DURATION)) {
|
||||
console.log('Using cached skins and selected skins');
|
||||
displayUnlockedSkins(JSON.parse(cachedSkins), JSON.parse(cachedSelectedSkins));
|
||||
} else {
|
||||
fetchAndCacheSkins();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function fetchAndCacheSkins() {
|
||||
const skinCustomization = document.getElementById('skinCustomization');
|
||||
if (skinCustomization) {
|
||||
skinCustomization.innerHTML = '<div class="loading-spinner">Loading skins...</div>';
|
||||
}
|
||||
|
||||
const cachedEtag = localStorage.getItem('skinCacheEtag');
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
if (cachedEtag) {
|
||||
headers['If-None-Match'] = cachedEtag;
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
fetch('/get-unlocked-skins', {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify({ username: currentUser }),
|
||||
}),
|
||||
fetch('/get-player-skin', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username: currentUser }),
|
||||
})
|
||||
])
|
||||
.then(([unlockedResponse, selectedResponse]) => {
|
||||
return Promise.all([
|
||||
handleUnlockedSkinsResponse(unlockedResponse),
|
||||
handleSelectedSkinsResponse(selectedResponse)
|
||||
]);
|
||||
})
|
||||
.then(([unlockedSkins, selectedSkins]) => {
|
||||
displayUnlockedSkins(unlockedSkins, selectedSkins);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading skin options:', error);
|
||||
displayNoSkinsMessage("An error occurred while loading skins. Please try again later.");
|
||||
});
|
||||
}
|
||||
|
||||
function handleUnlockedSkinsResponse(response) {
|
||||
if (response.status === 304) {
|
||||
console.log('Server returned 304 for unlocked skins, using cached data');
|
||||
return JSON.parse(localStorage.getItem(SKIN_CACHE_KEY));
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok for unlocked skins');
|
||||
}
|
||||
const newEtag = response.headers.get('ETag');
|
||||
if (newEtag) {
|
||||
localStorage.setItem('skinCacheEtag', newEtag);
|
||||
}
|
||||
return response.json().then(data => {
|
||||
localStorage.setItem(SKIN_CACHE_KEY, JSON.stringify(data.skins));
|
||||
localStorage.setItem(SKIN_CACHE_TIMESTAMP_KEY, Date.now().toString());
|
||||
return data.skins;
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectedSkinsResponse(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok for selected skins');
|
||||
}
|
||||
return response.json().then(data => {
|
||||
localStorage.setItem(SELECTED_SKINS_CACHE_KEY, JSON.stringify(data));
|
||||
return data;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function selectSkin(skinType, skinId, element) {
|
||||
if (!currentUser) {
|
||||
console.log('No user logged in');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/select-skin`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username: currentUser, skin_type: skinType, skin_id: skinId }),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not ok');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
console.log('Skin selected successfully');
|
||||
|
||||
// Remove 'selected' class from all skin options of the same type
|
||||
const options = document.querySelectorAll(`#${skinType}Options .skin-option`);
|
||||
options.forEach(option => option.classList.remove('selected'));
|
||||
|
||||
// Add 'selected' class to the clicked element
|
||||
element.classList.add('selected');
|
||||
|
||||
// Update the local cache
|
||||
updateLocalSelectedSkinCache(skinType, {
|
||||
name: element.getAttribute('title').split(' (')[0],
|
||||
value: skinType === 'color' ? element.style.backgroundColor : element.style.backgroundImage,
|
||||
effect: element.classList.contains('glow-effect') ? 'glow' : null
|
||||
});
|
||||
|
||||
loadPlayerSkin(currentUser);
|
||||
} else {
|
||||
console.error('Failed to select skin:', data.error);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error selecting skin:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function updateLocalSelectedSkinCache(skinType, skinData) {
|
||||
const cachedSelectedSkins = JSON.parse(localStorage.getItem(SELECTED_SKINS_CACHE_KEY) || '{}');
|
||||
cachedSelectedSkins[skinType] = skinData;
|
||||
localStorage.setItem(SELECTED_SKINS_CACHE_KEY, JSON.stringify(cachedSelectedSkins));
|
||||
}
|
||||
|
||||
|
||||
function updateSkinCache(skinType, skinId) {
|
||||
const cachedSkins = JSON.parse(localStorage.getItem(SKIN_CACHE_KEY) || '[]');
|
||||
const updatedSkins = cachedSkins.map(skin => {
|
||||
if (skin.type === skinType) {
|
||||
return { ...skin, id: skinId };
|
||||
}
|
||||
return skin;
|
||||
});
|
||||
localStorage.setItem(SKIN_CACHE_KEY, JSON.stringify(updatedSkins));
|
||||
}
|
||||
|
||||
|
||||
function displayUnlockedSkins(skins, selectedSkins) {
|
||||
const skinCustomization = document.getElementById('skinCustomization');
|
||||
if (!skinCustomization) {
|
||||
console.error('skinCustomization element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a document fragment to build the new content
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
const colorSelector = document.createElement('div');
|
||||
colorSelector.id = 'colorSelector';
|
||||
colorSelector.className = 'skin-section';
|
||||
colorSelector.innerHTML = '<h4>Colors</h4><div id="colorOptions"></div>';
|
||||
fragment.appendChild(colorSelector);
|
||||
|
||||
const hatSelector = document.createElement('div');
|
||||
hatSelector.id = 'hatSelector';
|
||||
hatSelector.className = 'skin-section';
|
||||
hatSelector.innerHTML = '<h4>Hats</h4><div id="hatOptions"></div>';
|
||||
fragment.appendChild(hatSelector);
|
||||
|
||||
// Append the fragment to skinCustomization
|
||||
skinCustomization.innerHTML = '';
|
||||
skinCustomization.appendChild(fragment);
|
||||
|
||||
const colorOptions = document.getElementById('colorOptions');
|
||||
const hatOptions = document.getElementById('hatOptions');
|
||||
|
||||
let hasColors = false;
|
||||
let hasHats = false;
|
||||
|
||||
skins.forEach(skin => {
|
||||
const optionElement = document.createElement('div');
|
||||
optionElement.classList.add('skin-option');
|
||||
optionElement.setAttribute('data-skin-id', skin.id);
|
||||
optionElement.setAttribute('title', `${skin.name} (${skin.rarity})`);
|
||||
|
||||
if (selectedSkins[skin.type] && selectedSkins[skin.type].name === skin.name) {
|
||||
optionElement.classList.add('selected');
|
||||
}
|
||||
|
||||
switch (skin.type) {
|
||||
case 'color':
|
||||
optionElement.style.backgroundColor = skin.value;
|
||||
if (skin.effect === 'glow') {
|
||||
optionElement.classList.add('glow-effect');
|
||||
}
|
||||
colorOptions.appendChild(optionElement);
|
||||
hasColors = true;
|
||||
break;
|
||||
case 'hat':
|
||||
optionElement.style.backgroundImage = `url(${skin.value})`;
|
||||
hatOptions.appendChild(optionElement);
|
||||
hasHats = true;
|
||||
break;
|
||||
}
|
||||
|
||||
optionElement.addEventListener('click', () => selectSkin(skin.type, skin.id, optionElement));
|
||||
});
|
||||
|
||||
if (selectedSkins.color && !hasColors) {
|
||||
const defaultColorElement = document.createElement('div');
|
||||
defaultColorElement.classList.add('skin-option', 'selected');
|
||||
defaultColorElement.style.backgroundColor = selectedSkins.color.value;
|
||||
defaultColorElement.setAttribute('title', selectedSkins.color.name);
|
||||
colorOptions.appendChild(defaultColorElement);
|
||||
hasColors = true;
|
||||
}
|
||||
|
||||
if (!hasColors) colorOptions.innerHTML = '<p>No colors unlocked yet</p>';
|
||||
if (!hasHats) hatOptions.innerHTML = '<p>No hats unlocked yet</p>';
|
||||
|
||||
// Fade in the skin sections
|
||||
setTimeout(() => {
|
||||
document.getElementById('colorSelector').classList.add('loaded');
|
||||
document.getElementById('hatSelector').classList.add('loaded');
|
||||
}, 50);
|
||||
|
||||
if (!hasColors && !hasHats) {
|
||||
displayNoSkinsMessage("You haven't unlocked any skins yet. Keep playing to earn skins!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function displayNoSkinsMessage(message) {
|
||||
const skinCustomization = document.getElementById('skinCustomization');
|
||||
if (skinCustomization) {
|
||||
skinCustomization.innerHTML = `<p class="no-skins-message">${message}</p>`;
|
||||
} else {
|
||||
console.error('skinCustomization element not found');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Call this function when the customization tab is opened
|
||||
function onCustomizationTabOpen() {
|
||||
loadSkinOptions();
|
||||
}
|
||||
Reference in new issue
Block a user