diff --git a/models/inventory.model.js b/models/inventory.model.js index 4912016..4a70e10 100644 --- a/models/inventory.model.js +++ b/models/inventory.model.js @@ -1,5 +1,6 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../db'); +const { faker } = require('@faker-js/faker'); const Inventory = sequelize.define('inventory', { id: { @@ -18,6 +19,11 @@ const Inventory = sequelize.define('inventory', { type: DataTypes.STRING, allowNull: false, }, + text_id: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: faker.string.uuid() + }, description: { type: DataTypes.TEXT, allowNull: false, @@ -42,6 +48,10 @@ const Inventory = sequelize.define('inventory', { type: DataTypes.INTEGER, allowNull: true }, // Длительность эффекта в секундах + canBeEquipped: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, equipped: { type: DataTypes.BOOLEAN, defaultValue: false diff --git a/models/items.model.js b/models/items.model.js index 7791bad..05548d0 100644 --- a/models/items.model.js +++ b/models/items.model.js @@ -1,5 +1,6 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../db'); +const { faker } = require('@faker-js/faker'); const Item = sequelize.define('item', { id: { @@ -7,6 +8,11 @@ const Item = sequelize.define('item', { autoIncrement: true, primaryKey: true, }, + text_id: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: faker.string.uuid() + }, name: { type: DataTypes.STRING, allowNull: false, @@ -35,6 +41,10 @@ const Item = sequelize.define('item', { type: DataTypes.INTEGER, allowNull: true }, // Длительность эффекта в секундах + canBeEquipped: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, }); module.exports = Item; diff --git a/models/world.model.js b/models/world.model.js index a038955..41849da 100644 --- a/models/world.model.js +++ b/models/world.model.js @@ -5,7 +5,9 @@ const World = sequelize.define('world', { id: {type: DataTypes.INTEGER, primaryKey: true, unique: true}, balance: {type: DataTypes.INTEGER, defaultValue: 50000000}, transactionfee: {type: DataTypes.INTEGER, defaultValue: 1}, - matPrice: {type: DataTypes.INTEGER, defaultValue: 100} + matPrice: {type: DataTypes.INTEGER, defaultValue: 100}, + itemsInCrimeShop: { type: DataTypes.ARRAY(DataTypes.INTEGER), defaultValue: [] } + }) module.exports = World; diff --git a/rpg.js b/rpg.js index 51c5756..cc29b36 100644 --- a/rpg.js +++ b/rpg.js @@ -92,8 +92,10 @@ rpg.action('rpg_profile', async (ctx) => { `; // Отправляем сообщение - ctx.reply(profile.trim(), Markup.inlineKeyboard([ - [Markup.button.callback('💼 Задачи', 'crime_missions')], + ctx.editMessageText(profile.trim(), Markup.inlineKeyboard([ + [Markup.button.callback('💳 Карточки', 'view_cards')], + [Markup.button.callback('🎒 Инвентарь', 'inventory')], + [Markup.button.callback('🔙 В меню', 'crime_menu')], ])); }); @@ -105,7 +107,7 @@ 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) && userId != 275416286){ + if (!usersCrimeNet.has(userId) && character.level === 1){ // Если пользователь вводит команду впервые usersCrimeNet.add(userId); const username = ctx.from.username || 'User'; @@ -171,8 +173,6 @@ rpg.hears('CampFireGG.Crime', async (ctx) => { Markup.inlineKeyboard([ [Markup.button.callback('💼 Задачи', 'crime_missions')], [Markup.button.callback('📊 Профиль', 'rpg_profile')], - [Markup.button.callback('💳 Карточки', 'view_cards')], - [Markup.button.callback('🎒 Инвентарь', 'inventory')], [Markup.button.callback('💰 Магазин', 'shop')], ]) ); @@ -241,6 +241,7 @@ rpg.action('view_cards', async (ctx) => { buttons.push([ Markup.button.callback(`💳 *${lastFourDigits}`, `brutecard_${card.id}`), //Markup.button.callback(`💰 *${lastFourDigits}`, `limitbuy_${card.id}`) + Markup.button.callback('🔙 В меню', 'crime_menu'), ]); }); @@ -374,7 +375,7 @@ rpg.action(/brutecard_/, async (ctx) => { } // Проверяем наличие "Эмулятора картридера" в инвентаре - const emulator = inventory.find((item) => item.name === 'Эмулятор картридера'); + const emulator = inventory.find((item) => item.text_id === 'cardreader_emulator'); if (!emulator) { return ctx.reply('Для запуска брутфорса вам нужен "Эмулятор картридера".'); } @@ -393,13 +394,15 @@ rpg.action(/brutecard_/, async (ctx) => { // Генерация кнопок 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: `${faker.finance.pin()}`, callback_data: `brute_fail_${cardId}` }); + buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` }); } } @@ -462,12 +465,14 @@ rpg.action(/brute_fail_/, async (ctx) => { 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: `${faker.finance.pin()}`, callback_data: `brute_fail_${cardId}` }); + buttons.push({ text: `${fakePin}`, callback_data: `brute_fail_${cardId}_${i}` }); } } @@ -501,9 +506,7 @@ rpg.action(/brute_fail_/, async (ctx) => { rpg.action(/brute_success_/, async (ctx) => { const cardId = ctx.match.input.split('_')[2]; - console.log(cardId) const card = await StolenCardsModel.findByPk(cardId); - console.log(card) if (!card) { return ctx.answerCbQuery('Карточка не найдена.'); } @@ -549,7 +552,7 @@ rpg.action('crime_missions', async (ctx) => { //[{text: 'Угон', callback_data: `WIP`}], // [{text: 'Ювелирка', callback_data: `WIP`}], //[{text: 'Банк', callback_data: `WIP`}], - [{text: '🔙 Back to Menu', callback_data: `crime_menu`}] + [{text: '🔙 В меню', callback_data: `crime_menu`}] ]), ); }); @@ -623,9 +626,7 @@ rpg.action(`PHONE`, async (ctx) => { } let randPhone = utils.rand(1,10) if (property.mobile.name) { - console.log(Math.round(phones[randPhone].price/100*70)) let dirtyMoney = Math.round(phones[randPhone].price/100*70) - console.log(character.intelligence + 10) Exp(ctx, character, character.intelligence + 10) character.dirtymoney += dirtyMoney return await ctx.reply(`Вы сбыли украденный ${phones[randPhone].name} за Ð${utils.spaces(dirtyMoney)}`) @@ -726,8 +727,8 @@ rpg.action('crime_menu', async (ctx) => { `💻 CampFireGG.Crime Menu`, Markup.inlineKeyboard([ [Markup.button.callback('💼 Задачи', 'crime_missions')], - [Markup.button.callback('📊 Профиль', 'crime_stats')], - [Markup.button.callback('💰 ...', 'crime_market')], + [Markup.button.callback('📊 Профиль', 'rpg_profile')], + [Markup.button.callback('💰 Магазин', 'shop')], ]) ); }); @@ -885,7 +886,7 @@ rpg.action('inventory', async (ctx) => { const unequippedItems = inventory.filter((item) => !item.equipped); if (equippedItems.length > 0) { - message += '🛡️ *Снаряженные предметы:*\n'; + message += '🛡️ Снаряженные предметы:\n'; equippedItems.forEach((item) => { message += `- ${item.name} (${item.type})\n`; }); @@ -893,7 +894,7 @@ rpg.action('inventory', async (ctx) => { } if (unequippedItems.length > 0) { - message += '🎒 *Предметы в инвентаре:*\n'; + message += '🎒 Предметы в инвентаре:\n'; unequippedItems.forEach((item) => { message += `- ${item.name} (${item.type})\n`; }); @@ -903,20 +904,50 @@ rpg.action('inventory', async (ctx) => { // Кнопки для взаимодействия const buttons = [ ...unequippedItems.map((item) => - Markup.button.callback(`🎯 Использовать ${item.name}`, `use_item_${item.id}`) + 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.replyWithMarkdown(message, Markup.inlineKeyboard(buttons, { columns: 2 })); + 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); @@ -936,17 +967,25 @@ rpg.action(/use_item_(\d+)/, async (ctx) => { 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.reply(resultMessages); + await ctx.answerCbQuery(resultMessages, {show_alert: true}); } - // Снаряжаем предмет - item.equipped = true; - await item.save(); - - ctx.reply(`Вы успешно снарядили ${item.name}.`); + 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) => { @@ -955,47 +994,65 @@ rpg.action(/unequip_item_(\d+)/, async (ctx) => { const item = await InventoryModel.findByPk(itemId); if (!item || item.telegram_id != ctx.from.id) { - return ctx.reply('Этот предмет не найден в вашем инвентаре.'); + return ctx.answerCbQuery('Этот предмет не найден в вашем инвентаре.'); } if (!item.equipped) { - return ctx.reply(`${item.name} не снаряжен.`); + return ctx.answerCbQuery(`${item.name} не снаряжен.`); } if (item.effectData) { const resultMessages = processEffects(character, item.effectData, false); - await ctx.reply(resultMessages); + await ctx.editMessageText(resultMessages); } item.equipped = false; await item.save(); - ctx.reply(`Вы успешно сняли ${item.name}.`); + ctx.answerCbQuery(`Вы успешно сняли ${item.name}.`); }); - rpg.action('shop', async (ctx) => { - const items = await ItemsModel.findAll(); + // Получаем текущую запись мира + 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('Магазин пуст!'); + return ctx.reply('Магазин пуст!'); } let message = 'Добро пожаловать в магазин! Здесь вы можете купить предметы:\n\n'; // Генерируем кнопки, каждая кнопка будет в отдельной строке const buttons = items.map((item) => [ - Markup.button.callback(`${item.name} - ${item.price}₽`, `buy_item_${item.id}`) + Markup.button.callback(`${item.name} - ${item.price}₽`, `buy_item_${item.id}`) ]); + buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]); // Отправляем сообщение с клавиатурой - await ctx.reply(message, Markup.inlineKeyboard(buttons)); + 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('Предмет не найден.'); @@ -1008,27 +1065,29 @@ rpg.action(/buy_item_(\d+)/, async (ctx) => { } if (character.balance < item.price) { - return ctx.reply('У вас недостаточно средств для покупки этого предмета.'); + return ctx.answerCbQuery('У вас недостаточно средств для покупки этого предмета.', {show_alert: true}); } // Снимаем деньги с баланса - character.balance -= item.price; + 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, - equipped: false, + canBeEquipped: item.canBeEquipped, + equipped: false }); await character.save(); - ctx.reply(`Вы успешно купили ${item.name} за ${item.price}₽!`); + ctx.answerCbQuery(`Вы успешно купили ${item.name} за ${item.price}₽!`, {show_alert: true}); }); //////////////////////////////////////////////////////////////////////////////////////////// @@ -1111,6 +1170,12 @@ const processEffects = (character, effects, isEquipping) => { 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('Неизвестный эффект при снятии.'); } @@ -1126,7 +1191,7 @@ const processEffects = (character, effects, isEquipping) => { case 'damage_boost': character.force += effect.amount; character.save() - messages.push(`Ваш урон увеличен на ${effect.amount}.`); + messages.push(`Ваша сила увеличена на ${effect.amount}.`); break; case 'max_health_boost': @@ -1141,6 +1206,18 @@ const processEffects = (character, effects, isEquipping) => { 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('Неизвестный эффект при снаряжении.'); } @@ -1248,7 +1325,7 @@ const reduceStealedCards = async () => { await card.destroy(); // Удаляем карточки } - console.log(`Карточки для персонажа ${character.id} успешно обновлены.`); + console.log(`Карточки для персонажа ${character.name} успешно обновлены.`); } } @@ -1260,7 +1337,6 @@ const reduceStealedCards = async () => { async function generateCard(userId, balance) { - console.log(balance) if(!Number(balance)){ balance = faker.finance.amount({dec: 0})*100 console.log('Random balance: ' + balance) diff --git a/signs b/signs new file mode 100644 index 0000000..61b3e67 --- /dev/null +++ b/signs @@ -0,0 +1,15 @@ +Ð +💳 +💼 +📊 +💰 +📛 +⏏️ +🛡️ +🎒 +🎯 +🚫 +📜 +ℹ️ +〰️ +🔤 \ No newline at end of file