From a246ee0ca6cb82ff71be010710c175d4702118b9 Mon Sep 17 00:00:00 2001 From: Degradin <42292046+Degradin@users.noreply.github.com> Date: Tue, 21 Jan 2025 18:38:21 +0300 Subject: [PATCH] v6 Battle Update --- rpg.js | 252 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 200 insertions(+), 52 deletions(-) diff --git a/rpg.js b/rpg.js index b752e9d..5dd5da3 100644 --- a/rpg.js +++ b/rpg.js @@ -9,9 +9,6 @@ const sequelize = require('./db'); // Подключение базы данны // Подключаем обработчики const utils = require('./utils'); const handlers = require('./handlers'); -const { keyboard } = require('telegraf/markup'); -const { parse } = require('error-stack-parser'); -const { get } = require('http'); const { phones, expToUp, @@ -1075,12 +1072,14 @@ rpg.action(/view_item_(\d+)/, async (ctx) => { // Подготовка кнопок const buttons = []; if (!item.equipped) { - buttons.push({ text: `🎯 Использовать ${item.name}`, callback_data: `use_item_${item.id}` }); + buttons.push({ text: `🎯 Использовать`, callback_data: `use_item_${item.id}` }); } if (item.equipped && item.canBeEquipped) { - buttons.push({ text: `🚫 Снять ${item.name}`, callback_data: `unequip_item_${item.id}` }); + buttons.push({ text: `🚫 Снять`, callback_data: `unequip_item_${item.id}` }); } + buttons.push({ text: `💰 Продать`, callback_data: `sell_item_${item.id}` }); + // Формируем сообщение с эффектами let effectsMessage = '*Эффекты:\n*'; if (item.effectData) { @@ -1100,7 +1099,7 @@ rpg.action(/view_item_(\d+)/, async (ctx) => { // Отправляем фото и сообщение с кнопками await ctx.replyWithPhoto({ source: imagePath }, { // Отправка фото из папки - caption: `🎒 _*${utils.escape(item.name)}*_\n\n*Тип:* ${utils.escape(getItemType(item.type))}\n*Описание:* \n**>${utils.escape(item.description)}\n*Цена:* ${utils.escape(utils.spaces(item.price))}₽\n*Снаряжаемый:* ${utils.escape(item.canBeEquipped ? 'Да' : 'Нет')}\n${item.effectData ? effectsMessage : ""}`, + caption: `🎒 _*${utils.escape(item.name)}*_\n\n*Тип:* ${utils.escape(getItemType(item.type))}\n*Описание:* \n**>${utils.escape(item.description)}\n*Цена:* ${utils.escape(utils.spaces(item.price))}Ð \\(В магазине\\)\n*Снаряжаемый:* ${utils.escape(item.canBeEquipped ? 'Да' : 'Нет')}\n${item.effectData ? effectsMessage : ""}`, parse_mode: 'MarkdownV2', // Используем MarkdownV2 для форматирования reply_markup: { inline_keyboard: [buttons] // Кнопки передаем как часть структуры inline_keyboard @@ -1112,6 +1111,36 @@ rpg.action(/view_item_(\d+)/, async (ctx) => { } }); +rpg.action(/sell_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.answerCbQuery(`${item.name} нельзя продать, пока он снаряжен.`); + } + + // Продаем предмет (обычно за треть от цены, но в зависимости от интеллекта персонажа, цена может быть выше, вплоть до 100%) + + let sellPrice = Math.round(item.price * (0.3 + character.intelligence / 100)); + character.dirtymoney += sellPrice; + await InventoryModel.destroy({ where: { id: item.id } }); + await character.save(); + + ctx.editMessageCaption(`Вы продали ${item.name} за ${utils.spaces(sellPrice)}Ð`); + + logs(ctx, "Продажа предмета", { item, sellPrice }); +}); + rpg.action(/use_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const item = await InventoryModel.findByPk(itemId); @@ -1185,38 +1214,153 @@ rpg.action(/unequip_item_(\d+)/, async (ctx) => { }); rpg.action('shop', async (ctx) => { - // Получаем текущую запись мира + // Выводим красиво кнопки разделов магазина "Оружие", "Броня", "Аксессуары" и т.д. + await ctx.editMessageText('🛒 Магазин', Markup.inlineKeyboard([ + [{ text: 'Оружие', callback_data: 'shop_weapon' }], + [{ text: 'Броня', callback_data: 'shop_armor' }], + [{ text: 'Аксессуары', callback_data: 'shop_accessory' }], + [{ text: 'Расходники', callback_data: 'shop_consumable' }], + [{ text: 'Импланты', callback_data: 'shop_implant' }], + [{ text: '🔙 В меню', callback_data: 'crime_menu' }], + ])); +}); + +rpg.action('shop_weapon', async (ctx) => { + const items = await ItemsModel.findAll({ where: { type: 'weapon' } }); 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.editMessageText('В магазине нет оружия.'); } - let message = 'Добро пожаловать в магазин! Здесь вы можете купить предметы:\n\n'; + // Проверяем есть ли найденные предметы (id) в world.itemsInCrimeShop + let itemsInCrimeShop = items.filter((item) => world.itemsInCrimeShop.includes(item.id)); - // Генерируем кнопки, каждая кнопка будет в отдельной строке - const buttons = items.map((item) => [ - Markup.button.callback(`${item.name} - ${item.price}₽`, `buy_item_${item.id}`) - ]); - buttons.push([Markup.button.callback('🔙 В меню', 'crime_menu')]); + if (itemsInCrimeShop.length === 0) { + return ctx.editMessageText('В магазине нет оружия.'); + } - // Отправляем сообщение с клавиатурой - await ctx.editMessageText(message, Markup.inlineKeyboard(buttons)); + let message = '*🛒 Оружие в магазине:*\n\n' + itemsInCrimeShop.forEach((item) => { + message += `🔹 ${item.name} - ${item.price}₽\n`; + } + ); + + await ctx.editMessageText(message, Markup.inlineKeyboard([ + itemsInCrimeShop.map((item) => Markup.button.callback(`Купить ${item.name}`, `buy_item_${item.id}`)), + [{ text: '🔙 В меню', callback_data: 'shop' }] + ])); }); +rpg.action('shop_armor', async (ctx) => { + const items = await ItemsModel.findAll({ where: { type: 'armor' } }); + const world = await WorldModel.findOne({ where: { id: 1 } }); + + if (items.length === 0) { + return ctx.editMessageText('В магазине нет брони.'); + } + + // Проверяем есть ли найденные предметы (id) в world.itemsInCrimeShop + let itemsInCrimeShop = items.filter((item) => world.itemsInCrimeShop.includes(item.id)); + + if (itemsInCrimeShop.length === 0) { + return ctx.editMessageText('В магазине нет брони.'); + } + + let message = '*🛒 Броня в магазине:*\n\n' + itemsInCrimeShop.forEach((item) => { + message += `🔹 ${item.name} - ${item.price}₽\n`; + } + ); + + await ctx.editMessageText(message, Markup.inlineKeyboard([ + itemsInCrimeShop.map((item) => Markup.button.callback(`Купить ${item.name}`, `buy_item_${item.id}`)), + [{ text: '🔙 В меню', callback_data: 'shop' }] + ])); +}); + +rpg.action('shop_accessory', async (ctx) => { + const items = await ItemsModel.findAll({ where: { type: 'accessory' } }); + const world = await WorldModel.findOne({ where: { id: 1 } }); + + if (items.length === 0) { + return ctx.editMessageText('В магазине нет аксессуаров.'); + } + + // Проверяем есть ли найденные предметы (id) в world.itemsInCrimeShop + let itemsInCrimeShop = items.filter((item) => world.itemsInCrimeShop.includes(item.id)); + + if (itemsInCrimeShop.length === 0) { + return ctx.editMessageText('В магазине нет аксессуаров.'); + } + + let message = '*🛒 Аксессуары в магазине:*\n\n' + itemsInCrimeShop.forEach((item) => { + message += `🔹 ${item.name} - ${item.price}₽\n`; + } + ); + + await ctx.editMessageText(message, Markup.inlineKeyboard([ + itemsInCrimeShop.map((item) => Markup.button.callback(`Купить ${item.name}`, `buy_item_${item.id}`)), + [{ text: '🔙 В меню', callback_data: 'shop' }] + ])); +}); + +rpg.action('shop_consumable', async (ctx) => { + const items = await ItemsModel.findAll({ where: { type: 'consumable' } }); + const world = await WorldModel.findOne({ where: { id: 1 } }); + + if (items.length === 0) { + return ctx.editMessageText('В магазине нет расходников.'); + } + + // Проверяем есть ли найденные предметы (id) в world.itemsInCrimeShop + let itemsInCrimeShop = items.filter((item) => world.itemsInCrimeShop.includes(item.id)); + + if (itemsInCrimeShop.length === 0) { + return ctx.editMessageText('В магазине нет расходников.'); + } + + let message = '*🛒 Расходники в магазине:*\n\n' + itemsInCrimeShop.forEach((item) => { + message += `🔹 ${item.name} - ${item.price}₽\n`; + } + ); + + await ctx.editMessageText(message, Markup.inlineKeyboard([ + itemsInCrimeShop.map((item) => Markup.button.callback(`Купить ${item.name}`, `buy_item_${item.id}`)), + [{ text: '🔙 В меню', callback_data: 'shop' }] + ])) +}); + +rpg.action('shop_implant', async (ctx) => { + const items = await ItemsModel.findAll({ where: { type: 'implant' } }); + const world = await WorldModel.findOne({ where: { id: 1 } }); + + if (items.length === 0) { + return ctx.editMessageText('В магазине нет имплантов.'); + } + + // Проверяем есть ли найденные предметы (id) в world.itemsInCrimeShop + let itemsInCrimeShop = items.filter((item) => world.itemsInCrimeShop.includes(item.id)); + + if (itemsInCrimeShop.length === 0) { + return ctx.editMessageText('В магазине нет имплантов.'); + } + + let message = '*🛒 Импланты в магазине:*\n\n' + itemsInCrimeShop.forEach((item) => { + message += `🔹 ${item.name} - ${item.price}₽\n`; + } + ); + + await ctx.editMessageText(message, Markup.inlineKeyboard([ + itemsInCrimeShop.map((item) => Markup.button.callback(`Купить ${item.name}`, `buy_item_${item.id}`)), + [{ text: '🔙 В меню', callback_data: 'shop' }] + ])); +}); + + rpg.action(/buy_item_(\d+)/, async (ctx) => { const itemId = parseInt(ctx.match[1], 10); const item = await ItemsModel.findByPk(itemId); @@ -1369,6 +1513,9 @@ const processEffects = (character, effects, isEquipping) => { case 'max_health_boost': character.max_hp = Math.max(1, character.max_hp - effect.amount); + if (character.hp > character.max_hp) { + character.hp = character.max_hp; + } character.save() messages.push(`Ваше максимальное здоровье уменьшено на ${effect.amount}.`); break; @@ -1441,6 +1588,7 @@ const processEffects = (character, effects, isEquipping) => { case 'max_health_boost': character.max_hp += effect.amount; + character.hp += effect.amount; character.save() messages.push(`Ваше максимальное здоровье увеличено на ${effect.amount}.`); break; @@ -1614,34 +1762,34 @@ function removeButton(ctx, buttonId) { // Восстановление здоровья (HP) async function recoverHealth() { - const characters = await CharacterModel.findAll({ - where: { - hp: { - [Op.lt]: 100, // Восстанавливаем только тем, у кого HP меньше 100 - }, - }, - }); - + const characters = await CharacterModel.findAll() +// Восстанавливаем только тем, у кого HP меньше максимума for (const character of characters) { - const recoveryRate = character.resilience; // Восстановление 10 HP за интервал - character.hp = Math.min(character.hp + recoveryRate, 100); // Не превышаем максимум + if (character.hp >= character.max_hp) { + continue; // Пропускаем, если здоровье полное + } + let recoveryRate = character.resilience; // Восстановление за интервал + if (recoveryRate <= 0) { + recoveryRate = 1; // Минимум 1 HP + } + character.hp = Math.min(character.hp + recoveryRate, character.max_hp); // Не превышаем максимум await character.save(); } } // Восстановление выносливости (стамины) async function recoverStamina() { - const characters = await CharacterModel.findAll({ - where: { - stamina: { - [Op.lt]: 100, // Восстанавливаем только тем, у кого стамина меньше 100 - }, - }, - }); - + const characters = await CharacterModel.findAll() + // Восстанавливаем только тем, у кого стамина меньше максимума for (const character of characters) { - const recoveryRate = character.endurance; // Восстановление 5 стамины за интервал - character.stamina = Math.min(character.stamina + recoveryRate, 100); // Не превышаем максимум + if (character.stamina >= character.max_stamina) { + continue; // Пропускаем, если стамина полная + } + let recoveryRate = character.endurance; // Восстановление 5 стамины за интервал + if (recoveryRate <= 0) { + recoveryRate = 1; // Минимум 1 стамина + } + character.stamina = Math.min(character.stamina + recoveryRate, character.max_stamina); // Не превышаем максимум await character.save(); } } @@ -1725,7 +1873,7 @@ async function Exp(ctx, character, experience) { // Периодическое выполнение задач function startRecoveryIntervals() { - const interval = 60000; // 1 мин Интервал в миллисекундах (например, 1 минута) + const interval = 1000; // 1 мин Интервал в миллисекундах (например, 1 минута) setInterval(recoverHealth, interval); setInterval(recoverStamina, interval); } @@ -2175,7 +2323,7 @@ rpg.action(/hit_\d+/, async (ctx) => { battle.status = "completed"; await battle.save(); logs(ctx, "Победа персонажа", { enemy, battle }); - Exp(ctx, character, enemy.level * 10) + Exp(ctx, character, enemy.level * 5) character.enemiesKilled += 1; await character.save(); let droppedItems = await dropItem(character, enemy.id); @@ -2435,7 +2583,7 @@ rpg.action(/critical_\d+/, async (ctx) => { if (battle.enemy_hp <= 0) { battle.status = "completed"; await battle.save(); - Exp(ctx, character, enemy.level * 11) + Exp(ctx, character, enemy.level * 6) character.enemiesKilled += 1; await character.save(); let droppedItems = await dropItem(character, enemy.id);