From 3870fe54d496bf0c4caf84b11e37727b34e60278 Mon Sep 17 00:00:00 2001 From: "maxim.dolgolyov" Date: Mon, 23 Feb 2026 21:16:00 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?Hommie=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Hommie/audio.js | 303 +++++++++++++++++++++++ Hommie/data.js | 588 ++++++++++++++++++++++++++++++++++++++++++++ Hommie/effects3d.js | 166 +++++++++++++ Hommie/index.html | 135 ++++++++++ Hommie/style.css | 386 +++++++++++++++++++++++++++++ 5 files changed, 1578 insertions(+) create mode 100644 Hommie/audio.js create mode 100644 Hommie/data.js create mode 100644 Hommie/effects3d.js create mode 100644 Hommie/index.html create mode 100644 Hommie/style.css diff --git a/Hommie/audio.js b/Hommie/audio.js new file mode 100644 index 0000000..5539c98 --- /dev/null +++ b/Hommie/audio.js @@ -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(); diff --git a/Hommie/data.js b/Hommie/data.js new file mode 100644 index 0000000..1538909 --- /dev/null +++ b/Hommie/data.js @@ -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; +} diff --git a/Hommie/effects3d.js b/Hommie/effects3d.js new file mode 100644 index 0000000..3c66f53 --- /dev/null +++ b/Hommie/effects3d.js @@ -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; diff --git a/Hommie/index.html b/Hommie/index.html new file mode 100644 index 0000000..b96af02 --- /dev/null +++ b/Hommie/index.html @@ -0,0 +1,135 @@ + + + + + + Симулятор Бомжа 3D + + + +
+
+

🏚️ СИМУЛЯТОР БОМЖА 3D

+
+ День 1 + 08:00 +
+
+ +
+
+ + +
+ +
+
+
❤️
+
+
+
+ 100 +
+
+
+
+
+
+ 100 +
+
+
💰
+ 0₽ +
+
+ + +
+ 🏙️ + Улица +
+ + +
+
+ 🎒 Рюкзак + 0/10 +
+
+
+ + +
+
🚇 Локации
+ + + + + +
+ + +
Нажми E
+
+ + + + + +
+ +

СИМУЛЯТОР БОМЖА 3D

+

Выживание на улице

+ +
+
W A S D Ходить
+
E Взаимодействовать
+
I Инвентарь
+
C Крафтинг
+
Q Квесты
+
P Магазин
+
L Ломбард
+
M Карта
+
ПРОБЕЛ Атаковать
+
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + diff --git a/Hommie/style.css b/Hommie/style.css new file mode 100644 index 0000000..c410871 --- /dev/null +++ b/Hommie/style.css @@ -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); +}