Загрузить файлы в «Hommie»
This commit is contained in:
303
Hommie/audio.js
Normal file
303
Hommie/audio.js
Normal file
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Audio System - Звуковая система (Web Audio API)
|
||||
*/
|
||||
|
||||
class AudioSystem {
|
||||
constructor() {
|
||||
this.context = null;
|
||||
this.enabled = true;
|
||||
this.volume = 0.5;
|
||||
this.musicEnabled = true;
|
||||
this.musicOscillator = null;
|
||||
this.musicGain = null;
|
||||
}
|
||||
|
||||
// Инициализация аудио контекста
|
||||
init() {
|
||||
try {
|
||||
this.context = new (window.AudioContext || window.webkitAudioContext)();
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.warn('Audio not supported:', e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Возобновить контекст (нужно после взаимодействия пользователя)
|
||||
resume() {
|
||||
if (this.context && this.context.state === 'suspended') {
|
||||
this.context.resume();
|
||||
}
|
||||
}
|
||||
|
||||
// Звук шагов
|
||||
playFootstep() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'square';
|
||||
osc.frequency.setValueAtTime(80, this.context.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(40, this.context.currentTime + 0.1);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.1, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.1);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.1);
|
||||
}
|
||||
|
||||
// Звук удара
|
||||
playHit() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
// Шум удара
|
||||
const bufferSize = this.context.sampleRate * 0.1;
|
||||
const buffer = this.context.createBuffer(1, bufferSize, this.context.sampleRate);
|
||||
const data = buffer.getChannelData(0);
|
||||
|
||||
for (let i = 0; i < bufferSize; i++) {
|
||||
data[i] = (Math.random() * 2 - 1) * (1 - i / bufferSize);
|
||||
}
|
||||
|
||||
const noise = this.context.createBufferSource();
|
||||
noise.buffer = buffer;
|
||||
|
||||
const filter = this.context.createBiquadFilter();
|
||||
filter.type = 'lowpass';
|
||||
filter.frequency.value = 1000;
|
||||
|
||||
const gain = this.context.createGain();
|
||||
gain.gain.setValueAtTime(this.volume * 0.3, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
||||
|
||||
noise.connect(filter);
|
||||
filter.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
noise.start();
|
||||
}
|
||||
|
||||
// Звук убийства
|
||||
playKill() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
// Низкий удар
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'sawtooth';
|
||||
osc.frequency.setValueAtTime(200, this.context.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(30, this.context.currentTime + 0.3);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.4, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.3);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.3);
|
||||
}
|
||||
|
||||
// Звукденег
|
||||
playCoin() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(800, this.context.currentTime);
|
||||
osc.frequency.setValueAtTime(1200, this.context.currentTime + 0.05);
|
||||
osc.frequency.setValueAtTime(1600, this.context.currentTime + 0.1);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.15);
|
||||
}
|
||||
|
||||
// Звук предмета
|
||||
playItem() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(600, this.context.currentTime);
|
||||
osc.frequency.setValueAtTime(900, this.context.currentTime + 0.08);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.15, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.2);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.2);
|
||||
}
|
||||
|
||||
// Звук еды
|
||||
playEat() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'triangle';
|
||||
osc.frequency.setValueAtTime(400, this.context.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(200, this.context.currentTime + 0.15);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.15);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.15);
|
||||
}
|
||||
|
||||
// Звук HP
|
||||
playHeal() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc1 = this.context.createOscillator();
|
||||
const osc2 = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc1.connect(gain);
|
||||
osc2.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc1.type = 'sine';
|
||||
osc1.frequency.setValueAtTime(500, this.context.currentTime);
|
||||
osc1.frequency.setValueAtTime(700, this.context.currentTime + 0.1);
|
||||
|
||||
osc2.type = 'sine';
|
||||
osc2.frequency.setValueAtTime(750, this.context.currentTime + 0.05);
|
||||
osc2.frequency.setValueAtTime(1000, this.context.currentTime + 0.15);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.2, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.25);
|
||||
|
||||
osc1.start(this.context.currentTime);
|
||||
osc2.start(this.context.currentTime + 0.05);
|
||||
osc1.stop(this.context.currentTime + 0.2);
|
||||
osc2.stop(this.context.currentTime + 0.25);
|
||||
}
|
||||
|
||||
// Звук получения урона
|
||||
playDamage() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'sawtooth';
|
||||
osc.frequency.setValueAtTime(150, this.context.currentTime);
|
||||
osc.frequency.exponentialRampToValueAtTime(50, this.context.currentTime + 0.2);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.3, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.2);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.2);
|
||||
}
|
||||
|
||||
// Звук UI (кнопка)
|
||||
playUIClick() {
|
||||
if (!this.enabled || !this.context) return;
|
||||
|
||||
const osc = this.context.createOscillator();
|
||||
const gain = this.context.createGain();
|
||||
|
||||
osc.connect(gain);
|
||||
gain.connect(this.context.destination);
|
||||
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(1000, this.context.currentTime);
|
||||
|
||||
gain.gain.setValueAtTime(this.volume * 0.1, this.context.currentTime);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, this.context.currentTime + 0.05);
|
||||
|
||||
osc.start(this.context.currentTime);
|
||||
osc.stop(this.context.currentTime + 0.05);
|
||||
}
|
||||
|
||||
// Фоновая музыка (простая мелодия)
|
||||
startMusic() {
|
||||
if (!this.musicEnabled || !this.context) return;
|
||||
|
||||
// Остановить предыдущую музыку
|
||||
this.stopMusic();
|
||||
|
||||
// Создать осциллятор для фоновой музыки
|
||||
this.musicOscillator = this.context.createOscillator();
|
||||
this.musicGain = this.context.createGain();
|
||||
|
||||
this.musicOscillator.connect(this.musicGain);
|
||||
this.musicGain.connect(this.context.destination);
|
||||
|
||||
this.musicOscillator.type = 'sine';
|
||||
this.musicOscillator.frequency.value = 220;
|
||||
this.musicGain.gain.value = this.volume * 0.05;
|
||||
|
||||
// Менять частоту каждые 2 секунды
|
||||
const notes = [220, 262, 294, 330, 349, 392, 440, 392, 349, 330, 294, 262];
|
||||
let noteIndex = 0;
|
||||
|
||||
this.musicInterval = setInterval(() => {
|
||||
if (this.musicEnabled && this.musicOscillator) {
|
||||
this.musicOscillator.frequency.value = notes[noteIndex];
|
||||
noteIndex = (noteIndex + 1) % notes.length;
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
this.musicOscillator.start();
|
||||
}
|
||||
|
||||
// Остановить музыку
|
||||
stopMusic() {
|
||||
if (this.musicInterval) {
|
||||
clearInterval(this.musicInterval);
|
||||
}
|
||||
if (this.musicOscillator) {
|
||||
this.musicOscillator.stop();
|
||||
this.musicOscillator = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Переключить звук
|
||||
toggle() {
|
||||
this.enabled = !this.enabled;
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
// Переключить музыку
|
||||
toggleMusic() {
|
||||
this.musicEnabled = !this.musicEnabled;
|
||||
if (this.musicEnabled) {
|
||||
this.startMusic();
|
||||
} else {
|
||||
this.stopMusic();
|
||||
}
|
||||
return this.musicEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Глобальный экземпляр
|
||||
window.Audio = new AudioSystem();
|
||||
588
Hommie/data.js
Normal file
588
Hommie/data.js
Normal file
@@ -0,0 +1,588 @@
|
||||
/**
|
||||
* Game Data - Все данные игры (FENYA EDITION)
|
||||
*/
|
||||
|
||||
const GameData = {
|
||||
// Размеры карты
|
||||
TILE_SIZE: 40,
|
||||
MAP_WIDTH: 20,
|
||||
MAP_HEIGHT: 15,
|
||||
|
||||
// Типы предметов (с иконками)
|
||||
ITEM_ICONS: {
|
||||
'🍞 Хлеб': '🍞',
|
||||
'🍎 Яблоко': '🍎',
|
||||
'🥫 Консерва': '🥫',
|
||||
'🍜 Остатки': '🍜',
|
||||
'🥡 Картонка': '🥡',
|
||||
'🥤 Напиток': '🥤',
|
||||
'💊 Таблетки': '💊',
|
||||
'💊 Лекарства': '💊',
|
||||
'📦 Коробка': '📦',
|
||||
'🧥 Куртка': '🧥',
|
||||
'🧴 Мыло': '🧴',
|
||||
'🎫 Билет': '🎫',
|
||||
'🛒 Продукты': '🛒',
|
||||
'🔪 Заточка': '🔪',
|
||||
'🍺 Стекломой': '🍺',
|
||||
'💨 Бутырка': '💨',
|
||||
'⚱️ Марганцовка': '⚱️',
|
||||
'🍾 Водка': '🍾',
|
||||
'🍶 Самогон': '🍶',
|
||||
'🍷 Плодовоягодное': '🍷'
|
||||
},
|
||||
|
||||
// Еда
|
||||
FOOD_ITEMS: ['🍞 Хлеб', '🍎 Яблоко', '🥫 Консерва', '🍜 Остатки', '🥡 Картонка', '🥤 Напиток'],
|
||||
|
||||
// Полезные предметы
|
||||
USEFUL_ITEMS: ['💊 Таблетки', '📦 Коробка', '🧥 Куртка', '🧴 Мыло'],
|
||||
|
||||
// Оружие
|
||||
WEAPONS: ['🔪 Заточка', '🍺 Стекломой'],
|
||||
|
||||
// Наркотики и алкоголь
|
||||
DRUGS: ['💨 Бутырка', '⚱️ Марганцовка'],
|
||||
ALCOHOL: ['🍾 Водка', '🍶 Самогон', '🍷 Плодовоягодное'],
|
||||
|
||||
// Цены и значения
|
||||
PRICES: {
|
||||
food: 50,
|
||||
drink: 30,
|
||||
medicine: 100,
|
||||
ticket: 50,
|
||||
weapons: 200,
|
||||
drugs: 150
|
||||
},
|
||||
|
||||
// Эффекты
|
||||
EFFECTS: {
|
||||
foodHealth: 15,
|
||||
foodEnergy: 20,
|
||||
pillHealth: 30,
|
||||
medicineHealth: 50,
|
||||
drinkEnergy: 15,
|
||||
restEnergy: 30,
|
||||
restHealth: 10,
|
||||
bedEnergy: 50,
|
||||
bedHealth: 20,
|
||||
nightHealthLoss: 5,
|
||||
starvationHealthLoss: 0.1,
|
||||
drugHigh: 50,
|
||||
drugEnergy: 100,
|
||||
drugHealthLoss: 20,
|
||||
vodkaEnergy: 80,
|
||||
vodkaHealthLoss: 15,
|
||||
samogonEnergy: 100,
|
||||
samogonHealthLoss: 25,
|
||||
plodovoEnergy: 60,
|
||||
plodovoHealthLoss: 10,
|
||||
combatDamage: 15,
|
||||
combatEnemyDamage: 10
|
||||
},
|
||||
|
||||
// FENYA - Диалоги и названия
|
||||
FENYA: {
|
||||
// Приветствия
|
||||
greetings: [
|
||||
'Здорово, кент!',
|
||||
'Чё, как оно?',
|
||||
'Привет, бродяга!',
|
||||
'Здорово, бомж!'
|
||||
],
|
||||
|
||||
// Прощания
|
||||
farewells: [
|
||||
'Давай, не пропадай!',
|
||||
'Сiao, кент!',
|
||||
'Не звезди!',
|
||||
'Пока, братва!'
|
||||
],
|
||||
|
||||
// Описания
|
||||
descriptions: {
|
||||
street: 'Качай двор, кент. Тут можо шариться по мусорняку и на халяву поесть.',
|
||||
subway: 'Метро - наша тема. Тепло, народ ходит, можно стрельнуть.',
|
||||
hospital: 'Больничка - тут могут подлатать, но халявы нет.',
|
||||
shelter: 'Барделка (приют) - перекантоваться можно, но там козлы.',
|
||||
park: 'Парк - тут качаются и отдыхают нормальные пацанчики.'
|
||||
},
|
||||
|
||||
// Действия
|
||||
actions: {
|
||||
search: 'Роюсь в мусоре...',
|
||||
rest: 'Отдыхаю, братва...',
|
||||
eat: 'Жру, что нашёл...',
|
||||
fight: 'Бью морду!'
|
||||
}
|
||||
},
|
||||
|
||||
// NPC диалоги (по фене)
|
||||
NPC_DIALOGS: {
|
||||
// Улица - светлые бомжи
|
||||
street: {
|
||||
person: [
|
||||
'Здорово, кент! Как покатушки?',
|
||||
'Чё, в поиске? Я тоже тут торчу.',
|
||||
'Видел нормальную малину?',
|
||||
'Давай, не сдавайся, брат!',
|
||||
'Я тут третий день качаюсь...',
|
||||
'Бизнес идёт? Да никак, кент...'
|
||||
],
|
||||
person2: [
|
||||
'Привет, новенький? Я тут главный!',
|
||||
'Мусарка за углом - твоя, не лезь в мой район!',
|
||||
'Чё, на халяву хочешь? Давай работай!',
|
||||
'Я бомж со стажем, кент!'
|
||||
]
|
||||
},
|
||||
|
||||
// Барыги (торговцы)
|
||||
dealer: [
|
||||
'Чё, кент, чё надо?',
|
||||
'Есть всё, но за бабки!',
|
||||
'Могу подогнать, но дорого!',
|
||||
'Хочешь норму - плати!',
|
||||
'Стукач? Не, я честный барыга!'
|
||||
],
|
||||
|
||||
// Метро
|
||||
subway: {
|
||||
musician: [
|
||||
'Эй, хороший трек, правда?',
|
||||
'Дам бабло, играй ещё!',
|
||||
'Слышал новый трек?',
|
||||
'Вот это я понимаю - искусство, кент!'
|
||||
],
|
||||
beggar: [
|
||||
'Подай на хлебушек...',
|
||||
'Смилуйся, брат...',
|
||||
'Три дня не жрал...',
|
||||
'Люди злые стали, кент...'
|
||||
]
|
||||
},
|
||||
|
||||
// Больница
|
||||
hospital: {
|
||||
doctor: [
|
||||
'Тебе нужна помощь? Приём платный - 200 рублей!',
|
||||
'Без бабла не лечим!',
|
||||
'Можешь полежать в коридоре, если очень надо!',
|
||||
'Вот таблетки от головы, бесплатно'
|
||||
],
|
||||
nurse: [
|
||||
'Доктор сейчас занят...',
|
||||
'Очередь на приём - три часа!',
|
||||
'Воды хочешь? Бесплатно!',
|
||||
'Не шуми, тут больные!'
|
||||
],
|
||||
patient: [
|
||||
'Лежу тут уже третью неделю...',
|
||||
'Врачи нормальные, но очередь - жесть!',
|
||||
'Хочу домой, но бабла нет на лечение...',
|
||||
'Болезнь замучила, кент...'
|
||||
]
|
||||
},
|
||||
|
||||
// Приют
|
||||
shelter: {
|
||||
volunteer: [
|
||||
'Привет! Хочешь поесть? Сейчас раздача через час!',
|
||||
'Можешь убраться - заплатим 50 рублей!',
|
||||
'Вот шмотки, держи, не мёрзни!',
|
||||
'Не сдавайся, всё будет норм, кент!',
|
||||
'Кровать свободна, отдыхай!'
|
||||
]
|
||||
},
|
||||
|
||||
// Парк
|
||||
park: {
|
||||
jogger: [
|
||||
'Бегаю каждый день для здоровья!',
|
||||
'Не хочешь присоединиться, кент?',
|
||||
'Свежий воздух - это жизнь!'
|
||||
],
|
||||
dogWalker: [
|
||||
'Собаки - наши друзья!',
|
||||
'Не трогай их, могут укусить!',
|
||||
'Погладь, не бойся!'
|
||||
],
|
||||
benchPerson: [
|
||||
'Красивый парк, правда?',
|
||||
'Хорошая погодка для качалки!',
|
||||
'Тут всегда тихо, кент...'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Тёмные бомжи (враги ночью)
|
||||
DARK_HOMELESS: [
|
||||
{ name: 'Отморозок', damage: 10, health: 30, phrase: 'Гони бабло, козёл!' },
|
||||
{ name: 'Бомжара', damage: 15, health: 40, phrase: 'Щас тебе наваляю!' },
|
||||
{ name: 'Чугун', damage: 20, health: 50, phrase: 'Ты мой, сука!' },
|
||||
{ name: 'Кабан', damage: 25, health: 60, phrase: 'Бей его!' }
|
||||
],
|
||||
|
||||
// Лут с врагов
|
||||
ENEMY_LOOT: [
|
||||
'🍞 Хлеб',
|
||||
'🥤 Напиток',
|
||||
'💊 Таблетки',
|
||||
'💰 Копейка',
|
||||
'📦 Коробка'
|
||||
],
|
||||
|
||||
// Цена оружия и алкоголя у барыг
|
||||
DEALER_PRICES: {
|
||||
'🔪 Заточка': 200,
|
||||
'🍺 Стекломой': 150,
|
||||
'💨 Бутырка': 100,
|
||||
'⚱️ Марганцовка': 200,
|
||||
'🍾 Водка': 80,
|
||||
'🍶 Самогон': 50,
|
||||
'🍷 Плодовоягодное': 30,
|
||||
'🍞 Хлеб': 30,
|
||||
'🥤 Напиток': 20,
|
||||
'💊 Таблетки': 50
|
||||
},
|
||||
|
||||
// Магазины
|
||||
SHOPS: {
|
||||
street: {
|
||||
name: 'Магазин',
|
||||
icon: '🏪',
|
||||
items: [
|
||||
{ item: '🍞 Хлеб', price: 30 },
|
||||
{ item: '🥤 Напиток', price: 20 },
|
||||
{ item: '💊 Таблетки', price: 80 },
|
||||
{ item: '🧴 Мыло', price: 25 },
|
||||
{ item: '🧥 Куртка', price: 150 }
|
||||
]
|
||||
},
|
||||
subway: {
|
||||
name: 'Киоск',
|
||||
icon: '🏪',
|
||||
items: [
|
||||
{ item: '🍞 Хлеб', price: 25 },
|
||||
{ item: '🥤 Напиток', price: 15 },
|
||||
{ item: '🎫 Билет', price: 50 }
|
||||
]
|
||||
},
|
||||
hospital: {
|
||||
name: 'Аптека',
|
||||
icon: '💊',
|
||||
items: [
|
||||
{ item: '💊 Таблетки', price: 100 },
|
||||
{ item: '💊 Лекарства', price: 200 }
|
||||
]
|
||||
},
|
||||
shelter: {
|
||||
name: 'Приют',
|
||||
icon: '🏠',
|
||||
items: [
|
||||
{ item: '🍞 Хлеб', price: 10 },
|
||||
{ item: '🧥 Куртка', price: 50 },
|
||||
{ item: '🧴 Мыло', price: 10 }
|
||||
]
|
||||
},
|
||||
park: {
|
||||
name: 'Лавка',
|
||||
icon: '🏪',
|
||||
items: [
|
||||
{ item: '🍎 Яблоко', price: 20 },
|
||||
{ item: '🥤 Напиток', price: 25 },
|
||||
{ item: '🍜 Остатки', price: 40 }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
// Карта мира
|
||||
WORLD_MAP: {
|
||||
name: 'Карта города',
|
||||
icon: '🗺️',
|
||||
locations: {
|
||||
street: { x: 50, y: 50, name: 'Улица', icon: '🏙️', cost: 0 },
|
||||
subway: { x: 30, y: 70, name: 'Метро', icon: '🚇', cost: 25 },
|
||||
park: { x: 70, y: 30, name: 'Парк', icon: '🌳', cost: 0 },
|
||||
hospital: { x: 80, y: 70, name: 'Больница', icon: '🏥', cost: 50 },
|
||||
shelter: { x: 20, y: 30, name: 'Приют', icon: '🏠', cost: 30 },
|
||||
beach: { x: 10, y: 80, name: 'Пляж', icon: '🏖️', cost: 20 },
|
||||
construction: { x: 90, y: 40, name: 'Стройка', icon: '🏗️', cost: 0 },
|
||||
river: { x: 60, y: 10, name: 'Река', icon: '🌊', cost: 10 }
|
||||
}
|
||||
},
|
||||
|
||||
// Ломбард
|
||||
PAWNSHOP: {
|
||||
name: 'Ломбард',
|
||||
icon: '🏦',
|
||||
prices: {
|
||||
'🧥 Куртка': 80,
|
||||
'🧴 Мыло': 15,
|
||||
'🔪 Заточка': 100,
|
||||
'🍺 Стекломой': 75,
|
||||
'📦 Коробка': 10
|
||||
}
|
||||
},
|
||||
|
||||
// Система крафтинга
|
||||
CRAFTING_RECIPES: [
|
||||
{
|
||||
name: '🔪 Заточка',
|
||||
ingredients: ['📦 Коробка', '🧴 Мыло'],
|
||||
result: '🔪 Заточка',
|
||||
description: 'Оружие из подручных материалов',
|
||||
damage: 20
|
||||
},
|
||||
{
|
||||
name: '🍺 Стекломой',
|
||||
ingredients: ['🍷 Плодовоягодное', '🧴 Мыло'],
|
||||
result: '🍺 Стекломой',
|
||||
description: 'Оружие из бутылки',
|
||||
damage: 15
|
||||
},
|
||||
{
|
||||
name: '🧥 Тёплая куртка',
|
||||
ingredients: ['🧥 Куртка', '📦 Коробка'],
|
||||
result: '🧥 Тёплая куртка',
|
||||
description: 'Защита от холода',
|
||||
defense: 10
|
||||
},
|
||||
{
|
||||
name: '🥫 Зелье здоровья',
|
||||
ingredients: ['💊 Таблетки', '🥤 Напиток'],
|
||||
result: '🥫 Зелье здоровья',
|
||||
description: 'Восстанавливает 50 HP',
|
||||
heal: 50
|
||||
},
|
||||
{
|
||||
name: '🎒 Усиленный рюкзак',
|
||||
ingredients: ['📦 Коробка', '📦 Коробка'],
|
||||
result: '🎒 Усиленный рюкзак',
|
||||
description: '+5 слотов инвентаря',
|
||||
inventory: 5
|
||||
}
|
||||
],
|
||||
|
||||
// Система квестов
|
||||
QUESTS: {
|
||||
// Квесты улицы
|
||||
street: [
|
||||
{
|
||||
id: 'street_1',
|
||||
title: 'Найди еду',
|
||||
description: 'Найди 3 продукта питания в мусорках',
|
||||
icon: '🍞',
|
||||
target: 'collect_food',
|
||||
targetCount: 3,
|
||||
reward: { money: 50 },
|
||||
requiredLocation: 'street',
|
||||
repeat: false
|
||||
},
|
||||
{
|
||||
id: 'street_2',
|
||||
title: 'Защита района',
|
||||
description: 'Победи 2 врагов ночью',
|
||||
icon: '⚔️',
|
||||
target: 'kill_enemies',
|
||||
targetCount: 2,
|
||||
reward: { money: 100, item: '🔪 Заточка' },
|
||||
requiredLocation: 'street',
|
||||
repeat: false
|
||||
},
|
||||
{
|
||||
id: 'street_3',
|
||||
title: 'Подработка',
|
||||
description: 'Найди 100 рублей за день',
|
||||
icon: '💰',
|
||||
target: 'earn_money',
|
||||
targetCount: 100,
|
||||
reward: { money: 50 },
|
||||
requiredLocation: 'street',
|
||||
repeat: true
|
||||
}
|
||||
],
|
||||
// Квесты метро
|
||||
subway: [
|
||||
{
|
||||
id: 'subway_1',
|
||||
title: 'Музыкант',
|
||||
description: 'Послушай музыканта в метро',
|
||||
icon: '🎵',
|
||||
target: 'talk_npc',
|
||||
targetCount: 1,
|
||||
reward: { money: 20 },
|
||||
requiredLocation: 'subway',
|
||||
repeat: false
|
||||
},
|
||||
{
|
||||
id: 'subway_2',
|
||||
title: 'Безбилетник',
|
||||
description: 'Проезь без билета 3 раза',
|
||||
icon: '🎫',
|
||||
target: 'ride_free',
|
||||
targetCount: 3,
|
||||
reward: { money: 30 },
|
||||
requiredLocation: 'subway',
|
||||
repeat: true
|
||||
}
|
||||
],
|
||||
// Квесты больницы
|
||||
hospital: [
|
||||
{
|
||||
id: 'hospital_1',
|
||||
title: 'Медицинская помощь',
|
||||
description: 'Получи лечение в больнице',
|
||||
icon: '💉',
|
||||
target: 'get_treatment',
|
||||
targetCount: 1,
|
||||
reward: { health: 50 },
|
||||
requiredLocation: 'hospital',
|
||||
repeat: true,
|
||||
cost: 100
|
||||
},
|
||||
{
|
||||
id: 'hospital_2',
|
||||
title: 'Таблетки',
|
||||
description: 'Купи 2 упаковки таблеток',
|
||||
icon: '💊',
|
||||
target: 'buy_item',
|
||||
targetCount: 2,
|
||||
item: '💊 Таблетки',
|
||||
reward: { money: 50 },
|
||||
requiredLocation: 'hospital',
|
||||
repeat: false
|
||||
}
|
||||
],
|
||||
// Квесты приюта
|
||||
shelter: [
|
||||
{
|
||||
id: 'shelter_1',
|
||||
title: 'Уборка',
|
||||
description: 'Уберись в приюте за 50 рублей',
|
||||
icon: '🧹',
|
||||
target: 'work',
|
||||
targetCount: 1,
|
||||
reward: { money: 50, energy: 30 },
|
||||
requiredLocation: 'shelter',
|
||||
repeat: true
|
||||
},
|
||||
{
|
||||
id: 'shelter_2',
|
||||
title: 'Ночлег',
|
||||
description: 'Отдохни в приюте',
|
||||
icon: '🛏️',
|
||||
target: 'rest',
|
||||
targetCount: 1,
|
||||
reward: { energy: 50, health: 20 },
|
||||
requiredLocation: 'shelter',
|
||||
repeat: true
|
||||
}
|
||||
],
|
||||
// Квесты парка
|
||||
park: [
|
||||
{
|
||||
id: 'park_1',
|
||||
title: 'Собеседник',
|
||||
description: 'Поговори с 3 людьми в парке',
|
||||
icon: '💬',
|
||||
target: 'talk_npc',
|
||||
targetCount: 3,
|
||||
reward: { money: 30 },
|
||||
requiredLocation: 'park',
|
||||
repeat: false
|
||||
},
|
||||
{
|
||||
id: 'park_2',
|
||||
title: 'Безопасность',
|
||||
description: 'Победи врага в парке',
|
||||
icon: '🥊',
|
||||
target: 'kill_enemies',
|
||||
targetCount: 1,
|
||||
reward: { money: 75 },
|
||||
requiredLocation: 'park',
|
||||
repeat: true
|
||||
}
|
||||
],
|
||||
// Квесты пляжа
|
||||
beach: [
|
||||
{
|
||||
id: 'beach_1',
|
||||
title: 'Отдых на пляже',
|
||||
description: 'Отдохни на пляже',
|
||||
icon: '🏖️',
|
||||
target: 'rest',
|
||||
targetCount: 1,
|
||||
reward: { health: 20, energy: 30 },
|
||||
requiredLocation: 'beach',
|
||||
repeat: true
|
||||
},
|
||||
{
|
||||
id: 'beach_2',
|
||||
title: 'Мусорщик',
|
||||
description: 'Найди 5 предметов на пляже',
|
||||
icon: '🎣',
|
||||
target: 'collect_food',
|
||||
targetCount: 5,
|
||||
reward: { money: 40 },
|
||||
requiredLocation: 'beach',
|
||||
repeat: false
|
||||
}
|
||||
],
|
||||
// Квесты стройки
|
||||
construction: [
|
||||
{
|
||||
id: 'construction_1',
|
||||
title: 'Рабочий день',
|
||||
description: 'Поработай на стройке',
|
||||
icon: '🔨',
|
||||
target: 'work',
|
||||
targetCount: 1,
|
||||
reward: { money: 100 },
|
||||
requiredLocation: 'construction',
|
||||
repeat: true
|
||||
},
|
||||
{
|
||||
id: 'construction_2',
|
||||
title: 'Опасная территория',
|
||||
description: 'Победи охранника',
|
||||
icon: '⚔️',
|
||||
target: 'kill_enemies',
|
||||
targetCount: 1,
|
||||
reward: { money: 150 },
|
||||
requiredLocation: 'construction',
|
||||
repeat: false
|
||||
}
|
||||
],
|
||||
// Квесты реки
|
||||
river: [
|
||||
{
|
||||
id: 'river_1',
|
||||
title: 'Рыбак',
|
||||
description: 'Поймай рыбу',
|
||||
icon: '🐟',
|
||||
target: 'fish',
|
||||
targetCount: 1,
|
||||
reward: { item: '🍜 Остатки' },
|
||||
requiredLocation: 'river',
|
||||
repeat: true
|
||||
},
|
||||
{
|
||||
id: 'river_2',
|
||||
title: 'Купание',
|
||||
description: 'Искупайся в реке',
|
||||
icon: '🏊',
|
||||
target: 'rest',
|
||||
targetCount: 1,
|
||||
reward: { health: 15 },
|
||||
requiredLocation: 'river',
|
||||
repeat: true
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// Экспорт для использования
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = GameData;
|
||||
}
|
||||
166
Hommie/effects3d.js
Normal file
166
Hommie/effects3d.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* Effects3D Class - Visual Effects for 3D Game
|
||||
*/
|
||||
|
||||
class Effects3D {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.particles = [];
|
||||
}
|
||||
|
||||
// Создание системы частиц
|
||||
createParticleSystem(x, y, z, color, count = 20) {
|
||||
const geometry = new THREE.BufferGeometry();
|
||||
const positions = [];
|
||||
const velocities = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
positions.push(x, y, z);
|
||||
velocities.push(
|
||||
(Math.random() - 0.5) * 0.3,
|
||||
Math.random() * 0.2,
|
||||
(Math.random() - 0.5) * 0.3
|
||||
);
|
||||
}
|
||||
|
||||
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
||||
|
||||
const material = new THREE.PointsMaterial({
|
||||
color: color,
|
||||
size: 0.15,
|
||||
transparent: true,
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
const particles = new THREE.Points(geometry, material);
|
||||
this.scene.add(particles);
|
||||
|
||||
return {
|
||||
mesh: particles,
|
||||
velocities: velocities,
|
||||
life: 1.0,
|
||||
update: () => {
|
||||
const positions = particles.geometry.attributes.position.array;
|
||||
for (let i = 0; i < count; i++) {
|
||||
positions[i * 3] += velocities[i * 3];
|
||||
positions[i * 3 + 1] += velocities[i * 3 + 1];
|
||||
positions[i * 3 + 2] += velocities[i * 3 + 2];
|
||||
velocities[i * 3 + 1] -= 0.01; // гравитация
|
||||
}
|
||||
particles.geometry.attributes.position.needsUpdate = true;
|
||||
particles.material.opacity = this.life;
|
||||
this.life -= 0.02;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Эффект при получении урона
|
||||
showDamageEffect(x, z) {
|
||||
const effect = this.createParticleSystem(x, 1, z, 0xff0000, 15);
|
||||
this.particles.push(effect);
|
||||
}
|
||||
|
||||
// Эффект при получении денег
|
||||
showMoneyEffect(x, z) {
|
||||
const effect = this.createParticleSystem(x, 1, z, 0xffd700, 10);
|
||||
this.particles.push(effect);
|
||||
}
|
||||
|
||||
// Эффект при лечении
|
||||
showHealEffect(x, z) {
|
||||
const effect = this.createParticleSystem(x, 1, z, 0x00ff00, 15);
|
||||
this.particles.push(effect);
|
||||
}
|
||||
|
||||
// Эффект при убийстве врага
|
||||
showKillEffect(x, z) {
|
||||
const effect = this.createParticleSystem(x, 1, z, 0xff4444, 25);
|
||||
this.particles.push(effect);
|
||||
}
|
||||
|
||||
// Обновить все частицы
|
||||
update() {
|
||||
for (let i = this.particles.length - 1; i >= 0; i--) {
|
||||
const p = this.particles[i];
|
||||
p.update();
|
||||
if (p.life <= 0) {
|
||||
this.scene.remove(p.mesh);
|
||||
p.mesh.geometry.dispose();
|
||||
p.mesh.material.dispose();
|
||||
this.particles.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Тряска экрана
|
||||
shakeScreen(intensity = 0.5) {
|
||||
const originalPos = Game.camera.position.clone();
|
||||
const shake = () => {
|
||||
Game.camera.position.x = originalPos.x + (Math.random() - 0.5) * intensity;
|
||||
Game.camera.position.y = originalPos.y + (Math.random() - 0.5) * intensity;
|
||||
};
|
||||
|
||||
const interval = setInterval(shake, 30);
|
||||
setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
Game.camera.position.copy(originalPos);
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// Плавающий текст
|
||||
createFloatingText(x, y, z, text, color = '#ffffff') {
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
canvas.width = 256;
|
||||
canvas.height = 64;
|
||||
|
||||
context.fillStyle = 'transparent';
|
||||
context.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
context.font = 'bold 32px Arial';
|
||||
context.fillStyle = color;
|
||||
context.textAlign = 'center';
|
||||
context.fillText(text, 128, 40);
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas);
|
||||
const material = new THREE.SpriteMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
opacity: 1
|
||||
});
|
||||
|
||||
const sprite = new THREE.Sprite(material);
|
||||
sprite.position.set(x, y + 2, z);
|
||||
sprite.scale.set(2, 0.5, 1);
|
||||
|
||||
this.scene.add(sprite);
|
||||
|
||||
// Анимация всплывания
|
||||
const animate = () => {
|
||||
sprite.position.y += 0.05;
|
||||
sprite.material.opacity -= 0.02;
|
||||
if (sprite.material.opacity <= 0) {
|
||||
this.scene.remove(sprite);
|
||||
texture.dispose();
|
||||
material.dispose();
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
animate();
|
||||
}
|
||||
|
||||
// Очистка всех эффектов
|
||||
dispose() {
|
||||
this.particles.forEach(p => {
|
||||
this.scene.remove(p.mesh);
|
||||
p.mesh.geometry.dispose();
|
||||
p.mesh.material.dispose();
|
||||
});
|
||||
this.particles = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Глобальный экземпляр
|
||||
window.Effects = null;
|
||||
135
Hommie/index.html
Normal file
135
Hommie/index.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Симулятор Бомжа 3D</title>
|
||||
<link rel="stylesheet" href="css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="game-wrapper">
|
||||
<div id="game-header">
|
||||
<h1>🏚️ СИМУЛЯТОР БОМЖА 3D</h1>
|
||||
<div id="day-time">
|
||||
<span id="day-display">День 1</span>
|
||||
<span id="time-display">08:00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="game-container">
|
||||
<div id="game-canvas"></div>
|
||||
|
||||
<!-- UI Overlay -->
|
||||
<div id="ui-overlay">
|
||||
<!-- Stats Panel -->
|
||||
<div id="stats-panel" class="panel">
|
||||
<div class="stat-row">
|
||||
<div class="stat-icon">❤️</div>
|
||||
<div class="stat-bar-container">
|
||||
<div class="stat-bar health-bar" id="health-bar"></div>
|
||||
</div>
|
||||
<span class="stat-value" id="health-value">100</span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-icon">⚡</div>
|
||||
<div class="stat-bar-container">
|
||||
<div class="stat-bar energy-bar" id="energy-bar"></div>
|
||||
</div>
|
||||
<span class="stat-value" id="energy-value">100</span>
|
||||
</div>
|
||||
<div class="stat-row">
|
||||
<div class="stat-icon">💰</div>
|
||||
<span class="stat-value money-value" id="money-value">0₽</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Location Bar -->
|
||||
<div id="location-bar">
|
||||
<span id="location-icon">🏙️</span>
|
||||
<span id="location-name">Улица</span>
|
||||
</div>
|
||||
|
||||
<!-- Inventory Panel -->
|
||||
<div id="inventory-panel" class="panel">
|
||||
<div class="panel-header">
|
||||
<span>🎒 Рюкзак</span>
|
||||
<span id="inv-count">0/10</span>
|
||||
</div>
|
||||
<div id="inventory-grid"></div>
|
||||
</div>
|
||||
|
||||
<!-- Locations Menu -->
|
||||
<div id="locations-panel" class="panel">
|
||||
<div class="panel-header">🚇 Локации</div>
|
||||
<button class="loc-btn" data-location="street" onclick="Game.changeLocation('street')">
|
||||
<span>🏙️</span> Улица
|
||||
</button>
|
||||
<button class="loc-btn" data-location="subway" onclick="Game.changeLocation('subway')">
|
||||
<span>🚇</span> Метро
|
||||
</button>
|
||||
<button class="loc-btn" data-location="park" onclick="Game.changeLocation('park')">
|
||||
<span>🌳</span> Парк
|
||||
</button>
|
||||
<button class="loc-btn" data-location="hospital" onclick="Game.changeLocation('hospital')">
|
||||
<span>🏥</span> Больница
|
||||
</button>
|
||||
<button class="loc-btn" data-location="shelter" onclick="Game.changeLocation('shelter')">
|
||||
<span>🏠</span> Приют
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Action Hint -->
|
||||
<div id="action-hint">Нажми <span class="key">E</span></div>
|
||||
</div>
|
||||
|
||||
<!-- Message Modal -->
|
||||
<div id="message-modal" class="modal">
|
||||
<div id="message-icon"></div>
|
||||
<div id="message-text"></div>
|
||||
<button class="modal-btn" onclick="UI.closeMessage()">ОК</button>
|
||||
</div>
|
||||
|
||||
<!-- Title Screen -->
|
||||
<div id="title-screen">
|
||||
<div class="title-logo">🏚️</div>
|
||||
<h1>СИМУЛЯТОР БОМЖА 3D</h1>
|
||||
<p class="subtitle">Выживание на улице</p>
|
||||
<button class="start-btn" onclick="initGame()">НАЧАТЬ ИГРУ</button>
|
||||
<div class="controls-info">
|
||||
<div><span class="key">W A S D</span> Ходить</div>
|
||||
<div><span class="key">E</span> Взаимодействовать</div>
|
||||
<div><span class="key">I</span> Инвентарь</div>
|
||||
<div><span class="key">C</span> Крафтинг</div>
|
||||
<div><span class="key">Q</span> Квесты</div>
|
||||
<div><span class="key">P</span> Магазин</div>
|
||||
<div><span class="key">L</span> Ломбард</div>
|
||||
<div><span class="key">M</span> Карта</div>
|
||||
<div><span class="key">ПРОБЕЛ</span> Атаковать</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="game-footer">
|
||||
<span>🏚️ Homeless Simulator 3D v1.0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/EffectComposer.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/RenderPass.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/UnrealBloomPass.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/FilmPass.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/LuminosityHighPassShader.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/shaders/CopyShader.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/postprocessing/ShaderPass.js"></script>
|
||||
<script src="js/data.js"></script>
|
||||
<script src="js/textures.js"></script>
|
||||
<script src="js/audio.js"></script>
|
||||
<script src="js/player3d.js"></script>
|
||||
<script src="js/map3d.js"></script>
|
||||
<script src="js/objects3d.js"></script>
|
||||
<script src="js/effects3d.js"></script>
|
||||
<script src="js/ui.js"></script>
|
||||
<script src="js/game3d.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
386
Hommie/style.css
Normal file
386
Hommie/style.css
Normal file
@@ -0,0 +1,386 @@
|
||||
/* СИМУЛЯТОР БОМЖА 3D - Styles */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Roboto:wght@400;700&display=swap');
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary: #e94560;
|
||||
--secondary: #4ecdc4;
|
||||
--dark: #1a1a2e;
|
||||
--darker: #0f0f23;
|
||||
--gold: #f39c12;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--dark) 0%, #16213e 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#game-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
#game-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
padding: 15px 25px;
|
||||
background: rgba(26, 26, 46, 0.95);
|
||||
border-radius: 15px;
|
||||
border: 2px solid var(--primary);
|
||||
box-shadow: 0 0 30px rgba(233, 69, 96, 0.3);
|
||||
}
|
||||
|
||||
#game-header h1 {
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 16px;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
#day-time {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
#day-time span {
|
||||
padding: 8px 15px;
|
||||
background: rgba(0,0,0,0.4);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#day-display { color: var(--gold); }
|
||||
#time-display { color: var(--secondary); }
|
||||
|
||||
/* Game Container */
|
||||
#game-container {
|
||||
position: relative;
|
||||
width: 900px;
|
||||
height: 600px;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
border: 3px solid var(--primary);
|
||||
box-shadow: 0 0 50px rgba(233, 69, 96, 0.4);
|
||||
}
|
||||
|
||||
#game-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* UI Overlay */
|
||||
#ui-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#ui-overlay > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.panel {
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
border: 2px solid var(--primary);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
padding: 10px 15px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 10px;
|
||||
color: var(--primary);
|
||||
border-bottom: 1px solid rgba(233, 69, 96, 0.3);
|
||||
}
|
||||
|
||||
/* Stats Panel */
|
||||
#stats-panel {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.stat-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-row:last-child { margin-bottom: 0; }
|
||||
|
||||
.stat-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.stat-bar-container {
|
||||
width: 100px;
|
||||
height: 16px;
|
||||
background: #333;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.stat-bar {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
.health-bar { background: linear-gradient(90deg, #e74c3c, #ff6b6b); }
|
||||
.energy-bar { background: linear-gradient(90deg, #4ecdc4, #45b7aa); }
|
||||
|
||||
.stat-value {
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 10px;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.money-value { color: var(--gold); }
|
||||
|
||||
/* Location Bar */
|
||||
#location-bar {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 10px 25px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
border: 2px solid var(--gold);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
#location-icon { font-size: 18px; }
|
||||
#location-name { color: var(--gold); }
|
||||
|
||||
/* Inventory Panel */
|
||||
#inventory-panel {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 15px;
|
||||
padding: 10px;
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
#inventory-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.inv-slot {
|
||||
aspect-ratio: 1;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 2px solid #555;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.inv-slot:hover { border-color: var(--primary); transform: scale(1.1); }
|
||||
.inv-slot.empty { opacity: 0.3; cursor: default; }
|
||||
.inv-slot.empty:hover { transform: none; border-color: #555; }
|
||||
|
||||
/* Locations Panel */
|
||||
#locations-panel {
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
right: 15px;
|
||||
width: 150px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.loc-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid #555;
|
||||
border-radius: 5px;
|
||||
color: #fff;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 7px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.loc-btn:hover { background: var(--primary); }
|
||||
.loc-btn.active { background: var(--primary); border-color: var(--gold); }
|
||||
|
||||
/* Action Hint */
|
||||
#action-hint {
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding: 8px 20px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
border: 1px solid #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#action-hint.visible { opacity: 1; }
|
||||
|
||||
.key {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
background: var(--primary);
|
||||
border-radius: 4px;
|
||||
margin: 0 3px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
.modal {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.95);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
border: 3px solid var(--primary);
|
||||
text-align: center;
|
||||
display: none;
|
||||
z-index: 100;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.modal.show { display: block; }
|
||||
|
||||
#message-icon {
|
||||
font-size: 40px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#message-text {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
padding: 12px 30px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 9px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal-btn:hover { background: #ff6b6b; }
|
||||
|
||||
/* Title Screen */
|
||||
#title-screen {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#title-screen.hidden { display: none; }
|
||||
|
||||
.title-logo {
|
||||
font-size: 60px;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
#title-screen h1 {
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 20px;
|
||||
color: var(--primary);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #888;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 15px 50px;
|
||||
font-family: 'Press Start 2P', cursive;
|
||||
font-size: 11px;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.start-btn:hover { transform: scale(1.05); }
|
||||
|
||||
.controls-info {
|
||||
margin-top: 30px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
font-size: 9px;
|
||||
color: #555;
|
||||
justify-content: center;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-15px); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
#game-footer {
|
||||
font-size: 10px;
|
||||
color: rgba(255,255,255,0.3);
|
||||
}
|
||||
Reference in New Issue
Block a user