const { Telegraf, Scenes, session, Markup, Stage, Composer } = require('telegraf'); const schedule = require('node-schedule'); const { faker, fakerRU } = require('@faker-js/faker'); // Подключаем необходимые библиотеки const { Op } = require('sequelize'); const sequelize = require('./db'); // Подключение базы данных // Подключаем обработчики const utils = require('./utils'); const handlers = require('./handlers'); const { phones, expToUp, UserModel, CharacterModel, InventoryModel, ItemsModel, Enemy, Location, Battle, StolenCardsModel, WorldModel, PropertyModel, AFKPropertyModel, BusinessModel, BlockModel, EnterpriseModel, WarehouseModel, TruckModel, ResourcePriceModel, SkillsModel, DailyModel } = global.config const rpg = new Composer(); rpg.use(async (ctx, next) => { if (ctx.update.edited_channel_post) return console.log(`[RPG] Channel post updated`); let id = ctx.from.id let username = ctx.from.username; if (username == null) username = ctx.from.id; const currentTime = utils.getCurrentTime(); switch (ctx.updateType) { case 'message': console.log(`${currentTime}: [RPG] ${username}: ${ctx.update.message.text}`); break; case 'callback_query': console.log(`${currentTime}: [RPG] ${username}: ${ctx.update.callback_query.data}`); break; case 'edited_message': console.log(`${currentTime}: [RPG] ${username} edited: ${ctx.update.edited_message.text}`); break; default: console.log(`${currentTime}: [RPG] Unknown update type:`, ctx.update); } await next(); // Передаем управление следующему middleware }); rpg.hears('RPG', async (ctx) => { let message = `Меню:\n`; const buttons = []; buttons.push([{ text: 'Мой Персонаж', callback_data: 'rpg_profile' }]); return await ctx.reply(message, Markup.inlineKeyboard(buttons).resize()); }); rpg.action('rpg_profile', async (ctx) => { const telegramId = ctx.from.id; // Ищем персонажа const character = await CharacterModel.findOne({ where: { telegram_id: telegramId } }); const stolenCards = await StolenCardsModel.findAll({ where: { userId: character.telegram_id } }); if (!character) { return ctx.reply('Персонаж не найден. Создайте нового персонажа, чтобы начать игру!'); } // Формируем профиль const profile = ` 🎭 Профиль персонажа 👤 Имя: ${character.name || 'Не указано'} 🏆 Уровень: ${character.level} (${character.exp}/${utils.spaces(expToUp[character.level])}) ❤️ Здоровье: ${character.hp}/${character.max_hp} 🔥 Стамина: ${character.stamina}/${character.max_stamina} 💪 Сила (F): ${character.force} 🧠 Интеллект (I): ${character.intelligence} 🛡️ Устойчивость (R): ${character.resilience} 🔋 Выносливость (E): ${character.endurance} ➕ Свободные очки F.I.R.E: ${character.firePoints} 💰 Баланс: ${utils.spaces(character.dirtymoney)}Ð 🃏 Украденные карты: ${stolenCards.length} `; const buttons = [ [Markup.button.callback('💳 Карточки', 'view_cards')], [Markup.button.callback('🎒 Инвентарь', 'inventory')], ]; if (character.firePoints >= 1) { buttons.push([Markup.button.callback('➕ Распределить очки', 'fire_distribute')]); } buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]); // Отправляем сообщение ctx.editMessageText(profile.trim(), Markup.inlineKeyboard(buttons)); }); // Основная команда для вывода кнопок распределения F.I.R.E. rpg.action('fire_distribute', async (ctx) => { // Получаем данные пользователя const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character) { return ctx.reply('Вы ещё не создали персонажа!'); } // Проверяем, есть ли свободные очки if (character.firePoints <= 0) { return ctx.reply('У вас нет свободных очков для распределения.'); } // Формируем текст сообщения const message = ` 🔥 Распределение очков F.I.R.E 🔥 У вас есть ${character.firePoints} свободных очков. Выберите характеристику, чтобы увеличить её уровень: 💪 Сила (F): ${character.force} 🧠 Интеллект (I): ${character.intelligence} 🛡️ Устойчивость (R): ${character.resilience} 🔋 Выносливость (E): ${character.endurance} `; // Создаём кнопки для каждой характеристики const keyboard = Markup.inlineKeyboard([ Markup.button.callback('💪', 'increase_force'), Markup.button.callback('🧠', 'increase_intelligence'), Markup.button.callback('🎯', 'increase_resilience'), Markup.button.callback('💖', 'increase_endurance'), ], { columns: 2 }); // Отправляем сообщение с кнопками await ctx.reply(message, keyboard); }); // Обработчики для нажатий на кнопки rpg.action(/^increase_(force|intelligence|resilience|endurance)$/, async (ctx) => { const attribute = ctx.match[1]; // Получаем характеристику из callback_data const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character) { return ctx.answerCbQuery('Персонаж не найден.'); } // Проверяем наличие свободных очков if (character.firePoints <= 0) { return ctx.answerCbQuery('У вас недостаточно очков!'); } // Прокачиваем выбранную характеристику switch (attribute) { case 'force': character.force++; break; case 'intelligence': character.intelligence++; break; case 'resilience': character.resilience++; break; case 'endurance': character.endurance++; break; } // Уменьшаем количество очков и сохраняем изменения character.firePoints--; await character.save(); // Отправляем обновлённое сообщение const updatedMessage = ` 🔥 Распределение очков F.I.R.E 🔥 У вас есть ${character.firePoints} свободных очков. Выберите характеристику, чтобы увеличить её уровень: 💪 Сила (F): ${character.force} 🧠 Интеллект (I): ${character.intelligence} 🛡️ Устойчивость (R): ${character.resilience} 🔋 Выносливость (E): ${character.endurance} `; await ctx.editMessageText(updatedMessage, { reply_markup: ctx.update.callback_query.message.reply_markup }); ctx.answerCbQuery(`Вы увеличили ${attribute.toUpperCase()}!`); }); const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // Хранилище для отслеживания пользователей, которые уже прошли ввод const usersCrimeNet = new Set(); rpg.hears('CampFireGG.Crime', async (ctx) => { const userId = ctx.from.id; const character = await CharacterModel.findOne({ where: { telegram_id: userId } }); if (!usersCrimeNet.has(userId) && character.level === 1) { // Если пользователь вводит команду впервые usersCrimeNet.add(userId); const username = ctx.from.username || 'User'; const randomServer = `secure.net-${Math.floor(Math.random() * 99 + 1)}.campfiregg.crime`; const sshCommand = `ssh ${username}@${randomServer}`; // Сообщение для симуляции const lines = [ `${sshCommand}`, `> Connecting to secure servers with RSA key...`, `> Authentication in progress...`, `> User "${username}" identified.`, `> Accessing CampFireGG.Crime...`, `> Connection established. Welcome, ${username}.`, ]; // Отправляем первое сообщение let sentMessage = await ctx.reply('> Initializing CampFireGG.Crime...'); // Симуляция показа строки ssh блоками let currentText = ''; let index = 0; while (index < lines[0].length) { const blockSize = Math.floor(Math.random() * 3) + 2; // Генерируем блок от 2 до 4 символов currentText += lines[0].slice(index, index + blockSize); // Добавляем блок текста index += blockSize; // Увеличиваем индекс await ctx.telegram.editMessageText(sentMessage.chat.id, sentMessage.message_id, undefined, currentText); await delay(100); // Задержка между блоками } // Последующие строки добавляются полностью, по одной for (let i = 1; i < lines.length; i++) { await delay(1500); // Задержка между строками currentText += `\n${lines[i]}`; await ctx.telegram.editMessageText(sentMessage.chat.id, sentMessage.message_id, undefined, currentText); } // Финальное меню после задержки await delay(2000); const buttons = [ [Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('📊 Профиль', 'rpg_profile')], ]; //buttons.push([Markup.button.callback('💳 Карточки', 'view_cards')]); // Добавляем кнопку сообщения, если уровень персонажа равен 1 if (character && character.level === 1) { buttons.push([Markup.button.callback('📩 (1) Сообщение', '1st_message')]); } await ctx.telegram.editMessageText( sentMessage.chat.id, sentMessage.message_id, undefined, `🚨 Добро пожаловать в CampFireGG.Crime 🚨\n\n`, Markup.inlineKeyboard(buttons) ) } else { // Показываем только меню, если команда уже была введена await ctx.reply( `🚨 Добро пожаловать в CampFireGG.Crime 🚨\n\n`, Markup.inlineKeyboard([ [Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('📊 Профиль', 'rpg_profile')], [Markup.button.callback('💰 Магазин', 'shop')], ]) ); } }); rpg.action('1st_message', async (ctx) => { await ctx.answerCbQuery(); const message = `👤 Сообщение от неизвестного пользователя: \`\`\` "Добро пожаловать в CampFireGG.Crime. Если ты здесь, значит, у тебя есть вопросы. Забудь о них. Просто слушай." 🔥 F.I.R.E — твой путь к успеху: "Каждый из нас что-то может. Кто-то силён, кто-то вынослив, а кто-то дохуя умный. В мире CampFireGG.Crime это называется F.I.R.E. Запомни эти буквы — от них зависит твоя жизнь здесь." Force: Твоя мощь. Определяет, сколько импакта ты можешь внести. Чем выше, тем проще ликвидировать цель. Intelligence: Твой интеллект. Чем ты умнее, тем больше шансов, что операция пройдет гладко. А ещё мозги ускоряют твое обучение. Resilience: Твоя стойкость. Сколько времени тебе нужно, чтобы встать на ноги после провала? Чем выше этот параметр, тем быстрее ты восстанавливаешься. Endurance: Твоя выносливость. Без неё ты не сможешь много работать, а значит, и много зарабатывать. 💰 Деньги? Грязные. Иначе здесь не бывает. "Всё, что ты заработаешь здесь, будет грязным. Потратить можно только здесь, но есть способ отмыть деньги и перенести их на свой основной счёт. Всё зависит от твоего желания и возможностей." 💳 Карты? Да, их можно обналичить. "Карточки — это всегда плюс. Взломал, достал, получил деньги. Всё просто. Главное — не профукай их." 🕶️ Последний совет: "Каждое действие — это риск. Чем больше ты идёшь на риск, тем больше получаешь. Но если ошибёшься, можешь встрять. Так что думай." "Добро пожаловать. Удачи тебе, новобранец." \`\`\` `; await ctx.replyWithMarkdownV2(message, { disable_web_page_preview: true }); }); rpg.action('view_cards', async (ctx) => { try { // Получаем пользователя по Telegram ID const character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.reply('Персонаж не найден!'); } // Получаем все украденные карточки для этого пользователя const stolenCards = await StolenCardsModel.findAll({ where: { userId: character.telegram_id } }); if (stolenCards.length === 0) { return ctx.reply('У вас нет украденных карточек.'); } const buttons = []; let message = 'Ваши украденные карты:\n\n'; stolenCards.forEach(card => { const lastFourDigits = card.cardNumber.slice(-4); // Берем последние 4 цифры карточки message += `🔒 Карта: *${lastFourDigits}\n`; // Генерация кнопок для брутфорса и покупки за лимит buttons.push([ Markup.button.callback(`💳 *${lastFourDigits}`, `brutecard_${card.id}`), //Markup.button.callback(`💰 *${lastFourDigits}`, `limitbuy_${card.id}`) Markup.button.callback('🔙 В меню', 'crime_menu'), ]); }); // Отправляем сообщение с кнопками await ctx.reply(message, Markup.inlineKeyboard(buttons)); } catch (error) { console.error('Ошибка при выводе карточек:', error); await ctx.reply('Произошла ошибка при получении ваших карточек.'); } }); rpg.action(/limitbuy_/, async (ctx) => { try { const cardId = ctx.match.input.split('_')[1]; // ID карточки const card = await StolenCardsModel.findByPk(cardId); // Получаем карточку if (!card) { return ctx.answerCbQuery('Карточка не найдена.'); } const character = await CharacterModel.findByPk(ctx.from.id); // Получаем персонажа if (!character) { return ctx.answerCbQuery('Персонаж не найден.'); } // Получаем 10 случайных предметов из базы const items = await ItemsModel.findAll({ limit: 10 }); // Если у пользователя нет записей о попытках, создаем их if (!attempts[ctx.from.id]) { attempts[ctx.from.id] = 0; } // Генерация кнопок с предметами const buttons = []; items.forEach(item => { const buttonText = `${item.name} - ¥${item.price}`; buttons.push([Markup.button.callback(buttonText, `purchase_${item.id}_${card.id}`)]); }); await ctx.reply( 'Выберите товар для покупки:\nПопробуйте угадать лимит карты.\n\n', Markup.inlineKeyboard(buttons) ); } catch (error) { console.error('Ошибка при обработке покупки через лимит:', error); } }); rpg.action(/purchase_/, async (ctx) => { try { const [_, itemId, cardId] = ctx.match.input.split('_'); const item = await ItemsModel.findByPk(itemId); const card = await StolenCardsModel.findByPk(cardId); const character = await CharacterModel.findByPk(ctx.from.id); if (!item || !card || !character) { return ctx.answerCbQuery('Ошибка при обработке покупки.'); } // Проверяем, не исчерпаны ли попытки if (attempts[ctx.from.id] >= 3) { // Сбрасываем попытки и уничтожаем карточку delete attempts[ctx.from.id]; await StolenCardsModel.destroy({ where: { id: cardId } }); return ctx.editMessageText( '❌ Все попытки исчерпаны! Карточка стала недействительной.' ); } // Если баланс карты меньше, чем цена товара, увеличиваем количество попыток if (card.balance < item.price) { attempts[ctx.from.id]++; await ctx.answerCbQuery(`Недостаточно средств! Попыток осталось: ${3 - ctx.session.attempts}`); return ctx.editMessageText(`Вы выбрали товар за ¥${item.price}, но у вас недостаточно средств.`); } // Если баланс карты достаточно для покупки, добавляем предмет в инвентарь character.InventoryModel.push(item.id); await character.save(); // Если товар электронный, начисляем процент от стоимости на счет if (item.type === 'digital') { const profit = Math.round(item.price * 0.2); // 20% от стоимости товара character.dirtymoney += profit; await character.save(); await ctx.answerCbQuery(`Вы приобрели ${item.name} и перепродали за ¥${profit}.`); } else { await ctx.answerCbQuery(`Вы купили ${item.name}.`); } // Блокируем карту после покупки card.destroy({ where: { id: cardId } }); await card.save(); // Оповещаем пользователя await ctx.editMessageText(`${item.name} успешно куплен! Карта заблокирована.`); } catch (error) { console.error('Ошибка при обработке покупки:', error); } }); rpg.action(/purchase_fail_/, async (ctx) => { try { const [_, itemId, cardId] = ctx.match.input.split('_'); const item = await ItemsModel.findByPk(itemId); const card = await StolenCardsModel.findByPk(cardId); if (!item || !card) { return ctx.answerCbQuery('Ошибка при проверке предмета или карты.'); } await ctx.answerCbQuery('Недостаточно средств для покупки.'); } catch (error) { console.error('Ошибка при обработке неудачной покупки:', error); } }); // Обработка попытки взлома карточки rpg.action(/brutecard_/, async (ctx) => { const cardId = ctx.match.input.split('_')[1]; // Получаем ID карточки из callback_data const card = await StolenCardsModel.findByPk(cardId); // Ищем карточку в базе данных const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); // Загружаем предметы из инвентаря if (!card) { return ctx.answerCbQuery('Карточка не найдена.'); } // Проверяем наличие "Эмулятора картридера" в инвентаре const emulator = inventory.find((item) => item.text_id === 'cardreader_emulator'); if (!emulator) { return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".'); } // Удаляем "Эмулятор картридера" из инвентаря await InventoryModel.destroy({ where: { id: emulator.id } }); const buttonsCount = 10; // Количество кнопок const winButton = utils.rand(1, buttonsCount); // Выбираем случайную кнопку для успешного пина const maxAttempts = 3; // Максимальное количество попыток // Если у пользователя нет записей о попытках, создаем их if (!attempts[ctx.from.id]) { attempts[ctx.from.id] = 0; } // Генерация кнопок const buttons = []; let fakePin = 1234 for (let i = 1; i <= buttonsCount; i++) { if (i === winButton) { // Если это победная кнопка (правильный ПИН) buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` }); } else { fakePin = faker.finance.pin() // Если это неудачная кнопка (случайный ПИН) buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` }); } } // Разбиваем кнопки на строки по 5 кнопок const rows = []; while (buttons.length > 0) { rows.push(buttons.splice(0, 5)); // Разбиваем на подмассивы по 5 кнопок } // Сортируем кнопки случайным образом const keyboard = Markup.inlineKeyboard( rows.sort(() => Math.random() - 0.5).map((row) => row) // Сортируем строки кнопок случайным образом ); // Отправляем сообщение с клавиатурой await ctx.reply( `💳 Брутфорс карты\n ━━━━━━━━━━━━━━━━━━━━ ${card.bankName.toUpperCase()} ━━━━━━━━━━━━━━━━━━━━ Номер: ${card.cardNumber} CVV: ${card.cvv} Владелец: ${card.holderName.toUpperCase()} Срок действия: ${card.expiresDate} ━━━━━━━━━━━━━━━━━━━━ Попробуйте подобрать правильный ПИН-код. У вас есть ${maxAttempts - attempts[ctx.from.id]} попыток. "Эмулятор картридера" был использован.`, keyboard ); }); // Обработка неудачной попытки взлома rpg.action(/brute_fail_/, async (ctx) => { const cardId = ctx.match.input.split('_')[2]; const maxAttempts = 3; // Максимальное количество попыток // Увеличиваем количество попыток attempts[ctx.from.id]++; // Проверяем, не исчерпаны ли попытки if (attempts[ctx.from.id] >= maxAttempts) { // Сбрасываем попытки и уничтожаем карточку delete attempts[ctx.from.id]; await StolenCardsModel.destroy({ where: { id: cardId } }); return ctx.editMessageText( '❌ Все попытки исчерпаны! Карточка стала недействительной.' ); } // Ответ на нажатие кнопки ctx.answerCbQuery(`❌ Неправильный ПИН. Попыток осталось: ${maxAttempts - attempts[ctx.from.id]}`); // Обновляем клавиатуру const card = await StolenCardsModel.findByPk(cardId); const buttonsCount = 10; const winButton = utils.rand(1, buttonsCount); const buttons = []; let fakePin = 1234 for (let i = 1; i <= buttonsCount; i++) { fakePin = faker.finance.pin() if (i === winButton) { buttons.push({ text: `${card.pin}`, callback_data: `brute_success_${cardId}` }); } else { buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` }); } } const rows = []; while (buttons.length > 0) { rows.push(buttons.splice(0, 5)); } const keyboard = Markup.inlineKeyboard( rows.sort(() => Math.random() - 0.5).map((row) => row) ); // Обновляем сообщение с клавиатурой await ctx.editMessageText( `💳 Брутфорс карты\n ━━━━━━━━━━━━━━━━━━━━ ${card.bankName.toUpperCase()} ━━━━━━━━━━━━━━━━━━━━ Номер: ${card.cardNumber} CVV: ${card.cvv} Владелец: ${card.holderName.toUpperCase()} Срок действия: ${card.expiresDate} ━━━━━━━━━━━━━━━━━━━━ Попробуйте подобрать правильный ПИН-код. У вас есть ${maxAttempts - attempts[ctx.from.id]} попыток.`, keyboard ); }); // Обработка успешной попытки взлома rpg.action(/brute_success_/, async (ctx) => { const cardId = ctx.match.input.split('_')[2]; const card = await StolenCardsModel.findByPk(cardId); if (!card) { return ctx.answerCbQuery('Карточка не найдена.'); } delete attempts[ctx.from.id]; const amount = card.balance; // Сумма денег за карточку const character = await CharacterModel.findByPk(ctx.from.id); // Увеличиваем грязные деньги пользователя character.dirtymoney += amount; await character.save(); // Удаляем карточку await card.destroy(); // Отправляем сообщение о выигрыше await ctx.editMessageText( `💳 Брутфорс карты\n ━━━━━━━━━━━━━━━━━━━━ ${card.bankName.toUpperCase()} ━━━━━━━━━━━━━━━━━━━━ Номер: ${card.cardNumber} CVV: ${card.cvv} Владелец: ${card.holderName.toUpperCase()} Срок действия: ${card.expiresDate} ━━━━━━━━━━━━━━━━━━━━ ✅ Взлом успешен! Вы получили Ð${amount}.` ); }); // Обработчики кнопок rpg.action('crime_missions', async (ctx) => { await ctx.answerCbQuery(); await ctx.editMessageText(`💼 **Задачи:**\n Для карманных краж тебе не нужно ничего, главное не попадись.\n А вот после ограбления магазина в переулке не спрячешься, без тачки на миссию нельзя.`, Markup.inlineKeyboard([ [{ text: 'Карманные кражи [1 lvl.]', callback_data: `POCKET_ACTION` }], [{ text: 'Магазин [5 lvl.]', callback_data: `SHOP_ACTION` }], //[{text: 'Банкомат', callback_data: `WIP`}], // [{text: 'Банковское отделение', callback_data: `WIP`}], //[{text: 'Угон', callback_data: `WIP`}], // [{text: 'Ювелирка', callback_data: `WIP`}], //[{text: 'Банк', callback_data: `WIP`}], [{ text: '🔙 В меню', callback_data: `crime_menu` }] ]), ); }); rpg.action(`POCKET_ACTION`, async (ctx) => { let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); let pocketsteal = character.pocketstealcd if (character.level < 1) return ctx.editMessageText('Доступно с 1 уровня!') if (character.stamina < 10) return ctx.editMessageText('Вы устали!') let cooldown = utils.setCooldown(character, 3600, pocketsteal) if (character.pocketstealcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`); character.pocketstealcd = cooldown.endTime character.stamina -= 10 character.save() ctx.editMessageText('Выберите объект', Markup.inlineKeyboard([ [ { text: 'Карман', callback_data: `POCKET_TARGET` }, { text: 'Бумажник', callback_data: `POCKET_WALLET` }, { text: 'Сумка', callback_data: `POCKET_BAG` } ] ])) }); rpg.action(`POCKET_TARGET`, async (ctx) => { ctx.editMessageText('В кармане обнаружено', Markup.inlineKeyboard([ [ { text: 'Деньги', callback_data: `MONEY_IN_POCKET` }, { text: 'Телефон', callback_data: `PHONE` } ] ])) }); rpg.action(`MONEY_IN_POCKET`, async (ctx) => { // Получаем пользователя и его персонажа //let user = await UserModel.findByPk(ctx.from.id); let character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.editMessageText('У вас нет персонажа. Создайте его перед началом.'); } // Расчёт шанса на успешную кражу let baseChance = 40; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 if (randomRoll > chance) { Exp(ctx, character, 1) return ctx.editMessageText('Вы были замечены во время кражи.'); } // Успешная кража let moneyIn = utils.rand(5, 1000); Exp(ctx, character, character.intelligence + 3) character.dirtymoney += moneyIn; await character.save(); return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из кармана.`); }); rpg.action(`PHONE`, async (ctx) => { let character = await CharacterModel.findByPk(ctx.from.id); //let user = await UserModel.findByPk(ctx.from.id) let property = await PropertyModel.findByPk(ctx.from.id); // Расчёт шанса на успешную кражу let baseChance = 20; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 if (chance < randomRoll) { Exp(ctx, character, 1) return ctx.editMessageText('Вы были замечены во время кражи.') } let randPhone = utils.rand(1, 10) if (property.mobile.name) { let dirtyMoney = Math.round(phones[randPhone].price / 100 * 70) Exp(ctx, character, character.intelligence + 10) character.dirtymoney += dirtyMoney return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`) } property.mobile = phones[randPhone] await character.save() await property.save() return ctx.editMessageText(`Вы успешно украли ${phones[randPhone].name} из кармана.`) }); rpg.action(`POCKET_WALLET`, async (ctx) => { ctx.editMessageText('В бумажнике обнаружено', Markup.inlineKeyboard([ [ { text: 'Деньги', callback_data: `MONEY_IN_WALLET` }, { text: 'Карточка', callback_data: `CARD_IN_WALLET` } ] ])) }); rpg.action(`MONEY_IN_WALLET`, async (ctx) => { //let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); // Расчёт шанса на успешную кражу let baseChance = 35; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 if (chance < randomRoll) { Exp(ctx, character, 1) return ctx.editMessageText('Вы были замечены во время кражи.') } let moneyIn = utils.rand(1000, 10000) Exp(ctx, character, character.intelligence + 5) character.dirtymoney += moneyIn character.save() return ctx.editMessageText(`Вы успешно украли Ð${utils.spaces(moneyIn)} из бумажника.`) }); rpg.action(`CARD_IN_WALLET`, async (ctx) => { //let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); let baseChance = 20; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 if (chance < randomRoll) { Exp(ctx, character, 2) return ctx.editMessageText('Вы были замечены во время кражи.') } Exp(ctx, character, character.intelligence + 7) generateCard(ctx.from.id) character.save() return ctx.editMessageText(`Вы успешно украли 💳 из бумажника.`) }); rpg.action(`POCKET_BAG`, async (ctx) => { //let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); let baseChance = 20; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 if (chance < randomRoll) { Exp(ctx, character, 1) return ctx.editMessageText('Вы были замечены во время кражи.') } let times = utils.rand(2, 20) let moneyIn = 0 let text = `` let values = 0 for (i = 1; i <= times; i++) { randomize = utils.rand(1, 100) switch (randomize) { case 2: values = utils.rand(10000, 50000) moneyIn += values text += `+ Ð${utils.spaces(values)}\n` break; case 7: values = utils.rand(10000, 100000) moneyIn += values text += `+ Ð${utils.spaces(values)}\n` break; default: values = utils.rand(100, 3000) moneyIn += values text += `+ Ð${utils.spaces(values)}\n` break; } } Exp(ctx, character, character.intelligence + times) character.dirtymoney += moneyIn character.save() return ctx.editMessageText(`Вы успешно украли сумку и сбыли все ценности из нее:\n${text}\nОбщий куш: Ð${utils.spaces(moneyIn)}`) }); rpg.action('crime_menu', async (ctx) => { await ctx.answerCbQuery(); await ctx.editMessageText( `💻 CampFireGG.Crime Menu`, Markup.inlineKeyboard([ [Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('📊 Профиль', 'rpg_profile')], [Markup.button.callback('💰 Магазин', 'shop')], ]) ); }); rpg.action('SHOP_ACTION', async (ctx) => { let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); let property = await PropertyModel.findByPk(ctx.from.id); if (character.level < 5) return ctx.editMessageText('Доступно с 5 уровня!') if (property.car1 == 0) return ctx.editMessageText('Для данного задания нужна тачка.') if (character.stamina < 25) return ctx.editMessageText('Вы устали!') let shoprobcd = character.shoprobcd let cooldown = utils.setCooldown(character, 3600, shoprobcd) if (character.shoprobcd > cooldown.currentTime) return ctx.editMessageText(`📛 Данное действие будет доступно через ${cooldown.timeLeftInMinutes} мин.`); character.shoprobcd = cooldown.endTime character.stamina -= 25 character.save() return ctx.editMessageText('Стадии:', Markup.inlineKeyboard([ [{ text: 'Взлом кассы', callback_data: `SHOP_CASH_BREAK` }], [{ text: 'Разбить кассу', callback_data: `SHOP_CASH_SMASH` }] ])) }); rpg.action(`SHOP_CASH_BREAK`, async (ctx) => { let character = await CharacterModel.findByPk(ctx.from.id); let baseChance = 20; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 let cashIn = utils.rand(1000, 10000) let timer = 1000 if (chance < randomRoll) { const keyboard = generateKeyboard(); ctx.deleteMessage() return ctx.reply('Касса закрыта, вы начали взлом замка:', keyboard); //ctx.editMessageText('Вы начали взлом кассы.'); //return ctx.scene.enter('LOCKPICK') } for (i = 0; i < utils.rand(character.endurance, character.endurance + 15); i++) { setTimeout(() => { cashIn += utils.rand(1000, 10000) ctx.editMessageText(`⏏️ Вы достали из кассы: Ð${utils.spaces(cashIn)}`) }, timer) timer += 500 } setTimeout(() => { Exp(ctx, character, character.intelligence + 25) character.dirtymoney += cashIn character.save() return ctx.editMessageText(`Вы достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([ [{ text: 'Завершить ограбление', callback_data: `SHOP_END` }] ])) }, timer + 300) }); rpg.action(`SHOP_CASH_SMASH`, async (ctx) => { let character = await CharacterModel.findByPk(ctx.from.id); let baseChance = 20; // Базовый шанс let chance = baseChance + character.intelligence * 2; // Увеличиваем шанс на 2% за каждый пункт "Разума". let randomRoll = utils.rand(0, 100); // Случайное число от 0 до 100 let cashIn = utils.rand(1000, 10000) let timer = 1000 if (chance < randomRoll) { Exp(ctx, character, 1) return ctx.editMessageText('Вы разбили кассовый аппарат, и сработала сигнализация. Вы сбежали.') } ctx.editMessageText('Вы разбили кассовый аппарат, и сработала сигнализация.') for (i = 0; i < utils.rand(character.endurance, character.endurance + 8); i++) { setTimeout(() => { cashIn += utils.rand(500, 5000) ctx.editMessageText(`⏏️ Вы в спешке достали из кассы: Ð${utils.spaces(cashIn)}`) }, timer) timer += 500 } setTimeout(() => { Exp(ctx, character, character.intelligence + 10) character.dirtymoney += cashIn character.save() return ctx.editMessageText(`Вы в спешке достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([ [{ text: 'Завершить ограбление', callback_data: `SHOP_END` }] ])) }, timer + 300) }); rpg.action(`SHOP_CASH_BREAK_SUCCESS`, async (ctx) => { let user = await UserModel.findByPk(ctx.from.id) let character = await CharacterModel.findByPk(ctx.from.id); delete attempts[ctx.from.id]; let cashIn = utils.rand(1000, 10000) let timer = 100 for (i = 0; i < utils.rand(character.endurance, character.endurance + 15); i++) { setTimeout(() => { cashIn += utils.rand(3000, 10000) ctx.editMessageText(`⏏️ Вы достали из кассы: Ð${utils.spaces(cashIn)}`) }, timer) timer += 500 } setTimeout(() => { Exp(ctx, character, character.intelligence + 30) character.dirtymoney += cashIn character.save() return ctx.editMessageText(`Вы достали из кассы Ð${utils.spaces(cashIn)}, пора валить.`, Markup.inlineKeyboard([ [{ text: 'Завершить ограбление', callback_data: `SHOP_END` }] ])) }, timer + 300) }); rpg.action(`SHOP_END`, async (ctx) => { return ctx.editMessageText('Ограбление завершено!') }); const attempts = {}; // Храним количество попыток взлома для каждого пользователя rpg.action(/lock_*/, async (ctx) => { const userId = ctx.from.id; const buttonId = ctx.update.callback_query.data; // Если пользователя нет в списке попыток, добавляем его if (!attempts[userId]) { attempts[userId] = 0; } // Увеличиваем количество попыток attempts[userId] += 1; // Проверяем, не исчерпаны ли попытки if (attempts[userId] >= 5) { delete attempts[userId]; // Сбрасываем попытки после провала return ctx.editMessageText('Взлом провалился. Замок остается нетронутым.'); } // Ответ на нажатие кнопки ctx.answerCbQuery(`Штифт не сдвинулся. Попыток осталось: ${5 - attempts[userId]}`); removeButton(ctx, buttonId); }); rpg.action('inventory', async (ctx) => { try { const character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.reply('Персонаж не найден!'); } // Загружаем предметы из инвентаря const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); if (inventory.length === 0) { return ctx.reply('Ваш инвентарь пуст.'); } let message = '*Ваш инвентарь:*\n\n'; // Группируем предметы по их состоянию (снаряженные/обычные) const equippedItems = inventory.filter((item) => item.equipped); const unequippedItems = inventory.filter((item) => !item.equipped); if (equippedItems.length > 0) { message += '🛡️ Снаряженные предметы:\n'; equippedItems.forEach((item) => { message += `- ${item.name} (${item.type})\n`; }); message += '\n'; } if (unequippedItems.length > 0) { message += '🎒 Предметы в инвентаре:\n'; unequippedItems.forEach((item) => { message += `- ${item.name} (${item.type})\n`; }); message += '\n'; } // Кнопки для взаимодействия const buttons = [ ...unequippedItems.map((item) => Markup.button.callback(`🔎 ${item.name}`, `view_item_${item.id}`) ), ...equippedItems.map((item) => Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`) ), ]; buttons.push(Markup.button.callback('🔙 В меню', 'crime_menu')); await ctx.editMessageText(message, Markup.inlineKeyboard(buttons, { columns: 2 })); } catch (error) { console.error('Ошибка при выводе инвентаря:', error); await ctx.reply('Произошла ошибка при отображении вашего инвентаря.'); } }); rpg.action(/view_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const item = await InventoryModel.findByPk(itemId); if (!item || item.telegram_id != ctx.from.id) { return ctx.reply('Этот предмет не найден в вашем инвентаре.'); } const character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.reply('Персонаж не найден.'); } if (item.equipped) { return ctx.reply(`${item.name} уже снаряжен.`); } const buttons = [] let message = `ℹ️ ${item.name}\n` message += ` 📜 ${item.description}\n\n 〰️Редкость: ${item.rarity}\n 🔤Тип: ${item.type}` if (!item.equipped) { buttons.push([Markup.button.callback(`🎯 Использовать ${item.name}`, `use_item_${item.id}`)]); } if (item.equipped && item.canBeEquipped) { buttons.push([Markup.button.callback(`🚫 Снять ${item.name}`, `unequip_item_${item.id}`)]); } await ctx.replyWithMarkdown(message, Markup.inlineKeyboard(buttons, { columns: 2 })); }); rpg.action(/use_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const item = await InventoryModel.findByPk(itemId); if (!item || item.telegram_id != ctx.from.id) { return ctx.reply('Этот предмет не найден в вашем инвентаре.'); } const character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.reply('Персонаж не найден.'); } if (item.equipped) { return ctx.reply(`${item.name} уже снаряжен.`); } if (!item.canBeEquipped) { return ctx.answerCbQuery(`🚫 ${item.name} нельзя сейчас использовать.`); } // Применяем эффекты предмета if (item.effectData) { const resultMessages = processEffects(character, item.effectData, true); await ctx.answerCbQuery(resultMessages, { show_alert: true }); } if (item.canBeEquipped && item.type != 'consumable') { item.equipped = true; await item.save(); ctx.reply(`Вы снарядили ${item.name}.`); } if (item.canBeEquipped && item.type == 'consumable') { await InventoryModel.destroy({ where: { id: item.id } }); ctx.reply(`Вы использовали ${item.name}.`); } }); rpg.action(/unequip_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const character = await CharacterModel.findByPk(ctx.from.id); const item = await InventoryModel.findByPk(itemId); if (!item || item.telegram_id != ctx.from.id) { return ctx.answerCbQuery('Этот предмет не найден в вашем инвентаре.'); } if (!item.equipped) { return ctx.answerCbQuery(`${item.name} не снаряжен.`); } if (item.effectData) { const resultMessages = processEffects(character, item.effectData, false); await ctx.editMessageText(resultMessages); } item.equipped = false; await item.save(); ctx.answerCbQuery(`Вы успешно сняли ${item.name}.`); }); rpg.action('shop', async (ctx) => { // Получаем текущую запись мира const world = await WorldModel.findOne({ where: { id: 1 } }); if (!world) { return ctx.reply('Магазин недоступен.'); } // Получаем ID предметов, доступных в магазине const { itemsInCrimeShop } = world; if (!itemsInCrimeShop || itemsInCrimeShop.length === 0) { return ctx.reply('Магазин пуст!'); } // Загружаем только те предметы, ID которых есть в массиве const items = await ItemsModel.findAll({ where: { id: itemsInCrimeShop } }); if (items.length === 0) { return ctx.reply('Магазин пуст!'); } let message = 'Добро пожаловать в магазин! Здесь вы можете купить предметы:\n\n'; // Генерируем кнопки, каждая кнопка будет в отдельной строке const buttons = items.map((item) => [ Markup.button.callback(`${item.name} - ${item.price}₽`, `buy_item_${item.id}`) ]); buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]); // Отправляем сообщение с клавиатурой await ctx.editMessageText(message, Markup.inlineKeyboard(buttons)); }); rpg.action(/buy_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const item = await ItemsModel.findByPk(itemId); const inventory = await InventoryModel.findAll({ where: { telegram_id: ctx.from.id } }); if (inventory.length >= 15) { return ctx.answerCbQuery('Инвентарь полон.', { show_alert: true }); } if (!item) { return ctx.reply('Предмет не найден.'); } const character = await CharacterModel.findByPk(ctx.from.id); if (!character) { return ctx.reply('Персонаж не найден.'); } if (character.balance < item.price) { return ctx.answerCbQuery('У вас недостаточно средств для покупки этого предмета.', { show_alert: true }); } // Снимаем деньги с баланса character.dirtymoney -= item.price; // Добавляем предмет в инвентарь await InventoryModel.create({ telegram_id: ctx.from.id, name: item.name, text_id: item.text_id, description: item.description, effectData: item.effectData, price: item.price, rarity: item.rarity, type: item.type, duration: item.duration, canBeEquipped: item.canBeEquipped, equipped: false }); await character.save(); ctx.answerCbQuery(`Вы успешно купили ${item.name} за ${item.price}₽!`, { show_alert: true }); }); //////////////////////////////////////////////////////////////////////////////////////////// CharacterModel.prototype.addEquippedItem = async function (item) { // Проверяем, находится ли предмет в инвентаре персонажа const inventoryItem = await InventoryModel.findOne({ where: { telegram_id: this.telegram_id, id: item.id }, }); if (!inventoryItem) { throw new Error('Предмет не найден в вашем инвентаре.'); } // Проверяем, не снаряжен ли уже этот предмет if (inventoryItem.equipped) { throw new Error('Этот предмет уже снаряжен.'); } // Снаряжаем предмет inventoryItem.equipped = true; await inventoryItem.save(); // Применяем эффекты предмета let resultMessages = []; if (inventoryItem.effectData) { resultMessages = processEffects(this, inventoryItem.effectData, true); await this.save(); // Сохраняем изменения в характеристиках персонажа } return resultMessages; }; CharacterModel.prototype.removeEquippedItem = async function (item) { // Проверяем, находится ли предмет в инвентаре персонажа const inventoryItem = await InventoryModel.findOne({ where: { telegram_id: this.telegram_id, id: item.id }, }); if (!inventoryItem || !inventoryItem.equipped) { throw new Error('Этот предмет не снаряжен.'); } // Снимаем предмет inventoryItem.equipped = false; await inventoryItem.save(); // Убираем эффекты предмета let resultMessages = []; if (inventoryItem.effectData) { resultMessages = processEffects(this, inventoryItem.effectData, false); await this.save(); // Сохраняем изменения в характеристиках персонажа } return resultMessages; }; const processEffects = (character, effects, isEquipping) => { let messages = []; effects.forEach((effect) => { if (!isEquipping) { // Снимаем эффекты switch (effect.type) { case 'damage_boost': character.force = Math.max(0, character.force - effect.amount); character.save() messages.push(`Ваш урон уменьшен на ${effect.amount}.`); break; case 'max_health_boost': character.max_hp = Math.max(1, character.max_hp - effect.amount); character.save() messages.push(`Ваше максимальное здоровье уменьшено на ${effect.amount}.`); break; case 'stamina_penalty': character.max_stamina += effect.amount; character.save() messages.push(`Ваша выносливость увеличена на ${effect.amount}.`); break; case 'intelligence_boost': character.intelligence -= Math.max(0, character.intelligence - effect.amount); character.save() messages.push(`Ваш интеллект уменьшена на ${effect.amount}.`); break; default: messages.push('Неизвестный эффект при снятии.'); } } else { // Применяем эффекты switch (effect.type) { case 'heal': character.hp = Math.min(character.max_hp, character.hp + effect.amount); character.save() messages.push(`Вы восстановили ${effect.amount} HP.`); break; case 'damage_boost': character.force += effect.amount; character.save() messages.push(`Ваша сила увеличена на ${effect.amount}.`); break; case 'max_health_boost': character.max_hp += effect.amount; character.save() messages.push(`Ваше максимальное здоровье увеличено на ${effect.amount}.`); break; case 'stamina_penalty': character.max_stamina -= effect.amount; character.save() messages.push(`Ваша выносливость уменьшена на ${effect.amount}.`); break; case 'stamina_recover': character.stamina = Math.min(character.max_stamina, character.stamina + effect.amount); character.save() messages.push(`Вы восстановили ${effect.amount} стамины.`); break; case 'intelligence_boost': character.intelligence += effect.amount; character.save() messages.push(`Ваш интеллект увеличен на ${effect.amount}.`); break; default: messages.push('Неизвестный эффект при снаряжении.'); } } }); return messages.join('\n'); }; function generateKeyboard() { const buttonsCount = 10; const buttons = []; const winButton = utils.rand(1, 10); for (let i = 1; i <= buttonsCount; i++) { if (i === winButton) { buttons.push({ text: `🔒`, callback_data: `SHOP_CASH_BREAK_SUCCESS` }); } else { buttons.push({ text: `🔒`, callback_data: `lock_${i}` }); } } const rows = []; while (buttons.length > 0) { rows.push(buttons.splice(0, 5)); // Разбиваем на подмассивы по 5 кнопок } console.log(rows) return { reply_markup: { inline_keyboard: rows, }, }; } function removeButton(ctx, buttonId) { const keyboard = ctx.update.callback_query.message.reply_markup.inline_keyboard; for (let row of keyboard) { for (let i = 0; i < row.length; i++) { if (row[i].callback_data === buttonId) { row[i].text = `🔓`; break; } } } ctx.editMessageText('Взлом замка:', Markup.inlineKeyboard(keyboard).resize()); } // Восстановление здоровья (HP) async function recoverHealth() { const characters = await CharacterModel.findAll({ where: { hp: { [Op.lt]: 100, // Восстанавливаем только тем, у кого HP меньше 100 }, }, }); for (const character of characters) { const recoveryRate = character.resilience; // Восстановление 10 HP за интервал character.hp = Math.min(character.hp + recoveryRate, 100); // Не превышаем максимум await character.save(); } } // Восстановление выносливости (стамины) async function recoverStamina() { const characters = await CharacterModel.findAll({ where: { stamina: { [Op.lt]: 100, // Восстанавливаем только тем, у кого стамина меньше 100 }, }, }); for (const character of characters) { const recoveryRate = character.endurance; // Восстановление 5 стамины за интервал character.stamina = Math.min(character.stamina + recoveryRate, 100); // Не превышаем максимум await character.save(); } } const reduceStealedCards = async () => { try { const characters = await CharacterModel.findAll(); for (const character of characters) { const stolenCards = await StolenCardsModel.findAll({ where: { userId: character.telegram_id } }); if (stolenCards.length > 0) { // Для каждой карточки уменьшаем баланс на половину for (const card of stolenCards) { card.balance = Math.ceil(card.balance / 2); // Баланс делим на 2 и округляем вверх await card.save(); // Сохраняем изменения карточки } // Удаляем половину карточек const cardsToDelete = Math.ceil(stolenCards.length / 2); const cardsToRemove = stolenCards.slice(0, cardsToDelete); for (const card of cardsToRemove) { await card.destroy(); // Удаляем карточки } console.log(`Карточки для персонажа ${character.name} успешно обновлены.`); } } console.log('Карточки всех игроков успешно обновлены.'); } catch (error) { console.error('Ошибка при обновлении карточек:', error); } }; async function generateCard(userId, balance) { if (!Number(balance)) { balance = faker.finance.amount({ dec: 0 }) * 100 console.log('Random balance: ' + balance) } else { balance = Math.floor(balance); console.log('Set balance: ' + balance) } const cardNumber = `${faker.finance.creditCardNumber()}`; // Генерация номера карты const pin = `${faker.finance.pin()}`; // Четырехзначный ПИН const cvv = `${faker.finance.creditCardCVV()}`; // Трехзначный CVV const holderName = `${faker.person.firstName()} ${faker.person.lastName()}`; // Имя и фамилия const bankName = faker.company.name(); // Название банка const expiresDate = `${Math.floor(Math.random() * 12) + 1}/${2025 + Math.floor(Math.random() * 5)}`; // Срок действия await StolenCardsModel.create({ userId, balance, cardNumber, pin, cvv, holderName, bankName, expiresDate, }); } async function Exp(ctx, character, experience) { character.exp += experience await ctx.sendMessage(`❇️ +${experience} exp.`) await character.save() for (i in expToUp) { if (character.exp >= expToUp[character.level]) { character.exp -= expToUp[character.level] character.level += 1 character.firePoints += 1 await character.save() await ctx.sendMessage(`⤴️ Уровень вашего персонажа повысился до ${character.level}!`) } } } // Периодическое выполнение задач function startRecoveryIntervals() { const interval = 60000; // 1 мин Интервал в миллисекундах (например, 1 минута) setInterval(recoverHealth, interval); setInterval(recoverStamina, interval); } // Планируем выполнение задачи каждый день в 12 ночи schedule.scheduleJob('0 0 * * *', reduceStealedCards); rpg.command('force_reduce', async (ctx) => { reduceStealedCards() return await ctx.reply(`Принудительно`) }) rpg.command('steal_card', async (ctx) => { await generateCard(ctx.from.id) ctx.reply('💳 Вы украли новую карточку! Проверьте её в вашем инвентаре.'); }); startRecoveryIntervals() const fillEnemies = async () => { const enemies = [ { name: "Бандит", description: "Обычный бандит с ножом", level: 1, hp: 50, damage: 10, loot: [1, 2], rarity: 1 }, { name: "Вор", description: "Мелкий воришка", level: 1, hp: 30, damage: 5, loot: [3], rarity: 1 }, { name: "Собака", description: "Голодная дворняга", level: 1, hp: 40, damage: 8, loot: [], rarity: 2 }, { name: "Амбал-бандит", description: "Сильный и опасный бандит", level: 3, hp: 120, damage: 25, loot: [4, 5], rarity: 3 }, ]; for (const enemy of enemies) { await Enemy.create(enemy); } console.log("Enemies filled."); }; const fillLocations = async () => { const locations = [ { name: "Заброшенный склад", description: "Темный склад с кучей мусора.", enemies: [1, 2, 3], level: 1, loot: [6, 7], rarity: 1 }, { name: "Лесопосадка", description: "Густой лес с опасными тропами.", enemies: [2, 3, 4], level: 2, loot: [8, 9], rarity: 2 }, ]; for (const location of locations) { await Location.create(location); } console.log("Locations filled."); }; const generateBattles = async () => { const locations = await Location.findAll(); for (const location of locations) { const enemies = location.enemies; const generatedEnemies = enemies.map((enemyId) => { const rarity = Math.random(); return rarity < 0.7 ? enemyId : null; // Шанс 70% для обычных врагов }).filter(Boolean); for (const enemyId of generatedEnemies) { const enemy = await Enemy.findByPk(enemyId); if (!enemy) continue; await Battle.create({ enemy: enemy.id, location: location.id, character: null, enemy_hp: enemy.hp, status: "inactive", }); } } console.log("Battles generated."); }; rpg.command("locations", async (ctx) => { const locations = await Location.findAll(); if (!locations.length) { return ctx.reply("Нет доступных локаций для исследования."); } const locationButtons = locations.map((location) => ({ text: location.name, callback_data: `viewlocation_${location.id}`, })); const keyboard = Markup.inlineKeyboard( locationButtons.map((button) => [button]) // Одна кнопка на строку ); await ctx.reply("Выберите локацию для исследования:", keyboard); }); rpg.action(/viewlocation_\d+/, async (ctx) => { const locationId = ctx.match[0].split("_")[1]; const location = await Location.findByPk(locationId); if (!location) { return ctx.reply("Локация не найдена."); } const lootItems = location.loot.length ? location.loot.map((id) => `#${id}`).join(", ") : "Нет"; const description = `🏞️ ${location.name}\n\n${location.description}\n\nУровень: ${location.level}\nДобыча: ${lootItems}\n`; const keyboard = Markup.inlineKeyboard([ [{ text: "Исследовать", callback_data: `explore_${location.id}` }], [{ text: "Охота", callback_data: `hunt_location_${location.id}` }], ]); await ctx.reply(description, keyboard); }); rpg.action(/hunt_location_\d+/, async (ctx) => { const locationId = ctx.match[0].split('_')[2]; // Получаем ID локации const location = await Location.findByPk(locationId); if (!location) { return ctx.reply("Локация не найдена."); } const enemyIds = location.enemies || []; if (enemyIds.length === 0) { return ctx.reply("В этой локации сейчас нет врагов. Попробуйте позже."); } const activeBattles = await Battle.findAll({ where: { location: location.id, status: 'inactive' }, }); if (activeBattles.length === 0) { return ctx.reply("В этой локации сейчас нет активных врагов. Попробуйте позже."); } const activeEnemyIds = activeBattles.map((battle) => battle.enemy); const enemies = await Enemy.findAll({ where: { id: activeEnemyIds } }); if (enemies.length === 0) { return ctx.reply("В этой локации сейчас нет врагов."); } const buttons = activeBattles.map((battle) => { const enemy = enemies.find((e) => e.id === battle.enemy); return { text: `${enemy.name} (HP: ${battle.enemy_hp})`, callback_data: `start_battle_${battle.id}`, }; }); const keyboard = Markup.inlineKeyboard( buttons.map((btn) => [btn]) ); await ctx.reply( `🔍 Локация: ${location.name}\n\nВыберите врага для охоты:`, keyboard ); }); rpg.action(/start_battle_\d+/, async (ctx) => { const battleId = ctx.match[0].split('_')[2]; // Получаем ID битвы const battle = await Battle.findByPk(battleId); const enemy = await Enemy.findByPk(battle.enemy); if (!battle || battle.status !== 'inactive') { return ctx.reply("Этот враг уже сражается с другим игроком или сражение завершено."); } const existingBattle = await Battle.findOne({ where: { character: ctx.from.id, status: 'active' }, }); if (existingBattle) { return ctx.reply("Вы уже участвуете в другом сражении!"); } const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character) { return ctx.reply("Ваш персонаж не найден. Создайте его, чтобы начать играть!"); } battle.status = 'active'; battle.character = ctx.from.id; await battle.save(); await startBattle(ctx, character, enemy, battle); }); const startBattle = async (ctx, character, enemy, battle) => { await ctx.reply( `⚔️ Начинается сражение!\n\nВаш противник: ${enemy.name}\n🛡️ Уровень: ${enemy.level}\n❤️ Здоровье: ${enemy.hp}\n\nВы готовы?`, Markup.inlineKeyboard([[{ text: "Атаковать", callback_data: `attack_${battle.id}` }]]) ); }; const enemyTurn = async (ctx, character, battle) => { const enemy = await Enemy.findByPk(battle.enemy); const dodgeChance = Math.min(character.resistance * 0.05, 0.5); const isDodged = Math.random() < dodgeChance; if (isDodged) { return ctx.reply("💨 Вы уклонились от атаки противника!"); } const damage = enemy.damage; character.hp -= damage; if (character.hp <= 0) { battle.status = "failed"; await battle.save(); return ctx.reply( `💔 Противник нанес вам ${damage} урона!\n\nВы потерпели поражение от ${enemy.name}.` ); } await character.save(); await ctx.reply(`💔 Противник нанес вам ${damage} урона. У вас осталось ${character.hp} HP.`); }; rpg.action(/attack_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; const battle = await Battle.findByPk(battleId); if (!battle || battle.status !== "active") { return ctx.reply("Сражение завершено или не существует."); } const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character || battle.character != ctx.from.id) { return ctx.reply("Это не ваша битва."); } const enemy = await Enemy.findByPk(battle.enemy); if (!enemy) { return ctx.reply("Противник не найден."); } // Генерация кнопок const buttons = []; const totalButtons = 10; // Всего 10 кнопок const intelligenceFactor = Math.min(character.intelligence || 1, totalButtons - 1); // Интеллект увеличивает кнопки попадания const hitDamage = () => Math.floor(Math.random() * (character.force || 10)) + 1; // Урон от силы const criticalDamage = Math.floor((character.force || 10) * 2); // Критический урон // Добавляем кнопки с попаданием for (let i = 0; i < intelligenceFactor; i++) { buttons.push({ text: `Урон: ${hitDamage()}`, callback_data: `hit_${battleId}`, }); } // Добавляем кнопку с критическим уроном buttons.push({ text: `Крит: ${criticalDamage}`, callback_data: `critical_${battleId}`, }); // Добавляем кнопки промаха while (buttons.length < totalButtons) { buttons.push({ text: "Промах", callback_data: `miss_${battleId}`, }); } // Перетасовываем кнопки и создаём клавиатуру const shuffledButtons = buttons.sort(() => Math.random() - 0.5); const keyboard = Markup.inlineKeyboard( shuffledButtons.map((btn, index) => [btn]).reduce((rows, btn, i) => { if (i % 2 === 0) rows.push([]); rows[rows.length - 1].push(btn[0]); return rows; }, []) ); // Логи битвы const logs = battle.logs || []; const logMessage = logs.slice(-5).map((log) => `• ${log}`).join("\n"); // Сообщение с информацией await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + `⚔️ Урон врага: ${enemy.damage}\n\n` + `📜 Логи битвы:\n${logMessage || "Пока ничего не произошло."}\n\n` + `🎯 Выберите цель для атаки:`, keyboard ); }); // Обработка попадания rpg.action(/hit_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; const battle = await Battle.findByPk(battleId); if (!battle || battle.status !== "active") { return ctx.reply("Сражение завершено или не существует."); } const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character || battle.character != ctx.from.id) { return ctx.reply("Это не ваша битва."); } const enemy = await Enemy.findByPk(battle.enemy); if (!enemy) { return ctx.reply("Противник не найден."); } // Урон const damage = Math.floor(Math.random() * (character.force || 10)) + 1; battle.enemy_hp -= damage; // Логи battle.logs = battle.logs || []; battle.logs.push(`Вы нанесли ${damage} урона врагу.`); // Проверка на победу if (battle.enemy_hp <= 0) { battle.status = "completed"; await battle.save(); return ctx.editMessageText(`🎉 Вы победили ${enemy.name}!`); } await battle.save(); // Обновление сообщения с новыми кнопками const buttons = generateBattleButtons(character, battle); // Генерация новых кнопок const keyboard = Markup.inlineKeyboard(buttons); const logs = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + `⚔️ Урон врага: ${enemy.damage}\n\n` + `📜 Логи битвы:\n${logs || "Пока ничего не произошло."}\n\n` + `🎯 Выберите цель для атаки:`, keyboard ); }); // Функция для генерации кнопок function generateBattleButtons(character, battle) { const buttons = []; const totalButtons = 10; const intelligenceFactor = Math.min(character.intelligence || 1, totalButtons - 1); const hitDamage = () => Math.floor(Math.random() * (character.force || 10)) + 1; const criticalDamage = Math.floor((character.force || 10) * 2); // Генерация кнопок с уроном от атаки for (let i = 0; i < intelligenceFactor; i++) { buttons.push({ text: `Урон: ${hitDamage()}`, callback_data: `hit_${battle.id}`, }); } // Генерация кнопки с критическим уроном buttons.push({ text: `Крит: ${criticalDamage}`, callback_data: `critical_${battle.id}`, }); // Заполнение оставшихся кнопок промахами while (buttons.length < totalButtons) { buttons.push({ text: "Промах", callback_data: `miss_${battle.id}`, }); } // Перетасовывание кнопок const shuffledButtons = buttons.sort(() => Math.random() - 0.5); // Разбиение кнопок на 2 ряда (5 кнопок в каждой строке) const rows = []; for (let i = 0; i < shuffledButtons.length; i += 5) { rows.push(shuffledButtons.slice(i, i + 5)); } return rows; } // Обработка критического удара rpg.action(/critical_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; const battle = await Battle.findByPk(battleId); if (!battle || battle.status !== "active") { return ctx.reply("Сражение завершено или не существует."); } const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character || battle.character != ctx.from.id) { return ctx.reply("Это не ваша битва."); } const enemy = await Enemy.findByPk(battle.enemy); if (!enemy) { return ctx.reply("Противник не найден."); } // Критический урон const damage = Math.floor((character.force || 10) * 2); battle.enemy_hp -= damage; // Логи battle.logs = battle.logs || []; battle.logs.push(`Критический удар! Вы нанесли ${damage} урона врагу.`); // Проверка на победу if (battle.enemy_hp <= 0) { battle.status = "completed"; await battle.save(); return ctx.editMessageText( `🎉 Вы победили ${enemy.name}!\n\n📜 Логи битвы:\n${battle.logs.slice(-5).map((log) => `• ${log}`).join("\n")}` ); } await battle.save(); // Генерация новых кнопок const buttons = generateBattleButtons(character, battle); const keyboard = Markup.inlineKeyboard(buttons); const logs = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + `⚔️ Урон врага: ${enemy.damage}\n\n` + `📜 Логи битвы:\n${logs || "Пока ничего не произошло."}\n\n` + `🎯 Выберите цель для атаки:`, keyboard ); }); // Обработка промаха rpg.action(/miss_\d+/, async (ctx) => { const battleId = ctx.match[0].split("_")[1]; const battle = await Battle.findByPk(battleId); if (!battle || battle.status !== "active") { return ctx.reply("Сражение завершено или не существует."); } const character = await CharacterModel.findOne({ where: { telegram_id: ctx.from.id } }); if (!character || battle.character != ctx.from.id) { return ctx.reply("Это не ваша битва."); } const enemy = await Enemy.findByPk(battle.enemy); if (!enemy) { return ctx.reply("Противник не найден."); } // Логи battle.logs = battle.logs || []; battle.logs.push("Вы промахнулись."); await enemyTurn(ctx, character, battle); await battle.save(); // Генерация новых кнопок const buttons = generateBattleButtons(character, battle); const keyboard = Markup.inlineKeyboard(buttons); const logs = battle.logs.slice(-5).map((log) => `• ${log}`).join("\n"); await ctx.editMessageText( `⚔️ Сражение с ${enemy.name}\n\n` + `❤️ Здоровье врага: ${battle.enemy_hp}/${enemy.hp}\n` + `⚔️ Урон врага: ${enemy.damage}\n\n` + `📜 Логи битвы:\n${logs || "Пока ничего не произошло."}\n\n` + `🎯 Выберите цель для атаки:`, keyboard ); }); const fillItemsTable = async () => { try { const items = []; // Генерируем 10 случайных предметов for (let i = 0; i < 10; i++) { const item = { name: fakerRU.commerce.productName(), // Название товара description: fakerRU.lorem.sentence(), // Описание товара price: fakerRU.commerce.price({ dec: 0 }), // Цена товара rarity: 1, // Редкость (от 1 до 5) type: 'other', // Тип предмета }; items.push(item); } // Вставляем все 10 предметов в таблицу await ItemsModel.bulkCreate(items); console.log('Таблица предметов успешно заполнена!'); } catch (error) { console.error('Ошибка при заполнении таблицы предметов:', error); } }; // Запускаем функцию //fillItemsTable(); module.exports = rpg;