Files
Hommie_RPG_Game/js/game/Achievements.js
Maxim Dolgolyov fb5f09212b Initial commit: 3D Hommie RPG game
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 01:04:09 +03:00

164 lines
8.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export class Achievements {
constructor(game) {
this.game = game;
this.unlocked = new Set();
this.list = [
// Выживание
{ id: 'first_day', title: 'Новая жизнь', desc: 'Прожить 1 день', icon: '🌅', category: 'survival' },
{ id: 'week_survivor', title: 'Недельный марафон', desc: 'Прожить 7 дней', icon: '📅', category: 'survival' },
{ id: 'month_survivor', title: 'Бывалый', desc: 'Прожить 28 дней', icon: '🏆', category: 'survival' },
{ id: 'winter_survivor', title: 'Морозоустойчивый', desc: 'Пережить зиму', icon: '❄️', category: 'survival' },
{ id: 'full_year', title: 'Годовщина', desc: 'Прожить все 4 сезона', icon: '🎊', category: 'survival' },
{ id: 'near_death', title: 'На грани', desc: 'Выжить при здоровье < 5', icon: '💀', category: 'survival' },
{ id: 'well_fed', title: 'Сытый', desc: 'Держать сытость на 100 в течение 5 минут', icon: '🍽️', category: 'survival' },
// Социальные
{ id: 'first_talk', title: 'Общительный', desc: 'Поговорить с 3 NPC', icon: '💬', category: 'social' },
{ id: 'all_npcs', title: 'Душа компании', desc: 'Поговорить со всеми NPC', icon: '🤝', category: 'social' },
{ id: 'best_friend', title: 'Лучший друг', desc: 'Приручить пса', icon: '🐕', category: 'social' },
{ id: 'respected', title: 'Уважаемый', desc: 'Достичь репутации 50+', icon: '⭐', category: 'social' },
{ id: 'beloved', title: 'Свой человек', desc: 'Достичь репутации 80+', icon: '💛', category: 'social' },
// Экономика
{ id: 'first_money', title: 'Первый рубль', desc: 'Заработать первые деньги', icon: '💰', category: 'economy' },
{ id: 'rich_100', title: 'Копилка', desc: 'Накопить 100₽', icon: '🪙', category: 'economy' },
{ id: 'rich_500', title: 'Состояние', desc: 'Накопить 500₽', icon: '💎', category: 'economy' },
{ id: 'rich_1000', title: 'Богач', desc: 'Накопить 1000₽', icon: '👑', category: 'economy' },
{ id: 'first_job_done', title: 'Трудяга', desc: 'Выполнить первую подработку', icon: '🔧', category: 'economy' },
{ id: 'jobs_10', title: 'Работяга', desc: 'Выполнить 10 подработок', icon: '🏗️', category: 'economy' },
{ id: 'bottle_king', title: 'Король бутылок', desc: 'Сдать 20 бутылок', icon: '🍾', category: 'economy' },
// Боевые
{ id: 'first_fight', title: 'Боец', desc: 'Отбиться от врага', icon: '👊', category: 'combat' },
{ id: 'fighter_5', title: 'Бывалый боец', desc: 'Победить 5 врагов', icon: '🥊', category: 'combat' },
{ id: 'survivor_combat', title: 'Несгибаемый', desc: 'Отбиться от 3 врагов подряд', icon: '🛡️', category: 'combat' },
// Исследование
{ id: 'explorer', title: 'Исследователь', desc: 'Посетить все локации', icon: '🗺️', category: 'explore' },
{ id: 'crafter', title: 'Мастер', desc: 'Создать 5 предметов', icon: '🔨', category: 'explore' },
{ id: 'skill_max', title: 'Эксперт', desc: 'Достичь 5 уровня любого навыка', icon: '📚', category: 'explore' },
{ id: 'fully_equipped', title: 'Экипированный', desc: 'Заполнить все слоты экипировки', icon: '🛡️', category: 'explore' },
{ id: 'shelter_built', title: 'Свой угол', desc: 'Построить укрытие', icon: '🏠', category: 'explore' },
];
}
// Вызывается из разных мест кода
check(id) {
if (this.unlocked.has(id)) return;
const achievement = this.list.find(a => a.id === id);
if (!achievement) return;
this.unlocked.add(id);
this.showPopup(achievement);
this.game.sound.playQuestComplete();
this.game.player.stats.mood = Math.min(100, this.game.player.stats.mood + 5);
}
// Проверки, вызываемые каждый тик
updateChecks() {
const player = this.game.player;
const day = this.game.gameDay;
// Выживание
if (day >= 2) this.check('first_day');
if (day >= 8) this.check('week_survivor');
if (day >= 29) this.check('month_survivor');
// Здоровье
if (player.stats.health > 0 && player.stats.health < 5) {
this.check('near_death');
}
// Деньги
if (player.stats.money >= 1) this.check('first_money');
if (player.stats.money >= 100) this.check('rich_100');
if (player.stats.money >= 500) this.check('rich_500');
if (player.stats.money >= 1000) this.check('rich_1000');
// Репутация
if (this.game.reputation.value >= 50) this.check('respected');
if (this.game.reputation.value >= 80) this.check('beloved');
// Пёс
if (this.game.dog.adopted) this.check('best_friend');
// Навыки
const skills = this.game.skills.skills;
for (const skill of Object.values(skills)) {
if (skill.level >= 5) {
this.check('skill_max');
break;
}
}
// Экипировка
if (this.game.equipment.getFilledSlots() === 4) {
this.check('fully_equipped');
}
}
showPopup(achievement) {
let popup = document.getElementById('achievement-popup');
if (!popup) {
popup = document.createElement('div');
popup.id = 'achievement-popup';
popup.style.cssText = `
position: fixed;
top: -80px;
left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, rgba(20,20,40,0.95), rgba(40,30,60,0.95));
border: 2px solid #ffd740;
border-radius: 8px;
padding: 12px 24px;
z-index: 60;
display: flex;
align-items: center;
gap: 12px;
transition: top 0.5s ease;
backdrop-filter: blur(8px);
box-shadow: 0 4px 20px rgba(255,215,64,0.3);
pointer-events: none;
`;
document.body.appendChild(popup);
}
popup.innerHTML = `
<span style="font-size:2rem;">${achievement.icon}</span>
<div>
<div style="font-size:0.7rem;color:#ffd740;font-weight:700;text-transform:uppercase;letter-spacing:0.1em;">Достижение разблокировано!</div>
<div style="font-size:1rem;font-weight:700;color:#fff;margin-top:2px;">${achievement.title}</div>
<div style="font-size:0.75rem;color:#aaa;">${achievement.desc}</div>
</div>
`;
// Анимация
setTimeout(() => popup.style.top = '20px', 50);
setTimeout(() => popup.style.top = '-80px', 4000);
}
getByCategory(category) {
return this.list.filter(a => a.category === category);
}
getProgress() {
return { unlocked: this.unlocked.size, total: this.list.length };
}
getSaveData() {
return { unlocked: [...this.unlocked] };
}
loadSaveData(data) {
if (data && data.unlocked) {
this.unlocked = new Set(data.unlocked);
}
}
reset() {
this.unlocked = new Set();
}
}