Обновить clicker.html
This commit is contained in:
+579
-24
@@ -246,9 +246,69 @@
|
|||||||
.shop-section::-webkit-scrollbar-thumb:hover {
|
.shop-section::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(255, 215, 0, 0.5);
|
background: rgba(255, 215, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Индикатор сохранения */
|
||||||
|
.save-indicator {
|
||||||
|
position: fixed;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
color: white;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
z-index: 1000;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-indicator.show {
|
||||||
|
display: flex;
|
||||||
|
animation: slideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-indicator.success {
|
||||||
|
border-color: rgba(76, 175, 80, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-indicator.error {
|
||||||
|
border-color: rgba(244, 67, 54, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(100px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-top-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="save-indicator" id="saveIndicator">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<span id="saveIndicatorText">Сохранение...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="game-container">
|
<div class="game-container">
|
||||||
<div class="clicker-section">
|
<div class="clicker-section">
|
||||||
<div class="stats">
|
<div class="stats">
|
||||||
@@ -283,6 +343,431 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// API клиент
|
||||||
|
class GameAPI {
|
||||||
|
constructor(gameSlug) {
|
||||||
|
this.gameSlug = gameSlug;
|
||||||
|
this.tableName = 'game_save';
|
||||||
|
}
|
||||||
|
|
||||||
|
getCSRFToken() {
|
||||||
|
return document.querySelector('[name=csrfmiddlewaretoken]')?.value ||
|
||||||
|
document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestPermission(operation, description = '') {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'permission-modal';
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="permission-modal-content">
|
||||||
|
<div class="permission-modal-header">
|
||||||
|
<span class="material-icons-round">security</span>
|
||||||
|
<h3>Запрос разрешения</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="permission-modal-body">
|
||||||
|
<p>Игра <strong>"${this.gameSlug}"</strong> хочет:</p>
|
||||||
|
<div class="permission-operation">
|
||||||
|
<span class="material-icons-round">${this.getOperationIcon(operation)}</span>
|
||||||
|
<strong>${this.getOperationName(operation)}</strong>
|
||||||
|
</div>
|
||||||
|
${description ? `<p class="permission-description">${description}</p>` : ''}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="permission-modal-footer">
|
||||||
|
<label class="permission-checkbox">
|
||||||
|
<input type="checkbox" id="dontAskAgain">
|
||||||
|
<span>Не спрашивать больше для этой операции</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="permission-buttons">
|
||||||
|
<button class="btn btn-danger" id="denyAllBtn">
|
||||||
|
<span class="material-icons-round">block</span>
|
||||||
|
Запретить всё
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary" id="denyBtn">
|
||||||
|
<span class="material-icons-round">close</span>
|
||||||
|
Запретить
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-success" id="allowBtn">
|
||||||
|
<span class="material-icons-round">check</span>
|
||||||
|
Разрешить
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-primary" id="allowAllBtn">
|
||||||
|
<span class="material-icons-round">done_all</span>
|
||||||
|
Разрешить всё
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
|
const dontAskAgain = modal.querySelector('#dontAskAgain');
|
||||||
|
const allowBtn = modal.querySelector('#allowBtn');
|
||||||
|
const denyBtn = modal.querySelector('#denyBtn');
|
||||||
|
const allowAllBtn = modal.querySelector('#allowAllBtn');
|
||||||
|
const denyAllBtn = modal.querySelector('#denyAllBtn');
|
||||||
|
|
||||||
|
const handleResponse = async (allow, dontAsk, allowAll, denyAll) => {
|
||||||
|
await this.savePermission(operation, allow, dontAsk, allowAll, denyAll);
|
||||||
|
modal.remove();
|
||||||
|
resolve(allow);
|
||||||
|
};
|
||||||
|
|
||||||
|
allowBtn.onclick = () => handleResponse(true, dontAskAgain.checked, false, false);
|
||||||
|
denyBtn.onclick = () => handleResponse(false, dontAskAgain.checked, false, false);
|
||||||
|
allowAllBtn.onclick = () => handleResponse(true, true, true, false);
|
||||||
|
denyAllBtn.onclick = () => handleResponse(false, true, false, true);
|
||||||
|
|
||||||
|
modal.onclick = (e) => {
|
||||||
|
if (e.target === modal) {
|
||||||
|
handleResponse(false, false, false, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPermission(operation) {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/user/permissions/', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCSRFToken()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
const permissions = data.permissions || {};
|
||||||
|
const gamePerms = permissions[this.gameSlug] || {};
|
||||||
|
return gamePerms[this.tableName]?.[operation] ?? null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка загрузки разрешений:', error);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async savePermission(operation, allow, dontAskAgain, allowAll, denyAll) {
|
||||||
|
try {
|
||||||
|
await fetch('/api/user/permissions/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCSRFToken()
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
game_slug: this.gameSlug,
|
||||||
|
table_name: this.tableName,
|
||||||
|
operation: operation,
|
||||||
|
allow: allow,
|
||||||
|
dont_ask_again: dontAskAgain,
|
||||||
|
allow_all: allowAll,
|
||||||
|
deny_all: denyAll
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка сохранения разрешения:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkPermission(operation, description = '') {
|
||||||
|
const cached = await this.getPermission(operation);
|
||||||
|
if (cached !== null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
return await this.requestPermission(operation, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadGame() {
|
||||||
|
const allowed = await this.checkPermission('select', 'Загрузить сохранение игры');
|
||||||
|
if (!allowed) {
|
||||||
|
throw new Error('Загрузка запрещена');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/api/game/select/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCSRFToken()
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
game_slug: this.gameSlug,
|
||||||
|
table_name: this.tableName,
|
||||||
|
conditions: { 'key': 'game_state' }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка загрузки');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return data.data?.[0]?.value || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveGame(state) {
|
||||||
|
const allowed = await this.checkPermission('insert', 'Сохранить прогресс игры');
|
||||||
|
if (!allowed) {
|
||||||
|
throw new Error('Сохранение запрещено');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/api/game/insert/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCSRFToken()
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
game_slug: this.gameSlug,
|
||||||
|
table_name: this.tableName,
|
||||||
|
data: {
|
||||||
|
'key': 'game_state',
|
||||||
|
'value': JSON.stringify(state),
|
||||||
|
'updated_at': new Date().toISOString()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка сохранения');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteGame() {
|
||||||
|
const allowed = await this.checkPermission('delete', 'Удалить сохранение игры');
|
||||||
|
if (!allowed) {
|
||||||
|
throw new Error('Удаление запрещено');
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('/api/game/delete/', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-CSRFToken': this.getCSRFToken()
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
game_slug: this.gameSlug,
|
||||||
|
table_name: this.tableName,
|
||||||
|
conditions: { 'key': 'game_state' }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Ошибка удаления');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperationIcon(operation) {
|
||||||
|
const icons = {
|
||||||
|
'select': 'download',
|
||||||
|
'insert': 'save',
|
||||||
|
'delete': 'delete'
|
||||||
|
};
|
||||||
|
return icons[operation] || 'help';
|
||||||
|
}
|
||||||
|
|
||||||
|
getOperationName(operation) {
|
||||||
|
const names = {
|
||||||
|
'select': 'Загрузить данные',
|
||||||
|
'insert': 'Сохранить данные',
|
||||||
|
'delete': 'Удалить данные'
|
||||||
|
};
|
||||||
|
return names[operation] || operation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSS для модального окна
|
||||||
|
const modalStyles = `
|
||||||
|
<style>
|
||||||
|
.permission-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10000;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-content {
|
||||||
|
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 30px;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 90%;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
animation: modalSlideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-50px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 2px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-header .material-icons-round {
|
||||||
|
font-size: 32px;
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-body {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-body p {
|
||||||
|
margin: 10px 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-operation {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border: 1px solid rgba(255, 215, 0, 0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 15px 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-operation .material-icons-round {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-operation strong {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-description {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-modal-footer {
|
||||||
|
border-top: 2px solid rgba(255, 255, 255, 0.1);
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-checkbox {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-checkbox input[type="checkbox"] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.permission-buttons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 12px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn .material-icons-round {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.permission-buttons {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
document.head.insertAdjacentHTML('beforeend', modalStyles);
|
||||||
|
|
||||||
|
// Инициализация API
|
||||||
|
const gameAPI = new GameAPI('clicker-magnat');
|
||||||
|
|
||||||
// Описания улучшений
|
// Описания улучшений
|
||||||
const upgradeDescriptions = {
|
const upgradeDescriptions = {
|
||||||
clickPower: 'Увеличивает доход за клик на 1',
|
clickPower: 'Увеличивает доход за клик на 1',
|
||||||
@@ -302,23 +787,81 @@
|
|||||||
hyperAuto: '💎 Гипер автокликер'
|
hyperAuto: '💎 Гипер автокликер'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Загрузка сохранения
|
// Показ индикатора сохранения
|
||||||
function loadGame() {
|
function showSaveIndicator(text, type = 'loading') {
|
||||||
const saved = localStorage.getItem('clickerGame');
|
const indicator = document.getElementById('saveIndicator');
|
||||||
if (saved) {
|
const indicatorText = document.getElementById('saveIndicatorText');
|
||||||
const savedState = JSON.parse(saved);
|
|
||||||
gameState = { ...gameState, ...savedState };
|
indicator.className = 'save-indicator show';
|
||||||
// Восстанавливаем стоимость улучшений
|
if (type === 'success') {
|
||||||
for (let key in gameState.upgrades) {
|
indicator.classList.add('success');
|
||||||
const upgrade = gameState.upgrades[key];
|
indicator.querySelector('.spinner').style.display = 'none';
|
||||||
upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.level));
|
} else if (type === 'error') {
|
||||||
|
indicator.classList.add('error');
|
||||||
|
indicator.querySelector('.spinner').style.display = 'none';
|
||||||
|
} else {
|
||||||
|
indicator.querySelector('.spinner').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
indicatorText.textContent = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideSaveIndicator() {
|
||||||
|
const indicator = document.getElementById('saveIndicator');
|
||||||
|
indicator.classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Загрузка сохранения через API
|
||||||
|
async function loadGame() {
|
||||||
|
try {
|
||||||
|
showSaveIndicator('Загрузка сохранения...');
|
||||||
|
const savedData = await gameAPI.loadGame();
|
||||||
|
|
||||||
|
if (savedData) {
|
||||||
|
const savedState = JSON.parse(savedData);
|
||||||
|
gameState = { ...gameState, ...savedState };
|
||||||
|
|
||||||
|
// Восстанавливаем стоимость улучшений
|
||||||
|
for (let key in gameState.upgrades) {
|
||||||
|
const upgrade = gameState.upgrades[key];
|
||||||
|
upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.level));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSaveIndicator('Сохранение загружено', 'success');
|
||||||
|
setTimeout(hideSaveIndicator, 2000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка загрузки:', error);
|
||||||
|
showSaveIndicator('Ошибка загрузки', 'error');
|
||||||
|
setTimeout(hideSaveIndicator, 2000);
|
||||||
|
|
||||||
|
// Fallback на localStorage
|
||||||
|
const saved = localStorage.getItem('clickerGame');
|
||||||
|
if (saved) {
|
||||||
|
const savedState = JSON.parse(saved);
|
||||||
|
gameState = { ...gameState, ...savedState };
|
||||||
|
for (let key in gameState.upgrades) {
|
||||||
|
const upgrade = gameState.upgrades[key];
|
||||||
|
upgrade.cost = Math.floor(upgrade.baseCost * Math.pow(1.15, upgrade.level));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сохранение игры
|
// Сохранение игры через API
|
||||||
function saveGame() {
|
async function saveGame() {
|
||||||
localStorage.setItem('clickerGame', JSON.stringify(gameState));
|
try {
|
||||||
|
await gameAPI.saveGame(gameState);
|
||||||
|
showSaveIndicator('Сохранено', 'success');
|
||||||
|
setTimeout(hideSaveIndicator, 1500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка сохранения:', error);
|
||||||
|
showSaveIndicator('Ошибка сохранения', 'error');
|
||||||
|
setTimeout(hideSaveIndicator, 2000);
|
||||||
|
|
||||||
|
// Fallback на localStorage
|
||||||
|
localStorage.setItem('clickerGame', JSON.stringify(gameState));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подсчет дохода за клик
|
// Подсчет дохода за клик
|
||||||
@@ -405,38 +948,50 @@
|
|||||||
setTimeout(() => animation.remove(), 1000);
|
setTimeout(() => animation.remove(), 1000);
|
||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
saveGame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Автокликер (каждую секунду)
|
// Автокликер (каждую секунду)
|
||||||
function autoClick() {
|
function autoClick() {
|
||||||
const perSecond = getPerSecond();
|
const perSecond = getPerSecond();
|
||||||
if (perSecond > 0) {
|
if (perSecond > 0) {
|
||||||
gameState.score += perSecond / 10; // Обновляем 10 раз в секунду для плавности
|
gameState.score += perSecond / 10;
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сброс игры
|
// Сброс игры
|
||||||
function resetGame() {
|
async function resetGame() {
|
||||||
if (confirm('Вы уверены, что хотите сбросить весь прогресс?')) {
|
if (confirm('Вы уверены, что хотите сбросить весь прогресс?')) {
|
||||||
|
try {
|
||||||
|
showSaveIndicator('Удаление сохранения...');
|
||||||
|
await gameAPI.deleteGame();
|
||||||
|
showSaveIndicator('Прогресс сброшен', 'success');
|
||||||
|
setTimeout(hideSaveIndicator, 1500);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка удаления:', error);
|
||||||
|
showSaveIndicator('Ошибка удаления', 'error');
|
||||||
|
setTimeout(hideSaveIndicator, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
localStorage.removeItem('clickerGame');
|
localStorage.removeItem('clickerGame');
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Инициализация
|
// Инициализация
|
||||||
loadGame();
|
(async () => {
|
||||||
updateUI();
|
await loadGame();
|
||||||
|
updateUI();
|
||||||
|
|
||||||
document.getElementById('clickButton').addEventListener('click', handleClick);
|
document.getElementById('clickButton').addEventListener('click', handleClick);
|
||||||
document.getElementById('resetButton').addEventListener('click', resetGame);
|
document.getElementById('resetButton').addEventListener('click', resetGame);
|
||||||
|
|
||||||
// Автокликер каждые 100мс
|
// Автокликер каждые 100мс
|
||||||
setInterval(autoClick, 100);
|
setInterval(autoClick, 100);
|
||||||
|
|
||||||
// Автосохранение каждые 10 секунд
|
// Автосохранение каждые 30 секунд
|
||||||
setInterval(saveGame, 10000);
|
setInterval(saveGame, 30000);
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user